1/* $NetBSD: main.c,v 1.31 2012/03/20 20:34:59 matt Exp $ */ 2 3/* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__COPYRIGHT("@(#) Copyright (c) 1983, 1993\ 35 The Regents of the University of California. All rights reserved."); 36#if 0 37static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 38#else 39__RCSID("$NetBSD: main.c,v 1.31 2012/03/20 20:34:59 matt Exp $"); 40#endif 41#endif /* not lint */ 42 43/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 44 45/* 46 * TFTP User Program -- Command Interface. 47 */ 48#include <sys/types.h> 49#include <sys/param.h> 50#include <sys/socket.h> 51 52#include <netinet/in.h> 53 54#include <arpa/inet.h> 55#include <arpa/tftp.h> 56 57#include <ctype.h> 58#include <fcntl.h> 59#include <err.h> 60#include <errno.h> 61#include <netdb.h> 62#include <setjmp.h> 63#include <signal.h> 64#include <stdio.h> 65#include <stdlib.h> 66#include <string.h> 67#include <unistd.h> 68 69#include "extern.h" 70 71#define TIMEOUT 5 /* secs between rexmt's */ 72#define LBUFLEN 200 /* size of input buffer */ 73 74struct sockaddr_storage peeraddr; 75int f; 76int mf; 77int trace; 78int verbose; 79int tsize=0; 80int tout=0; 81size_t def_blksize = SEGSIZE; 82size_t blksize = SEGSIZE; 83in_addr_t mcaddr = INADDR_NONE; 84uint16_t mcport; 85u_int def_rexmtval = TIMEOUT; 86u_int rexmtval = TIMEOUT; 87ushort mcmasterslave; 88int maxtimeout = 5 * TIMEOUT; 89 90jmp_buf toplevel; 91 92static int connected; 93static char mode[32]; 94static char line[LBUFLEN]; 95static int margc; 96static char *margv[20]; 97static const char *prompt = "tftp"; 98static char hostname[MAXHOSTNAMELEN]; 99 100static void get(int, char **); 101static void help(int, char **); 102static void modecmd(int, char **); 103static void put(int, char **); 104static __dead void quit(int, char **); 105static void setascii(int, char **); 106static void setbinary(int, char **); 107static void setpeer0(const char *, const char *); 108static void setpeer(int, char **); 109static void setrexmt(int, char **); 110static void settimeout(int, char **); 111static void settrace(int, char **); 112static void setverbose(int, char **); 113static void setblksize(int, char **); 114static void settsize(int, char **); 115static void settimeoutopt(int, char **); 116static void status(int, char **); 117static char *tail(char *); 118static __dead void intr(int); 119static const struct cmd *getcmd(const char *); 120 121static __dead void command(void); 122 123static void getUsage(char *); 124static void makeargv(void); 125static void putUsage(const char *); 126static void settftpmode(const char *); 127 128#define HELPINDENT sizeof("connect") 129 130struct cmd { 131 const char *name; 132 const char *help; 133 void (*handler)(int, char **); 134}; 135 136static const char vhelp[] = "toggle verbose mode"; 137static const char thelp[] = "toggle packet tracing"; 138static const char tshelp[] = "toggle extended tsize option"; 139static const char tohelp[] = "toggle extended timeout option"; 140static const char blhelp[] = "set an alternative blocksize (def. 512)"; 141static const char chelp[] = "connect to remote tftp"; 142static const char qhelp[] = "exit tftp"; 143static const char hhelp[] = "print help information"; 144static const char shelp[] = "send file"; 145static const char rhelp[] = "receive file"; 146static const char mhelp[] = "set file transfer mode"; 147static const char sthelp[] = "show current status"; 148static const char xhelp[] = "set per-packet retransmission timeout"; 149static const char ihelp[] = "set total retransmission timeout"; 150static const char ashelp[] = "set mode to netascii"; 151static const char bnhelp[] = "set mode to octet"; 152 153static const struct cmd cmdtab[] = { 154 { "connect", chelp, setpeer }, 155 { "mode", mhelp, modecmd }, 156 { "put", shelp, put }, 157 { "get", rhelp, get }, 158 { "quit", qhelp, quit }, 159 { "verbose", vhelp, setverbose }, 160 { "blksize", blhelp, setblksize }, 161 { "tsize", tshelp, settsize }, 162 { "trace", thelp, settrace }, 163 { "status", sthelp, status }, 164 { "binary", bnhelp, setbinary }, 165 { "ascii", ashelp, setascii }, 166 { "rexmt", xhelp, setrexmt }, 167 { "timeout", ihelp, settimeout }, 168 { "tout", tohelp, settimeoutopt }, 169 { "?", hhelp, help }, 170 { .name = NULL } 171}; 172 173static struct modes { 174 const char *m_name; 175 const char *m_mode; 176} modes[] = { 177 { "ascii", "netascii" }, 178 { "netascii", "netascii" }, 179 { "binary", "octet" }, 180 { "image", "octet" }, 181 { "octet", "octet" }, 182/* { "mail", "mail" }, */ 183 { 0, 0 } 184}; 185 186int 187main(int argc, char *argv[]) 188{ 189 int c; 190 191 f = mf = -1; 192 (void)strlcpy(mode, "netascii", sizeof(mode)); 193 (void)signal(SIGINT, intr); 194 195 setprogname(argv[0]); 196 while ((c = getopt(argc, argv, "e")) != -1) { 197 switch (c) { 198 case 'e': 199 blksize = MAXSEGSIZE; 200 (void)strlcpy(mode, "octet", sizeof(mode)); 201 tsize = 1; 202 tout = 1; 203 break; 204 default: 205 (void)fprintf(stderr, 206 "Usage: %s [-e] host-name [port]\n", getprogname()); 207 exit(1); 208 } 209 } 210 argc -= optind; 211 argv += optind; 212 213 if (argc >= 1) { 214 if (setjmp(toplevel) != 0) 215 exit(0); 216 argc++; 217 argv--; 218 setpeer(argc, argv); 219 } 220 if (setjmp(toplevel) != 0) 221 (void)putchar('\n'); 222 command(); 223 return 0; 224} 225 226static void 227getmore(const char *cmd, const char *prm) 228{ 229 (void)strlcpy(line, cmd, sizeof(line)); 230 (void)printf("%s", prm); 231 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin); 232 makeargv(); 233} 234 235static void 236setpeer0(const char *host, const char *port) 237{ 238 struct addrinfo hints, *res0, *res; 239 int error, soopt; 240 struct sockaddr_storage ss; 241 const char *cause = "unknown"; 242 243 if (connected) { 244 (void)close(f); 245 f = -1; 246 } 247 connected = 0; 248 249 (void)memset(&hints, 0, sizeof(hints)); 250 hints.ai_family = PF_UNSPEC; 251 hints.ai_socktype = SOCK_DGRAM; 252 hints.ai_protocol = IPPROTO_UDP; 253 hints.ai_flags = AI_CANONNAME; 254 if (!port) 255 port = "tftp"; 256 error = getaddrinfo(host, port, &hints, &res0); 257 if (error) { 258 warnx("%s", gai_strerror(error)); 259 return; 260 } 261 262 for (res = res0; res; res = res->ai_next) { 263 if (res->ai_addrlen > sizeof(peeraddr)) 264 continue; 265 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 266 if (f == -1) { 267 cause = "socket"; 268 continue; 269 } 270 271 (void)memset(&ss, 0, sizeof(ss)); 272 ss.ss_family = res->ai_family; 273 ss.ss_len = res->ai_addrlen; 274 if (bind(f, (struct sockaddr *)(void *)&ss, 275 (socklen_t)ss.ss_len) == -1) { 276 cause = "bind"; 277 (void)close(f); 278 f = -1; 279 continue; 280 } 281 282 break; 283 } 284 285 if (f >= 0) { 286 soopt = 65536; 287 if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt)) 288 == -1) { 289 (void)close(f); 290 f = -1; 291 cause = "setsockopt SNDBUF"; 292 } 293 else if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, 294 sizeof(soopt)) == -1) { 295 (void)close(f); 296 f = -1; 297 cause = "setsockopt RCVBUF"; 298 } 299 } 300 301 if (f == -1 || res == NULL) 302 warn("%s", cause); 303 else { 304 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 305 (void)memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); 306 if (res->ai_canonname) { 307 (void)strlcpy(hostname, res->ai_canonname, 308 sizeof(hostname)); 309 } else 310 (void)strlcpy(hostname, host, sizeof(hostname)); 311 connected = 1; 312 } 313 314 freeaddrinfo(res0); 315} 316 317static void 318setpeer(int argc, char *argv[]) 319{ 320 321 if (argc < 2) { 322 getmore("Connect ", "(to) "); 323 argc = margc; 324 argv = margv; 325 } 326 if (argc < 2 || argc > 3) { 327 (void)printf("Usage: %s [-e] host-name [port]\n", 328 getprogname()); 329 return; 330 } 331 if (argc == 2) 332 setpeer0(argv[1], NULL); 333 else 334 setpeer0(argv[1], argv[2]); 335} 336 337static void 338modecmd(int argc, char *argv[]) 339{ 340 struct modes *p; 341 const char *sep; 342 343 if (argc < 2) { 344 (void)printf("Using %s mode to transfer files.\n", mode); 345 return; 346 } 347 if (argc == 2) { 348 for (p = modes; p->m_name; p++) 349 if (strcmp(argv[1], p->m_name) == 0) 350 break; 351 if (p->m_name) { 352 settftpmode(p->m_mode); 353 return; 354 } 355 (void)printf("%s: unknown mode\n", argv[1]); 356 /* drop through and print Usage message */ 357 } 358 359 (void)printf("Usage: %s [", argv[0]); 360 sep = " "; 361 for (p = modes; p->m_name; p++) { 362 (void)printf("%s%s", sep, p->m_name); 363 if (*sep == ' ') 364 sep = " | "; 365 } 366 (void)printf(" ]\n"); 367 return; 368} 369 370static void 371/*ARGSUSED*/ 372setbinary(int argc, char *argv[]) 373{ 374 375 settftpmode("octet"); 376} 377 378static void 379/*ARGSUSED*/ 380setascii(int argc, char *argv[]) 381{ 382 383 settftpmode("netascii"); 384} 385 386static void 387settftpmode(const char *newmode) 388{ 389 (void)strlcpy(mode, newmode, sizeof(mode)); 390 if (verbose) 391 (void)printf("mode set to %s\n", mode); 392} 393 394 395/* 396 * Send file(s). 397 */ 398static void 399put(int argc, char *argv[]) 400{ 401 int fd; 402 int n; 403 char *targ, *p; 404 405 if (argc < 2) { 406 getmore("send ", "(file) "); 407 argc = margc; 408 argv = margv; 409 } 410 if (argc < 2) { 411 putUsage(argv[0]); 412 return; 413 } 414 targ = argv[argc - 1]; 415 if (strrchr(argv[argc - 1], ':')) { 416 char *cp; 417 418 for (n = 1; n < argc - 1; n++) 419 if (strchr(argv[n], ':')) { 420 putUsage(argv[0]); 421 return; 422 } 423 cp = argv[argc - 1]; 424 targ = strrchr(cp, ':'); 425 *targ++ = 0; 426 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 427 cp[strlen(cp) - 1] = '\0'; 428 cp++; 429 } 430 setpeer0(cp, NULL); 431 } 432 if (!connected) { 433 (void)printf("No target machine specified.\n"); 434 return; 435 } 436 if (argc < 4) { 437 char *cp = argc == 2 ? tail(targ) : argv[1]; 438 fd = open(cp, O_RDONLY); 439 if (fd == -1) { 440 warn("%s", cp); 441 return; 442 } 443 if (verbose) 444 (void)printf("putting %s to %s:%s [%s]\n", 445 cp, hostname, targ, mode); 446 sendfile(fd, targ, mode); 447 return; 448 } 449 /* this assumes the target is a directory */ 450 /* on a remote unix system. hmmmm. */ 451 p = strchr(targ, '\0'); 452 *p++ = '/'; 453 for (n = 1; n < argc - 1; n++) { 454 (void)strcpy(p, tail(argv[n])); 455 fd = open(argv[n], O_RDONLY); 456 if (fd == -1) { 457 warn("%s", argv[n]); 458 continue; 459 } 460 if (verbose) 461 (void)printf("putting %s to %s:%s [%s]\n", 462 argv[n], hostname, targ, mode); 463 sendfile(fd, targ, mode); 464 } 465} 466 467static void 468putUsage(const char *s) 469{ 470 (void)printf("Usage: %s file ... host:target, or\n", s); 471 (void)printf(" %s file ... target (when already connected)\n", s); 472} 473 474/* 475 * Receive file(s). 476 */ 477static void 478get(int argc, char *argv[]) 479{ 480 int fd; 481 int n; 482 char *p; 483 char *src; 484 485 if (argc < 2) { 486 getmore("get ", "(files) "); 487 argc = margc; 488 argv = margv; 489 } 490 if (argc < 2) { 491 getUsage(argv[0]); 492 return; 493 } 494 if (!connected) { 495 for (n = 1; n < argc ; n++) 496 if (strrchr(argv[n], ':') == 0) { 497 getUsage(argv[0]); 498 return; 499 } 500 } 501 for (n = 1; n < argc ; n++) { 502 src = strrchr(argv[n], ':'); 503 if (src == NULL) 504 src = argv[n]; 505 else { 506 char *cp; 507 *src++ = 0; 508 cp = argv[n]; 509 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 510 cp[strlen(cp) - 1] = '\0'; 511 cp++; 512 } 513 setpeer0(cp, NULL); 514 if (!connected) 515 continue; 516 } 517 if (argc < 4) { 518 char *cp = argc == 3 ? argv[2] : tail(src); 519 fd = creat(cp, 0644); 520 if (fd == -1) { 521 warn("%s", cp); 522 return; 523 } 524 if (verbose) 525 (void)printf("getting from %s:%s to %s [%s]\n", 526 hostname, src, cp, mode); 527 recvfile(fd, src, mode); 528 break; 529 } 530 p = tail(src); /* new .. jdg */ 531 fd = creat(p, 0644); 532 if (fd == -1) { 533 warn("%s", p); 534 continue; 535 } 536 if (verbose) 537 (void)printf("getting from %s:%s to %s [%s]\n", 538 hostname, src, p, mode); 539 recvfile(fd, src, mode); 540 } 541} 542 543static void 544getUsage(char *s) 545{ 546 (void)printf("Usage: %s host:file host:file ... file, or\n", s); 547 (void)printf(" %s file file ... file if connected\n", s); 548} 549 550void 551setblksize(int argc, char *argv[]) 552{ 553 int t; 554 555 if (argc < 2) { 556 getmore("blksize ", "(blksize) "); 557 argc = margc; 558 argv = margv; 559 } 560 if (argc != 2) { 561 (void)printf("Usage: %s value\n", argv[0]); 562 return; 563 } 564 t = atoi(argv[1]); 565 if (t < 8 || t > 65464) 566 (void)printf("%s: bad value\n", argv[1]); 567 else 568 blksize = t; 569} 570 571static void 572setrexmt(int argc, char *argv[]) 573{ 574 int t; 575 576 if (argc < 2) { 577 getmore("Rexmt-timeout ", "(value) "); 578 argc = margc; 579 argv = margv; 580 } 581 if (argc != 2) { 582 (void)printf("Usage: %s value\n", argv[0]); 583 return; 584 } 585 t = atoi(argv[1]); 586 if (t < 0) 587 (void)printf("%s: bad value\n", argv[1]); 588 else 589 rexmtval = t; 590} 591 592static void 593settimeout(int argc, char *argv[]) 594{ 595 int t; 596 597 if (argc < 2) { 598 getmore("Maximum-timeout ", "(value) "); 599 argc = margc; 600 argv = margv; 601 } 602 if (argc != 2) { 603 (void)printf("Usage: %s value\n", argv[0]); 604 return; 605 } 606 t = atoi(argv[1]); 607 if (t < 0) 608 (void)printf("%s: bad value\n", argv[1]); 609 else 610 maxtimeout = t; 611} 612 613static void 614/*ARGSUSED*/ 615status(int argc, char *argv[]) 616{ 617 if (connected) 618 (void)printf("Connected to %s.\n", hostname); 619 else 620 (void)printf("Not connected.\n"); 621 (void)printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 622 verbose ? "on" : "off", trace ? "on" : "off"); 623 (void)printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 624 rexmtval, maxtimeout); 625} 626 627static void 628/*ARGSUSED*/ 629intr(int dummy) 630{ 631 632 (void)signal(SIGALRM, SIG_IGN); 633 (void)alarm(0); 634 longjmp(toplevel, -1); 635} 636 637static char * 638tail(char *filename) 639{ 640 char *s; 641 642 while (*filename) { 643 s = strrchr(filename, '/'); 644 if (s == NULL) 645 break; 646 if (s[1]) 647 return s + 1; 648 *s = '\0'; 649 } 650 return filename; 651} 652 653/* 654 * Command parser. 655 */ 656static __dead void 657command(void) 658{ 659 const struct cmd *c; 660 661 for (;;) { 662 (void)printf("%s> ", prompt); 663 if (fgets(line, LBUFLEN, stdin) == NULL) { 664 if (feof(stdin)) { 665 exit(0); 666 } else { 667 continue; 668 } 669 } 670 if (line[0] == '\0' || line[0] == '\n') 671 continue; 672 makeargv(); 673 if (margc == 0) 674 continue; 675 c = getcmd(margv[0]); 676 if (c == (struct cmd *)-1) { 677 (void)printf("?Ambiguous command\n"); 678 continue; 679 } 680 if (c == 0) { 681 (void)printf("?Invalid command\n"); 682 continue; 683 } 684 (*c->handler)(margc, margv); 685 } 686} 687 688static const struct cmd * 689getcmd(const char *name) 690{ 691 const char *p, *q; 692 const struct cmd *c, *found; 693 int nmatches, longest; 694 695 longest = 0; 696 nmatches = 0; 697 found = 0; 698 for (c = cmdtab; (p = c->name) != NULL; c++) { 699 for (q = name; *q == *p++; q++) 700 if (*q == 0) /* exact match? */ 701 return c; 702 if (!*q) { /* the name was a prefix */ 703 if (q - name > longest) { 704 longest = q - name; 705 nmatches = 1; 706 found = c; 707 } else if (q - name == longest) 708 nmatches++; 709 } 710 } 711 if (nmatches > 1) 712 return (struct cmd *)-1; 713 return found; 714} 715 716/* 717 * Slice a string up into argc/argv. 718 */ 719static void 720makeargv(void) 721{ 722 char *cp; 723 char **argp = margv; 724 725 margc = 0; 726 for (cp = line; *cp;) { 727 while (isspace((unsigned char)*cp)) 728 cp++; 729 if (*cp == '\0') 730 break; 731 *argp++ = cp; 732 margc += 1; 733 while (*cp != '\0' && !isspace((unsigned char)*cp)) 734 cp++; 735 if (*cp == '\0') 736 break; 737 *cp++ = '\0'; 738 } 739 *argp++ = NULL; 740} 741 742static void 743/*ARGSUSED*/ 744quit(int argc, char *argv[]) 745{ 746 747 exit(0); 748} 749 750/* 751 * Help command. 752 */ 753static void 754help(int argc, char *argv[]) 755{ 756 const struct cmd *c; 757 758 if (argc == 1) { 759 (void)printf("Commands may be abbreviated. Commands are:\n\n"); 760 for (c = cmdtab; c->name; c++) 761 (void)printf("%-*s\t%s\n", (int)HELPINDENT, c->name, 762 c->help); 763 return; 764 } 765 while (--argc > 0) { 766 char *arg; 767 arg = *++argv; 768 c = getcmd(arg); 769 if (c == (struct cmd *)-1) 770 (void)printf("?Ambiguous help command %s\n", arg); 771 else if (c == NULL) 772 (void)printf("?Invalid help command %s\n", arg); 773 else 774 (void)printf("%s\n", c->help); 775 } 776} 777 778static void 779/*ARGSUSED*/ 780settrace(int argc, char **argv) 781{ 782 trace = !trace; 783 (void)printf("Packet tracing %s.\n", trace ? "on" : "off"); 784} 785 786static void 787/*ARGSUSED*/ 788setverbose(int argc, char **argv) 789{ 790 verbose = !verbose; 791 (void)printf("Verbose mode %s.\n", verbose ? "on" : "off"); 792} 793 794static void 795/*ARGSUSED*/ 796settsize(int argc, char **argv) 797{ 798 tsize = !tsize; 799 (void)printf("Tsize mode %s.\n", tsize ? "on" : "off"); 800} 801 802static void 803/*ARGSUSED*/ 804settimeoutopt(int argc, char **argv) 805{ 806 tout = !tout; 807 (void)printf("Timeout option %s.\n", tout ? "on" : "off"); 808} 809