122404Sdist /* 233821Sbostic * Copyright (c) 1983 Regents of the University of California. 333821Sbostic * All rights reserved. 433821Sbostic * 542770Sbostic * %sccs.include.redist.c% 622404Sdist */ 722404Sdist 814554Ssam #ifndef lint 9*60062Storek static char sccsid[] = "@(#)tftp.c 5.11 (Berkeley) 05/16/93"; 1033821Sbostic #endif /* not lint */ 117770Ssam 1226094Sminshall /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 1326094Sminshall 147770Ssam /* 157770Ssam * TFTP User Program -- Protocol Machines 167770Ssam */ 177770Ssam #include <sys/types.h> 187770Ssam #include <sys/socket.h> 1926094Sminshall #include <sys/time.h> 209220Ssam 219220Ssam #include <netinet/in.h> 2226094Sminshall 2312399Ssam #include <arpa/tftp.h> 2412399Ssam 25*60062Storek #include <errno.h> 26*60062Storek #include <setjmp.h> 277770Ssam #include <signal.h> 287770Ssam #include <stdio.h> 29*60062Storek #include <unistd.h> 309220Ssam 31*60062Storek #include "extern.h" 32*60062Storek #include "tftpsubs.h" 33*60062Storek 347770Ssam extern int errno; 3526094Sminshall 36*60062Storek extern struct sockaddr_in peeraddr; /* filled in by main */ 37*60062Storek extern int f; /* the opened socket */ 3826094Sminshall extern int trace; 3926094Sminshall extern int verbose; 4026094Sminshall extern int rexmtval; 4126094Sminshall extern int maxtimeout; 4226094Sminshall 4326094Sminshall #define PKTSIZE SEGSIZE+4 4426094Sminshall char ackbuf[PKTSIZE]; 457770Ssam int timeout; 467770Ssam jmp_buf toplevel; 4713018Ssam jmp_buf timeoutbuf; 487770Ssam 49*60062Storek static void nak __P((int)); 50*60062Storek static int makerequest __P((int, const char *, struct tftphdr *, const char *)); 51*60062Storek static void printstats __P((const char *, unsigned long)); 52*60062Storek static void startclock __P((void)); 53*60062Storek static void stopclock __P((void)); 54*60062Storek static void timer __P((int)); 55*60062Storek static void tpacket __P((const char *, struct tftphdr *, int)); 567770Ssam 577770Ssam /* 587770Ssam * Send the requested file. 597770Ssam */ 60*60062Storek void 6126094Sminshall sendfile(fd, name, mode) 627770Ssam int fd; 637770Ssam char *name; 6426094Sminshall char *mode; 657770Ssam { 66*60062Storek register struct tftphdr *ap; /* data and ack packets */ 6726094Sminshall struct tftphdr *r_init(), *dp; 68*60062Storek register int n; 69*60062Storek volatile int block, size, convert; 70*60062Storek volatile unsigned long amount; 7126094Sminshall struct sockaddr_in from; 7226094Sminshall int fromlen; 7326094Sminshall FILE *file; 747770Ssam 75*60062Storek startclock(); /* start stat's clock */ 76*60062Storek dp = r_init(); /* reset fillbuf/read-ahead code */ 7726094Sminshall ap = (struct tftphdr *)ackbuf; 7826094Sminshall file = fdopen(fd, "r"); 7926094Sminshall convert = !strcmp(mode, "netascii"); 80*60062Storek block = 0; 81*60062Storek amount = 0; 8226094Sminshall 8313018Ssam signal(SIGALRM, timer); 847770Ssam do { 8513018Ssam if (block == 0) 8626094Sminshall size = makerequest(WRQ, name, dp, mode) - 4; 8713018Ssam else { 88*60062Storek /* size = read(fd, dp->th_data, SEGSIZE); */ 8926094Sminshall size = readit(file, &dp, convert); 907770Ssam if (size < 0) { 9126094Sminshall nak(errno + 100); 927770Ssam break; 937770Ssam } 9426094Sminshall dp->th_opcode = htons((u_short)DATA); 9526094Sminshall dp->th_block = htons((u_short)block); 967770Ssam } 977770Ssam timeout = 0; 9813018Ssam (void) setjmp(timeoutbuf); 9926104Sminshall send_data: 1007770Ssam if (trace) 10126094Sminshall tpacket("sent", dp, size + 4); 10246861Sbostic n = sendto(f, dp, size + 4, 0, 103*60062Storek (struct sockaddr *)&peeraddr, sizeof(peeraddr)); 1049220Ssam if (n != size + 4) { 10513018Ssam perror("tftp: sendto"); 10626094Sminshall goto abort; 1077770Ssam } 10826094Sminshall read_ahead(file, convert); 10926104Sminshall for ( ; ; ) { 11013018Ssam alarm(rexmtval); 11113018Ssam do { 112*60062Storek fromlen = sizeof(from); 113*60062Storek n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, 11446861Sbostic (struct sockaddr *)&from, &fromlen); 11513018Ssam } while (n <= 0); 1167770Ssam alarm(0); 11713018Ssam if (n < 0) { 11813018Ssam perror("tftp: recvfrom"); 11926094Sminshall goto abort; 12013018Ssam } 121*60062Storek peeraddr.sin_port = from.sin_port; /* added */ 12213018Ssam if (trace) 12326094Sminshall tpacket("received", ap, n); 12413018Ssam /* should verify packet came from server */ 12526094Sminshall ap->th_opcode = ntohs(ap->th_opcode); 12626094Sminshall ap->th_block = ntohs(ap->th_block); 12726094Sminshall if (ap->th_opcode == ERROR) { 12826094Sminshall printf("Error code %d: %s\n", ap->th_code, 12926094Sminshall ap->th_msg); 13026094Sminshall goto abort; 13113018Ssam } 13226104Sminshall if (ap->th_opcode == ACK) { 13326113Sminshall int j; 13426113Sminshall 13526104Sminshall if (ap->th_block == block) { 13626104Sminshall break; 13726104Sminshall } 13826113Sminshall /* On an error, try to synchronize 13926113Sminshall * both sides. 14026113Sminshall */ 14126113Sminshall j = synchnet(f); 14226113Sminshall if (j && trace) { 14326113Sminshall printf("discarded %d packets\n", 14426113Sminshall j); 14526113Sminshall } 14626113Sminshall if (ap->th_block == (block-1)) { 14726113Sminshall goto send_data; 14826113Sminshall } 14926104Sminshall } 15026104Sminshall } 1517770Ssam if (block > 0) 1527770Ssam amount += size; 1537770Ssam block++; 1547770Ssam } while (size == SEGSIZE || block == 1); 15526094Sminshall abort: 15626094Sminshall fclose(file); 15726094Sminshall stopclock(); 15826094Sminshall if (amount > 0) 15926094Sminshall printstats("Sent", amount); 1607770Ssam } 1617770Ssam 1627770Ssam /* 1637770Ssam * Receive a file. 1647770Ssam */ 165*60062Storek void 16626094Sminshall recvfile(fd, name, mode) 1677770Ssam int fd; 1687770Ssam char *name; 16926094Sminshall char *mode; 1707770Ssam { 17126094Sminshall register struct tftphdr *ap; 17226094Sminshall struct tftphdr *dp, *w_init(); 173*60062Storek register int n; 174*60062Storek volatile int block, size, firsttrip; 175*60062Storek volatile unsigned long amount; 17626094Sminshall struct sockaddr_in from; 177*60062Storek int fromlen; 17826094Sminshall FILE *file; 179*60062Storek volatile int convert; /* true if converting crlf -> lf */ 1807770Ssam 18126094Sminshall startclock(); 18226094Sminshall dp = w_init(); 18326094Sminshall ap = (struct tftphdr *)ackbuf; 18426094Sminshall file = fdopen(fd, "w"); 18526094Sminshall convert = !strcmp(mode, "netascii"); 186*60062Storek block = 1; 187*60062Storek firsttrip = 1; 188*60062Storek amount = 0; 18926094Sminshall 19013018Ssam signal(SIGALRM, timer); 1917770Ssam do { 19213018Ssam if (firsttrip) { 19326094Sminshall size = makerequest(RRQ, name, ap, mode); 19413018Ssam firsttrip = 0; 19513018Ssam } else { 19626094Sminshall ap->th_opcode = htons((u_short)ACK); 19726094Sminshall ap->th_block = htons((u_short)(block)); 19813018Ssam size = 4; 19913018Ssam block++; 20013018Ssam } 2017770Ssam timeout = 0; 20213018Ssam (void) setjmp(timeoutbuf); 20326094Sminshall send_ack: 2047770Ssam if (trace) 20526094Sminshall tpacket("sent", ap, size); 206*60062Storek if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, 207*60062Storek sizeof(peeraddr)) != size) { 20813018Ssam alarm(0); 20913018Ssam perror("tftp: sendto"); 21026094Sminshall goto abort; 2117770Ssam } 21226094Sminshall write_behind(file, convert); 21326094Sminshall for ( ; ; ) { 21413018Ssam alarm(rexmtval); 21526094Sminshall do { 216*60062Storek fromlen = sizeof(from); 21726094Sminshall n = recvfrom(f, dp, PKTSIZE, 0, 21846861Sbostic (struct sockaddr *)&from, &fromlen); 21916384Ssam } while (n <= 0); 2207770Ssam alarm(0); 22113018Ssam if (n < 0) { 22213018Ssam perror("tftp: recvfrom"); 22326094Sminshall goto abort; 22413018Ssam } 225*60062Storek peeraddr.sin_port = from.sin_port; /* added */ 22626094Sminshall if (trace) 22726094Sminshall tpacket("received", dp, n); 22826094Sminshall /* should verify client address */ 22926094Sminshall dp->th_opcode = ntohs(dp->th_opcode); 23026094Sminshall dp->th_block = ntohs(dp->th_block); 23126094Sminshall if (dp->th_opcode == ERROR) { 23226094Sminshall printf("Error code %d: %s\n", dp->th_code, 23326094Sminshall dp->th_msg); 23426094Sminshall goto abort; 23516384Ssam } 23626094Sminshall if (dp->th_opcode == DATA) { 23726113Sminshall int j; 23826113Sminshall 23926113Sminshall if (dp->th_block == block) { 240*60062Storek break; /* have next packet */ 24126113Sminshall } 24226113Sminshall /* On an error, try to synchronize 24326113Sminshall * both sides. 24426113Sminshall */ 24526113Sminshall j = synchnet(f); 24626113Sminshall if (j && trace) { 24726113Sminshall printf("discarded %d packets\n", j); 24826113Sminshall } 24926113Sminshall if (dp->th_block == (block-1)) { 250*60062Storek goto send_ack; /* resend ack */ 25126113Sminshall } 25216384Ssam } 25326094Sminshall } 254*60062Storek /* size = write(fd, dp->th_data, n - 4); */ 25526094Sminshall size = writeit(file, &dp, n - 4, convert); 2567770Ssam if (size < 0) { 25726094Sminshall nak(errno + 100); 25826094Sminshall break; 2597770Ssam } 2607770Ssam amount += size; 2617770Ssam } while (size == SEGSIZE); 262*60062Storek abort: /* ok to ack, since user */ 263*60062Storek ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 26426094Sminshall ap->th_block = htons((u_short)block); 265*60062Storek (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, 266*60062Storek sizeof(peeraddr)); 267*60062Storek write_behind(file, convert); /* flush last buffer */ 26826094Sminshall fclose(file); 26926094Sminshall stopclock(); 27026094Sminshall if (amount > 0) 27126094Sminshall printstats("Received", amount); 2727770Ssam } 2737770Ssam 274*60062Storek static int 27526094Sminshall makerequest(request, name, tp, mode) 2767770Ssam int request; 277*60062Storek const char *name; 27826094Sminshall struct tftphdr *tp; 279*60062Storek const char *mode; 2807770Ssam { 2817770Ssam register char *cp; 2827770Ssam 28326094Sminshall tp->th_opcode = htons((u_short)request); 28426094Sminshall cp = tp->th_stuff; 28526094Sminshall strcpy(cp, name); 28626094Sminshall cp += strlen(name); 2877770Ssam *cp++ = '\0'; 2887770Ssam strcpy(cp, mode); 28926094Sminshall cp += strlen(mode); 2907770Ssam *cp++ = '\0'; 29126094Sminshall return (cp - (char *)tp); 2927770Ssam } 2937770Ssam 2947770Ssam struct errmsg { 2957770Ssam int e_code; 2967770Ssam char *e_msg; 2977770Ssam } errmsgs[] = { 2987770Ssam { EUNDEF, "Undefined error code" }, 2997770Ssam { ENOTFOUND, "File not found" }, 3007770Ssam { EACCESS, "Access violation" }, 3017770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 3027770Ssam { EBADOP, "Illegal TFTP operation" }, 3037770Ssam { EBADID, "Unknown transfer ID" }, 3047770Ssam { EEXISTS, "File already exists" }, 3057770Ssam { ENOUSER, "No such user" }, 3067770Ssam { -1, 0 } 3077770Ssam }; 3087770Ssam 3097770Ssam /* 3107770Ssam * Send a nak packet (error message). 3117770Ssam * Error code passed in is one of the 3127770Ssam * standard TFTP codes, or a UNIX errno 3137770Ssam * offset by 100. 3147770Ssam */ 315*60062Storek static void 31626094Sminshall nak(error) 3177770Ssam int error; 3187770Ssam { 31942423Sbostic register struct errmsg *pe; 32026094Sminshall register struct tftphdr *tp; 3217770Ssam int length; 32242423Sbostic char *strerror(); 3237770Ssam 32426094Sminshall tp = (struct tftphdr *)ackbuf; 32526094Sminshall tp->th_opcode = htons((u_short)ERROR); 32626094Sminshall tp->th_code = htons((u_short)error); 3277770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 3287770Ssam if (pe->e_code == error) 3297770Ssam break; 33026094Sminshall if (pe->e_code < 0) { 33142423Sbostic pe->e_msg = strerror(error - 100); 33226094Sminshall tp->th_code = EUNDEF; 33326094Sminshall } 33426094Sminshall strcpy(tp->th_msg, pe->e_msg); 3357770Ssam length = strlen(pe->e_msg) + 4; 3367770Ssam if (trace) 33726094Sminshall tpacket("sent", tp, length); 338*60062Storek if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, 339*60062Storek sizeof(peeraddr)) != length) 34026094Sminshall perror("nak"); 3417770Ssam } 3427770Ssam 343*60062Storek static void 34426094Sminshall tpacket(s, tp, n) 345*60062Storek const char *s; 3467770Ssam struct tftphdr *tp; 3477770Ssam int n; 3487770Ssam { 3497770Ssam static char *opcodes[] = 3507770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3517770Ssam register char *cp, *file; 3527770Ssam u_short op = ntohs(tp->th_opcode); 3537770Ssam char *index(); 3547770Ssam 3557770Ssam if (op < RRQ || op > ERROR) 35626094Sminshall printf("%s opcode=%x ", s, op); 3577770Ssam else 35826094Sminshall printf("%s %s ", s, opcodes[op]); 3597770Ssam switch (op) { 3607770Ssam 3617770Ssam case RRQ: 3627770Ssam case WRQ: 3637770Ssam n -= 2; 3647770Ssam file = cp = tp->th_stuff; 3657770Ssam cp = index(cp, '\0'); 3667770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3677770Ssam break; 3687770Ssam 3697770Ssam case DATA: 3707770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3717770Ssam break; 3727770Ssam 3737770Ssam case ACK: 3747770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3757770Ssam break; 3767770Ssam 3777770Ssam case ERROR: 3787770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3797770Ssam break; 3807770Ssam } 3817770Ssam } 38226094Sminshall 38326094Sminshall struct timeval tstart; 38426094Sminshall struct timeval tstop; 38526094Sminshall 386*60062Storek static void 387*60062Storek startclock() 388*60062Storek { 389*60062Storek 390*60062Storek (void)gettimeofday(&tstart, NULL); 39126094Sminshall } 39226094Sminshall 393*60062Storek static void 394*60062Storek stopclock() 395*60062Storek { 396*60062Storek 397*60062Storek (void)gettimeofday(&tstop, NULL); 39826094Sminshall } 39926094Sminshall 400*60062Storek static void 40126094Sminshall printstats(direction, amount) 402*60062Storek const char *direction; 403*60062Storek unsigned long amount; 40426094Sminshall { 40526094Sminshall double delta; 40626094Sminshall /* compute delta in 1/10's second units */ 40726094Sminshall delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 40826094Sminshall ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 40926094Sminshall delta = delta/10.; /* back to seconds */ 41026094Sminshall printf("%s %d bytes in %.1f seconds", direction, amount, delta); 41126094Sminshall if (verbose) 41226094Sminshall printf(" [%.0f bits/sec]", (amount*8.)/delta); 41326094Sminshall putchar('\n'); 41426094Sminshall } 41526094Sminshall 416*60062Storek static void 417*60062Storek timer(sig) 418*60062Storek int sig; 419*60062Storek { 420*60062Storek 421*60062Storek timeout += rexmtval; 422*60062Storek if (timeout >= maxtimeout) { 423*60062Storek printf("Transfer timed out.\n"); 424*60062Storek longjmp(toplevel, -1); 425*60062Storek } 426*60062Storek longjmp(timeoutbuf, 1); 427*60062Storek } 428