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.8 (Berkeley) 10/11/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 == NULL) { 295 fprintf(stderr, "tftp: %s: ", cp); 296 herror((char *)NULL); 297 return; 298 } 299 bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 300 sin.sin_family = hp->h_addrtype; 301 connected = 1; 302 strcpy(hostname, hp->h_name); 303 } 304 if (!connected) { 305 printf("No target machine specified.\n"); 306 return; 307 } 308 if (argc < 4) { 309 cp = argc == 2 ? tail(targ) : argv[1]; 310 fd = open(cp, O_RDONLY); 311 if (fd < 0) { 312 fprintf(stderr, "tftp: "); perror(cp); 313 return; 314 } 315 if (verbose) 316 printf("putting %s to %s:%s [%s]\n", 317 cp, hostname, targ, mode); 318 sin.sin_port = port; 319 sendfile(fd, targ, mode); 320 return; 321 } 322 /* this assumes the target is a directory */ 323 /* on a remote unix system. hmmmm. */ 324 cp = index(targ, '\0'); 325 *cp++ = '/'; 326 for (n = 1; n < argc - 1; n++) { 327 strcpy(cp, tail(argv[n])); 328 fd = open(argv[n], O_RDONLY); 329 if (fd < 0) { 330 fprintf(stderr, "tftp: "); perror(argv[n]); 331 continue; 332 } 333 if (verbose) 334 printf("putting %s to %s:%s [%s]\n", 335 argv[n], hostname, targ, mode); 336 sin.sin_port = port; 337 sendfile(fd, targ, mode); 338 } 339 } 340 341 putusage(s) 342 char *s; 343 { 344 printf("usage: %s file ... host:target, or\n", s); 345 printf(" %s file ... target (when already connected)\n", s); 346 } 347 348 /* 349 * Receive file(s). 350 */ 351 get(argc, argv) 352 char *argv[]; 353 { 354 int fd; 355 register int n; 356 register char *cp; 357 char *src; 358 359 if (argc < 2) { 360 strcpy(line, "get "); 361 printf("(files) "); 362 gets(&line[strlen(line)]); 363 makeargv(); 364 argc = margc; 365 argv = margv; 366 } 367 if (argc < 2) { 368 getusage(argv[0]); 369 return; 370 } 371 if (!connected) { 372 for (n = 1; n < argc ; n++) 373 if (index(argv[n], ':') == 0) { 374 getusage(argv[0]); 375 return; 376 } 377 } 378 for (n = 1; n < argc ; n++) { 379 src = index(argv[n], ':'); 380 if (src == NULL) 381 src = argv[n]; 382 else { 383 struct hostent *hp; 384 385 *src++ = 0; 386 hp = gethostbyname(argv[n]); 387 if (hp == NULL) { 388 fprintf(stderr, "tftp: %s: ", argv[n]); 389 herror((char *)NULL); 390 continue; 391 } 392 bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 393 sin.sin_family = hp->h_addrtype; 394 connected = 1; 395 strcpy(hostname, hp->h_name); 396 } 397 if (argc < 4) { 398 cp = argc == 3 ? argv[2] : tail(src); 399 fd = creat(cp, 0644); 400 if (fd < 0) { 401 fprintf(stderr, "tftp: "); perror(cp); 402 return; 403 } 404 if (verbose) 405 printf("getting from %s:%s to %s [%s]\n", 406 hostname, src, cp, mode); 407 sin.sin_port = port; 408 recvfile(fd, src, mode); 409 break; 410 } 411 cp = tail(src); /* new .. jdg */ 412 fd = creat(cp, 0644); 413 if (fd < 0) { 414 fprintf(stderr, "tftp: "); perror(cp); 415 continue; 416 } 417 if (verbose) 418 printf("getting from %s:%s to %s [%s]\n", 419 hostname, src, cp, mode); 420 sin.sin_port = port; 421 recvfile(fd, src, mode); 422 } 423 } 424 425 getusage(s) 426 char * s; 427 { 428 printf("usage: %s host:file host:file ... file, or\n", s); 429 printf(" %s file file ... file if connected\n", s); 430 } 431 432 int rexmtval = TIMEOUT; 433 434 setrexmt(argc, argv) 435 char *argv[]; 436 { 437 int t; 438 439 if (argc < 2) { 440 strcpy(line, "Rexmt-timeout "); 441 printf("(value) "); 442 gets(&line[strlen(line)]); 443 makeargv(); 444 argc = margc; 445 argv = margv; 446 } 447 if (argc != 2) { 448 printf("usage: %s value\n", argv[0]); 449 return; 450 } 451 t = atoi(argv[1]); 452 if (t < 0) 453 printf("%s: bad value\n", t); 454 else 455 rexmtval = t; 456 } 457 458 int maxtimeout = 5 * TIMEOUT; 459 460 settimeout(argc, argv) 461 char *argv[]; 462 { 463 int t; 464 465 if (argc < 2) { 466 strcpy(line, "Maximum-timeout "); 467 printf("(value) "); 468 gets(&line[strlen(line)]); 469 makeargv(); 470 argc = margc; 471 argv = margv; 472 } 473 if (argc != 2) { 474 printf("usage: %s value\n", argv[0]); 475 return; 476 } 477 t = atoi(argv[1]); 478 if (t < 0) 479 printf("%s: bad value\n", t); 480 else 481 maxtimeout = t; 482 } 483 484 status(argc, argv) 485 char *argv[]; 486 { 487 if (connected) 488 printf("Connected to %s.\n", hostname); 489 else 490 printf("Not connected.\n"); 491 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 492 verbose ? "on" : "off", trace ? "on" : "off"); 493 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 494 rexmtval, maxtimeout); 495 } 496 497 intr() 498 { 499 signal(SIGALRM, SIG_IGN); 500 alarm(0); 501 longjmp(toplevel, -1); 502 } 503 504 char * 505 tail(filename) 506 char *filename; 507 { 508 register char *s; 509 510 while (*filename) { 511 s = rindex(filename, '/'); 512 if (s == NULL) 513 break; 514 if (s[1]) 515 return (s + 1); 516 *s = '\0'; 517 } 518 return (filename); 519 } 520 521 /* 522 * Command parser. 523 */ 524 command(top) 525 int top; 526 { 527 register struct cmd *c; 528 529 if (!top) 530 putchar('\n'); 531 for (;;) { 532 printf("%s> ", prompt); 533 if (gets(line) == 0) { 534 if (feof(stdin)) { 535 quit(); 536 } else { 537 continue; 538 } 539 } 540 if (line[0] == 0) 541 continue; 542 makeargv(); 543 c = getcmd(margv[0]); 544 if (c == (struct cmd *)-1) { 545 printf("?Ambiguous command\n"); 546 continue; 547 } 548 if (c == 0) { 549 printf("?Invalid command\n"); 550 continue; 551 } 552 (*c->handler)(margc, margv); 553 } 554 } 555 556 struct cmd * 557 getcmd(name) 558 register char *name; 559 { 560 register char *p, *q; 561 register struct cmd *c, *found; 562 register int nmatches, longest; 563 564 longest = 0; 565 nmatches = 0; 566 found = 0; 567 for (c = cmdtab; p = c->name; c++) { 568 for (q = name; *q == *p++; q++) 569 if (*q == 0) /* exact match? */ 570 return (c); 571 if (!*q) { /* the name was a prefix */ 572 if (q - name > longest) { 573 longest = q - name; 574 nmatches = 1; 575 found = c; 576 } else if (q - name == longest) 577 nmatches++; 578 } 579 } 580 if (nmatches > 1) 581 return ((struct cmd *)-1); 582 return (found); 583 } 584 585 /* 586 * Slice a string up into argc/argv. 587 */ 588 makeargv() 589 { 590 register char *cp; 591 register char **argp = margv; 592 593 margc = 0; 594 for (cp = line; *cp;) { 595 while (isspace(*cp)) 596 cp++; 597 if (*cp == '\0') 598 break; 599 *argp++ = cp; 600 margc += 1; 601 while (*cp != '\0' && !isspace(*cp)) 602 cp++; 603 if (*cp == '\0') 604 break; 605 *cp++ = '\0'; 606 } 607 *argp++ = 0; 608 } 609 610 /*VARARGS*/ 611 quit() 612 { 613 exit(0); 614 } 615 616 /* 617 * Help command. 618 */ 619 help(argc, argv) 620 int argc; 621 char *argv[]; 622 { 623 register struct cmd *c; 624 625 if (argc == 1) { 626 printf("Commands may be abbreviated. Commands are:\n\n"); 627 for (c = cmdtab; c->name; c++) 628 printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 629 return; 630 } 631 while (--argc > 0) { 632 register char *arg; 633 arg = *++argv; 634 c = getcmd(arg); 635 if (c == (struct cmd *)-1) 636 printf("?Ambiguous help command %s\n", arg); 637 else if (c == (struct cmd *)0) 638 printf("?Invalid help command %s\n", arg); 639 else 640 printf("%s\n", c->help); 641 } 642 } 643 644 /*VARARGS*/ 645 settrace() 646 { 647 trace = !trace; 648 printf("Packet tracing %s.\n", trace ? "on" : "off"); 649 } 650 651 /*VARARGS*/ 652 setverbose() 653 { 654 verbose = !verbose; 655 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 656 } 657