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