1*13018Ssam /* tftp.c 4.6 83/06/12 */ 27770Ssam 37770Ssam /* 47770Ssam * TFTP User Program -- Protocol Machines 57770Ssam */ 67770Ssam #include <sys/types.h> 77770Ssam #include <sys/socket.h> 89220Ssam 99220Ssam #include <netinet/in.h> 109220Ssam 1112399Ssam #include <arpa/tftp.h> 1212399Ssam 137770Ssam #include <signal.h> 147770Ssam #include <stdio.h> 157770Ssam #include <errno.h> 167770Ssam #include <setjmp.h> 179220Ssam 187770Ssam extern int errno; 197770Ssam extern struct sockaddr_in sin; 207770Ssam extern char mode[]; 217770Ssam int f; 227770Ssam int trace; 237770Ssam int verbose; 247770Ssam int connected; 257770Ssam char buf[BUFSIZ]; 26*13018Ssam int rexmtval; 27*13018Ssam int maxtimeout; 287770Ssam int timeout; 297770Ssam jmp_buf toplevel; 30*13018Ssam jmp_buf timeoutbuf; 317770Ssam 327770Ssam timer() 337770Ssam { 34*13018Ssam 35*13018Ssam timeout += rexmtval; 36*13018Ssam if (timeout >= maxtimeout) { 377770Ssam printf("Transfer timed out.\n"); 387770Ssam longjmp(toplevel, -1); 397770Ssam } 40*13018Ssam longjmp(timeoutbuf, 1); 417770Ssam } 427770Ssam 437770Ssam /* 447770Ssam * Send the requested file. 457770Ssam */ 467770Ssam sendfile(fd, name) 477770Ssam int fd; 487770Ssam char *name; 497770Ssam { 507770Ssam register struct tftphdr *tp = (struct tftphdr *)buf; 517770Ssam register int block = 0, size, n, amount = 0; 527770Ssam struct sockaddr_in from; 537770Ssam time_t start = time(0), delta; 549220Ssam int fromlen; 557770Ssam 56*13018Ssam signal(SIGALRM, timer); 577770Ssam do { 58*13018Ssam if (block == 0) 59*13018Ssam size = makerequest(WRQ, name) - 4; 60*13018Ssam else { 617770Ssam size = read(fd, tp->th_data, SEGSIZE); 627770Ssam if (size < 0) { 637770Ssam nak(errno + 100); 647770Ssam break; 657770Ssam } 667770Ssam tp->th_opcode = htons((u_short)DATA); 677770Ssam tp->th_block = htons((u_short)block); 687770Ssam } 697770Ssam timeout = 0; 70*13018Ssam (void) setjmp(timeoutbuf); 717770Ssam if (trace) 727770Ssam tpacket("sent", tp, size + 4); 739245Ssam n = sendto(f, buf, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 749220Ssam if (n != size + 4) { 75*13018Ssam perror("tftp: sendto"); 76*13018Ssam goto abort; 777770Ssam } 78*13018Ssam do { 79*13018Ssam alarm(rexmtval); 80*13018Ssam do { 81*13018Ssam fromlen = sizeof (from); 82*13018Ssam n = recvfrom(f, buf, sizeof (buf), 0, 83*13018Ssam (caddr_t)&from, &fromlen); 84*13018Ssam } while (n <= 0); 857770Ssam alarm(0); 86*13018Ssam if (n < 0) { 87*13018Ssam perror("tftp: recvfrom"); 88*13018Ssam goto abort; 89*13018Ssam } 90*13018Ssam if (trace) 91*13018Ssam tpacket("received", tp, n); 92*13018Ssam /* should verify packet came from server */ 93*13018Ssam tp->th_opcode = ntohs(tp->th_opcode); 94*13018Ssam tp->th_block = ntohs(tp->th_block); 95*13018Ssam if (tp->th_opcode == ERROR) { 96*13018Ssam printf("Error code %d: %s\n", tp->th_code, 97*13018Ssam tp->th_msg); 98*13018Ssam goto abort; 99*13018Ssam } 100*13018Ssam } while (tp->th_opcode != ACK && block != tp->th_block); 1017770Ssam if (block > 0) 1027770Ssam amount += size; 1037770Ssam block++; 1047770Ssam } while (size == SEGSIZE || block == 1); 105*13018Ssam abort: 1067770Ssam (void) close(fd); 1077770Ssam if (amount > 0) { 1087770Ssam delta = time(0) - start; 1097770Ssam printf("Sent %d bytes in %d seconds.\n", amount, delta); 1107770Ssam } 1117770Ssam } 1127770Ssam 1137770Ssam /* 1147770Ssam * Receive a file. 1157770Ssam */ 1167770Ssam recvfile(fd, name) 1177770Ssam int fd; 1187770Ssam char *name; 1197770Ssam { 1207770Ssam register struct tftphdr *tp = (struct tftphdr *)buf; 1217770Ssam register int block = 1, n, size, amount = 0; 1227770Ssam struct sockaddr_in from; 1237770Ssam time_t start = time(0), delta; 124*13018Ssam int fromlen, firsttrip = 1; 1257770Ssam 126*13018Ssam signal(SIGALRM, timer); 1277770Ssam do { 128*13018Ssam if (firsttrip) { 129*13018Ssam size = makerequest(RRQ, name); 130*13018Ssam firsttrip = 0; 131*13018Ssam } else { 132*13018Ssam tp->th_opcode = htons((u_short)ACK); 133*13018Ssam tp->th_block = htons((u_short)(block)); 134*13018Ssam size = 4; 135*13018Ssam block++; 136*13018Ssam } 1377770Ssam timeout = 0; 138*13018Ssam (void) setjmp(timeoutbuf); 1397770Ssam if (trace) 1407770Ssam tpacket("sent", tp, size); 141*13018Ssam if (sendto(f, buf, size, 0, (caddr_t)&sin, 142*13018Ssam sizeof (sin)) != size) { 143*13018Ssam alarm(0); 144*13018Ssam perror("tftp: sendto"); 145*13018Ssam goto abort; 1467770Ssam } 147*13018Ssam do { 148*13018Ssam alarm(rexmtval); 149*13018Ssam do 150*13018Ssam n = recvfrom(f, buf, sizeof (buf), 0, 151*13018Ssam (caddr_t)&from, &fromlen); 152*13018Ssam while (n <= 0); 1537770Ssam alarm(0); 154*13018Ssam if (n < 0) { 155*13018Ssam perror("tftp: recvfrom"); 156*13018Ssam goto abort; 157*13018Ssam } 158*13018Ssam if (trace) 159*13018Ssam tpacket("received", tp, n); 160*13018Ssam /* should verify client address */ 161*13018Ssam tp->th_opcode = ntohs(tp->th_opcode); 162*13018Ssam tp->th_block = ntohs(tp->th_block); 163*13018Ssam if (tp->th_opcode == ERROR) { 164*13018Ssam printf("Error code %d: %s\n", tp->th_code, 165*13018Ssam tp->th_msg); 166*13018Ssam goto abort; 167*13018Ssam } 168*13018Ssam } while (tp->th_opcode != DATA && block != tp->th_block); 1697770Ssam size = write(fd, tp->th_data, n - 4); 1707770Ssam if (size < 0) { 1717770Ssam nak(errno + 100); 1727770Ssam break; 1737770Ssam } 1747770Ssam amount += size; 1757770Ssam } while (size == SEGSIZE); 176*13018Ssam abort: 1777770Ssam tp->th_opcode = htons((u_short)ACK); 1787770Ssam tp->th_block = htons((u_short)block); 1799245Ssam (void) sendto(f, buf, 4, 0, &sin, sizeof (sin)); 1807770Ssam (void) close(fd); 1817770Ssam if (amount > 0) { 1827770Ssam delta = time(0) - start; 1837770Ssam printf("Received %d bytes in %d seconds.\n", amount, delta); 1847770Ssam } 1857770Ssam } 1867770Ssam 1877770Ssam makerequest(request, name) 1887770Ssam int request; 1897770Ssam char *name; 1907770Ssam { 1917770Ssam register struct tftphdr *tp; 1927770Ssam int size; 1937770Ssam register char *cp; 1947770Ssam 1957770Ssam tp = (struct tftphdr *)buf; 1967770Ssam tp->th_opcode = htons((u_short)request); 1977770Ssam strcpy(tp->th_stuff, name); 1987770Ssam size = strlen(name); 1997770Ssam cp = tp->th_stuff + strlen(name); 2007770Ssam *cp++ = '\0'; 2017770Ssam strcpy(cp, mode); 2027770Ssam cp += sizeof ("netascii") - 1; 2037770Ssam *cp++ = '\0'; 2047770Ssam return (cp - buf); 2057770Ssam } 2067770Ssam 2077770Ssam struct errmsg { 2087770Ssam int e_code; 2097770Ssam char *e_msg; 2107770Ssam } errmsgs[] = { 2117770Ssam { EUNDEF, "Undefined error code" }, 2127770Ssam { ENOTFOUND, "File not found" }, 2137770Ssam { EACCESS, "Access violation" }, 2147770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2157770Ssam { EBADOP, "Illegal TFTP operation" }, 2167770Ssam { EBADID, "Unknown transfer ID" }, 2177770Ssam { EEXISTS, "File already exists" }, 2187770Ssam { ENOUSER, "No such user" }, 2197770Ssam { -1, 0 } 2207770Ssam }; 2217770Ssam 2227770Ssam /* 2237770Ssam * Send a nak packet (error message). 2247770Ssam * Error code passed in is one of the 2257770Ssam * standard TFTP codes, or a UNIX errno 2267770Ssam * offset by 100. 2277770Ssam */ 2287770Ssam nak(error) 2297770Ssam int error; 2307770Ssam { 2317770Ssam register struct tftphdr *tp; 2327770Ssam int length; 2337770Ssam register struct errmsg *pe; 2347770Ssam extern char *sys_errlist[]; 2357770Ssam 2367770Ssam tp = (struct tftphdr *)buf; 2377770Ssam tp->th_opcode = htons((u_short)ERROR); 2387770Ssam tp->th_code = htons((u_short)error); 2397770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 2407770Ssam if (pe->e_code == error) 2417770Ssam break; 2427770Ssam if (pe->e_code < 0) 2437770Ssam pe->e_msg = sys_errlist[error - 100]; 2447770Ssam strcpy(tp->th_msg, pe->e_msg); 2457770Ssam length = strlen(pe->e_msg) + 4; 2467770Ssam if (trace) 2477770Ssam tpacket("sent", tp, length); 2487770Ssam if (send(f, &sin, buf, length) != length) 2497770Ssam perror("nak"); 2507770Ssam } 2517770Ssam 2527770Ssam tpacket(s, tp, n) 2537770Ssam struct tftphdr *tp; 2547770Ssam int n; 2557770Ssam { 2567770Ssam static char *opcodes[] = 2577770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 2587770Ssam register char *cp, *file; 2597770Ssam u_short op = ntohs(tp->th_opcode); 2607770Ssam char *index(); 2617770Ssam 2627770Ssam if (op < RRQ || op > ERROR) 2637770Ssam printf("%s opcode=%x ", s, op); 2647770Ssam else 2657770Ssam printf("%s %s ", s, opcodes[op]); 2667770Ssam switch (op) { 2677770Ssam 2687770Ssam case RRQ: 2697770Ssam case WRQ: 2707770Ssam n -= 2; 2717770Ssam file = cp = tp->th_stuff; 2727770Ssam cp = index(cp, '\0'); 2737770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 2747770Ssam break; 2757770Ssam 2767770Ssam case DATA: 2777770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 2787770Ssam break; 2797770Ssam 2807770Ssam case ACK: 2817770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 2827770Ssam break; 2837770Ssam 2847770Ssam case ERROR: 2857770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 2867770Ssam break; 2877770Ssam } 2887770Ssam } 289