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) 2/7/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: }