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.4 (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, 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 { 414 printf("usage: %s host:file host:file ... file, or\n", s); 415 printf(" %s file file ... file if connected\n", s); 416 } 417 418 int rexmtval = TIMEOUT; 419 420 setrexmt(argc, argv) 421 char *argv[]; 422 { 423 int t; 424 425 if (argc < 2) { 426 strcpy(line, "Rexmt-timeout "); 427 printf("(value) "); 428 gets(&line[strlen(line)]); 429 makeargv(); 430 argc = margc; 431 argv = margv; 432 } 433 if (argc != 2) { 434 printf("usage: %s value\n", argv[0]); 435 return; 436 } 437 t = atoi(argv[1]); 438 if (t < 0) 439 printf("%s: bad value\n", t); 440 else 441 rexmtval = t; 442 } 443 444 int maxtimeout = 5 * TIMEOUT; 445 446 settimeout(argc, argv) 447 char *argv[]; 448 { 449 int t; 450 451 if (argc < 2) { 452 strcpy(line, "Maximum-timeout "); 453 printf("(value) "); 454 gets(&line[strlen(line)]); 455 makeargv(); 456 argc = margc; 457 argv = margv; 458 } 459 if (argc != 2) { 460 printf("usage: %s value\n", argv[0]); 461 return; 462 } 463 t = atoi(argv[1]); 464 if (t < 0) 465 printf("%s: bad value\n", t); 466 else 467 maxtimeout = t; 468 } 469 470 status(argc, argv) 471 char *argv[]; 472 { 473 if (connected) 474 printf("Connected to %s.\n", hostname); 475 else 476 printf("Not connected.\n"); 477 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 478 verbose ? "on" : "off", trace ? "on" : "off"); 479 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 480 rexmtval, maxtimeout); 481 } 482 483 intr() 484 { 485 signal(SIGALRM, SIG_IGN); 486 alarm(0); 487 longjmp(toplevel, -1); 488 } 489 490 char * 491 tail(filename) 492 char *filename; 493 { 494 register char *s; 495 496 while (*filename) { 497 s = rindex(filename, '/'); 498 if (s == NULL) 499 break; 500 if (s[1]) 501 return (s + 1); 502 *s = '\0'; 503 } 504 return (filename); 505 } 506 507 /* 508 * Command parser. 509 */ 510 command(top) 511 int top; 512 { 513 register struct cmd *c; 514 515 if (!top) 516 putchar('\n'); 517 for (;;) { 518 printf("%s> ", prompt); 519 if (gets(line) == 0) { 520 if (feof(stdin)) { 521 quit(); 522 } else { 523 continue; 524 } 525 } 526 if (line[0] == 0) 527 continue; 528 makeargv(); 529 c = getcmd(margv[0]); 530 if (c == (struct cmd *)-1) { 531 printf("?Ambiguous command\n"); 532 continue; 533 } 534 if (c == 0) { 535 printf("?Invalid command\n"); 536 continue; 537 } 538 (*c->handler)(margc, margv); 539 } 540 } 541 542 struct cmd * 543 getcmd(name) 544 register char *name; 545 { 546 register char *p, *q; 547 register struct cmd *c, *found; 548 register int nmatches, longest; 549 550 longest = 0; 551 nmatches = 0; 552 found = 0; 553 for (c = cmdtab; p = c->name; c++) { 554 for (q = name; *q == *p++; q++) 555 if (*q == 0) /* exact match? */ 556 return (c); 557 if (!*q) { /* the name was a prefix */ 558 if (q - name > longest) { 559 longest = q - name; 560 nmatches = 1; 561 found = c; 562 } else if (q - name == longest) 563 nmatches++; 564 } 565 } 566 if (nmatches > 1) 567 return ((struct cmd *)-1); 568 return (found); 569 } 570 571 /* 572 * Slice a string up into argc/argv. 573 */ 574 makeargv() 575 { 576 register char *cp; 577 register char **argp = margv; 578 579 margc = 0; 580 for (cp = line; *cp;) { 581 while (isspace(*cp)) 582 cp++; 583 if (*cp == '\0') 584 break; 585 *argp++ = cp; 586 margc += 1; 587 while (*cp != '\0' && !isspace(*cp)) 588 cp++; 589 if (*cp == '\0') 590 break; 591 *cp++ = '\0'; 592 } 593 *argp++ = 0; 594 } 595 596 /*VARARGS*/ 597 quit() 598 { 599 exit(0); 600 } 601 602 /* 603 * Help command. 604 */ 605 help(argc, argv) 606 int argc; 607 char *argv[]; 608 { 609 register struct cmd *c; 610 611 if (argc == 1) { 612 printf("Commands may be abbreviated. Commands are:\n\n"); 613 for (c = cmdtab; c->name; c++) 614 printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 615 return; 616 } 617 while (--argc > 0) { 618 register char *arg; 619 arg = *++argv; 620 c = getcmd(arg); 621 if (c == (struct cmd *)-1) 622 printf("?Ambiguous help command %s\n", arg); 623 else if (c == (struct cmd *)0) 624 printf("?Invalid help command %s\n", arg); 625 else 626 printf("%s\n", c->help); 627 } 628 } 629 630 /*VARARGS*/ 631 settrace() 632 { 633 trace = !trace; 634 printf("Packet tracing %s.\n", trace ? "on" : "off"); 635 } 636 637 /*VARARGS*/ 638 setverbose() 639 { 640 verbose = !verbose; 641 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 642 } 643