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