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