1 /* $NetBSD: main.c,v 1.8 1997/10/07 09:19:42 mrg 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.8 1997/10/07 09:19:42 mrg 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 #include <sys/file.h> 55 56 #include <netinet/in.h> 57 58 #include <arpa/inet.h> 59 60 #include <ctype.h> 61 #include <errno.h> 62 #include <netdb.h> 63 #include <setjmp.h> 64 #include <signal.h> 65 #include <stdio.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <unistd.h> 69 70 #include "extern.h" 71 72 #define TIMEOUT 5 /* secs between rexmt's */ 73 #define LBUFLEN 200 /* size of input buffer */ 74 75 struct sockaddr_in peeraddr; 76 int f; 77 short port; 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 struct servent *sp; 88 89 void get __P((int, char **)); 90 void help __P((int, char **)); 91 void modecmd __P((int, char **)); 92 void put __P((int, char **)); 93 void quit __P((int, char **)); 94 void setascii __P((int, char **)); 95 void setbinary __P((int, 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 struct sockaddr_in s_in; 159 160 sp = getservbyname("tftp", "udp"); 161 if (sp == 0) { 162 fprintf(stderr, "tftp: udp/tftp: unknown service\n"); 163 exit(1); 164 } 165 f = socket(AF_INET, SOCK_DGRAM, 0); 166 if (f < 0) { 167 perror("tftp: socket"); 168 exit(3); 169 } 170 bzero((char *)&s_in, sizeof (s_in)); 171 s_in.sin_family = AF_INET; 172 if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { 173 perror("tftp: bind"); 174 exit(1); 175 } 176 strcpy(mode, "netascii"); 177 signal(SIGINT, intr); 178 if (argc > 1) { 179 if (setjmp(toplevel) != 0) 180 exit(0); 181 setpeer(argc, argv); 182 } 183 if (setjmp(toplevel) != 0) 184 (void)putchar('\n'); 185 command(); 186 return (0); 187 } 188 189 char hostname[100]; 190 191 void 192 setpeer(argc, argv) 193 int argc; 194 char *argv[]; 195 { 196 struct hostent *host; 197 198 if (argc < 2) { 199 strcpy(line, "Connect "); 200 printf("(to) "); 201 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 202 makeargv(); 203 argc = margc; 204 argv = margv; 205 } 206 if ((argc < 2) || (argc > 3)) { 207 printf("usage: %s host-name [port]\n", argv[0]); 208 return; 209 } 210 if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) { 211 peeraddr.sin_family = AF_INET; 212 (void) strcpy(hostname, argv[1]); 213 } else { 214 host = gethostbyname(argv[1]); 215 if (host == 0) { 216 connected = 0; 217 printf("%s: unknown host\n", argv[1]); 218 return; 219 } 220 peeraddr.sin_family = host->h_addrtype; 221 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); 222 (void) strncpy(hostname, host->h_name, sizeof(hostname)); 223 hostname[sizeof(hostname)-1] = 0; 224 } 225 port = sp->s_port; 226 if (argc == 3) { 227 port = atoi(argv[2]); 228 if (port < 0) { 229 printf("%s: bad port number\n", argv[2]); 230 connected = 0; 231 return; 232 } 233 port = htons(port); 234 } 235 connected = 1; 236 } 237 238 struct modes { 239 char *m_name; 240 char *m_mode; 241 } modes[] = { 242 { "ascii", "netascii" }, 243 { "netascii", "netascii" }, 244 { "binary", "octet" }, 245 { "image", "octet" }, 246 { "octet", "octet" }, 247 /* { "mail", "mail" }, */ 248 { 0, 0 } 249 }; 250 251 void 252 modecmd(argc, argv) 253 int argc; 254 char *argv[]; 255 { 256 register struct modes *p; 257 char *sep; 258 259 if (argc < 2) { 260 printf("Using %s mode to transfer files.\n", mode); 261 return; 262 } 263 if (argc == 2) { 264 for (p = modes; p->m_name; p++) 265 if (strcmp(argv[1], p->m_name) == 0) 266 break; 267 if (p->m_name) { 268 settftpmode(p->m_mode); 269 return; 270 } 271 printf("%s: unknown mode\n", argv[1]); 272 /* drop through and print usage message */ 273 } 274 275 printf("usage: %s [", argv[0]); 276 sep = " "; 277 for (p = modes; p->m_name; p++) { 278 printf("%s%s", sep, p->m_name); 279 if (*sep == ' ') 280 sep = " | "; 281 } 282 printf(" ]\n"); 283 return; 284 } 285 286 void 287 setbinary(argc, argv) 288 int argc; 289 char *argv[]; 290 { 291 292 settftpmode("octet"); 293 } 294 295 void 296 setascii(argc, argv) 297 int argc; 298 char *argv[]; 299 { 300 301 settftpmode("netascii"); 302 } 303 304 static void 305 settftpmode(newmode) 306 char *newmode; 307 { 308 strcpy(mode, newmode); 309 if (verbose) 310 printf("mode set to %s\n", mode); 311 } 312 313 314 /* 315 * Send file(s). 316 */ 317 void 318 put(argc, argv) 319 int argc; 320 char *argv[]; 321 { 322 int fd; 323 register int n; 324 register char *cp, *targ; 325 326 if (argc < 2) { 327 strcpy(line, "send "); 328 printf("(file) "); 329 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 330 makeargv(); 331 argc = margc; 332 argv = margv; 333 } 334 if (argc < 2) { 335 putusage(argv[0]); 336 return; 337 } 338 targ = argv[argc - 1]; 339 if (index(argv[argc - 1], ':')) { 340 char *cp; 341 struct hostent *hp; 342 343 for (n = 1; n < argc - 1; n++) 344 if (index(argv[n], ':')) { 345 putusage(argv[0]); 346 return; 347 } 348 cp = argv[argc - 1]; 349 targ = index(cp, ':'); 350 *targ++ = 0; 351 hp = gethostbyname(cp); 352 if (hp == NULL) { 353 fprintf(stderr, "tftp: %s: ", cp); 354 herror((char *)NULL); 355 return; 356 } 357 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length); 358 peeraddr.sin_family = hp->h_addrtype; 359 connected = 1; 360 strncpy(hostname, hp->h_name, sizeof(hostname)); 361 hostname[sizeof(hostname)-1] = 0; 362 } 363 if (!connected) { 364 printf("No target machine specified.\n"); 365 return; 366 } 367 if (argc < 4) { 368 cp = argc == 2 ? tail(targ) : argv[1]; 369 fd = open(cp, O_RDONLY); 370 if (fd < 0) { 371 fprintf(stderr, "tftp: "); perror(cp); 372 return; 373 } 374 if (verbose) 375 printf("putting %s to %s:%s [%s]\n", 376 cp, hostname, targ, mode); 377 peeraddr.sin_port = port; 378 sendfile(fd, targ, mode); 379 return; 380 } 381 /* this assumes the target is a directory */ 382 /* on a remote unix system. hmmmm. */ 383 cp = index(targ, '\0'); 384 *cp++ = '/'; 385 for (n = 1; n < argc - 1; n++) { 386 strcpy(cp, tail(argv[n])); 387 fd = open(argv[n], O_RDONLY); 388 if (fd < 0) { 389 fprintf(stderr, "tftp: "); perror(argv[n]); 390 continue; 391 } 392 if (verbose) 393 printf("putting %s to %s:%s [%s]\n", 394 argv[n], hostname, targ, mode); 395 peeraddr.sin_port = port; 396 sendfile(fd, targ, mode); 397 } 398 } 399 400 static void 401 putusage(s) 402 char *s; 403 { 404 printf("usage: %s file ... host:target, or\n", s); 405 printf(" %s file ... target (when already connected)\n", s); 406 } 407 408 /* 409 * Receive file(s). 410 */ 411 void 412 get(argc, argv) 413 int argc; 414 char *argv[]; 415 { 416 int fd; 417 register int n; 418 register char *cp; 419 char *src; 420 421 if (argc < 2) { 422 strcpy(line, "get "); 423 printf("(files) "); 424 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 425 makeargv(); 426 argc = margc; 427 argv = margv; 428 } 429 if (argc < 2) { 430 getusage(argv[0]); 431 return; 432 } 433 if (!connected) { 434 for (n = 1; n < argc ; n++) 435 if (index(argv[n], ':') == 0) { 436 getusage(argv[0]); 437 return; 438 } 439 } 440 for (n = 1; n < argc ; n++) { 441 src = index(argv[n], ':'); 442 if (src == NULL) 443 src = argv[n]; 444 else { 445 struct hostent *hp; 446 447 *src++ = 0; 448 hp = gethostbyname(argv[n]); 449 if (hp == NULL) { 450 fprintf(stderr, "tftp: %s: ", argv[n]); 451 herror((char *)NULL); 452 continue; 453 } 454 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 455 hp->h_length); 456 peeraddr.sin_family = hp->h_addrtype; 457 connected = 1; 458 strncpy(hostname, hp->h_name, sizeof(hostname)); 459 hostname[sizeof(hostname)-1] = 0; 460 } 461 if (argc < 4) { 462 cp = argc == 3 ? argv[2] : tail(src); 463 fd = creat(cp, 0644); 464 if (fd < 0) { 465 fprintf(stderr, "tftp: "); perror(cp); 466 return; 467 } 468 if (verbose) 469 printf("getting from %s:%s to %s [%s]\n", 470 hostname, src, cp, mode); 471 peeraddr.sin_port = port; 472 recvfile(fd, src, mode); 473 break; 474 } 475 cp = tail(src); /* new .. jdg */ 476 fd = creat(cp, 0644); 477 if (fd < 0) { 478 fprintf(stderr, "tftp: "); perror(cp); 479 continue; 480 } 481 if (verbose) 482 printf("getting from %s:%s to %s [%s]\n", 483 hostname, src, cp, mode); 484 peeraddr.sin_port = port; 485 recvfile(fd, src, mode); 486 } 487 } 488 489 static void 490 getusage(s) 491 char *s; 492 { 493 printf("usage: %s host:file host:file ... file, or\n", s); 494 printf(" %s file file ... file if connected\n", s); 495 } 496 497 int rexmtval = TIMEOUT; 498 499 void 500 setrexmt(argc, argv) 501 int argc; 502 char *argv[]; 503 { 504 int t; 505 506 if (argc < 2) { 507 strcpy(line, "Rexmt-timeout "); 508 printf("(value) "); 509 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 510 makeargv(); 511 argc = margc; 512 argv = margv; 513 } 514 if (argc != 2) { 515 printf("usage: %s value\n", argv[0]); 516 return; 517 } 518 t = atoi(argv[1]); 519 if (t < 0) 520 printf("%s: bad value\n", argv[1]); 521 else 522 rexmtval = t; 523 } 524 525 int maxtimeout = 5 * TIMEOUT; 526 527 void 528 settimeout(argc, argv) 529 int argc; 530 char *argv[]; 531 { 532 int t; 533 534 if (argc < 2) { 535 strcpy(line, "Maximum-timeout "); 536 printf("(value) "); 537 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 538 makeargv(); 539 argc = margc; 540 argv = margv; 541 } 542 if (argc != 2) { 543 printf("usage: %s value\n", argv[0]); 544 return; 545 } 546 t = atoi(argv[1]); 547 if (t < 0) 548 printf("%s: bad value\n", argv[1]); 549 else 550 maxtimeout = t; 551 } 552 553 void 554 status(argc, argv) 555 int argc; 556 char *argv[]; 557 { 558 if (connected) 559 printf("Connected to %s.\n", hostname); 560 else 561 printf("Not connected.\n"); 562 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 563 verbose ? "on" : "off", trace ? "on" : "off"); 564 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 565 rexmtval, maxtimeout); 566 } 567 568 void 569 intr(dummy) 570 int dummy; 571 { 572 573 signal(SIGALRM, SIG_IGN); 574 alarm(0); 575 longjmp(toplevel, -1); 576 } 577 578 char * 579 tail(filename) 580 char *filename; 581 { 582 register char *s; 583 584 while (*filename) { 585 s = rindex(filename, '/'); 586 if (s == NULL) 587 break; 588 if (s[1]) 589 return (s + 1); 590 *s = '\0'; 591 } 592 return (filename); 593 } 594 595 /* 596 * Command parser. 597 */ 598 static __dead void 599 command() 600 { 601 register struct cmd *c; 602 603 for (;;) { 604 printf("%s> ", prompt); 605 if (fgets(line, LBUFLEN, stdin) == 0) { 606 if (feof(stdin)) { 607 exit(0); 608 } else { 609 continue; 610 } 611 } 612 if ((line[0] == 0) || (line[0] == '\n')) 613 continue; 614 makeargv(); 615 if (margc == 0) 616 continue; 617 c = getcmd(margv[0]); 618 if (c == (struct cmd *)-1) { 619 printf("?Ambiguous command\n"); 620 continue; 621 } 622 if (c == 0) { 623 printf("?Invalid command\n"); 624 continue; 625 } 626 (*c->handler)(margc, margv); 627 } 628 } 629 630 struct cmd * 631 getcmd(name) 632 register char *name; 633 { 634 register char *p, *q; 635 register struct cmd *c, *found; 636 register int nmatches, longest; 637 638 longest = 0; 639 nmatches = 0; 640 found = 0; 641 for (c = cmdtab; (p = c->name) != NULL; c++) { 642 for (q = name; *q == *p++; q++) 643 if (*q == 0) /* exact match? */ 644 return (c); 645 if (!*q) { /* the name was a prefix */ 646 if (q - name > longest) { 647 longest = q - name; 648 nmatches = 1; 649 found = c; 650 } else if (q - name == longest) 651 nmatches++; 652 } 653 } 654 if (nmatches > 1) 655 return ((struct cmd *)-1); 656 return (found); 657 } 658 659 /* 660 * Slice a string up into argc/argv. 661 */ 662 static void 663 makeargv() 664 { 665 register char *cp; 666 register char **argp = margv; 667 668 margc = 0; 669 for (cp = line; *cp;) { 670 while (isspace(*cp)) 671 cp++; 672 if (*cp == '\0') 673 break; 674 *argp++ = cp; 675 margc += 1; 676 while (*cp != '\0' && !isspace(*cp)) 677 cp++; 678 if (*cp == '\0') 679 break; 680 *cp++ = '\0'; 681 } 682 *argp++ = 0; 683 } 684 685 void 686 quit(argc, argv) 687 int argc; 688 char *argv[]; 689 { 690 691 exit(0); 692 } 693 694 /* 695 * Help command. 696 */ 697 void 698 help(argc, argv) 699 int argc; 700 char *argv[]; 701 { 702 register struct cmd *c; 703 704 if (argc == 1) { 705 printf("Commands may be abbreviated. Commands are:\n\n"); 706 for (c = cmdtab; c->name; c++) 707 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 708 return; 709 } 710 while (--argc > 0) { 711 register char *arg; 712 arg = *++argv; 713 c = getcmd(arg); 714 if (c == (struct cmd *)-1) 715 printf("?Ambiguous help command %s\n", arg); 716 else if (c == (struct cmd *)0) 717 printf("?Invalid help command %s\n", arg); 718 else 719 printf("%s\n", c->help); 720 } 721 } 722 723 void 724 settrace(argc, argv) 725 int argc; 726 char **argv; 727 { 728 trace = !trace; 729 printf("Packet tracing %s.\n", trace ? "on" : "off"); 730 } 731 732 void 733 setverbose(argc, argv) 734 int argc; 735 char **argv; 736 { 737 verbose = !verbose; 738 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 739 } 740