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