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