114554Ssam #ifndef lint 2*17268Ssam static char sccsid[] = "@(#)tftp.c 4.9 (Berkeley) 10/18/84"; 314554Ssam #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> 1216384Ssam #include <arpa/inet.h> 1312399Ssam #include <arpa/tftp.h> 1412399Ssam 157770Ssam #include <signal.h> 167770Ssam #include <stdio.h> 177770Ssam #include <errno.h> 187770Ssam #include <setjmp.h> 1916384Ssam #include <netdb.h> 209220Ssam 217770Ssam extern int errno; 227770Ssam extern struct sockaddr_in sin; 237770Ssam extern char mode[]; 247770Ssam int f; 257770Ssam int trace; 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; 5416384Ssam struct sockaddr_in from, to; 557770Ssam time_t start = time(0), delta; 5616384Ssam int fromlen, aborted = 0; 577770Ssam 5816384Ssam to = sin; 5913018Ssam signal(SIGALRM, timer); 607770Ssam do { 6113018Ssam if (block == 0) 6213018Ssam size = makerequest(WRQ, name) - 4; 6313018Ssam else { 647770Ssam size = read(fd, tp->th_data, SEGSIZE); 657770Ssam if (size < 0) { 6616384Ssam nak(&to, errno + 100); 677770Ssam break; 687770Ssam } 697770Ssam tp->th_opcode = htons((u_short)DATA); 707770Ssam tp->th_block = htons((u_short)block); 717770Ssam } 727770Ssam timeout = 0; 7313018Ssam (void) setjmp(timeoutbuf); 747770Ssam if (trace) 7516384Ssam tpacket("sent", &to, tp, size + 4); 7616384Ssam n = sendto(f, buf, size + 4, 0, (caddr_t)&to, sizeof (to)); 779220Ssam if (n != size + 4) { 7813018Ssam perror("tftp: sendto"); 7916384Ssam aborted = 1; 8016384Ssam goto done; 817770Ssam } 8213018Ssam do { 8316384Ssam again: 8413018Ssam alarm(rexmtval); 8513018Ssam do { 8613018Ssam fromlen = sizeof (from); 8713018Ssam n = recvfrom(f, buf, sizeof (buf), 0, 8813018Ssam (caddr_t)&from, &fromlen); 8913018Ssam } while (n <= 0); 907770Ssam alarm(0); 9113018Ssam if (n < 0) { 9213018Ssam perror("tftp: recvfrom"); 9316384Ssam aborted = 1; 9416384Ssam goto done; 9513018Ssam } 9616384Ssam if (to.sin_addr.s_addr != from.sin_addr.s_addr) { 9716384Ssam tpacket("discarded (wrong host)", &from, tp, n); 9816384Ssam goto again; 9916384Ssam } 10016384Ssam if (to.sin_port = sin.sin_port) 10116384Ssam to.sin_port = from.sin_port; 10216384Ssam if (to.sin_port != from.sin_port) { 10316384Ssam tpacket("discarded (wrong port)", &from, tp, n); 10416384Ssam goto again; 10516384Ssam } 10613018Ssam if (trace) 10716384Ssam tpacket("received", &from, tp, n); 10813018Ssam /* should verify packet came from server */ 10913018Ssam tp->th_opcode = ntohs(tp->th_opcode); 11013018Ssam tp->th_block = ntohs(tp->th_block); 11113018Ssam if (tp->th_opcode == ERROR) { 11213018Ssam printf("Error code %d: %s\n", tp->th_code, 11313018Ssam tp->th_msg); 11416384Ssam aborted = 1; 11516384Ssam goto done; 11613018Ssam } 11713018Ssam } while (tp->th_opcode != ACK && block != tp->th_block); 1187770Ssam if (block > 0) 1197770Ssam amount += size; 1207770Ssam block++; 1217770Ssam } while (size == SEGSIZE || block == 1); 12216384Ssam if (!aborted && amount > 0) { 1237770Ssam delta = time(0) - start; 1247770Ssam printf("Sent %d bytes in %d seconds.\n", amount, delta); 1257770Ssam } 12616384Ssam done: 12716384Ssam (void) close(fd); 12816384Ssam return (aborted); 1297770Ssam } 1307770Ssam 1317770Ssam /* 1327770Ssam * Receive a file. 1337770Ssam */ 1347770Ssam recvfile(fd, name) 1357770Ssam int fd; 1367770Ssam char *name; 1377770Ssam { 1387770Ssam register struct tftphdr *tp = (struct tftphdr *)buf; 1397770Ssam register int block = 1, n, size, amount = 0; 14016384Ssam struct sockaddr_in from, to; 1417770Ssam time_t start = time(0), delta; 14216384Ssam int fromlen, firsttrip = 1, aborted = 0; 1437770Ssam 14416384Ssam to = sin; 14513018Ssam signal(SIGALRM, timer); 1467770Ssam do { 14713018Ssam if (firsttrip) { 14813018Ssam size = makerequest(RRQ, name); 14913018Ssam firsttrip = 0; 15013018Ssam } else { 15113018Ssam tp->th_opcode = htons((u_short)ACK); 15213018Ssam tp->th_block = htons((u_short)(block)); 15313018Ssam size = 4; 15413018Ssam block++; 15513018Ssam } 1567770Ssam timeout = 0; 15713018Ssam (void) setjmp(timeoutbuf); 1587770Ssam if (trace) 15916384Ssam tpacket("sent", &to, tp, size); 16016384Ssam if (sendto(f, buf, size, 0, (caddr_t)&to, 16116384Ssam sizeof (to)) != size) { 16213018Ssam alarm(0); 16313018Ssam perror("tftp: sendto"); 16416384Ssam aborted = 1; 16516384Ssam goto done; 1667770Ssam } 16713018Ssam do { 16816384Ssam again: 16913018Ssam alarm(rexmtval); 17016384Ssam do { 17116384Ssam fromlen = sizeof (from); 17213018Ssam n = recvfrom(f, buf, sizeof (buf), 0, 17313018Ssam (caddr_t)&from, &fromlen); 17416384Ssam } while (n <= 0); 1757770Ssam alarm(0); 17613018Ssam if (n < 0) { 17713018Ssam perror("tftp: recvfrom"); 17816384Ssam aborted = 1; 17916384Ssam goto done; 18013018Ssam } 18116384Ssam if (to.sin_addr.s_addr != from.sin_addr.s_addr) { 18216384Ssam tpacket("discarded (wrong host)", &from, tp, n); 18316384Ssam goto again; 18416384Ssam } 18516384Ssam if (to.sin_port = sin.sin_port) 18616384Ssam to.sin_port = from.sin_port; 18716384Ssam if (to.sin_port != from.sin_port) { 18816384Ssam tpacket("discarded (wrong port)", &from, tp, n); 18916384Ssam goto again; 19016384Ssam } 19113018Ssam if (trace) 19216384Ssam tpacket("received", &from, tp, n); 19313018Ssam tp->th_opcode = ntohs(tp->th_opcode); 19413018Ssam tp->th_block = ntohs(tp->th_block); 19513018Ssam if (tp->th_opcode == ERROR) { 19613018Ssam printf("Error code %d: %s\n", tp->th_code, 19713018Ssam tp->th_msg); 19816384Ssam aborted = 1; 19916384Ssam goto done; 20013018Ssam } 20116384Ssam } while (tp->th_opcode != DATA && tp->th_block != block); 2027770Ssam size = write(fd, tp->th_data, n - 4); 2037770Ssam if (size < 0) { 20416384Ssam perror("tftp: write"); 20516384Ssam nak(&to, errno + 100); 20616384Ssam aborted = 1; 20716384Ssam goto done; 2087770Ssam } 2097770Ssam amount += size; 2107770Ssam } while (size == SEGSIZE); 21116384Ssam done: 2127770Ssam tp->th_opcode = htons((u_short)ACK); 2137770Ssam tp->th_block = htons((u_short)block); 21416384Ssam (void) sendto(f, buf, 4, 0, &to, sizeof (to)); 2157770Ssam (void) close(fd); 21616384Ssam if (!aborted && amount > 0) { 2177770Ssam delta = time(0) - start; 2187770Ssam printf("Received %d bytes in %d seconds.\n", amount, delta); 2197770Ssam } 22016384Ssam return (aborted); 2217770Ssam } 2227770Ssam 2237770Ssam makerequest(request, name) 2247770Ssam int request; 2257770Ssam char *name; 2267770Ssam { 2277770Ssam register struct tftphdr *tp; 2287770Ssam int size; 2297770Ssam register char *cp; 2307770Ssam 2317770Ssam tp = (struct tftphdr *)buf; 2327770Ssam tp->th_opcode = htons((u_short)request); 2337770Ssam strcpy(tp->th_stuff, name); 2347770Ssam size = strlen(name); 2357770Ssam cp = tp->th_stuff + strlen(name); 2367770Ssam *cp++ = '\0'; 2377770Ssam strcpy(cp, mode); 2387770Ssam cp += sizeof ("netascii") - 1; 2397770Ssam *cp++ = '\0'; 2407770Ssam return (cp - buf); 2417770Ssam } 2427770Ssam 2437770Ssam struct errmsg { 2447770Ssam int e_code; 2457770Ssam char *e_msg; 2467770Ssam } errmsgs[] = { 2477770Ssam { EUNDEF, "Undefined error code" }, 2487770Ssam { ENOTFOUND, "File not found" }, 2497770Ssam { EACCESS, "Access violation" }, 2507770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2517770Ssam { EBADOP, "Illegal TFTP operation" }, 2527770Ssam { EBADID, "Unknown transfer ID" }, 2537770Ssam { EEXISTS, "File already exists" }, 2547770Ssam { ENOUSER, "No such user" }, 2557770Ssam { -1, 0 } 2567770Ssam }; 2577770Ssam 2587770Ssam /* 2597770Ssam * Send a nak packet (error message). 2607770Ssam * Error code passed in is one of the 2617770Ssam * standard TFTP codes, or a UNIX errno 2627770Ssam * offset by 100. 2637770Ssam */ 26416384Ssam nak(to, error) 26516384Ssam struct sockaddr_in *to; 2667770Ssam int error; 2677770Ssam { 2687770Ssam register struct tftphdr *tp; 2697770Ssam int length; 2707770Ssam register struct errmsg *pe; 2717770Ssam extern char *sys_errlist[]; 2727770Ssam 2737770Ssam tp = (struct tftphdr *)buf; 2747770Ssam tp->th_opcode = htons((u_short)ERROR); 2757770Ssam tp->th_code = htons((u_short)error); 2767770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 2777770Ssam if (pe->e_code == error) 2787770Ssam break; 2797770Ssam if (pe->e_code < 0) 2807770Ssam pe->e_msg = sys_errlist[error - 100]; 2817770Ssam strcpy(tp->th_msg, pe->e_msg); 2827770Ssam length = strlen(pe->e_msg) + 4; 2837770Ssam if (trace) 28416384Ssam tpacket("sent", to, tp, length); 28516384Ssam if (sendto(f, buf, length, 0, to, sizeof (*to)) != length) 28616384Ssam perror("tftp: nak"); 2877770Ssam } 2887770Ssam 28916384Ssam tpacket(s, sin, tp, n) 29016384Ssam struct sockaddr_in *sin; 2917770Ssam struct tftphdr *tp; 2927770Ssam int n; 2937770Ssam { 2947770Ssam static char *opcodes[] = 2957770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 2967770Ssam register char *cp, *file; 2977770Ssam u_short op = ntohs(tp->th_opcode); 2987770Ssam char *index(); 2997770Ssam 30016384Ssam printf("%s ", s); 30116384Ssam if (sin) { 30216384Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 30316384Ssam sizeof (sin->sin_addr), AF_INET); 30416384Ssam 30516384Ssam printf("%s.%d ", 30616384Ssam hp == 0 ? inet_ntoa(sin->sin_addr) : hp->h_name, 30716384Ssam ntohs(sin->sin_port)); 30816384Ssam } 3097770Ssam if (op < RRQ || op > ERROR) 31016384Ssam printf("opcode=%x ", op); 3117770Ssam else 31216384Ssam printf("%s ", opcodes[op]); 3137770Ssam switch (op) { 3147770Ssam 3157770Ssam case RRQ: 3167770Ssam case WRQ: 3177770Ssam n -= 2; 3187770Ssam file = cp = tp->th_stuff; 3197770Ssam cp = index(cp, '\0'); 3207770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3217770Ssam break; 3227770Ssam 3237770Ssam case DATA: 3247770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3257770Ssam break; 3267770Ssam 3277770Ssam case ACK: 3287770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3297770Ssam break; 3307770Ssam 3317770Ssam case ERROR: 3327770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3337770Ssam break; 334*17268Ssam 335*17268Ssam default: 336*17268Ssam putchar('\n'); 337*17268Ssam break; 3387770Ssam } 3397770Ssam } 340