114554Ssam #ifndef lint 2*17269Ssam static char sccsid[] = "@(#)tftp.c 4.10 (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; 27*17269Ssam char sbuf[BUFSIZ]; /* send buffer */ 28*17269Ssam char rbuf[BUFSIZ]; /* receive buffer */ 2913018Ssam int rexmtval; 3013018Ssam int maxtimeout; 317770Ssam int timeout; 327770Ssam jmp_buf toplevel; 3313018Ssam jmp_buf timeoutbuf; 347770Ssam 357770Ssam timer() 367770Ssam { 3713018Ssam 3813018Ssam timeout += rexmtval; 3913018Ssam if (timeout >= maxtimeout) { 407770Ssam printf("Transfer timed out.\n"); 417770Ssam longjmp(toplevel, -1); 427770Ssam } 4313018Ssam longjmp(timeoutbuf, 1); 447770Ssam } 457770Ssam 467770Ssam /* 477770Ssam * Send the requested file. 487770Ssam */ 497770Ssam sendfile(fd, name) 507770Ssam int fd; 517770Ssam char *name; 527770Ssam { 53*17269Ssam register struct tftphdr *stp = (struct tftphdr *)sbuf; 54*17269Ssam register struct tftphdr *rtp = (struct tftphdr *)rbuf; 557770Ssam register int block = 0, size, n, amount = 0; 5616384Ssam struct sockaddr_in from, to; 577770Ssam time_t start = time(0), delta; 5816384Ssam int fromlen, aborted = 0; 597770Ssam 6016384Ssam to = sin; 6113018Ssam signal(SIGALRM, timer); 627770Ssam do { 6313018Ssam if (block == 0) 6413018Ssam size = makerequest(WRQ, name) - 4; 6513018Ssam else { 66*17269Ssam size = read(fd, stp->th_data, SEGSIZE); 677770Ssam if (size < 0) { 6816384Ssam nak(&to, errno + 100); 697770Ssam break; 707770Ssam } 71*17269Ssam stp->th_opcode = htons((u_short)DATA); 72*17269Ssam stp->th_block = htons((u_short)block); 737770Ssam } 747770Ssam timeout = 0; 7513018Ssam (void) setjmp(timeoutbuf); 767770Ssam if (trace) 77*17269Ssam tpacket("sent", &to, stp, size + 4); 78*17269Ssam n = sendto(f, sbuf, size + 4, 0, (caddr_t)&to, sizeof (to)); 799220Ssam if (n != size + 4) { 8013018Ssam perror("tftp: sendto"); 8116384Ssam aborted = 1; 8216384Ssam goto done; 837770Ssam } 8413018Ssam do { 8516384Ssam again: 8613018Ssam alarm(rexmtval); 8713018Ssam do { 8813018Ssam fromlen = sizeof (from); 89*17269Ssam n = recvfrom(f, rbuf, sizeof (rbuf), 0, 9013018Ssam (caddr_t)&from, &fromlen); 9113018Ssam } while (n <= 0); 927770Ssam alarm(0); 9313018Ssam if (n < 0) { 9413018Ssam perror("tftp: recvfrom"); 9516384Ssam aborted = 1; 9616384Ssam goto done; 9713018Ssam } 9816384Ssam if (to.sin_addr.s_addr != from.sin_addr.s_addr) { 99*17269Ssam tpacket("discarded (wrong host)", 100*17269Ssam &from, rtp, n); 10116384Ssam goto again; 10216384Ssam } 10316384Ssam if (to.sin_port = sin.sin_port) 10416384Ssam to.sin_port = from.sin_port; 10516384Ssam if (to.sin_port != from.sin_port) { 106*17269Ssam tpacket("discarded (wrong port)", 107*17269Ssam &from, rtp, n); 10816384Ssam goto again; 10916384Ssam } 11013018Ssam if (trace) 111*17269Ssam tpacket("received", &from, rtp, n); 11213018Ssam /* should verify packet came from server */ 113*17269Ssam rtp->th_opcode = ntohs(rtp->th_opcode); 114*17269Ssam rtp->th_block = ntohs(rtp->th_block); 115*17269Ssam if (rtp->th_opcode == ERROR) { 116*17269Ssam printf("Error code %d: %s\n", rtp->th_code, 117*17269Ssam rtp->th_msg); 11816384Ssam aborted = 1; 11916384Ssam goto done; 12013018Ssam } 121*17269Ssam } while (rtp->th_opcode != ACK && block != rtp->th_block); 1227770Ssam if (block > 0) 1237770Ssam amount += size; 1247770Ssam block++; 1257770Ssam } while (size == SEGSIZE || block == 1); 12616384Ssam if (!aborted && amount > 0) { 1277770Ssam delta = time(0) - start; 1287770Ssam printf("Sent %d bytes in %d seconds.\n", amount, delta); 1297770Ssam } 13016384Ssam done: 13116384Ssam (void) close(fd); 13216384Ssam return (aborted); 1337770Ssam } 1347770Ssam 1357770Ssam /* 1367770Ssam * Receive a file. 1377770Ssam */ 1387770Ssam recvfile(fd, name) 1397770Ssam int fd; 1407770Ssam char *name; 1417770Ssam { 142*17269Ssam register struct tftphdr *stp = (struct tftphdr *)sbuf; 143*17269Ssam register struct tftphdr *rtp = (struct tftphdr *)rbuf; 1447770Ssam register int block = 1, n, size, amount = 0; 14516384Ssam struct sockaddr_in from, to; 1467770Ssam time_t start = time(0), delta; 14716384Ssam int fromlen, firsttrip = 1, aborted = 0; 1487770Ssam 14916384Ssam to = sin; 15013018Ssam signal(SIGALRM, timer); 1517770Ssam do { 15213018Ssam if (firsttrip) { 15313018Ssam size = makerequest(RRQ, name); 15413018Ssam firsttrip = 0; 15513018Ssam } else { 156*17269Ssam stp->th_opcode = htons((u_short)ACK); 157*17269Ssam stp->th_block = htons((u_short)(block)); 15813018Ssam size = 4; 15913018Ssam block++; 16013018Ssam } 1617770Ssam timeout = 0; 16213018Ssam (void) setjmp(timeoutbuf); 1637770Ssam if (trace) 164*17269Ssam tpacket("sent", &to, stp, size); 165*17269Ssam if (sendto(f, sbuf, size, 0, (caddr_t)&to, 16616384Ssam sizeof (to)) != size) { 16713018Ssam alarm(0); 16813018Ssam perror("tftp: sendto"); 16916384Ssam aborted = 1; 17016384Ssam goto done; 1717770Ssam } 17213018Ssam do { 17316384Ssam again: 17413018Ssam alarm(rexmtval); 17516384Ssam do { 17616384Ssam fromlen = sizeof (from); 177*17269Ssam n = recvfrom(f, rbuf, sizeof (rbuf), 0, 17813018Ssam (caddr_t)&from, &fromlen); 17916384Ssam } while (n <= 0); 1807770Ssam alarm(0); 18113018Ssam if (n < 0) { 18213018Ssam perror("tftp: recvfrom"); 18316384Ssam aborted = 1; 18416384Ssam goto done; 18513018Ssam } 18616384Ssam if (to.sin_addr.s_addr != from.sin_addr.s_addr) { 187*17269Ssam tpacket("discarded (wrong host)", 188*17269Ssam &from, rtp, n); 18916384Ssam goto again; 19016384Ssam } 19116384Ssam if (to.sin_port = sin.sin_port) 19216384Ssam to.sin_port = from.sin_port; 19316384Ssam if (to.sin_port != from.sin_port) { 194*17269Ssam tpacket("discarded (wrong port)", 195*17269Ssam &from, rtp, n); 19616384Ssam goto again; 19716384Ssam } 19813018Ssam if (trace) 199*17269Ssam tpacket("received", &from, rtp, n); 200*17269Ssam rtp->th_opcode = ntohs(rtp->th_opcode); 201*17269Ssam rtp->th_block = ntohs(rtp->th_block); 202*17269Ssam if (rtp->th_opcode == ERROR) { 203*17269Ssam printf("Error code %d: %s\n", rtp->th_code, 204*17269Ssam rtp->th_msg); 20516384Ssam aborted = 1; 20616384Ssam goto done; 20713018Ssam } 208*17269Ssam } while (rtp->th_opcode != DATA && rtp->th_block != block); 209*17269Ssam size = write(fd, rtp->th_data, n - 4); 2107770Ssam if (size < 0) { 21116384Ssam perror("tftp: write"); 21216384Ssam nak(&to, errno + 100); 21316384Ssam aborted = 1; 21416384Ssam goto done; 2157770Ssam } 2167770Ssam amount += size; 2177770Ssam } while (size == SEGSIZE); 21816384Ssam done: 219*17269Ssam stp->th_opcode = htons((u_short)ACK); 220*17269Ssam stp->th_block = htons((u_short)block); 221*17269Ssam (void) sendto(f, sbuf, 4, 0, &to, sizeof (to)); 2227770Ssam (void) close(fd); 22316384Ssam if (!aborted && amount > 0) { 2247770Ssam delta = time(0) - start; 2257770Ssam printf("Received %d bytes in %d seconds.\n", amount, delta); 2267770Ssam } 22716384Ssam return (aborted); 2287770Ssam } 2297770Ssam 2307770Ssam makerequest(request, name) 2317770Ssam int request; 2327770Ssam char *name; 2337770Ssam { 234*17269Ssam register struct tftphdr *stp; 2357770Ssam int size; 2367770Ssam register char *cp; 2377770Ssam 238*17269Ssam stp = (struct tftphdr *)sbuf; 239*17269Ssam stp->th_opcode = htons((u_short)request); 240*17269Ssam strcpy(stp->th_stuff, name); 2417770Ssam size = strlen(name); 242*17269Ssam cp = stp->th_stuff + strlen(name); 2437770Ssam *cp++ = '\0'; 2447770Ssam strcpy(cp, mode); 2457770Ssam cp += sizeof ("netascii") - 1; 2467770Ssam *cp++ = '\0'; 247*17269Ssam return (cp - sbuf); 2487770Ssam } 2497770Ssam 2507770Ssam struct errmsg { 2517770Ssam int e_code; 2527770Ssam char *e_msg; 2537770Ssam } errmsgs[] = { 2547770Ssam { EUNDEF, "Undefined error code" }, 2557770Ssam { ENOTFOUND, "File not found" }, 2567770Ssam { EACCESS, "Access violation" }, 2577770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2587770Ssam { EBADOP, "Illegal TFTP operation" }, 2597770Ssam { EBADID, "Unknown transfer ID" }, 2607770Ssam { EEXISTS, "File already exists" }, 2617770Ssam { ENOUSER, "No such user" }, 2627770Ssam { -1, 0 } 2637770Ssam }; 2647770Ssam 2657770Ssam /* 2667770Ssam * Send a nak packet (error message). 2677770Ssam * Error code passed in is one of the 2687770Ssam * standard TFTP codes, or a UNIX errno 2697770Ssam * offset by 100. 2707770Ssam */ 27116384Ssam nak(to, error) 27216384Ssam struct sockaddr_in *to; 2737770Ssam int error; 2747770Ssam { 275*17269Ssam register struct tftphdr *stp; 2767770Ssam int length; 2777770Ssam register struct errmsg *pe; 2787770Ssam extern char *sys_errlist[]; 2797770Ssam 280*17269Ssam stp = (struct tftphdr *)sbuf; 281*17269Ssam stp->th_opcode = htons((u_short)ERROR); 282*17269Ssam stp->th_code = htons((u_short)error); 2837770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 2847770Ssam if (pe->e_code == error) 2857770Ssam break; 2867770Ssam if (pe->e_code < 0) 2877770Ssam pe->e_msg = sys_errlist[error - 100]; 288*17269Ssam strcpy(stp->th_msg, pe->e_msg); 2897770Ssam length = strlen(pe->e_msg) + 4; 2907770Ssam if (trace) 291*17269Ssam tpacket("sent", to, stp, length); 292*17269Ssam if (sendto(f, sbuf, length, 0, to, sizeof (*to)) != length) 29316384Ssam perror("tftp: nak"); 2947770Ssam } 2957770Ssam 29616384Ssam tpacket(s, sin, tp, n) 29716384Ssam struct sockaddr_in *sin; 2987770Ssam struct tftphdr *tp; 2997770Ssam int n; 3007770Ssam { 3017770Ssam static char *opcodes[] = 3027770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3037770Ssam register char *cp, *file; 3047770Ssam u_short op = ntohs(tp->th_opcode); 3057770Ssam char *index(); 3067770Ssam 30716384Ssam printf("%s ", s); 30816384Ssam if (sin) { 30916384Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 31016384Ssam sizeof (sin->sin_addr), AF_INET); 31116384Ssam 31216384Ssam printf("%s.%d ", 31316384Ssam hp == 0 ? inet_ntoa(sin->sin_addr) : hp->h_name, 31416384Ssam ntohs(sin->sin_port)); 31516384Ssam } 3167770Ssam if (op < RRQ || op > ERROR) 31716384Ssam printf("opcode=%x ", op); 3187770Ssam else 31916384Ssam printf("%s ", opcodes[op]); 3207770Ssam switch (op) { 3217770Ssam 3227770Ssam case RRQ: 3237770Ssam case WRQ: 3247770Ssam n -= 2; 3257770Ssam file = cp = tp->th_stuff; 3267770Ssam cp = index(cp, '\0'); 3277770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3287770Ssam break; 3297770Ssam 3307770Ssam case DATA: 3317770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3327770Ssam break; 3337770Ssam 3347770Ssam case ACK: 3357770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3367770Ssam break; 3377770Ssam 3387770Ssam case ERROR: 3397770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3407770Ssam break; 34117268Ssam 34217268Ssam default: 34317268Ssam putchar('\n'); 34417268Ssam break; 3457770Ssam } 3467770Ssam } 347