114553Ssam #ifndef lint 2*16382Ssam static char sccsid[] = "@(#)main.c 4.8 (Berkeley) 04/12/84"; 314553Ssam #endif 47769Ssam 57769Ssam /* 67769Ssam * TFTP User Program -- Command Interface. 77769Ssam */ 87769Ssam #include <sys/types.h> 97769Ssam #include <sys/socket.h> 1013017Ssam #include <sys/file.h> 119219Ssam 129219Ssam #include <netinet/in.h> 139219Ssam 147769Ssam #include <signal.h> 157769Ssam #include <stdio.h> 167769Ssam #include <errno.h> 177769Ssam #include <setjmp.h> 187769Ssam #include <ctype.h> 198384Ssam #include <netdb.h> 207769Ssam 2113017Ssam #define TIMEOUT 5 /* secs between rexmt's */ 2213017Ssam 239219Ssam struct sockaddr_in sin; 247769Ssam int f; 257769Ssam int trace; 267769Ssam int verbose; 277769Ssam int connected; 287769Ssam char mode[32]; 297769Ssam char line[200]; 307769Ssam int margc; 317769Ssam char *margv[20]; 327769Ssam char *prompt = "tftp"; 337769Ssam jmp_buf toplevel; 347769Ssam int intr(); 358384Ssam struct servent *sp; 367769Ssam 377769Ssam int quit(), help(), setverbose(), settrace(), status(); 3813017Ssam int get(), put(), setpeer(), setmode(), setrexmt(), settimeout(); 397769Ssam 407769Ssam #define HELPINDENT (sizeof("connect")) 417769Ssam 427769Ssam struct cmd { 437769Ssam char *name; 447769Ssam char *help; 457769Ssam int (*handler)(); 467769Ssam }; 477769Ssam 487769Ssam char vhelp[] = "toggle verbose mode"; 497769Ssam char thelp[] = "toggle packet tracing"; 507769Ssam char chelp[] = "connect to remote tftp"; 517769Ssam char qhelp[] = "exit tftp"; 527769Ssam char hhelp[] = "print help information"; 537769Ssam char shelp[] = "send file"; 547769Ssam char rhelp[] = "receive file"; 557769Ssam char mhelp[] = "set file transfer mode"; 567769Ssam char sthelp[] = "show current status"; 5713017Ssam char xhelp[] = "set per-packet retransmission timeout"; 5813017Ssam char ihelp[] = "set total retransmission timeout"; 597769Ssam 607769Ssam struct cmd cmdtab[] = { 617769Ssam { "connect", chelp, setpeer }, 627769Ssam { "mode", mhelp, setmode }, 637769Ssam { "put", shelp, put }, 647769Ssam { "get", rhelp, get }, 657769Ssam { "quit", qhelp, quit }, 667769Ssam { "verbose", vhelp, setverbose }, 677769Ssam { "trace", thelp, settrace }, 687769Ssam { "status", sthelp, status }, 6913017Ssam { "rexmt", xhelp, setrexmt }, 7013017Ssam { "timeout", ihelp, settimeout }, 717769Ssam { "?", hhelp, help }, 727769Ssam 0 737769Ssam }; 747769Ssam 757769Ssam struct cmd *getcmd(); 767769Ssam char *tail(); 777769Ssam char *index(); 787769Ssam char *rindex(); 797769Ssam 807769Ssam main(argc, argv) 817769Ssam char *argv[]; 827769Ssam { 8313017Ssam struct sockaddr_in sin; 8413017Ssam int top; 8513017Ssam 868384Ssam sp = getservbyname("tftp", "udp"); 878384Ssam if (sp == 0) { 888384Ssam fprintf(stderr, "tftp: udp/tftp: unknown service\n"); 898384Ssam exit(1); 908384Ssam } 919262Ssam f = socket(AF_INET, SOCK_DGRAM, 0, 0); 927769Ssam if (f < 0) { 9313017Ssam perror("tftp: socket"); 947769Ssam exit(3); 957769Ssam } 9613017Ssam bzero((char *)&sin, sizeof (sin)); 9713017Ssam sin.sin_family = AF_INET; 9813017Ssam if (bind(f, &sin, sizeof (sin)) < 0) { 9913017Ssam perror("tftp: bind"); 10013017Ssam exit(1); 10113017Ssam } 1027769Ssam strcpy(mode, "netascii"); 10313017Ssam signal(SIGINT, intr); 1047769Ssam if (argc > 1) { 1057769Ssam if (setjmp(toplevel) != 0) 1067769Ssam exit(0); 1077769Ssam setpeer(argc, argv); 1087769Ssam } 10913017Ssam top = setjmp(toplevel) == 0; 1107769Ssam for (;;) 11113017Ssam command(top); 1127769Ssam } 1137769Ssam 1148384Ssam char *hostname; 1158384Ssam char hnamebuf[32]; 1167769Ssam 1177769Ssam setpeer(argc, argv) 1187769Ssam int argc; 1197769Ssam char *argv[]; 1207769Ssam { 1217769Ssam register int c; 1228384Ssam struct hostent *host; 1237769Ssam 1247769Ssam if (argc < 2) { 1257769Ssam strcpy(line, "Connect "); 1267769Ssam printf("(to) "); 1277769Ssam gets(&line[strlen(line)]); 1287769Ssam makeargv(); 1297769Ssam argc = margc; 1307769Ssam argv = margv; 1317769Ssam } 1327769Ssam if (argc > 3) { 1337769Ssam printf("usage: %s host-name [port]\n", argv[0]); 1347769Ssam return; 1357769Ssam } 1368384Ssam host = gethostbyname(argv[1]); 1378384Ssam if (host) { 1389219Ssam sin.sin_family = host->h_addrtype; 1398384Ssam bcopy(host->h_addr, &sin.sin_addr, host->h_length); 1408384Ssam hostname = host->h_name; 1418384Ssam } else { 1429219Ssam sin.sin_family = AF_INET; 1438384Ssam sin.sin_addr.s_addr = inet_addr(argv[1]); 1448384Ssam if (sin.sin_addr.s_addr == -1) { 1458384Ssam connected = 0; 1468384Ssam printf("%s: unknown host\n", argv[1]); 1478384Ssam return; 1488384Ssam } 1498384Ssam strcpy(hnamebuf, argv[1]); 1508384Ssam hostname = hnamebuf; 1517769Ssam } 1528384Ssam sin.sin_port = sp->s_port; 1537769Ssam if (argc == 3) { 1547769Ssam sin.sin_port = atoi(argv[2]); 1557769Ssam if (sin.sin_port < 0) { 1567769Ssam printf("%s: bad port number\n", argv[2]); 1577769Ssam connected = 0; 1587769Ssam return; 1597769Ssam } 1609971Ssam sin.sin_port = htons((u_short)sin.sin_port); 1618384Ssam } 1627769Ssam connected = 1; 1637769Ssam } 1647769Ssam 1657769Ssam struct modes { 1667769Ssam char *m_name; 1677769Ssam char *m_mode; 1687769Ssam } modes[] = { 1697769Ssam { "ascii", "netascii" }, 1707769Ssam { "binary", "octect" }, 1717769Ssam { "mail", "mail" }, 1727769Ssam { 0, 0 } 1737769Ssam }; 1747769Ssam 1757769Ssam setmode(argc, argv) 1767769Ssam char *argv[]; 1777769Ssam { 1787769Ssam register struct modes *p; 1797769Ssam 1807769Ssam if (argc > 2) { 1817769Ssam char *sep; 1827769Ssam 1837769Ssam printf("usage: %s [", argv[0]); 1847769Ssam sep = " "; 1857769Ssam for (p = modes; p->m_name; p++) { 1867769Ssam printf("%s%s", sep, p->m_name); 1877769Ssam if (*sep == ' ') 1887769Ssam sep = " | "; 1897769Ssam } 1907769Ssam printf(" ]\n"); 1917769Ssam return; 1927769Ssam } 1937769Ssam if (argc < 2) { 1947769Ssam printf("Using %s mode to transfer files.\n", mode); 1957769Ssam return; 1967769Ssam } 1977769Ssam for (p = modes; p->m_name; p++) 1987769Ssam if (strcmp(argv[1], p->m_name) == 0) 1997769Ssam break; 2007769Ssam if (p->m_name) 2017769Ssam strcpy(mode, p->m_mode); 2027769Ssam else 2037769Ssam printf("%s: unknown mode\n", argv[1]); 2047769Ssam } 2057769Ssam 2067769Ssam /* 2077769Ssam * Send file(s). 2087769Ssam */ 2097769Ssam put(argc, argv) 2107769Ssam char *argv[]; 2117769Ssam { 2127769Ssam int fd; 2137769Ssam register int n, addr; 2147769Ssam register char *cp, *targ; 2157769Ssam 2167769Ssam if (argc < 2) { 2177769Ssam strcpy(line, "send "); 2187769Ssam printf("(file) "); 2197769Ssam gets(&line[strlen(line)]); 2207769Ssam makeargv(); 2217769Ssam argc = margc; 2227769Ssam argv = margv; 2237769Ssam } 2247769Ssam if (argc < 2) { 2257769Ssam putusage(argv[0]); 2267769Ssam return; 2277769Ssam } 2287769Ssam targ = argv[argc - 1]; 2297769Ssam if (index(argv[argc - 1], ':')) { 2308384Ssam char *cp; 2318384Ssam struct hostent *hp; 2327769Ssam 2337769Ssam for (n = 1; n < argc - 1; n++) 2347769Ssam if (index(argv[n], ':')) { 2357769Ssam putusage(argv[0]); 2367769Ssam return; 2377769Ssam } 2388384Ssam cp = argv[argc - 1]; 2398384Ssam targ = index(cp, ':'); 2407769Ssam *targ++ = 0; 2418384Ssam hp = gethostbyname(cp); 2428384Ssam if (hp == 0) { 2438384Ssam printf("%s: Unknown host.\n", cp); 2447769Ssam return; 2457769Ssam } 2469219Ssam bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 2478384Ssam sin.sin_family = hp->h_addrtype; 2487769Ssam connected = 1; 2498384Ssam hostname = hp->h_name; 2507769Ssam } 2517769Ssam if (!connected) { 2527769Ssam printf("No target machine specified.\n"); 2537769Ssam return; 2547769Ssam } 2557769Ssam if (argc < 4) { 2567769Ssam cp = argc == 2 ? tail(targ) : argv[1]; 25713017Ssam fd = open(cp, O_RDONLY); 2587769Ssam if (fd < 0) { 25913017Ssam fprintf(stderr, "tftp: "); perror(cp); 2607769Ssam return; 2617769Ssam } 2627769Ssam sendfile(fd, targ); 2637769Ssam return; 2647769Ssam } 2657769Ssam cp = index(targ, '\0'); 2667769Ssam *cp++ = '/'; 2677769Ssam for (n = 1; n < argc - 1; n++) { 2687769Ssam strcpy(cp, tail(argv[n])); 26913017Ssam fd = open(argv[n], O_RDONLY); 2707769Ssam if (fd < 0) { 27113017Ssam fprintf(stderr, "tftp: "); perror(argv[n]); 2727769Ssam continue; 2737769Ssam } 2747769Ssam sendfile(fd, targ); 2757769Ssam } 2767769Ssam } 2777769Ssam 2787769Ssam putusage(s) 2797769Ssam char *s; 2807769Ssam { 2817769Ssam printf("usage: %s file ... host:target, or\n", s); 2827769Ssam printf(" %s file ... target (when already connected)\n", s); 2837769Ssam } 2847769Ssam 2857769Ssam /* 2867769Ssam * Receive file(s). 2877769Ssam */ 2887769Ssam get(argc, argv) 2897769Ssam char *argv[]; 2907769Ssam { 2917769Ssam int fd; 2927769Ssam register int n, addr; 2937769Ssam register char *cp; 2947769Ssam char *src; 2957769Ssam 2967769Ssam if (argc < 2) { 2977769Ssam strcpy(line, "get "); 2987769Ssam printf("(files) "); 2997769Ssam gets(&line[strlen(line)]); 3007769Ssam makeargv(); 3017769Ssam argc = margc; 3027769Ssam argv = margv; 3037769Ssam } 3047769Ssam if (argc < 2) { 3057769Ssam getusage(argv[0]); 3067769Ssam return; 3077769Ssam } 3087769Ssam if (!connected) 3097769Ssam for (n = 1; n < argc - 1; n++) 3107769Ssam if (index(argv[n], ':') == 0) { 3117769Ssam getusage(argv[0]); 3127769Ssam return; 3137769Ssam } 3147769Ssam for (n = 1; argc == 2 || n < argc - 1; n++) { 3157769Ssam src = index(argv[n], ':'); 3167769Ssam if (src == NULL) 3177769Ssam src = argv[n]; 3187769Ssam else { 3198384Ssam struct hostent *hp; 3208384Ssam 3217769Ssam *src++ = 0; 3228384Ssam hp = gethostbyname(argv[n]); 3238384Ssam if (hp == 0) { 3247769Ssam printf("%s: Unknown host.\n", argv[n]); 3257769Ssam continue; 3267769Ssam } 3279219Ssam bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 3288384Ssam sin.sin_family = hp->h_addrtype; 3297769Ssam connected = 1; 3308384Ssam hostname = hp->h_name; 3317769Ssam } 3327769Ssam if (argc < 4) { 3337769Ssam cp = argc == 3 ? argv[2] : tail(src); 3347769Ssam fd = creat(cp, 0644); 3357769Ssam if (fd < 0) { 33613017Ssam fprintf(stderr, "tftp: "); perror(cp); 3377769Ssam return; 3387769Ssam } 3397769Ssam recvfile(fd, src); 3407769Ssam break; 3417769Ssam } 3427769Ssam cp = index(argv[argc - 1], '\0'); 3437769Ssam *cp++ = '/'; 3447769Ssam strcpy(cp, tail(src)); 3457769Ssam fd = creat(src, 0644); 3467769Ssam if (fd < 0) { 34713017Ssam fprintf(stderr, "tftp: "); perror(src); 3487769Ssam continue; 3497769Ssam } 3507769Ssam recvfile(fd, src); 3517769Ssam } 3527769Ssam } 3537769Ssam 3547769Ssam getusage(s) 3557769Ssam { 3567769Ssam printf("usage: %s host:file host:file ... file, or\n", s); 3577769Ssam printf(" %s file file ... file if connected\n", s); 3587769Ssam } 3597769Ssam 36013017Ssam int rexmtval = TIMEOUT; 36113017Ssam 36213017Ssam setrexmt(argc, argv) 36313017Ssam char *argv[]; 36413017Ssam { 36513017Ssam int t; 36613017Ssam 36713017Ssam if (argc < 2) { 36813017Ssam strcpy(line, "Rexmt-timeout "); 36913017Ssam printf("(value) "); 37013017Ssam gets(&line[strlen(line)]); 37113017Ssam makeargv(); 37213017Ssam argc = margc; 37313017Ssam argv = margv; 37413017Ssam } 37513017Ssam if (argc != 2) { 37613017Ssam printf("usage: %s value\n", argv[0]); 37713017Ssam return; 37813017Ssam } 37913017Ssam t = atoi(argv[1]); 38013017Ssam if (t < 0) 38113017Ssam printf("%s: bad value\n", t); 38213017Ssam else 38313017Ssam rexmtval = t; 38413017Ssam } 38513017Ssam 38613017Ssam int maxtimeout = 5 * TIMEOUT; 38713017Ssam 38813017Ssam settimeout(argc, argv) 38913017Ssam char *argv[]; 39013017Ssam { 39113017Ssam int t; 39213017Ssam 39313017Ssam if (argc < 2) { 39413017Ssam strcpy(line, "Maximum-timeout "); 39513017Ssam printf("(value) "); 39613017Ssam gets(&line[strlen(line)]); 39713017Ssam makeargv(); 39813017Ssam argc = margc; 39913017Ssam argv = margv; 40013017Ssam } 40113017Ssam if (argc != 2) { 40213017Ssam printf("usage: %s value\n", argv[0]); 40313017Ssam return; 40413017Ssam } 40513017Ssam t = atoi(argv[1]); 40613017Ssam if (t < 0) 40713017Ssam printf("%s: bad value\n", t); 40813017Ssam else 40913017Ssam maxtimeout = t; 41013017Ssam } 41113017Ssam 4127769Ssam status(argc, argv) 4137769Ssam char *argv[]; 4147769Ssam { 4157769Ssam if (connected) 4168384Ssam printf("Connected to %s.\n", hostname); 4177769Ssam else 4187769Ssam printf("Not connected.\n"); 4197769Ssam printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 4207769Ssam verbose ? "on" : "off", trace ? "on" : "off"); 42113017Ssam printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 42213017Ssam rexmtval, maxtimeout); 4237769Ssam } 4247769Ssam 4257769Ssam intr() 4267769Ssam { 42713017Ssam 428*16382Ssam alarm(0); 4297769Ssam longjmp(toplevel, -1); 4307769Ssam } 4317769Ssam 4327769Ssam char * 4337769Ssam tail(filename) 4347769Ssam char *filename; 4357769Ssam { 4367769Ssam register char *s; 4377769Ssam 4387769Ssam while (*filename) { 4397769Ssam s = rindex(filename, '/'); 4407769Ssam if (s == NULL) 4417769Ssam break; 4427769Ssam if (s[1]) 4437769Ssam return (s + 1); 4447769Ssam *s = '\0'; 4457769Ssam } 4467769Ssam return (filename); 4477769Ssam } 4487769Ssam 4497769Ssam /* 4507769Ssam * Command parser. 4517769Ssam */ 4527769Ssam command(top) 4537769Ssam int top; 4547769Ssam { 4557769Ssam register struct cmd *c; 4567769Ssam 4577769Ssam if (!top) 4587769Ssam putchar('\n'); 4597769Ssam for (;;) { 4607769Ssam printf("%s> ", prompt); 4617769Ssam if (gets(line) == 0) 46213017Ssam continue; 4637769Ssam if (line[0] == 0) 46413017Ssam continue; 4657769Ssam makeargv(); 4667769Ssam c = getcmd(margv[0]); 4677769Ssam if (c == (struct cmd *)-1) { 4687769Ssam printf("?Ambiguous command\n"); 4697769Ssam continue; 4707769Ssam } 4717769Ssam if (c == 0) { 4727769Ssam printf("?Invalid command\n"); 4737769Ssam continue; 4747769Ssam } 4757769Ssam (*c->handler)(margc, margv); 4767769Ssam } 4777769Ssam } 4787769Ssam 4797769Ssam struct cmd * 4807769Ssam getcmd(name) 4817769Ssam register char *name; 4827769Ssam { 4837769Ssam register char *p, *q; 4847769Ssam register struct cmd *c, *found; 4857769Ssam register int nmatches, longest; 4867769Ssam 4877769Ssam longest = 0; 4887769Ssam nmatches = 0; 4897769Ssam found = 0; 4907769Ssam for (c = cmdtab; p = c->name; c++) { 4917769Ssam for (q = name; *q == *p++; q++) 4927769Ssam if (*q == 0) /* exact match? */ 4937769Ssam return (c); 4947769Ssam if (!*q) { /* the name was a prefix */ 4957769Ssam if (q - name > longest) { 4967769Ssam longest = q - name; 4977769Ssam nmatches = 1; 4987769Ssam found = c; 4997769Ssam } else if (q - name == longest) 5007769Ssam nmatches++; 5017769Ssam } 5027769Ssam } 5037769Ssam if (nmatches > 1) 5047769Ssam return ((struct cmd *)-1); 5057769Ssam return (found); 5067769Ssam } 5077769Ssam 5087769Ssam /* 5097769Ssam * Slice a string up into argc/argv. 5107769Ssam */ 5117769Ssam makeargv() 5127769Ssam { 5137769Ssam register char *cp; 5147769Ssam register char **argp = margv; 5157769Ssam 5167769Ssam margc = 0; 5177769Ssam for (cp = line; *cp;) { 5187769Ssam while (isspace(*cp)) 5197769Ssam cp++; 5207769Ssam if (*cp == '\0') 5217769Ssam break; 5227769Ssam *argp++ = cp; 5237769Ssam margc += 1; 5247769Ssam while (*cp != '\0' && !isspace(*cp)) 5257769Ssam cp++; 5267769Ssam if (*cp == '\0') 5277769Ssam break; 5287769Ssam *cp++ = '\0'; 5297769Ssam } 5307769Ssam *argp++ = 0; 5317769Ssam } 5327769Ssam 5337769Ssam /*VARARGS*/ 5347769Ssam quit() 5357769Ssam { 5367769Ssam exit(0); 5377769Ssam } 5387769Ssam 5397769Ssam /* 5407769Ssam * Help command. 5417769Ssam */ 5427769Ssam help(argc, argv) 5437769Ssam int argc; 5447769Ssam char *argv[]; 5457769Ssam { 5467769Ssam register struct cmd *c; 5477769Ssam 5487769Ssam if (argc == 1) { 5497769Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 5507769Ssam for (c = cmdtab; c->name; c++) 5517769Ssam printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 5527769Ssam return; 5537769Ssam } 5547769Ssam while (--argc > 0) { 5557769Ssam register char *arg; 5567769Ssam arg = *++argv; 5577769Ssam c = getcmd(arg); 5587769Ssam if (c == (struct cmd *)-1) 5597769Ssam printf("?Ambiguous help command %s\n", arg); 5607769Ssam else if (c == (struct cmd *)0) 5617769Ssam printf("?Invalid help command %s\n", arg); 5627769Ssam else 5637769Ssam printf("%s\n", c->help); 5647769Ssam } 5657769Ssam } 5667769Ssam 5677769Ssam /* 5687769Ssam * Call routine with argc, argv set from args (terminated by 0). 5697769Ssam */ 5707769Ssam /* VARARGS2 */ 5717769Ssam call(routine, args) 5727769Ssam int (*routine)(); 5737769Ssam int args; 5747769Ssam { 5757769Ssam register int *argp; 5767769Ssam register int argc; 5777769Ssam 5787769Ssam for (argc = 0, argp = &args; *argp++ != 0; argc++) 5797769Ssam ; 5807769Ssam (*routine)(argc, &args); 5817769Ssam } 5827769Ssam 5837769Ssam /*VARARGS*/ 5847769Ssam settrace() 5857769Ssam { 5867769Ssam trace = !trace; 5877769Ssam printf("Packet tracing %s.\n", trace ? "on" : "off"); 5887769Ssam } 5897769Ssam 5907769Ssam /*VARARGS*/ 5917769Ssam setverbose() 5927769Ssam { 5937769Ssam verbose = !verbose; 5947769Ssam printf("Verbose mode %s.\n", verbose ? "on" : "off"); 5957769Ssam } 596