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