1*14554Ssam #ifndef lint 2*14554Ssam static char sccsid[] = "@(#)tftp.c 4.7 (Berkeley) 08/11/83"; 3*14554Ssam #endif 47770Ssam 57770Ssam /* 67770Ssam * TFTP User Program -- Protocol Machines 77770Ssam */ 87770Ssam #include <sys/types.h> 97770Ssam #include <sys/socket.h> 109220Ssam 119220Ssam #include <netinet/in.h> 129220Ssam 1312399Ssam #include <arpa/tftp.h> 1412399Ssam 157770Ssam #include <signal.h> 167770Ssam #include <stdio.h> 177770Ssam #include <errno.h> 187770Ssam #include <setjmp.h> 199220Ssam 207770Ssam extern int errno; 217770Ssam extern struct sockaddr_in sin; 227770Ssam extern char mode[]; 237770Ssam int f; 247770Ssam int trace; 257770Ssam int verbose; 267770Ssam int connected; 277770Ssam char buf[BUFSIZ]; 2813018Ssam int rexmtval; 2913018Ssam int maxtimeout; 307770Ssam int timeout; 317770Ssam jmp_buf toplevel; 3213018Ssam jmp_buf timeoutbuf; 337770Ssam 347770Ssam timer() 357770Ssam { 3613018Ssam 3713018Ssam timeout += rexmtval; 3813018Ssam if (timeout >= maxtimeout) { 397770Ssam printf("Transfer timed out.\n"); 407770Ssam longjmp(toplevel, -1); 417770Ssam } 4213018Ssam longjmp(timeoutbuf, 1); 437770Ssam } 447770Ssam 457770Ssam /* 467770Ssam * Send the requested file. 477770Ssam */ 487770Ssam sendfile(fd, name) 497770Ssam int fd; 507770Ssam char *name; 517770Ssam { 527770Ssam register struct tftphdr *tp = (struct tftphdr *)buf; 537770Ssam register int block = 0, size, n, amount = 0; 547770Ssam struct sockaddr_in from; 557770Ssam time_t start = time(0), delta; 569220Ssam int fromlen; 577770Ssam 5813018Ssam signal(SIGALRM, timer); 597770Ssam do { 6013018Ssam if (block == 0) 6113018Ssam size = makerequest(WRQ, name) - 4; 6213018Ssam else { 637770Ssam size = read(fd, tp->th_data, SEGSIZE); 647770Ssam if (size < 0) { 657770Ssam nak(errno + 100); 667770Ssam break; 677770Ssam } 687770Ssam tp->th_opcode = htons((u_short)DATA); 697770Ssam tp->th_block = htons((u_short)block); 707770Ssam } 717770Ssam timeout = 0; 7213018Ssam (void) setjmp(timeoutbuf); 737770Ssam if (trace) 747770Ssam tpacket("sent", tp, size + 4); 759245Ssam n = sendto(f, buf, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 769220Ssam if (n != size + 4) { 7713018Ssam perror("tftp: sendto"); 7813018Ssam goto abort; 797770Ssam } 8013018Ssam do { 8113018Ssam alarm(rexmtval); 8213018Ssam do { 8313018Ssam fromlen = sizeof (from); 8413018Ssam n = recvfrom(f, buf, sizeof (buf), 0, 8513018Ssam (caddr_t)&from, &fromlen); 8613018Ssam } while (n <= 0); 877770Ssam alarm(0); 8813018Ssam if (n < 0) { 8913018Ssam perror("tftp: recvfrom"); 9013018Ssam goto abort; 9113018Ssam } 9213018Ssam if (trace) 9313018Ssam tpacket("received", tp, n); 9413018Ssam /* should verify packet came from server */ 9513018Ssam tp->th_opcode = ntohs(tp->th_opcode); 9613018Ssam tp->th_block = ntohs(tp->th_block); 9713018Ssam if (tp->th_opcode == ERROR) { 9813018Ssam printf("Error code %d: %s\n", tp->th_code, 9913018Ssam tp->th_msg); 10013018Ssam goto abort; 10113018Ssam } 10213018Ssam } while (tp->th_opcode != ACK && block != tp->th_block); 1037770Ssam if (block > 0) 1047770Ssam amount += size; 1057770Ssam block++; 1067770Ssam } while (size == SEGSIZE || block == 1); 10713018Ssam abort: 1087770Ssam (void) close(fd); 1097770Ssam if (amount > 0) { 1107770Ssam delta = time(0) - start; 1117770Ssam printf("Sent %d bytes in %d seconds.\n", amount, delta); 1127770Ssam } 1137770Ssam } 1147770Ssam 1157770Ssam /* 1167770Ssam * Receive a file. 1177770Ssam */ 1187770Ssam recvfile(fd, name) 1197770Ssam int fd; 1207770Ssam char *name; 1217770Ssam { 1227770Ssam register struct tftphdr *tp = (struct tftphdr *)buf; 1237770Ssam register int block = 1, n, size, amount = 0; 1247770Ssam struct sockaddr_in from; 1257770Ssam time_t start = time(0), delta; 12613018Ssam int fromlen, firsttrip = 1; 1277770Ssam 12813018Ssam signal(SIGALRM, timer); 1297770Ssam do { 13013018Ssam if (firsttrip) { 13113018Ssam size = makerequest(RRQ, name); 13213018Ssam firsttrip = 0; 13313018Ssam } else { 13413018Ssam tp->th_opcode = htons((u_short)ACK); 13513018Ssam tp->th_block = htons((u_short)(block)); 13613018Ssam size = 4; 13713018Ssam block++; 13813018Ssam } 1397770Ssam timeout = 0; 14013018Ssam (void) setjmp(timeoutbuf); 1417770Ssam if (trace) 1427770Ssam tpacket("sent", tp, size); 14313018Ssam if (sendto(f, buf, size, 0, (caddr_t)&sin, 14413018Ssam sizeof (sin)) != size) { 14513018Ssam alarm(0); 14613018Ssam perror("tftp: sendto"); 14713018Ssam goto abort; 1487770Ssam } 14913018Ssam do { 15013018Ssam alarm(rexmtval); 15113018Ssam do 15213018Ssam n = recvfrom(f, buf, sizeof (buf), 0, 15313018Ssam (caddr_t)&from, &fromlen); 15413018Ssam while (n <= 0); 1557770Ssam alarm(0); 15613018Ssam if (n < 0) { 15713018Ssam perror("tftp: recvfrom"); 15813018Ssam goto abort; 15913018Ssam } 16013018Ssam if (trace) 16113018Ssam tpacket("received", tp, n); 16213018Ssam /* should verify client address */ 16313018Ssam tp->th_opcode = ntohs(tp->th_opcode); 16413018Ssam tp->th_block = ntohs(tp->th_block); 16513018Ssam if (tp->th_opcode == ERROR) { 16613018Ssam printf("Error code %d: %s\n", tp->th_code, 16713018Ssam tp->th_msg); 16813018Ssam goto abort; 16913018Ssam } 17013018Ssam } while (tp->th_opcode != DATA && block != tp->th_block); 1717770Ssam size = write(fd, tp->th_data, n - 4); 1727770Ssam if (size < 0) { 1737770Ssam nak(errno + 100); 1747770Ssam break; 1757770Ssam } 1767770Ssam amount += size; 1777770Ssam } while (size == SEGSIZE); 17813018Ssam abort: 1797770Ssam tp->th_opcode = htons((u_short)ACK); 1807770Ssam tp->th_block = htons((u_short)block); 1819245Ssam (void) sendto(f, buf, 4, 0, &sin, sizeof (sin)); 1827770Ssam (void) close(fd); 1837770Ssam if (amount > 0) { 1847770Ssam delta = time(0) - start; 1857770Ssam printf("Received %d bytes in %d seconds.\n", amount, delta); 1867770Ssam } 1877770Ssam } 1887770Ssam 1897770Ssam makerequest(request, name) 1907770Ssam int request; 1917770Ssam char *name; 1927770Ssam { 1937770Ssam register struct tftphdr *tp; 1947770Ssam int size; 1957770Ssam register char *cp; 1967770Ssam 1977770Ssam tp = (struct tftphdr *)buf; 1987770Ssam tp->th_opcode = htons((u_short)request); 1997770Ssam strcpy(tp->th_stuff, name); 2007770Ssam size = strlen(name); 2017770Ssam cp = tp->th_stuff + strlen(name); 2027770Ssam *cp++ = '\0'; 2037770Ssam strcpy(cp, mode); 2047770Ssam cp += sizeof ("netascii") - 1; 2057770Ssam *cp++ = '\0'; 2067770Ssam return (cp - buf); 2077770Ssam } 2087770Ssam 2097770Ssam struct errmsg { 2107770Ssam int e_code; 2117770Ssam char *e_msg; 2127770Ssam } errmsgs[] = { 2137770Ssam { EUNDEF, "Undefined error code" }, 2147770Ssam { ENOTFOUND, "File not found" }, 2157770Ssam { EACCESS, "Access violation" }, 2167770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2177770Ssam { EBADOP, "Illegal TFTP operation" }, 2187770Ssam { EBADID, "Unknown transfer ID" }, 2197770Ssam { EEXISTS, "File already exists" }, 2207770Ssam { ENOUSER, "No such user" }, 2217770Ssam { -1, 0 } 2227770Ssam }; 2237770Ssam 2247770Ssam /* 2257770Ssam * Send a nak packet (error message). 2267770Ssam * Error code passed in is one of the 2277770Ssam * standard TFTP codes, or a UNIX errno 2287770Ssam * offset by 100. 2297770Ssam */ 2307770Ssam nak(error) 2317770Ssam int error; 2327770Ssam { 2337770Ssam register struct tftphdr *tp; 2347770Ssam int length; 2357770Ssam register struct errmsg *pe; 2367770Ssam extern char *sys_errlist[]; 2377770Ssam 2387770Ssam tp = (struct tftphdr *)buf; 2397770Ssam tp->th_opcode = htons((u_short)ERROR); 2407770Ssam tp->th_code = htons((u_short)error); 2417770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 2427770Ssam if (pe->e_code == error) 2437770Ssam break; 2447770Ssam if (pe->e_code < 0) 2457770Ssam pe->e_msg = sys_errlist[error - 100]; 2467770Ssam strcpy(tp->th_msg, pe->e_msg); 2477770Ssam length = strlen(pe->e_msg) + 4; 2487770Ssam if (trace) 2497770Ssam tpacket("sent", tp, length); 2507770Ssam if (send(f, &sin, buf, length) != length) 2517770Ssam perror("nak"); 2527770Ssam } 2537770Ssam 2547770Ssam tpacket(s, tp, n) 2557770Ssam struct tftphdr *tp; 2567770Ssam int n; 2577770Ssam { 2587770Ssam static char *opcodes[] = 2597770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 2607770Ssam register char *cp, *file; 2617770Ssam u_short op = ntohs(tp->th_opcode); 2627770Ssam char *index(); 2637770Ssam 2647770Ssam if (op < RRQ || op > ERROR) 2657770Ssam printf("%s opcode=%x ", s, op); 2667770Ssam else 2677770Ssam printf("%s %s ", s, opcodes[op]); 2687770Ssam switch (op) { 2697770Ssam 2707770Ssam case RRQ: 2717770Ssam case WRQ: 2727770Ssam n -= 2; 2737770Ssam file = cp = tp->th_stuff; 2747770Ssam cp = index(cp, '\0'); 2757770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 2767770Ssam break; 2777770Ssam 2787770Ssam case DATA: 2797770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 2807770Ssam break; 2817770Ssam 2827770Ssam case ACK: 2837770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 2847770Ssam break; 2857770Ssam 2867770Ssam case ERROR: 2877770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 2887770Ssam break; 2897770Ssam } 2907770Ssam } 291