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