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