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