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