1*22403Sdist /* 2*22403Sdist * Copyright (c) 1983 Regents of the University of California. 3*22403Sdist * All rights reserved. The Berkeley software License Agreement 4*22403Sdist * specifies the terms and conditions for redistribution. 5*22403Sdist */ 6*22403Sdist 714553Ssam #ifndef lint 8*22403Sdist char copyright[] = 9*22403Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10*22403Sdist All rights reserved.\n"; 11*22403Sdist #endif not lint 127769Ssam 13*22403Sdist #ifndef lint 14*22403Sdist static char sccsid[] = "@(#)main.c 5.1 (Berkeley) 06/06/85"; 15*22403Sdist #endif not lint 16*22403Sdist 177769Ssam /* 187769Ssam * TFTP User Program -- Command Interface. 197769Ssam */ 207769Ssam #include <sys/types.h> 217769Ssam #include <sys/socket.h> 2213017Ssam #include <sys/file.h> 239219Ssam 249219Ssam #include <netinet/in.h> 259219Ssam 267769Ssam #include <signal.h> 277769Ssam #include <stdio.h> 287769Ssam #include <errno.h> 297769Ssam #include <setjmp.h> 307769Ssam #include <ctype.h> 318384Ssam #include <netdb.h> 327769Ssam 3313017Ssam #define TIMEOUT 5 /* secs between rexmt's */ 3413017Ssam 3516383Ssam struct sockaddr_in sin = { AF_INET }; 367769Ssam int f; 377769Ssam int trace; 387769Ssam int connected; 397769Ssam char mode[32]; 407769Ssam char line[200]; 417769Ssam int margc; 427769Ssam char *margv[20]; 437769Ssam char *prompt = "tftp"; 447769Ssam jmp_buf toplevel; 457769Ssam int intr(); 468384Ssam struct servent *sp; 477769Ssam 4816383Ssam int quit(), help(), settrace(), status(); 4913017Ssam int get(), put(), setpeer(), setmode(), setrexmt(), settimeout(); 507769Ssam 517769Ssam #define HELPINDENT (sizeof("connect")) 527769Ssam 537769Ssam struct cmd { 547769Ssam char *name; 557769Ssam char *help; 567769Ssam int (*handler)(); 577769Ssam }; 587769Ssam 597769Ssam char thelp[] = "toggle packet tracing"; 607769Ssam char chelp[] = "connect to remote tftp"; 617769Ssam char qhelp[] = "exit tftp"; 627769Ssam char hhelp[] = "print help information"; 637769Ssam char shelp[] = "send file"; 647769Ssam char rhelp[] = "receive file"; 657769Ssam char mhelp[] = "set file transfer mode"; 667769Ssam char sthelp[] = "show current status"; 6713017Ssam char xhelp[] = "set per-packet retransmission timeout"; 6813017Ssam char ihelp[] = "set total retransmission timeout"; 697769Ssam 707769Ssam struct cmd cmdtab[] = { 717769Ssam { "connect", chelp, setpeer }, 727769Ssam { "mode", mhelp, setmode }, 737769Ssam { "put", shelp, put }, 747769Ssam { "get", rhelp, get }, 757769Ssam { "quit", qhelp, quit }, 767769Ssam { "trace", thelp, settrace }, 777769Ssam { "status", sthelp, status }, 7813017Ssam { "rexmt", xhelp, setrexmt }, 7913017Ssam { "timeout", ihelp, settimeout }, 807769Ssam { "?", hhelp, help }, 817769Ssam 0 827769Ssam }; 837769Ssam 847769Ssam struct cmd *getcmd(); 857769Ssam char *tail(); 867769Ssam char *index(); 877769Ssam char *rindex(); 887769Ssam 897769Ssam main(argc, argv) 907769Ssam char *argv[]; 917769Ssam { 9213017Ssam int top; 9313017Ssam 948384Ssam sp = getservbyname("tftp", "udp"); 958384Ssam if (sp == 0) { 968384Ssam fprintf(stderr, "tftp: udp/tftp: unknown service\n"); 978384Ssam exit(1); 988384Ssam } 999262Ssam f = socket(AF_INET, SOCK_DGRAM, 0, 0); 1007769Ssam if (f < 0) { 10113017Ssam perror("tftp: socket"); 1027769Ssam exit(3); 1037769Ssam } 10413017Ssam if (bind(f, &sin, sizeof (sin)) < 0) { 10513017Ssam perror("tftp: bind"); 10613017Ssam exit(1); 10713017Ssam } 10816383Ssam strcpy(mode, "octet"); 10913017Ssam signal(SIGINT, intr); 1107769Ssam if (argc > 1) { 1117769Ssam if (setjmp(toplevel) != 0) 1127769Ssam exit(0); 1137769Ssam setpeer(argc, argv); 1147769Ssam } 11513017Ssam top = setjmp(toplevel) == 0; 1167769Ssam for (;;) 11713017Ssam command(top); 1187769Ssam } 1197769Ssam 1208384Ssam char *hostname; 1218384Ssam char hnamebuf[32]; 1227769Ssam 1237769Ssam setpeer(argc, argv) 1247769Ssam int argc; 1257769Ssam char *argv[]; 1267769Ssam { 1277769Ssam register int c; 1288384Ssam struct hostent *host; 1297769Ssam 1307769Ssam if (argc < 2) { 1317769Ssam strcpy(line, "Connect "); 1327769Ssam printf("(to) "); 1337769Ssam gets(&line[strlen(line)]); 1347769Ssam makeargv(); 1357769Ssam argc = margc; 1367769Ssam argv = margv; 1377769Ssam } 1387769Ssam if (argc > 3) { 1397769Ssam printf("usage: %s host-name [port]\n", argv[0]); 1407769Ssam return; 1417769Ssam } 1428384Ssam host = gethostbyname(argv[1]); 1438384Ssam if (host) { 1449219Ssam sin.sin_family = host->h_addrtype; 1458384Ssam bcopy(host->h_addr, &sin.sin_addr, host->h_length); 1468384Ssam hostname = host->h_name; 1478384Ssam } else { 1489219Ssam sin.sin_family = AF_INET; 1498384Ssam sin.sin_addr.s_addr = inet_addr(argv[1]); 1508384Ssam if (sin.sin_addr.s_addr == -1) { 1518384Ssam connected = 0; 1528384Ssam printf("%s: unknown host\n", argv[1]); 1538384Ssam return; 1548384Ssam } 1558384Ssam strcpy(hnamebuf, argv[1]); 1568384Ssam hostname = hnamebuf; 1577769Ssam } 1588384Ssam sin.sin_port = sp->s_port; 1597769Ssam if (argc == 3) { 1607769Ssam sin.sin_port = atoi(argv[2]); 1617769Ssam if (sin.sin_port < 0) { 1627769Ssam printf("%s: bad port number\n", argv[2]); 1637769Ssam connected = 0; 1647769Ssam return; 1657769Ssam } 1669971Ssam sin.sin_port = htons((u_short)sin.sin_port); 1678384Ssam } 1687769Ssam connected = 1; 1697769Ssam } 1707769Ssam 1717769Ssam struct modes { 1727769Ssam char *m_name; 1737769Ssam char *m_mode; 1747769Ssam } modes[] = { 17516383Ssam { "binary", "octet" }, 17616383Ssam { "image", "octet" }, 17716383Ssam { "octet", "octet" }, 1787769Ssam { 0, 0 } 1797769Ssam }; 1807769Ssam 1817769Ssam setmode(argc, argv) 1827769Ssam char *argv[]; 1837769Ssam { 1847769Ssam register struct modes *p; 1857769Ssam 1867769Ssam if (argc > 2) { 1877769Ssam char *sep; 1887769Ssam 1897769Ssam printf("usage: %s [", argv[0]); 1907769Ssam sep = " "; 1917769Ssam for (p = modes; p->m_name; p++) { 1927769Ssam printf("%s%s", sep, p->m_name); 1937769Ssam if (*sep == ' ') 1947769Ssam sep = " | "; 1957769Ssam } 1967769Ssam printf(" ]\n"); 1977769Ssam return; 1987769Ssam } 1997769Ssam if (argc < 2) { 2007769Ssam printf("Using %s mode to transfer files.\n", mode); 2017769Ssam return; 2027769Ssam } 2037769Ssam for (p = modes; p->m_name; p++) 2047769Ssam if (strcmp(argv[1], p->m_name) == 0) 2057769Ssam break; 2067769Ssam if (p->m_name) 2077769Ssam strcpy(mode, p->m_mode); 2087769Ssam else 2097769Ssam printf("%s: unknown mode\n", argv[1]); 2107769Ssam } 2117769Ssam 2127769Ssam /* 2137769Ssam * Send file(s). 2147769Ssam */ 2157769Ssam put(argc, argv) 2167769Ssam char *argv[]; 2177769Ssam { 2187769Ssam int fd; 2197769Ssam register int n, addr; 2207769Ssam register char *cp, *targ; 2217769Ssam 2227769Ssam if (argc < 2) { 2237769Ssam strcpy(line, "send "); 2247769Ssam printf("(file) "); 2257769Ssam gets(&line[strlen(line)]); 2267769Ssam makeargv(); 2277769Ssam argc = margc; 2287769Ssam argv = margv; 2297769Ssam } 2307769Ssam if (argc < 2) { 2317769Ssam putusage(argv[0]); 2327769Ssam return; 2337769Ssam } 2347769Ssam targ = argv[argc - 1]; 2357769Ssam if (index(argv[argc - 1], ':')) { 2368384Ssam char *cp; 2378384Ssam struct hostent *hp; 2387769Ssam 2397769Ssam for (n = 1; n < argc - 1; n++) 2407769Ssam if (index(argv[n], ':')) { 2417769Ssam putusage(argv[0]); 2427769Ssam return; 2437769Ssam } 2448384Ssam cp = argv[argc - 1]; 2458384Ssam targ = index(cp, ':'); 2467769Ssam *targ++ = 0; 2478384Ssam hp = gethostbyname(cp); 2488384Ssam if (hp == 0) { 2498384Ssam printf("%s: Unknown host.\n", cp); 2507769Ssam return; 2517769Ssam } 2529219Ssam bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 2538384Ssam sin.sin_family = hp->h_addrtype; 2547769Ssam connected = 1; 2558384Ssam hostname = hp->h_name; 2567769Ssam } 2577769Ssam if (!connected) { 2587769Ssam printf("No target machine specified.\n"); 2597769Ssam return; 2607769Ssam } 2617769Ssam if (argc < 4) { 2627769Ssam cp = argc == 2 ? tail(targ) : argv[1]; 26313017Ssam fd = open(cp, O_RDONLY); 2647769Ssam if (fd < 0) { 26513017Ssam fprintf(stderr, "tftp: "); perror(cp); 2667769Ssam return; 2677769Ssam } 26816383Ssam (void) sendfile(fd, targ); 2697769Ssam return; 2707769Ssam } 2717769Ssam cp = index(targ, '\0'); 2727769Ssam *cp++ = '/'; 2737769Ssam for (n = 1; n < argc - 1; n++) { 2747769Ssam strcpy(cp, tail(argv[n])); 27513017Ssam fd = open(argv[n], O_RDONLY); 2767769Ssam if (fd < 0) { 27713017Ssam fprintf(stderr, "tftp: "); perror(argv[n]); 2787769Ssam continue; 2797769Ssam } 28016383Ssam (void) sendfile(fd, targ); 2817769Ssam } 2827769Ssam } 2837769Ssam 2847769Ssam putusage(s) 2857769Ssam char *s; 2867769Ssam { 2877769Ssam printf("usage: %s file ... host:target, or\n", s); 2887769Ssam printf(" %s file ... target (when already connected)\n", s); 2897769Ssam } 2907769Ssam 2917769Ssam /* 2927769Ssam * Receive file(s). 2937769Ssam */ 2947769Ssam get(argc, argv) 2957769Ssam char *argv[]; 2967769Ssam { 2977769Ssam int fd; 2987769Ssam register int n, addr; 2997769Ssam register char *cp; 3007769Ssam char *src; 3017769Ssam 3027769Ssam if (argc < 2) { 3037769Ssam strcpy(line, "get "); 3047769Ssam printf("(files) "); 3057769Ssam gets(&line[strlen(line)]); 3067769Ssam makeargv(); 3077769Ssam argc = margc; 3087769Ssam argv = margv; 3097769Ssam } 3107769Ssam if (argc < 2) { 3117769Ssam getusage(argv[0]); 3127769Ssam return; 3137769Ssam } 3147769Ssam if (!connected) 3157769Ssam for (n = 1; n < argc - 1; n++) 3167769Ssam if (index(argv[n], ':') == 0) { 3177769Ssam getusage(argv[0]); 3187769Ssam return; 3197769Ssam } 3207769Ssam for (n = 1; argc == 2 || n < argc - 1; n++) { 3217769Ssam src = index(argv[n], ':'); 3227769Ssam if (src == NULL) 3237769Ssam src = argv[n]; 3247769Ssam else { 3258384Ssam struct hostent *hp; 3268384Ssam 3277769Ssam *src++ = 0; 3288384Ssam hp = gethostbyname(argv[n]); 3298384Ssam if (hp == 0) { 3307769Ssam printf("%s: Unknown host.\n", argv[n]); 3317769Ssam continue; 3327769Ssam } 3339219Ssam bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 3348384Ssam sin.sin_family = hp->h_addrtype; 3357769Ssam connected = 1; 3368384Ssam hostname = hp->h_name; 3377769Ssam } 3387769Ssam if (argc < 4) { 3397769Ssam cp = argc == 3 ? argv[2] : tail(src); 3407769Ssam fd = creat(cp, 0644); 3417769Ssam if (fd < 0) { 34213017Ssam fprintf(stderr, "tftp: "); perror(cp); 3437769Ssam return; 3447769Ssam } 34516383Ssam (void) recvfile(fd, src); 3467769Ssam break; 3477769Ssam } 3487769Ssam cp = index(argv[argc - 1], '\0'); 3497769Ssam *cp++ = '/'; 3507769Ssam strcpy(cp, tail(src)); 3517769Ssam fd = creat(src, 0644); 3527769Ssam if (fd < 0) { 35313017Ssam fprintf(stderr, "tftp: "); perror(src); 3547769Ssam continue; 3557769Ssam } 35616383Ssam (void) recvfile(fd, src); 3577769Ssam } 3587769Ssam } 3597769Ssam 3607769Ssam getusage(s) 3617769Ssam { 3627769Ssam printf("usage: %s host:file host:file ... file, or\n", s); 3637769Ssam printf(" %s file file ... file if connected\n", s); 3647769Ssam } 3657769Ssam 36613017Ssam int rexmtval = TIMEOUT; 36713017Ssam 36813017Ssam setrexmt(argc, argv) 36913017Ssam char *argv[]; 37013017Ssam { 37113017Ssam int t; 37213017Ssam 37313017Ssam if (argc < 2) { 37413017Ssam strcpy(line, "Rexmt-timeout "); 37513017Ssam printf("(value) "); 37613017Ssam gets(&line[strlen(line)]); 37713017Ssam makeargv(); 37813017Ssam argc = margc; 37913017Ssam argv = margv; 38013017Ssam } 38113017Ssam if (argc != 2) { 38213017Ssam printf("usage: %s value\n", argv[0]); 38313017Ssam return; 38413017Ssam } 38513017Ssam t = atoi(argv[1]); 38613017Ssam if (t < 0) 38713017Ssam printf("%s: bad value\n", t); 38813017Ssam else 38913017Ssam rexmtval = t; 39013017Ssam } 39113017Ssam 39213017Ssam int maxtimeout = 5 * TIMEOUT; 39313017Ssam 39413017Ssam settimeout(argc, argv) 39513017Ssam char *argv[]; 39613017Ssam { 39713017Ssam int t; 39813017Ssam 39913017Ssam if (argc < 2) { 40013017Ssam strcpy(line, "Maximum-timeout "); 40113017Ssam printf("(value) "); 40213017Ssam gets(&line[strlen(line)]); 40313017Ssam makeargv(); 40413017Ssam argc = margc; 40513017Ssam argv = margv; 40613017Ssam } 40713017Ssam if (argc != 2) { 40813017Ssam printf("usage: %s value\n", argv[0]); 40913017Ssam return; 41013017Ssam } 41113017Ssam t = atoi(argv[1]); 41213017Ssam if (t < 0) 41313017Ssam printf("%s: bad value\n", t); 41413017Ssam else 41513017Ssam maxtimeout = t; 41613017Ssam } 41713017Ssam 4187769Ssam status(argc, argv) 4197769Ssam char *argv[]; 4207769Ssam { 4217769Ssam if (connected) 4228384Ssam printf("Connected to %s.\n", hostname); 4237769Ssam else 4247769Ssam printf("Not connected.\n"); 42516383Ssam printf("Mode: %s Tracing: %s\n", mode, trace ? "on" : "off"); 42613017Ssam printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 42713017Ssam rexmtval, maxtimeout); 4287769Ssam } 4297769Ssam 4307769Ssam intr() 4317769Ssam { 43213017Ssam 43316382Ssam alarm(0); 4347769Ssam longjmp(toplevel, -1); 4357769Ssam } 4367769Ssam 4377769Ssam char * 4387769Ssam tail(filename) 4397769Ssam char *filename; 4407769Ssam { 4417769Ssam register char *s; 4427769Ssam 4437769Ssam while (*filename) { 4447769Ssam s = rindex(filename, '/'); 4457769Ssam if (s == NULL) 4467769Ssam break; 4477769Ssam if (s[1]) 4487769Ssam return (s + 1); 4497769Ssam *s = '\0'; 4507769Ssam } 4517769Ssam return (filename); 4527769Ssam } 4537769Ssam 4547769Ssam /* 4557769Ssam * Command parser. 4567769Ssam */ 4577769Ssam command(top) 4587769Ssam int top; 4597769Ssam { 4607769Ssam register struct cmd *c; 4617769Ssam 4627769Ssam if (!top) 4637769Ssam putchar('\n'); 4647769Ssam for (;;) { 4657769Ssam printf("%s> ", prompt); 4667769Ssam if (gets(line) == 0) 46713017Ssam continue; 4687769Ssam if (line[0] == 0) 46913017Ssam continue; 4707769Ssam makeargv(); 4717769Ssam c = getcmd(margv[0]); 4727769Ssam if (c == (struct cmd *)-1) { 4737769Ssam printf("?Ambiguous command\n"); 4747769Ssam continue; 4757769Ssam } 4767769Ssam if (c == 0) { 4777769Ssam printf("?Invalid command\n"); 4787769Ssam continue; 4797769Ssam } 4807769Ssam (*c->handler)(margc, margv); 4817769Ssam } 4827769Ssam } 4837769Ssam 4847769Ssam struct cmd * 4857769Ssam getcmd(name) 4867769Ssam register char *name; 4877769Ssam { 4887769Ssam register char *p, *q; 4897769Ssam register struct cmd *c, *found; 4907769Ssam register int nmatches, longest; 4917769Ssam 4927769Ssam longest = 0; 4937769Ssam nmatches = 0; 4947769Ssam found = 0; 4957769Ssam for (c = cmdtab; p = c->name; c++) { 4967769Ssam for (q = name; *q == *p++; q++) 4977769Ssam if (*q == 0) /* exact match? */ 4987769Ssam return (c); 4997769Ssam if (!*q) { /* the name was a prefix */ 5007769Ssam if (q - name > longest) { 5017769Ssam longest = q - name; 5027769Ssam nmatches = 1; 5037769Ssam found = c; 5047769Ssam } else if (q - name == longest) 5057769Ssam nmatches++; 5067769Ssam } 5077769Ssam } 5087769Ssam if (nmatches > 1) 5097769Ssam return ((struct cmd *)-1); 5107769Ssam return (found); 5117769Ssam } 5127769Ssam 5137769Ssam /* 5147769Ssam * Slice a string up into argc/argv. 5157769Ssam */ 5167769Ssam makeargv() 5177769Ssam { 5187769Ssam register char *cp; 5197769Ssam register char **argp = margv; 5207769Ssam 5217769Ssam margc = 0; 5227769Ssam for (cp = line; *cp;) { 5237769Ssam while (isspace(*cp)) 5247769Ssam cp++; 5257769Ssam if (*cp == '\0') 5267769Ssam break; 5277769Ssam *argp++ = cp; 5287769Ssam margc += 1; 5297769Ssam while (*cp != '\0' && !isspace(*cp)) 5307769Ssam cp++; 5317769Ssam if (*cp == '\0') 5327769Ssam break; 5337769Ssam *cp++ = '\0'; 5347769Ssam } 5357769Ssam *argp++ = 0; 5367769Ssam } 5377769Ssam 5387769Ssam /*VARARGS*/ 5397769Ssam quit() 5407769Ssam { 5417769Ssam exit(0); 5427769Ssam } 5437769Ssam 5447769Ssam /* 5457769Ssam * Help command. 5467769Ssam */ 5477769Ssam help(argc, argv) 5487769Ssam int argc; 5497769Ssam char *argv[]; 5507769Ssam { 5517769Ssam register struct cmd *c; 5527769Ssam 5537769Ssam if (argc == 1) { 5547769Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 5557769Ssam for (c = cmdtab; c->name; c++) 5567769Ssam printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 5577769Ssam return; 5587769Ssam } 5597769Ssam while (--argc > 0) { 5607769Ssam register char *arg; 5617769Ssam arg = *++argv; 5627769Ssam c = getcmd(arg); 5637769Ssam if (c == (struct cmd *)-1) 5647769Ssam printf("?Ambiguous help command %s\n", arg); 5657769Ssam else if (c == (struct cmd *)0) 5667769Ssam printf("?Invalid help command %s\n", arg); 5677769Ssam else 5687769Ssam printf("%s\n", c->help); 5697769Ssam } 5707769Ssam } 5717769Ssam 5727769Ssam /*VARARGS*/ 5737769Ssam settrace() 5747769Ssam { 5757769Ssam trace = !trace; 5767769Ssam printf("Packet tracing %s.\n", trace ? "on" : "off"); 5777769Ssam } 578