1 /* $OpenBSD: main.c,v 1.20 2003/09/29 11:23:38 jmc Exp $ */ 2 /* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 static const char copyright[] = 35 "@(#) Copyright (c) 1983, 1993\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 42 #endif 43 static const char rcsid[] = "$OpenBSD: main.c,v 1.20 2003/09/29 11:23:38 jmc Exp $"; 44 #endif /* not lint */ 45 46 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 47 48 /* 49 * TFTP User Program -- Command Interface. 50 */ 51 #include <sys/types.h> 52 #include <sys/socket.h> 53 #include <sys/file.h> 54 55 #include <netinet/in.h> 56 57 #include <arpa/inet.h> 58 59 #include <ctype.h> 60 #include <errno.h> 61 #include <netdb.h> 62 #include <setjmp.h> 63 #include <signal.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <unistd.h> 68 #include <err.h> 69 70 #include "extern.h" 71 72 #define TIMEOUT 5 /* secs between rexmt's */ 73 #define LBUFLEN 200 /* size of input buffer */ 74 #define MAXARGV 20 75 76 struct sockaddr_in peeraddr; 77 int f; 78 short port; 79 int trace; 80 int verbose; 81 int connected; 82 char mode[32]; 83 char line[LBUFLEN]; 84 int margc; 85 char *margv[MAXARGV+1]; 86 char *prompt = "tftp"; 87 jmp_buf toplevel; 88 void intr(int); 89 struct servent *sp; 90 91 void get(int, char **); 92 void help(int, char **); 93 void modecmd(int, char **); 94 void put(int, char **); 95 void quit(int, char **); 96 void setascii(int, char **); 97 void setbinary(int, char **); 98 void setpeer(int, char **); 99 void setrexmt(int, char **); 100 void settimeout(int, char **); 101 void settrace(int, char **); 102 void setverbose(int, char **); 103 void status(int, char **); 104 105 static __dead void command(void); 106 107 static void getusage(char *); 108 static int makeargv(void); 109 static void putusage(char *); 110 static void settftpmode(char *); 111 112 #define HELPINDENT (sizeof("connect")) 113 114 struct cmd { 115 char *name; 116 char *help; 117 void (*handler)(int, char **); 118 }; 119 120 char vhelp[] = "toggle verbose mode"; 121 char thelp[] = "toggle packet tracing"; 122 char chelp[] = "connect to remote tftp"; 123 char qhelp[] = "exit tftp"; 124 char hhelp[] = "print help information"; 125 char shelp[] = "send file"; 126 char rhelp[] = "receive file"; 127 char mhelp[] = "set file transfer mode"; 128 char sthelp[] = "show current status"; 129 char xhelp[] = "set per-packet retransmission timeout"; 130 char ihelp[] = "set total retransmission timeout"; 131 char ashelp[] = "set mode to netascii"; 132 char bnhelp[] = "set mode to octet"; 133 134 struct cmd cmdtab[] = { 135 { "connect", chelp, setpeer }, 136 { "mode", mhelp, modecmd }, 137 { "put", shelp, put }, 138 { "get", rhelp, get }, 139 { "quit", qhelp, quit }, 140 { "verbose", vhelp, setverbose }, 141 { "trace", thelp, settrace }, 142 { "status", sthelp, status }, 143 { "binary", bnhelp, setbinary }, 144 { "ascii", ashelp, setascii }, 145 { "rexmt", xhelp, setrexmt }, 146 { "timeout", ihelp, settimeout }, 147 { "?", hhelp, help }, 148 { NULL, NULL, NULL } 149 }; 150 151 struct cmd *getcmd(char *); 152 char *tail(char *); 153 154 int 155 main(int argc, char *argv[]) 156 { 157 struct sockaddr_in s_in; 158 159 sp = getservbyname("tftp", "udp"); 160 if (sp == 0) 161 errx(1, "udp/tftp: unknown service"); 162 f = socket(AF_INET, SOCK_DGRAM, 0); 163 if (f < 0) 164 err(3, "tftp: socket"); 165 bzero((char *)&s_in, sizeof (s_in)); 166 s_in.sin_family = AF_INET; 167 if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) 168 err(1, "tftp: bind"); 169 strlcpy(mode, "netascii", sizeof mode); 170 signal(SIGINT, intr); 171 if (argc > 1) { 172 if (setjmp(toplevel) != 0) 173 exit(0); 174 setpeer(argc, argv); 175 } 176 if (setjmp(toplevel) != 0) 177 (void)putchar('\n'); 178 command(); 179 return (0); 180 } 181 182 char hostname[MAXHOSTNAMELEN]; 183 184 void 185 setpeer(int argc, char *argv[]) 186 { 187 struct hostent *host; 188 189 if (argc < 2) { 190 strlcpy(line, "Connect ", sizeof line); 191 printf("(to) "); 192 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 193 if (makeargv()) 194 return; 195 argc = margc; 196 argv = margv; 197 } 198 if ((argc < 2) || (argc > 3)) { 199 printf("usage: %s [host [port]]\n", argv[0]); 200 return; 201 } 202 if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) { 203 peeraddr.sin_family = AF_INET; 204 (void) strncpy(hostname, argv[1], sizeof hostname); 205 hostname[sizeof(hostname)-1] = '\0'; 206 } else { 207 host = gethostbyname(argv[1]); 208 if (host == 0) { 209 connected = 0; 210 printf("%s: unknown host\n", argv[1]); 211 return; 212 } 213 peeraddr.sin_family = host->h_addrtype; 214 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); 215 (void) strlcpy(hostname, host->h_name, sizeof hostname); 216 } 217 port = sp->s_port; 218 if (argc == 3) { 219 port = atoi(argv[2]); 220 if (port < 0) { 221 printf("%s: bad port number\n", argv[2]); 222 connected = 0; 223 return; 224 } 225 port = htons(port); 226 } 227 connected = 1; 228 } 229 230 struct modes { 231 char *m_name; 232 char *m_mode; 233 } modes[] = { 234 { "ascii", "netascii" }, 235 { "netascii", "netascii" }, 236 { "binary", "octet" }, 237 { "image", "octet" }, 238 { "octet", "octet" }, 239 /* { "mail", "mail" }, */ 240 { NULL, NULL } 241 }; 242 243 void 244 modecmd(int argc, char *argv[]) 245 { 246 struct modes *p; 247 char *sep; 248 249 if (argc < 2) { 250 printf("Using %s mode to transfer files.\n", mode); 251 return; 252 } 253 if (argc == 2) { 254 for (p = modes; p->m_name != NULL; p++) 255 if (strcmp(argv[1], p->m_name) == 0) 256 break; 257 if (p->m_name) { 258 settftpmode(p->m_mode); 259 return; 260 } 261 printf("%s: unknown mode\n", argv[1]); 262 /* drop through and print usage message */ 263 } 264 265 printf("usage: %s [", argv[0]); 266 sep = " "; 267 for (p = modes; p->m_name != NULL; p++) { 268 printf("%s%s", sep, p->m_name); 269 if (*sep == ' ') 270 sep = " | "; 271 } 272 printf(" ]\n"); 273 return; 274 } 275 276 void 277 setbinary(int argc, char *argv[]) 278 { 279 280 settftpmode("octet"); 281 } 282 283 void 284 setascii(int argc, char *argv[]) 285 { 286 287 settftpmode("netascii"); 288 } 289 290 static void 291 settftpmode(char *newmode) 292 { 293 strlcpy(mode, newmode, sizeof mode); 294 if (verbose) 295 printf("mode set to %s\n", mode); 296 } 297 298 299 /* 300 * Send file(s). 301 */ 302 void 303 put(int argc, char *argv[]) 304 { 305 int fd; 306 int n; 307 char *cp, *targ; 308 309 if (argc < 2) { 310 strlcpy(line, "send ", sizeof line); 311 printf("(file) "); 312 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 313 if (makeargv()) 314 return; 315 argc = margc; 316 argv = margv; 317 } 318 if (argc < 2) { 319 putusage(argv[0]); 320 return; 321 } 322 targ = argv[argc - 1]; 323 if (strchr(argv[argc - 1], ':')) { 324 char *cp; 325 struct hostent *hp; 326 327 for (n = 1; n < argc - 1; n++) 328 if (strchr(argv[n], ':')) { 329 putusage(argv[0]); 330 return; 331 } 332 cp = argv[argc - 1]; 333 targ = strchr(cp, ':'); 334 *targ++ = 0; 335 hp = gethostbyname(cp); 336 if (hp == NULL) { 337 warnx("%s: %s", cp, hstrerror(h_errno)); 338 return; 339 } 340 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length); 341 peeraddr.sin_family = hp->h_addrtype; 342 connected = 1; 343 port = sp->s_port; 344 strlcpy(hostname, hp->h_name, sizeof hostname); 345 } 346 if (!connected) { 347 printf("No target machine specified.\n"); 348 return; 349 } 350 if (argc < 4) { 351 cp = argc == 2 ? tail(targ) : argv[1]; 352 fd = open(cp, O_RDONLY); 353 if (fd < 0) { 354 warn("open: %s", cp); 355 return; 356 } 357 if (verbose) 358 printf("putting %s to %s:%s [%s]\n", 359 cp, hostname, targ, mode); 360 peeraddr.sin_port = port; 361 sendfile(fd, targ, mode); 362 return; 363 } 364 365 /* this assumes the target is a directory */ 366 /* on a remote unix system. hmmmm. */ 367 for (n = 1; n < argc - 1; n++) { 368 if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1) 369 err(1, "asprintf"); 370 fd = open(argv[n], O_RDONLY); 371 if (fd < 0) { 372 warn("open: %s", argv[n]); 373 continue; 374 } 375 if (verbose) 376 printf("putting %s to %s:%s [%s]\n", 377 argv[n], hostname, cp, mode); 378 peeraddr.sin_port = port; 379 sendfile(fd, cp, mode); 380 free(cp); 381 } 382 } 383 384 static void 385 putusage(char *s) 386 { 387 printf("usage: %s file [[host:]remotename]\n", s); 388 printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", s); 389 } 390 391 /* 392 * Receive file(s). 393 */ 394 void 395 get(int argc, char *argv[]) 396 { 397 int fd; 398 int n; 399 char *cp; 400 char *src; 401 402 if (argc < 2) { 403 strlcpy(line, "get ", sizeof line); 404 printf("(files) "); 405 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 406 if (makeargv()) 407 return; 408 argc = margc; 409 argv = margv; 410 } 411 if (argc < 2) { 412 getusage(argv[0]); 413 return; 414 } 415 if (!connected) { 416 for (n = 1; n < argc ; n++) 417 if (strchr(argv[n], ':') == 0) { 418 getusage(argv[0]); 419 return; 420 } 421 } 422 for (n = 1; n < argc ; n++) { 423 src = strchr(argv[n], ':'); 424 if (src == NULL) 425 src = argv[n]; 426 else { 427 struct hostent *hp; 428 429 *src++ = 0; 430 hp = gethostbyname(argv[n]); 431 if (hp == NULL) { 432 warnx("%s: %s", argv[n], hstrerror(h_errno)); 433 continue; 434 } 435 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 436 hp->h_length); 437 peeraddr.sin_family = hp->h_addrtype; 438 connected = 1; 439 strlcpy(hostname, hp->h_name, sizeof hostname); 440 } 441 if (argc < 4) { 442 cp = argc == 3 ? argv[2] : tail(src); 443 fd = creat(cp, 0644); 444 if (fd < 0) { 445 warn("create: %s", cp); 446 return; 447 } 448 if (verbose) 449 printf("getting from %s:%s to %s [%s]\n", 450 hostname, src, cp, mode); 451 peeraddr.sin_port = port; 452 recvfile(fd, src, mode); 453 break; 454 } 455 cp = tail(src); /* new .. jdg */ 456 fd = creat(cp, 0644); 457 if (fd < 0) { 458 warn("create: %s", cp); 459 continue; 460 } 461 if (verbose) 462 printf("getting from %s:%s to %s [%s]\n", 463 hostname, src, cp, mode); 464 peeraddr.sin_port = port; 465 recvfile(fd, src, mode); 466 } 467 } 468 469 static void 470 getusage(char *s) 471 { 472 printf("usage: %s [host:]file [localname]\n", s); 473 printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); 474 } 475 476 int rexmtval = TIMEOUT; 477 478 void 479 setrexmt(int argc, char *argv[]) 480 { 481 int t; 482 483 if (argc < 2) { 484 strlcpy(line, "Rexmt-timeout ", sizeof line); 485 printf("(value) "); 486 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 487 if (makeargv()) 488 return; 489 argc = margc; 490 argv = margv; 491 } 492 if (argc != 2) { 493 printf("usage: %s value\n", argv[0]); 494 return; 495 } 496 t = atoi(argv[1]); 497 if (t < 0) 498 printf("%s: bad value\n", argv[1]); 499 else 500 rexmtval = t; 501 } 502 503 int maxtimeout = 5 * TIMEOUT; 504 505 void 506 settimeout(int argc, char *argv[]) 507 { 508 int t; 509 510 if (argc < 2) { 511 strlcpy(line, "Maximum-timeout ", sizeof line); 512 printf("(value) "); 513 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 514 if (makeargv()) 515 return; 516 argc = margc; 517 argv = margv; 518 } 519 if (argc != 2) { 520 printf("usage: %s value\n", argv[0]); 521 return; 522 } 523 t = atoi(argv[1]); 524 if (t < 0) 525 printf("%s: bad value\n", argv[1]); 526 else 527 maxtimeout = t; 528 } 529 530 void 531 status(int argc, char *argv[]) 532 { 533 if (connected) 534 printf("Connected to %s.\n", hostname); 535 else 536 printf("Not connected.\n"); 537 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 538 verbose ? "on" : "off", trace ? "on" : "off"); 539 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 540 rexmtval, maxtimeout); 541 } 542 543 void 544 intr(int signo) 545 { 546 547 signal(SIGALRM, SIG_IGN); 548 alarm(0); 549 longjmp(toplevel, -1); 550 } 551 552 char * 553 tail(char *filename) 554 { 555 char *s; 556 557 while (*filename) { 558 s = strrchr(filename, '/'); 559 if (s == NULL) 560 break; 561 if (s[1]) 562 return (s + 1); 563 *s = '\0'; 564 } 565 return (filename); 566 } 567 568 /* 569 * Command parser. 570 */ 571 static __dead void 572 command(void) 573 { 574 struct cmd *c; 575 576 for (;;) { 577 printf("%s> ", prompt); 578 if (fgets(line, LBUFLEN, stdin) == 0) { 579 if (feof(stdin)) { 580 exit(0); 581 } else { 582 continue; 583 } 584 } 585 if ((line[0] == 0) || (line[0] == '\n')) 586 continue; 587 if (makeargv()) 588 continue; 589 if (margc == 0) 590 continue; 591 c = getcmd(margv[0]); 592 if (c == (struct cmd *)-1) { 593 printf("?Ambiguous command\n"); 594 continue; 595 } 596 if (c == 0) { 597 printf("?Invalid command\n"); 598 continue; 599 } 600 (*c->handler)(margc, margv); 601 } 602 } 603 604 struct cmd * 605 getcmd(char *name) 606 { 607 char *p, *q; 608 struct cmd *c, *found; 609 int nmatches, longest; 610 611 longest = 0; 612 nmatches = 0; 613 found = 0; 614 for (c = cmdtab; (p = c->name) != NULL; c++) { 615 for (q = name; *q == *p++; q++) 616 if (*q == 0) /* exact match? */ 617 return (c); 618 if (!*q) { /* the name was a prefix */ 619 if (q - name > longest) { 620 longest = q - name; 621 nmatches = 1; 622 found = c; 623 } else if (q - name == longest) 624 nmatches++; 625 } 626 } 627 if (nmatches > 1) 628 return ((struct cmd *)-1); 629 return (found); 630 } 631 632 /* 633 * Slice a string up into argc/argv. 634 */ 635 static int 636 makeargv(void) 637 { 638 char *cp; 639 char **argp = margv; 640 int ret = 0; 641 642 margc = 0; 643 for (cp = line; *cp;) { 644 if (margc >= MAXARGV) { 645 printf("too many arguments\n"); 646 ret = 1; 647 break; 648 } 649 while (isspace(*cp)) 650 cp++; 651 if (*cp == '\0') 652 break; 653 *argp++ = cp; 654 margc += 1; 655 while (*cp != '\0' && !isspace(*cp)) 656 cp++; 657 if (*cp == '\0') 658 break; 659 *cp++ = '\0'; 660 } 661 *argp++ = 0; 662 return (ret); 663 } 664 665 void 666 quit(int argc, char *argv[]) 667 { 668 669 exit(0); 670 } 671 672 /* 673 * Help command. 674 */ 675 void 676 help(int argc, char *argv[]) 677 { 678 struct cmd *c; 679 680 if (argc == 1) { 681 printf("Commands may be abbreviated. Commands are:\n\n"); 682 for (c = cmdtab; c->name != NULL; c++) 683 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 684 return; 685 } 686 while (--argc > 0) { 687 char *arg; 688 arg = *++argv; 689 c = getcmd(arg); 690 if (c == (struct cmd *)-1) 691 printf("?Ambiguous help command %s\n", arg); 692 else if (c == (struct cmd *)0) 693 printf("?Invalid help command %s\n", arg); 694 else 695 printf("%s\n", c->help); 696 } 697 } 698 699 void 700 settrace(int argc, char *argv[]) 701 { 702 trace = !trace; 703 printf("Packet tracing %s.\n", trace ? "on" : "off"); 704 } 705 706 void 707 setverbose(int argc, char *argv[]) 708 { 709 verbose = !verbose; 710 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 711 } 712