1*13017Ssam /* main.c 4.6 83/06/12 */ 27769Ssam 37769Ssam /* 47769Ssam * TFTP User Program -- Command Interface. 57769Ssam */ 67769Ssam #include <sys/types.h> 77769Ssam #include <sys/socket.h> 8*13017Ssam #include <sys/file.h> 99219Ssam 109219Ssam #include <netinet/in.h> 119219Ssam 127769Ssam #include <signal.h> 137769Ssam #include <stdio.h> 147769Ssam #include <errno.h> 157769Ssam #include <setjmp.h> 167769Ssam #include <ctype.h> 178384Ssam #include <netdb.h> 187769Ssam 19*13017Ssam #define TIMEOUT 5 /* secs between rexmt's */ 20*13017Ssam 219219Ssam struct sockaddr_in sin; 227769Ssam int f; 237769Ssam int trace; 247769Ssam int verbose; 257769Ssam int connected; 267769Ssam char mode[32]; 277769Ssam char line[200]; 287769Ssam int margc; 297769Ssam char *margv[20]; 307769Ssam char *prompt = "tftp"; 317769Ssam jmp_buf toplevel; 327769Ssam int intr(); 338384Ssam struct servent *sp; 347769Ssam 357769Ssam int quit(), help(), setverbose(), settrace(), status(); 36*13017Ssam int get(), put(), setpeer(), setmode(), setrexmt(), settimeout(); 377769Ssam 387769Ssam #define HELPINDENT (sizeof("connect")) 397769Ssam 407769Ssam struct cmd { 417769Ssam char *name; 427769Ssam char *help; 437769Ssam int (*handler)(); 447769Ssam }; 457769Ssam 467769Ssam char vhelp[] = "toggle verbose mode"; 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"; 55*13017Ssam char xhelp[] = "set per-packet retransmission timeout"; 56*13017Ssam 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 { "verbose", vhelp, setverbose }, 657769Ssam { "trace", thelp, settrace }, 667769Ssam { "status", sthelp, status }, 67*13017Ssam { "rexmt", xhelp, setrexmt }, 68*13017Ssam { "timeout", ihelp, settimeout }, 697769Ssam { "?", hhelp, help }, 707769Ssam 0 717769Ssam }; 727769Ssam 737769Ssam struct cmd *getcmd(); 747769Ssam char *tail(); 757769Ssam char *index(); 767769Ssam char *rindex(); 777769Ssam 787769Ssam main(argc, argv) 797769Ssam char *argv[]; 807769Ssam { 81*13017Ssam struct sockaddr_in sin; 82*13017Ssam int top; 83*13017Ssam 848384Ssam sp = getservbyname("tftp", "udp"); 858384Ssam if (sp == 0) { 868384Ssam fprintf(stderr, "tftp: udp/tftp: unknown service\n"); 878384Ssam exit(1); 888384Ssam } 899262Ssam f = socket(AF_INET, SOCK_DGRAM, 0, 0); 907769Ssam if (f < 0) { 91*13017Ssam perror("tftp: socket"); 927769Ssam exit(3); 937769Ssam } 94*13017Ssam bzero((char *)&sin, sizeof (sin)); 95*13017Ssam sin.sin_family = AF_INET; 96*13017Ssam if (bind(f, &sin, sizeof (sin)) < 0) { 97*13017Ssam perror("tftp: bind"); 98*13017Ssam exit(1); 99*13017Ssam } 1007769Ssam strcpy(mode, "netascii"); 101*13017Ssam signal(SIGINT, intr); 1027769Ssam if (argc > 1) { 1037769Ssam if (setjmp(toplevel) != 0) 1047769Ssam exit(0); 1057769Ssam setpeer(argc, argv); 1067769Ssam } 107*13017Ssam top = setjmp(toplevel) == 0; 1087769Ssam for (;;) 109*13017Ssam command(top); 1107769Ssam } 1117769Ssam 1128384Ssam char *hostname; 1138384Ssam char hnamebuf[32]; 1147769Ssam 1157769Ssam setpeer(argc, argv) 1167769Ssam int argc; 1177769Ssam char *argv[]; 1187769Ssam { 1197769Ssam register int c; 1208384Ssam struct hostent *host; 1217769Ssam 1227769Ssam if (argc < 2) { 1237769Ssam strcpy(line, "Connect "); 1247769Ssam printf("(to) "); 1257769Ssam gets(&line[strlen(line)]); 1267769Ssam makeargv(); 1277769Ssam argc = margc; 1287769Ssam argv = margv; 1297769Ssam } 1307769Ssam if (argc > 3) { 1317769Ssam printf("usage: %s host-name [port]\n", argv[0]); 1327769Ssam return; 1337769Ssam } 1348384Ssam host = gethostbyname(argv[1]); 1358384Ssam if (host) { 1369219Ssam sin.sin_family = host->h_addrtype; 1378384Ssam bcopy(host->h_addr, &sin.sin_addr, host->h_length); 1388384Ssam hostname = host->h_name; 1398384Ssam } else { 1409219Ssam sin.sin_family = AF_INET; 1418384Ssam sin.sin_addr.s_addr = inet_addr(argv[1]); 1428384Ssam if (sin.sin_addr.s_addr == -1) { 1438384Ssam connected = 0; 1448384Ssam printf("%s: unknown host\n", argv[1]); 1458384Ssam return; 1468384Ssam } 1478384Ssam strcpy(hnamebuf, argv[1]); 1488384Ssam hostname = hnamebuf; 1497769Ssam } 1508384Ssam sin.sin_port = sp->s_port; 1517769Ssam if (argc == 3) { 1527769Ssam sin.sin_port = atoi(argv[2]); 1537769Ssam if (sin.sin_port < 0) { 1547769Ssam printf("%s: bad port number\n", argv[2]); 1557769Ssam connected = 0; 1567769Ssam return; 1577769Ssam } 1589971Ssam sin.sin_port = htons((u_short)sin.sin_port); 1598384Ssam } 1607769Ssam connected = 1; 1617769Ssam } 1627769Ssam 1637769Ssam struct modes { 1647769Ssam char *m_name; 1657769Ssam char *m_mode; 1667769Ssam } modes[] = { 1677769Ssam { "ascii", "netascii" }, 1687769Ssam { "binary", "octect" }, 1697769Ssam { "mail", "mail" }, 1707769Ssam { 0, 0 } 1717769Ssam }; 1727769Ssam 1737769Ssam setmode(argc, argv) 1747769Ssam char *argv[]; 1757769Ssam { 1767769Ssam register struct modes *p; 1777769Ssam 1787769Ssam if (argc > 2) { 1797769Ssam char *sep; 1807769Ssam 1817769Ssam printf("usage: %s [", argv[0]); 1827769Ssam sep = " "; 1837769Ssam for (p = modes; p->m_name; p++) { 1847769Ssam printf("%s%s", sep, p->m_name); 1857769Ssam if (*sep == ' ') 1867769Ssam sep = " | "; 1877769Ssam } 1887769Ssam printf(" ]\n"); 1897769Ssam return; 1907769Ssam } 1917769Ssam if (argc < 2) { 1927769Ssam printf("Using %s mode to transfer files.\n", mode); 1937769Ssam return; 1947769Ssam } 1957769Ssam for (p = modes; p->m_name; p++) 1967769Ssam if (strcmp(argv[1], p->m_name) == 0) 1977769Ssam break; 1987769Ssam if (p->m_name) 1997769Ssam strcpy(mode, p->m_mode); 2007769Ssam else 2017769Ssam printf("%s: unknown mode\n", argv[1]); 2027769Ssam } 2037769Ssam 2047769Ssam /* 2057769Ssam * Send file(s). 2067769Ssam */ 2077769Ssam put(argc, argv) 2087769Ssam char *argv[]; 2097769Ssam { 2107769Ssam int fd; 2117769Ssam register int n, addr; 2127769Ssam register char *cp, *targ; 2137769Ssam 2147769Ssam if (argc < 2) { 2157769Ssam strcpy(line, "send "); 2167769Ssam printf("(file) "); 2177769Ssam gets(&line[strlen(line)]); 2187769Ssam makeargv(); 2197769Ssam argc = margc; 2207769Ssam argv = margv; 2217769Ssam } 2227769Ssam if (argc < 2) { 2237769Ssam putusage(argv[0]); 2247769Ssam return; 2257769Ssam } 2267769Ssam targ = argv[argc - 1]; 2277769Ssam if (index(argv[argc - 1], ':')) { 2288384Ssam char *cp; 2298384Ssam struct hostent *hp; 2307769Ssam 2317769Ssam for (n = 1; n < argc - 1; n++) 2327769Ssam if (index(argv[n], ':')) { 2337769Ssam putusage(argv[0]); 2347769Ssam return; 2357769Ssam } 2368384Ssam cp = argv[argc - 1]; 2378384Ssam targ = index(cp, ':'); 2387769Ssam *targ++ = 0; 2398384Ssam hp = gethostbyname(cp); 2408384Ssam if (hp == 0) { 2418384Ssam printf("%s: Unknown host.\n", cp); 2427769Ssam return; 2437769Ssam } 2449219Ssam bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 2458384Ssam sin.sin_family = hp->h_addrtype; 2467769Ssam connected = 1; 2478384Ssam hostname = hp->h_name; 2487769Ssam } 2497769Ssam if (!connected) { 2507769Ssam printf("No target machine specified.\n"); 2517769Ssam return; 2527769Ssam } 2537769Ssam if (argc < 4) { 2547769Ssam cp = argc == 2 ? tail(targ) : argv[1]; 255*13017Ssam fd = open(cp, O_RDONLY); 2567769Ssam if (fd < 0) { 257*13017Ssam fprintf(stderr, "tftp: "); perror(cp); 2587769Ssam return; 2597769Ssam } 2607769Ssam sendfile(fd, targ); 2617769Ssam return; 2627769Ssam } 2637769Ssam cp = index(targ, '\0'); 2647769Ssam *cp++ = '/'; 2657769Ssam for (n = 1; n < argc - 1; n++) { 2667769Ssam strcpy(cp, tail(argv[n])); 267*13017Ssam fd = open(argv[n], O_RDONLY); 2687769Ssam if (fd < 0) { 269*13017Ssam fprintf(stderr, "tftp: "); perror(argv[n]); 2707769Ssam continue; 2717769Ssam } 2727769Ssam sendfile(fd, targ); 2737769Ssam } 2747769Ssam } 2757769Ssam 2767769Ssam putusage(s) 2777769Ssam char *s; 2787769Ssam { 2797769Ssam printf("usage: %s file ... host:target, or\n", s); 2807769Ssam printf(" %s file ... target (when already connected)\n", s); 2817769Ssam } 2827769Ssam 2837769Ssam /* 2847769Ssam * Receive file(s). 2857769Ssam */ 2867769Ssam get(argc, argv) 2877769Ssam char *argv[]; 2887769Ssam { 2897769Ssam int fd; 2907769Ssam register int n, addr; 2917769Ssam register char *cp; 2927769Ssam char *src; 2937769Ssam 2947769Ssam if (argc < 2) { 2957769Ssam strcpy(line, "get "); 2967769Ssam printf("(files) "); 2977769Ssam gets(&line[strlen(line)]); 2987769Ssam makeargv(); 2997769Ssam argc = margc; 3007769Ssam argv = margv; 3017769Ssam } 3027769Ssam if (argc < 2) { 3037769Ssam getusage(argv[0]); 3047769Ssam return; 3057769Ssam } 3067769Ssam if (!connected) 3077769Ssam for (n = 1; n < argc - 1; n++) 3087769Ssam if (index(argv[n], ':') == 0) { 3097769Ssam getusage(argv[0]); 3107769Ssam return; 3117769Ssam } 3127769Ssam for (n = 1; argc == 2 || n < argc - 1; n++) { 3137769Ssam src = index(argv[n], ':'); 3147769Ssam if (src == NULL) 3157769Ssam src = argv[n]; 3167769Ssam else { 3178384Ssam struct hostent *hp; 3188384Ssam 3197769Ssam *src++ = 0; 3208384Ssam hp = gethostbyname(argv[n]); 3218384Ssam if (hp == 0) { 3227769Ssam printf("%s: Unknown host.\n", argv[n]); 3237769Ssam continue; 3247769Ssam } 3259219Ssam bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); 3268384Ssam sin.sin_family = hp->h_addrtype; 3277769Ssam connected = 1; 3288384Ssam hostname = hp->h_name; 3297769Ssam } 3307769Ssam if (argc < 4) { 3317769Ssam cp = argc == 3 ? argv[2] : tail(src); 3327769Ssam fd = creat(cp, 0644); 3337769Ssam if (fd < 0) { 334*13017Ssam fprintf(stderr, "tftp: "); perror(cp); 3357769Ssam return; 3367769Ssam } 3377769Ssam recvfile(fd, src); 3387769Ssam break; 3397769Ssam } 3407769Ssam cp = index(argv[argc - 1], '\0'); 3417769Ssam *cp++ = '/'; 3427769Ssam strcpy(cp, tail(src)); 3437769Ssam fd = creat(src, 0644); 3447769Ssam if (fd < 0) { 345*13017Ssam fprintf(stderr, "tftp: "); perror(src); 3467769Ssam continue; 3477769Ssam } 3487769Ssam recvfile(fd, src); 3497769Ssam } 3507769Ssam } 3517769Ssam 3527769Ssam getusage(s) 3537769Ssam { 3547769Ssam printf("usage: %s host:file host:file ... file, or\n", s); 3557769Ssam printf(" %s file file ... file if connected\n", s); 3567769Ssam } 3577769Ssam 358*13017Ssam int rexmtval = TIMEOUT; 359*13017Ssam 360*13017Ssam setrexmt(argc, argv) 361*13017Ssam char *argv[]; 362*13017Ssam { 363*13017Ssam int t; 364*13017Ssam 365*13017Ssam if (argc < 2) { 366*13017Ssam strcpy(line, "Rexmt-timeout "); 367*13017Ssam printf("(value) "); 368*13017Ssam gets(&line[strlen(line)]); 369*13017Ssam makeargv(); 370*13017Ssam argc = margc; 371*13017Ssam argv = margv; 372*13017Ssam } 373*13017Ssam if (argc != 2) { 374*13017Ssam printf("usage: %s value\n", argv[0]); 375*13017Ssam return; 376*13017Ssam } 377*13017Ssam t = atoi(argv[1]); 378*13017Ssam if (t < 0) 379*13017Ssam printf("%s: bad value\n", t); 380*13017Ssam else 381*13017Ssam rexmtval = t; 382*13017Ssam } 383*13017Ssam 384*13017Ssam int maxtimeout = 5 * TIMEOUT; 385*13017Ssam 386*13017Ssam settimeout(argc, argv) 387*13017Ssam char *argv[]; 388*13017Ssam { 389*13017Ssam int t; 390*13017Ssam 391*13017Ssam if (argc < 2) { 392*13017Ssam strcpy(line, "Maximum-timeout "); 393*13017Ssam printf("(value) "); 394*13017Ssam gets(&line[strlen(line)]); 395*13017Ssam makeargv(); 396*13017Ssam argc = margc; 397*13017Ssam argv = margv; 398*13017Ssam } 399*13017Ssam if (argc != 2) { 400*13017Ssam printf("usage: %s value\n", argv[0]); 401*13017Ssam return; 402*13017Ssam } 403*13017Ssam t = atoi(argv[1]); 404*13017Ssam if (t < 0) 405*13017Ssam printf("%s: bad value\n", t); 406*13017Ssam else 407*13017Ssam maxtimeout = t; 408*13017Ssam } 409*13017Ssam 4107769Ssam status(argc, argv) 4117769Ssam char *argv[]; 4127769Ssam { 4137769Ssam if (connected) 4148384Ssam printf("Connected to %s.\n", hostname); 4157769Ssam else 4167769Ssam printf("Not connected.\n"); 4177769Ssam printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 4187769Ssam verbose ? "on" : "off", trace ? "on" : "off"); 419*13017Ssam printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 420*13017Ssam rexmtval, maxtimeout); 4217769Ssam } 4227769Ssam 4237769Ssam intr() 4247769Ssam { 425*13017Ssam 4267769Ssam longjmp(toplevel, -1); 4277769Ssam } 4287769Ssam 4297769Ssam char * 4307769Ssam tail(filename) 4317769Ssam char *filename; 4327769Ssam { 4337769Ssam register char *s; 4347769Ssam 4357769Ssam while (*filename) { 4367769Ssam s = rindex(filename, '/'); 4377769Ssam if (s == NULL) 4387769Ssam break; 4397769Ssam if (s[1]) 4407769Ssam return (s + 1); 4417769Ssam *s = '\0'; 4427769Ssam } 4437769Ssam return (filename); 4447769Ssam } 4457769Ssam 4467769Ssam /* 4477769Ssam * Command parser. 4487769Ssam */ 4497769Ssam command(top) 4507769Ssam int top; 4517769Ssam { 4527769Ssam register struct cmd *c; 4537769Ssam 4547769Ssam if (!top) 4557769Ssam putchar('\n'); 4567769Ssam for (;;) { 4577769Ssam printf("%s> ", prompt); 4587769Ssam if (gets(line) == 0) 459*13017Ssam continue; 4607769Ssam if (line[0] == 0) 461*13017Ssam continue; 4627769Ssam makeargv(); 4637769Ssam c = getcmd(margv[0]); 4647769Ssam if (c == (struct cmd *)-1) { 4657769Ssam printf("?Ambiguous command\n"); 4667769Ssam continue; 4677769Ssam } 4687769Ssam if (c == 0) { 4697769Ssam printf("?Invalid command\n"); 4707769Ssam continue; 4717769Ssam } 4727769Ssam (*c->handler)(margc, margv); 4737769Ssam } 4747769Ssam } 4757769Ssam 4767769Ssam struct cmd * 4777769Ssam getcmd(name) 4787769Ssam register char *name; 4797769Ssam { 4807769Ssam register char *p, *q; 4817769Ssam register struct cmd *c, *found; 4827769Ssam register int nmatches, longest; 4837769Ssam 4847769Ssam longest = 0; 4857769Ssam nmatches = 0; 4867769Ssam found = 0; 4877769Ssam for (c = cmdtab; p = c->name; c++) { 4887769Ssam for (q = name; *q == *p++; q++) 4897769Ssam if (*q == 0) /* exact match? */ 4907769Ssam return (c); 4917769Ssam if (!*q) { /* the name was a prefix */ 4927769Ssam if (q - name > longest) { 4937769Ssam longest = q - name; 4947769Ssam nmatches = 1; 4957769Ssam found = c; 4967769Ssam } else if (q - name == longest) 4977769Ssam nmatches++; 4987769Ssam } 4997769Ssam } 5007769Ssam if (nmatches > 1) 5017769Ssam return ((struct cmd *)-1); 5027769Ssam return (found); 5037769Ssam } 5047769Ssam 5057769Ssam /* 5067769Ssam * Slice a string up into argc/argv. 5077769Ssam */ 5087769Ssam makeargv() 5097769Ssam { 5107769Ssam register char *cp; 5117769Ssam register char **argp = margv; 5127769Ssam 5137769Ssam margc = 0; 5147769Ssam for (cp = line; *cp;) { 5157769Ssam while (isspace(*cp)) 5167769Ssam cp++; 5177769Ssam if (*cp == '\0') 5187769Ssam break; 5197769Ssam *argp++ = cp; 5207769Ssam margc += 1; 5217769Ssam while (*cp != '\0' && !isspace(*cp)) 5227769Ssam cp++; 5237769Ssam if (*cp == '\0') 5247769Ssam break; 5257769Ssam *cp++ = '\0'; 5267769Ssam } 5277769Ssam *argp++ = 0; 5287769Ssam } 5297769Ssam 5307769Ssam /*VARARGS*/ 5317769Ssam quit() 5327769Ssam { 5337769Ssam exit(0); 5347769Ssam } 5357769Ssam 5367769Ssam /* 5377769Ssam * Help command. 5387769Ssam */ 5397769Ssam help(argc, argv) 5407769Ssam int argc; 5417769Ssam char *argv[]; 5427769Ssam { 5437769Ssam register struct cmd *c; 5447769Ssam 5457769Ssam if (argc == 1) { 5467769Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 5477769Ssam for (c = cmdtab; c->name; c++) 5487769Ssam printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); 5497769Ssam return; 5507769Ssam } 5517769Ssam while (--argc > 0) { 5527769Ssam register char *arg; 5537769Ssam arg = *++argv; 5547769Ssam c = getcmd(arg); 5557769Ssam if (c == (struct cmd *)-1) 5567769Ssam printf("?Ambiguous help command %s\n", arg); 5577769Ssam else if (c == (struct cmd *)0) 5587769Ssam printf("?Invalid help command %s\n", arg); 5597769Ssam else 5607769Ssam printf("%s\n", c->help); 5617769Ssam } 5627769Ssam } 5637769Ssam 5647769Ssam /* 5657769Ssam * Call routine with argc, argv set from args (terminated by 0). 5667769Ssam */ 5677769Ssam /* VARARGS2 */ 5687769Ssam call(routine, args) 5697769Ssam int (*routine)(); 5707769Ssam int args; 5717769Ssam { 5727769Ssam register int *argp; 5737769Ssam register int argc; 5747769Ssam 5757769Ssam for (argc = 0, argp = &args; *argp++ != 0; argc++) 5767769Ssam ; 5777769Ssam (*routine)(argc, &args); 5787769Ssam } 5797769Ssam 5807769Ssam /*VARARGS*/ 5817769Ssam settrace() 5827769Ssam { 5837769Ssam trace = !trace; 5847769Ssam printf("Packet tracing %s.\n", trace ? "on" : "off"); 5857769Ssam } 5867769Ssam 5877769Ssam /*VARARGS*/ 5887769Ssam setverbose() 5897769Ssam { 5907769Ssam verbose = !verbose; 5917769Ssam printf("Verbose mode %s.\n", verbose ? "on" : "off"); 5927769Ssam } 593