114553Ssam #ifndef lint 2*16383Ssam static char sccsid[] = "@(#)main.c 4.9 (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 23*16383Ssam struct sockaddr_in sin = { AF_INET }; 247769Ssam int f; 257769Ssam int trace; 267769Ssam int connected; 277769Ssam char mode[32]; 287769Ssam char line[200]; 297769Ssam int margc; 307769Ssam char *margv[20]; 317769Ssam char *prompt = "tftp"; 327769Ssam jmp_buf toplevel; 337769Ssam int intr(); 348384Ssam struct servent *sp; 357769Ssam 36*16383Ssam int quit(), help(), settrace(), status(); 3713017Ssam int get(), put(), setpeer(), setmode(), setrexmt(), settimeout(); 387769Ssam 397769Ssam #define HELPINDENT (sizeof("connect")) 407769Ssam 417769Ssam struct cmd { 427769Ssam char *name; 437769Ssam char *help; 447769Ssam int (*handler)(); 457769Ssam }; 467769Ssam 477769Ssam char thelp[] = "toggle packet tracing"; 487769Ssam char chelp[] = "connect to remote tftp"; 497769Ssam char qhelp[] = "exit tftp"; 507769Ssam char hhelp[] = "print help information"; 517769Ssam char shelp[] = "send file"; 527769Ssam char rhelp[] = "receive file"; 537769Ssam char mhelp[] = "set file transfer mode"; 547769Ssam char sthelp[] = "show current status"; 5513017Ssam char xhelp[] = "set per-packet retransmission timeout"; 5613017Ssam char ihelp[] = "set total retransmission timeout"; 577769Ssam 587769Ssam struct cmd cmdtab[] = { 597769Ssam { "connect", chelp, setpeer }, 607769Ssam { "mode", mhelp, setmode }, 617769Ssam { "put", shelp, put }, 627769Ssam { "get", rhelp, get }, 637769Ssam { "quit", qhelp, quit }, 647769Ssam { "trace", thelp, settrace }, 657769Ssam { "status", sthelp, status }, 6613017Ssam { "rexmt", xhelp, setrexmt }, 6713017Ssam { "timeout", ihelp, settimeout }, 687769Ssam { "?", hhelp, help }, 697769Ssam 0 707769Ssam }; 717769Ssam 727769Ssam struct cmd *getcmd(); 737769Ssam char *tail(); 747769Ssam char *index(); 757769Ssam char *rindex(); 767769Ssam 777769Ssam main(argc, argv) 787769Ssam char *argv[]; 797769Ssam { 8013017Ssam int top; 8113017Ssam 828384Ssam sp = getservbyname("tftp", "udp"); 838384Ssam if (sp == 0) { 848384Ssam fprintf(stderr, "tftp: udp/tftp: unknown service\n"); 858384Ssam exit(1); 868384Ssam } 879262Ssam f = socket(AF_INET, SOCK_DGRAM, 0, 0); 887769Ssam if (f < 0) { 8913017Ssam perror("tftp: socket"); 907769Ssam exit(3); 917769Ssam } 9213017Ssam if (bind(f, &sin, sizeof (sin)) < 0) { 9313017Ssam perror("tftp: bind"); 9413017Ssam exit(1); 9513017Ssam } 96*16383Ssam strcpy(mode, "octet"); 9713017Ssam signal(SIGINT, intr); 987769Ssam if (argc > 1) { 997769Ssam if (setjmp(toplevel) != 0) 1007769Ssam exit(0); 1017769Ssam setpeer(argc, argv); 1027769Ssam } 10313017Ssam top = setjmp(toplevel) == 0; 1047769Ssam for (;;) 10513017Ssam command(top); 1067769Ssam } 1077769Ssam 1088384Ssam char *hostname; 1098384Ssam char hnamebuf[32]; 1107769Ssam 1117769Ssam setpeer(argc, argv) 1127769Ssam int argc; 1137769Ssam char *argv[]; 1147769Ssam { 1157769Ssam register int c; 1168384Ssam struct hostent *host; 1177769Ssam 1187769Ssam if (argc < 2) { 1197769Ssam strcpy(line, "Connect "); 1207769Ssam printf("(to) "); 1217769Ssam gets(&line[strlen(line)]); 1227769Ssam makeargv(); 1237769Ssam argc = margc; 1247769Ssam argv = margv; 1257769Ssam } 1267769Ssam if (argc > 3) { 1277769Ssam printf("usage: %s host-name [port]\n", argv[0]); 1287769Ssam return; 1297769Ssam } 1308384Ssam host = gethostbyname(argv[1]); 1318384Ssam if (host) { 1329219Ssam sin.sin_family = host->h_addrtype; 1338384Ssam bcopy(host->h_addr, &sin.sin_addr, host->h_length); 1348384Ssam hostname = host->h_name; 1358384Ssam } else { 1369219Ssam sin.sin_family = AF_INET; 1378384Ssam sin.sin_addr.s_addr = inet_addr(argv[1]); 1388384Ssam if (sin.sin_addr.s_addr == -1) { 1398384Ssam connected = 0; 1408384Ssam printf("%s: unknown host\n", argv[1]); 1418384Ssam return; 1428384Ssam } 1438384Ssam strcpy(hnamebuf, argv[1]); 1448384Ssam hostname = hnamebuf; 1457769Ssam } 1468384Ssam sin.sin_port = sp->s_port; 1477769Ssam if (argc == 3) { 1487769Ssam sin.sin_port = atoi(argv[2]); 1497769Ssam if (sin.sin_port < 0) { 1507769Ssam printf("%s: bad port number\n", argv[2]); 1517769Ssam connected = 0; 1527769Ssam return; 1537769Ssam } 1549971Ssam sin.sin_port = htons((u_short)sin.sin_port); 1558384Ssam } 1567769Ssam connected = 1; 1577769Ssam } 1587769Ssam 1597769Ssam struct modes { 1607769Ssam char *m_name; 1617769Ssam char *m_mode; 1627769Ssam } modes[] = { 163*16383Ssam { "binary", "octet" }, 164*16383Ssam { "image", "octet" }, 165*16383Ssam { "octet", "octet" }, 1667769Ssam { 0, 0 } 1677769Ssam }; 1687769Ssam 1697769Ssam setmode(argc, argv) 1707769Ssam char *argv[]; 1717769Ssam { 1727769Ssam register struct modes *p; 1737769Ssam 1747769Ssam if (argc > 2) { 1757769Ssam char *sep; 1767769Ssam 1777769Ssam printf("usage: %s [", argv[0]); 1787769Ssam sep = " "; 1797769Ssam for (p = modes; p->m_name; p++) { 1807769Ssam printf("%s%s", sep, p->m_name); 1817769Ssam if (*sep == ' ') 1827769Ssam sep = " | "; 1837769Ssam } 1847769Ssam printf(" ]\n"); 1857769Ssam return; 1867769Ssam } 1877769Ssam if (argc < 2) { 1887769Ssam printf("Using %s mode to transfer files.\n", mode); 1897769Ssam return; 1907769Ssam } 1917769Ssam for (p = modes; p->m_name; p++) 1927769Ssam if (strcmp(argv[1], p->m_name) == 0) 1937769Ssam break; 1947769Ssam if (p->m_name) 1957769Ssam strcpy(mode, p->m_mode); 1967769Ssam else 1977769Ssam printf("%s: unknown mode\n", argv[1]); 1987769Ssam } 1997769Ssam 2007769Ssam /* 2017769Ssam * Send file(s). 2027769Ssam */ 2037769Ssam put(argc, argv) 2047769Ssam char *argv[]; 2057769Ssam { 2067769Ssam int fd; 2077769Ssam register int n, addr; 2087769Ssam register char *cp, *targ; 2097769Ssam 2107769Ssam if (argc < 2) { 2117769Ssam strcpy(line, "send "); 2127769Ssam printf("(file) "); 2137769Ssam gets(&line[strlen(line)]); 2147769Ssam makeargv(); 2157769Ssam argc = margc; 2167769Ssam argv = margv; 2177769Ssam } 2187769Ssam if (argc < 2) { 2197769Ssam putusage(argv[0]); 2207769Ssam return; 2217769Ssam } 2227769Ssam targ = argv[argc - 1]; 2237769Ssam if (index(argv[argc - 1], ':')) { 2248384Ssam char *cp; 2258384Ssam struct hostent *hp; 2267769Ssam 2277769Ssam for (n = 1; n < argc - 1; n++) 2287769Ssam if (index(argv[n], ':')) { 2297769Ssam putusage(argv[0]); 2307769Ssam return; 2317769Ssam } 2328384Ssam cp = argv[argc - 1]; 2338384Ssam targ = index(cp, ':'); 2347769Ssam *targ++ = 0; 2358384Ssam hp = gethostbyname(cp); 2368384Ssam if (hp == 0) { 2378384Ssam printf("%s: Unknown host.\n", cp); 2387769Ssam return; 2397769Ssam } 2409219Ssam bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 2418384Ssam sin.sin_family = hp->h_addrtype; 2427769Ssam connected = 1; 2438384Ssam hostname = hp->h_name; 2447769Ssam } 2457769Ssam if (!connected) { 2467769Ssam printf("No target machine specified.\n"); 2477769Ssam return; 2487769Ssam } 2497769Ssam if (argc < 4) { 2507769Ssam cp = argc == 2 ? tail(targ) : argv[1]; 25113017Ssam fd = open(cp, O_RDONLY); 2527769Ssam if (fd < 0) { 25313017Ssam fprintf(stderr, "tftp: "); perror(cp); 2547769Ssam return; 2557769Ssam } 256*16383Ssam (void) sendfile(fd, targ); 2577769Ssam return; 2587769Ssam } 2597769Ssam cp = index(targ, '\0'); 2607769Ssam *cp++ = '/'; 2617769Ssam for (n = 1; n < argc - 1; n++) { 2627769Ssam strcpy(cp, tail(argv[n])); 26313017Ssam fd = open(argv[n], O_RDONLY); 2647769Ssam if (fd < 0) { 26513017Ssam fprintf(stderr, "tftp: "); perror(argv[n]); 2667769Ssam continue; 2677769Ssam } 268*16383Ssam (void) sendfile(fd, targ); 2697769Ssam } 2707769Ssam } 2717769Ssam 2727769Ssam putusage(s) 2737769Ssam char *s; 2747769Ssam { 2757769Ssam printf("usage: %s file ... host:target, or\n", s); 2767769Ssam printf(" %s file ... target (when already connected)\n", s); 2777769Ssam } 2787769Ssam 2797769Ssam /* 2807769Ssam * Receive file(s). 2817769Ssam */ 2827769Ssam get(argc, argv) 2837769Ssam char *argv[]; 2847769Ssam { 2857769Ssam int fd; 2867769Ssam register int n, addr; 2877769Ssam register char *cp; 2887769Ssam char *src; 2897769Ssam 2907769Ssam if (argc < 2) { 2917769Ssam strcpy(line, "get "); 2927769Ssam printf("(files) "); 2937769Ssam gets(&line[strlen(line)]); 2947769Ssam makeargv(); 2957769Ssam argc = margc; 2967769Ssam argv = margv; 2977769Ssam } 2987769Ssam if (argc < 2) { 2997769Ssam getusage(argv[0]); 3007769Ssam return; 3017769Ssam } 3027769Ssam if (!connected) 3037769Ssam for (n = 1; n < argc - 1; n++) 3047769Ssam if (index(argv[n], ':') == 0) { 3057769Ssam getusage(argv[0]); 3067769Ssam return; 3077769Ssam } 3087769Ssam for (n = 1; argc == 2 || n < argc - 1; n++) { 3097769Ssam src = index(argv[n], ':'); 3107769Ssam if (src == NULL) 3117769Ssam src = argv[n]; 3127769Ssam else { 3138384Ssam struct hostent *hp; 3148384Ssam 3157769Ssam *src++ = 0; 3168384Ssam hp = gethostbyname(argv[n]); 3178384Ssam if (hp == 0) { 3187769Ssam printf("%s: Unknown host.\n", argv[n]); 3197769Ssam continue; 3207769Ssam } 3219219Ssam bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 3228384Ssam sin.sin_family = hp->h_addrtype; 3237769Ssam connected = 1; 3248384Ssam hostname = hp->h_name; 3257769Ssam } 3267769Ssam if (argc < 4) { 3277769Ssam cp = argc == 3 ? argv[2] : tail(src); 3287769Ssam fd = creat(cp, 0644); 3297769Ssam if (fd < 0) { 33013017Ssam fprintf(stderr, "tftp: "); perror(cp); 3317769Ssam return; 3327769Ssam } 333*16383Ssam (void) recvfile(fd, src); 3347769Ssam break; 3357769Ssam } 3367769Ssam cp = index(argv[argc - 1], '\0'); 3377769Ssam *cp++ = '/'; 3387769Ssam strcpy(cp, tail(src)); 3397769Ssam fd = creat(src, 0644); 3407769Ssam if (fd < 0) { 34113017Ssam fprintf(stderr, "tftp: "); perror(src); 3427769Ssam continue; 3437769Ssam } 344*16383Ssam (void) recvfile(fd, src); 3457769Ssam } 3467769Ssam } 3477769Ssam 3487769Ssam getusage(s) 3497769Ssam { 3507769Ssam printf("usage: %s host:file host:file ... file, or\n", s); 3517769Ssam printf(" %s file file ... file if connected\n", s); 3527769Ssam } 3537769Ssam 35413017Ssam int rexmtval = TIMEOUT; 35513017Ssam 35613017Ssam setrexmt(argc, argv) 35713017Ssam char *argv[]; 35813017Ssam { 35913017Ssam int t; 36013017Ssam 36113017Ssam if (argc < 2) { 36213017Ssam strcpy(line, "Rexmt-timeout "); 36313017Ssam printf("(value) "); 36413017Ssam gets(&line[strlen(line)]); 36513017Ssam makeargv(); 36613017Ssam argc = margc; 36713017Ssam argv = margv; 36813017Ssam } 36913017Ssam if (argc != 2) { 37013017Ssam printf("usage: %s value\n", argv[0]); 37113017Ssam return; 37213017Ssam } 37313017Ssam t = atoi(argv[1]); 37413017Ssam if (t < 0) 37513017Ssam printf("%s: bad value\n", t); 37613017Ssam else 37713017Ssam rexmtval = t; 37813017Ssam } 37913017Ssam 38013017Ssam int maxtimeout = 5 * TIMEOUT; 38113017Ssam 38213017Ssam settimeout(argc, argv) 38313017Ssam char *argv[]; 38413017Ssam { 38513017Ssam int t; 38613017Ssam 38713017Ssam if (argc < 2) { 38813017Ssam strcpy(line, "Maximum-timeout "); 38913017Ssam printf("(value) "); 39013017Ssam gets(&line[strlen(line)]); 39113017Ssam makeargv(); 39213017Ssam argc = margc; 39313017Ssam argv = margv; 39413017Ssam } 39513017Ssam if (argc != 2) { 39613017Ssam printf("usage: %s value\n", argv[0]); 39713017Ssam return; 39813017Ssam } 39913017Ssam t = atoi(argv[1]); 40013017Ssam if (t < 0) 40113017Ssam printf("%s: bad value\n", t); 40213017Ssam else 40313017Ssam maxtimeout = t; 40413017Ssam } 40513017Ssam 4067769Ssam status(argc, argv) 4077769Ssam char *argv[]; 4087769Ssam { 4097769Ssam if (connected) 4108384Ssam printf("Connected to %s.\n", hostname); 4117769Ssam else 4127769Ssam printf("Not connected.\n"); 413*16383Ssam printf("Mode: %s Tracing: %s\n", mode, trace ? "on" : "off"); 41413017Ssam printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 41513017Ssam rexmtval, maxtimeout); 4167769Ssam } 4177769Ssam 4187769Ssam intr() 4197769Ssam { 42013017Ssam 42116382Ssam alarm(0); 4227769Ssam longjmp(toplevel, -1); 4237769Ssam } 4247769Ssam 4257769Ssam char * 4267769Ssam tail(filename) 4277769Ssam char *filename; 4287769Ssam { 4297769Ssam register char *s; 4307769Ssam 4317769Ssam while (*filename) { 4327769Ssam s = rindex(filename, '/'); 4337769Ssam if (s == NULL) 4347769Ssam break; 4357769Ssam if (s[1]) 4367769Ssam return (s + 1); 4377769Ssam *s = '\0'; 4387769Ssam } 4397769Ssam return (filename); 4407769Ssam } 4417769Ssam 4427769Ssam /* 4437769Ssam * Command parser. 4447769Ssam */ 4457769Ssam command(top) 4467769Ssam int top; 4477769Ssam { 4487769Ssam register struct cmd *c; 4497769Ssam 4507769Ssam if (!top) 4517769Ssam putchar('\n'); 4527769Ssam for (;;) { 4537769Ssam printf("%s> ", prompt); 4547769Ssam if (gets(line) == 0) 45513017Ssam continue; 4567769Ssam if (line[0] == 0) 45713017Ssam continue; 4587769Ssam makeargv(); 4597769Ssam c = getcmd(margv[0]); 4607769Ssam if (c == (struct cmd *)-1) { 4617769Ssam printf("?Ambiguous command\n"); 4627769Ssam continue; 4637769Ssam } 4647769Ssam if (c == 0) { 4657769Ssam printf("?Invalid command\n"); 4667769Ssam continue; 4677769Ssam } 4687769Ssam (*c->handler)(margc, margv); 4697769Ssam } 4707769Ssam } 4717769Ssam 4727769Ssam struct cmd * 4737769Ssam getcmd(name) 4747769Ssam register char *name; 4757769Ssam { 4767769Ssam register char *p, *q; 4777769Ssam register struct cmd *c, *found; 4787769Ssam register int nmatches, longest; 4797769Ssam 4807769Ssam longest = 0; 4817769Ssam nmatches = 0; 4827769Ssam found = 0; 4837769Ssam for (c = cmdtab; p = c->name; c++) { 4847769Ssam for (q = name; *q == *p++; q++) 4857769Ssam if (*q == 0) /* exact match? */ 4867769Ssam return (c); 4877769Ssam if (!*q) { /* the name was a prefix */ 4887769Ssam if (q - name > longest) { 4897769Ssam longest = q - name; 4907769Ssam nmatches = 1; 4917769Ssam found = c; 4927769Ssam } else if (q - name == longest) 4937769Ssam nmatches++; 4947769Ssam } 4957769Ssam } 4967769Ssam if (nmatches > 1) 4977769Ssam return ((struct cmd *)-1); 4987769Ssam return (found); 4997769Ssam } 5007769Ssam 5017769Ssam /* 5027769Ssam * Slice a string up into argc/argv. 5037769Ssam */ 5047769Ssam makeargv() 5057769Ssam { 5067769Ssam register char *cp; 5077769Ssam register char **argp = margv; 5087769Ssam 5097769Ssam margc = 0; 5107769Ssam for (cp = line; *cp;) { 5117769Ssam while (isspace(*cp)) 5127769Ssam cp++; 5137769Ssam if (*cp == '\0') 5147769Ssam break; 5157769Ssam *argp++ = cp; 5167769Ssam margc += 1; 5177769Ssam while (*cp != '\0' && !isspace(*cp)) 5187769Ssam cp++; 5197769Ssam if (*cp == '\0') 5207769Ssam break; 5217769Ssam *cp++ = '\0'; 5227769Ssam } 5237769Ssam *argp++ = 0; 5247769Ssam } 5257769Ssam 5267769Ssam /*VARARGS*/ 5277769Ssam quit() 5287769Ssam { 5297769Ssam exit(0); 5307769Ssam } 5317769Ssam 5327769Ssam /* 5337769Ssam * Help command. 5347769Ssam */ 5357769Ssam help(argc, argv) 5367769Ssam int argc; 5377769Ssam char *argv[]; 5387769Ssam { 5397769Ssam register struct cmd *c; 5407769Ssam 5417769Ssam if (argc == 1) { 5427769Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 5437769Ssam for (c = cmdtab; c->name; c++) 5447769Ssam printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 5457769Ssam return; 5467769Ssam } 5477769Ssam while (--argc > 0) { 5487769Ssam register char *arg; 5497769Ssam arg = *++argv; 5507769Ssam c = getcmd(arg); 5517769Ssam if (c == (struct cmd *)-1) 5527769Ssam printf("?Ambiguous help command %s\n", arg); 5537769Ssam else if (c == (struct cmd *)0) 5547769Ssam printf("?Invalid help command %s\n", arg); 5557769Ssam else 5567769Ssam printf("%s\n", c->help); 5577769Ssam } 5587769Ssam } 5597769Ssam 5607769Ssam /*VARARGS*/ 5617769Ssam settrace() 5627769Ssam { 5637769Ssam trace = !trace; 5647769Ssam printf("Packet tracing %s.\n", trace ? "on" : "off"); 5657769Ssam } 566