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