122404Sdist /* 226094Sminshall * Copyright (c) 1985 Regents of the University of California. 322404Sdist * All rights reserved. The Berkeley software License Agreement 422404Sdist * specifies the terms and conditions for redistribution. 522404Sdist */ 622404Sdist 714554Ssam #ifndef lint 8*26104Sminshall static char sccsid[] = "@(#)tftp.c 5.3 (Berkeley) 02/07/86"; 922404Sdist #endif not lint 107770Ssam 1126094Sminshall /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 1226094Sminshall 137770Ssam /* 147770Ssam * TFTP User Program -- Protocol Machines 157770Ssam */ 167770Ssam #include <sys/types.h> 177770Ssam #include <sys/socket.h> 18*26104Sminshall #include <sys/ioctl.h> 1926094Sminshall #include <sys/time.h> 209220Ssam 219220Ssam #include <netinet/in.h> 2226094Sminshall 2312399Ssam #include <arpa/tftp.h> 2412399Ssam 257770Ssam #include <signal.h> 267770Ssam #include <stdio.h> 277770Ssam #include <errno.h> 287770Ssam #include <setjmp.h> 299220Ssam 307770Ssam extern int errno; 3126094Sminshall 3226094Sminshall extern struct sockaddr_in sin; /* filled in by main */ 3326094Sminshall extern int f; /* the opened socket */ 3426094Sminshall extern int trace; 3526094Sminshall extern int verbose; 3626094Sminshall extern int rexmtval; 3726094Sminshall extern int maxtimeout; 3826094Sminshall 3926094Sminshall #define PKTSIZE SEGSIZE+4 4026094Sminshall char ackbuf[PKTSIZE]; 417770Ssam int timeout; 427770Ssam jmp_buf toplevel; 4313018Ssam jmp_buf timeoutbuf; 447770Ssam 457770Ssam timer() 467770Ssam { 4713018Ssam 4813018Ssam timeout += rexmtval; 4913018Ssam if (timeout >= maxtimeout) { 507770Ssam printf("Transfer timed out.\n"); 517770Ssam longjmp(toplevel, -1); 527770Ssam } 5313018Ssam longjmp(timeoutbuf, 1); 547770Ssam } 557770Ssam 567770Ssam /* 577770Ssam * Send the requested file. 587770Ssam */ 5926094Sminshall sendfile(fd, name, mode) 607770Ssam int fd; 617770Ssam char *name; 6226094Sminshall char *mode; 637770Ssam { 6426094Sminshall register struct tftphdr *ap; /* data and ack packets */ 6526094Sminshall struct tftphdr *r_init(), *dp; 6626094Sminshall register int block = 0, size, n; 6726094Sminshall register unsigned long amount = 0; 6826094Sminshall struct sockaddr_in from; 6926094Sminshall int fromlen; 7026094Sminshall int convert; /* true if doing nl->crlf conversion */ 7126094Sminshall FILE *file; 727770Ssam 7326094Sminshall startclock(); /* start stat's clock */ 7426094Sminshall dp = r_init(); /* reset fillbuf/read-ahead code */ 7526094Sminshall ap = (struct tftphdr *)ackbuf; 7626094Sminshall file = fdopen(fd, "r"); 7726094Sminshall convert = !strcmp(mode, "netascii"); 7826094Sminshall 7913018Ssam signal(SIGALRM, timer); 807770Ssam do { 8113018Ssam if (block == 0) 8226094Sminshall size = makerequest(WRQ, name, dp, mode) - 4; 8313018Ssam else { 8426094Sminshall /* size = read(fd, dp->th_data, SEGSIZE); */ 8526094Sminshall size = readit(file, &dp, convert); 867770Ssam if (size < 0) { 8726094Sminshall nak(errno + 100); 887770Ssam break; 897770Ssam } 9026094Sminshall dp->th_opcode = htons((u_short)DATA); 9126094Sminshall dp->th_block = htons((u_short)block); 927770Ssam } 937770Ssam timeout = 0; 9413018Ssam (void) setjmp(timeoutbuf); 95*26104Sminshall send_data: 96*26104Sminshall /* Now, we flush anything pending to be read */ 97*26104Sminshall /* This is to try to keep in synch between the two sides */ 98*26104Sminshall while (1) { 99*26104Sminshall int i, j = 0; 100*26104Sminshall char rbuf[PKTSIZE]; 101*26104Sminshall 102*26104Sminshall (void) ioctl(f, FIONREAD, &i); 103*26104Sminshall if (i) { 104*26104Sminshall j++; 105*26104Sminshall fromlen = sizeof from; 106*26104Sminshall n = recvfrom(f, rbuf, sizeof (rbuf), 0, 107*26104Sminshall (caddr_t)&from, &fromlen); 108*26104Sminshall } else { 109*26104Sminshall if (j && trace) { 110*26104Sminshall printf("discarded %d packets\n", j); 111*26104Sminshall } 112*26104Sminshall break; 113*26104Sminshall } 114*26104Sminshall } 1157770Ssam if (trace) 11626094Sminshall tpacket("sent", dp, size + 4); 11726094Sminshall n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 1189220Ssam if (n != size + 4) { 11913018Ssam perror("tftp: sendto"); 12026094Sminshall goto abort; 1217770Ssam } 12226094Sminshall read_ahead(file, convert); 123*26104Sminshall for ( ; ; ) { 12413018Ssam alarm(rexmtval); 12513018Ssam do { 12613018Ssam fromlen = sizeof (from); 12726094Sminshall n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 12813018Ssam (caddr_t)&from, &fromlen); 12913018Ssam } while (n <= 0); 1307770Ssam alarm(0); 13113018Ssam if (n < 0) { 13213018Ssam perror("tftp: recvfrom"); 13326094Sminshall goto abort; 13413018Ssam } 13526094Sminshall sin.sin_port = from.sin_port; /* added */ 13613018Ssam if (trace) 13726094Sminshall tpacket("received", ap, n); 13813018Ssam /* should verify packet came from server */ 13926094Sminshall ap->th_opcode = ntohs(ap->th_opcode); 14026094Sminshall ap->th_block = ntohs(ap->th_block); 14126094Sminshall if (ap->th_opcode == ERROR) { 14226094Sminshall printf("Error code %d: %s\n", ap->th_code, 14326094Sminshall ap->th_msg); 14426094Sminshall goto abort; 14513018Ssam } 146*26104Sminshall if (ap->th_opcode == ACK) { 147*26104Sminshall if (ap->th_block == block) { 148*26104Sminshall break; 149*26104Sminshall } else if (ap->th_block == (block-1)) { 150*26104Sminshall goto send_data; /* resend packet */ 151*26104Sminshall } 152*26104Sminshall } 153*26104Sminshall } 1547770Ssam if (block > 0) 1557770Ssam amount += size; 1567770Ssam block++; 1577770Ssam } while (size == SEGSIZE || block == 1); 15826094Sminshall abort: 15926094Sminshall fclose(file); 16026094Sminshall stopclock(); 16126094Sminshall if (amount > 0) 16226094Sminshall printstats("Sent", amount); 1637770Ssam } 1647770Ssam 1657770Ssam /* 1667770Ssam * Receive a file. 1677770Ssam */ 16826094Sminshall recvfile(fd, name, mode) 1697770Ssam int fd; 1707770Ssam char *name; 17126094Sminshall char *mode; 1727770Ssam { 17326094Sminshall register struct tftphdr *ap; 17426094Sminshall struct tftphdr *dp, *w_init(); 17526094Sminshall register int block = 1, n, size; 17626094Sminshall unsigned long amount = 0; 17726094Sminshall struct sockaddr_in from; 17826094Sminshall int fromlen, firsttrip = 1; 17926094Sminshall FILE *file; 18026094Sminshall int convert; /* true if converting crlf -> lf */ 1817770Ssam 18226094Sminshall startclock(); 18326094Sminshall dp = w_init(); 18426094Sminshall ap = (struct tftphdr *)ackbuf; 18526094Sminshall file = fdopen(fd, "w"); 18626094Sminshall convert = !strcmp(mode, "netascii"); 18726094Sminshall 18813018Ssam signal(SIGALRM, timer); 1897770Ssam do { 19013018Ssam if (firsttrip) { 19126094Sminshall size = makerequest(RRQ, name, ap, mode); 19213018Ssam firsttrip = 0; 19313018Ssam } else { 19426094Sminshall ap->th_opcode = htons((u_short)ACK); 19526094Sminshall ap->th_block = htons((u_short)(block)); 19613018Ssam size = 4; 19713018Ssam block++; 19813018Ssam } 1997770Ssam timeout = 0; 20013018Ssam (void) setjmp(timeoutbuf); 20126094Sminshall send_ack: 202*26104Sminshall /* Now, we flush anything pending to be read */ 203*26104Sminshall /* This is to try to keep in synch between the two sides */ 204*26104Sminshall while (1) { 205*26104Sminshall int i, j = 0; 206*26104Sminshall char rbuf[PKTSIZE]; 207*26104Sminshall 208*26104Sminshall (void) ioctl(f, FIONREAD, &i); 209*26104Sminshall if (i) { 210*26104Sminshall j++; 211*26104Sminshall fromlen = sizeof from; 212*26104Sminshall n = recvfrom(f, rbuf, sizeof (rbuf), 0, 213*26104Sminshall (caddr_t)&from, &fromlen); 214*26104Sminshall } else { 215*26104Sminshall if (j && trace) { 216*26104Sminshall printf("discarded %d packets\n", j); 217*26104Sminshall } 218*26104Sminshall break; 219*26104Sminshall } 220*26104Sminshall } 2217770Ssam if (trace) 22226094Sminshall tpacket("sent", ap, size); 22326094Sminshall if (sendto(f, ackbuf, size, 0, (caddr_t)&sin, 22426094Sminshall sizeof (sin)) != size) { 22513018Ssam alarm(0); 22613018Ssam perror("tftp: sendto"); 22726094Sminshall goto abort; 2287770Ssam } 22926094Sminshall write_behind(file, convert); 23026094Sminshall for ( ; ; ) { 23113018Ssam alarm(rexmtval); 23226094Sminshall do { 23316384Ssam fromlen = sizeof (from); 23426094Sminshall n = recvfrom(f, dp, PKTSIZE, 0, 23513018Ssam (caddr_t)&from, &fromlen); 23616384Ssam } while (n <= 0); 2377770Ssam alarm(0); 23813018Ssam if (n < 0) { 23913018Ssam perror("tftp: recvfrom"); 24026094Sminshall goto abort; 24113018Ssam } 24226094Sminshall sin.sin_port = from.sin_port; /* added */ 24326094Sminshall if (trace) 24426094Sminshall tpacket("received", dp, n); 24526094Sminshall /* should verify client address */ 24626094Sminshall dp->th_opcode = ntohs(dp->th_opcode); 24726094Sminshall dp->th_block = ntohs(dp->th_block); 24826094Sminshall if (dp->th_opcode == ERROR) { 24926094Sminshall printf("Error code %d: %s\n", dp->th_code, 25026094Sminshall dp->th_msg); 25126094Sminshall goto abort; 25216384Ssam } 25326094Sminshall if (dp->th_opcode == DATA) { 25426094Sminshall if (dp->th_block == block) 25526094Sminshall break; /* have next packet */ 25626094Sminshall if (dp->th_block == (block-1)) 25726094Sminshall goto send_ack; /* resend ack */ 25816384Ssam } 25926094Sminshall } 26026094Sminshall /* size = write(fd, dp->th_data, n - 4); */ 26126094Sminshall size = writeit(file, &dp, n - 4, convert); 2627770Ssam if (size < 0) { 26326094Sminshall nak(errno + 100); 26426094Sminshall break; 2657770Ssam } 2667770Ssam amount += size; 2677770Ssam } while (size == SEGSIZE); 26826094Sminshall abort: /* ok to ack, since user */ 26926094Sminshall ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 27026094Sminshall ap->th_block = htons((u_short)block); 27126094Sminshall (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin)); 27226094Sminshall write_behind(file, convert); /* flush last buffer */ 27326094Sminshall fclose(file); 27426094Sminshall stopclock(); 27526094Sminshall if (amount > 0) 27626094Sminshall printstats("Received", amount); 2777770Ssam } 2787770Ssam 27926094Sminshall makerequest(request, name, tp, mode) 2807770Ssam int request; 28126094Sminshall char *name, *mode; 28226094Sminshall struct tftphdr *tp; 2837770Ssam { 2847770Ssam register char *cp; 2857770Ssam 28626094Sminshall tp->th_opcode = htons((u_short)request); 28726094Sminshall cp = tp->th_stuff; 28826094Sminshall strcpy(cp, name); 28926094Sminshall cp += strlen(name); 2907770Ssam *cp++ = '\0'; 2917770Ssam strcpy(cp, mode); 29226094Sminshall cp += strlen(mode); 2937770Ssam *cp++ = '\0'; 29426094Sminshall return (cp - (char *)tp); 2957770Ssam } 2967770Ssam 2977770Ssam struct errmsg { 2987770Ssam int e_code; 2997770Ssam char *e_msg; 3007770Ssam } errmsgs[] = { 3017770Ssam { EUNDEF, "Undefined error code" }, 3027770Ssam { ENOTFOUND, "File not found" }, 3037770Ssam { EACCESS, "Access violation" }, 3047770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 3057770Ssam { EBADOP, "Illegal TFTP operation" }, 3067770Ssam { EBADID, "Unknown transfer ID" }, 3077770Ssam { EEXISTS, "File already exists" }, 3087770Ssam { ENOUSER, "No such user" }, 3097770Ssam { -1, 0 } 3107770Ssam }; 3117770Ssam 3127770Ssam /* 3137770Ssam * Send a nak packet (error message). 3147770Ssam * Error code passed in is one of the 3157770Ssam * standard TFTP codes, or a UNIX errno 3167770Ssam * offset by 100. 3177770Ssam */ 31826094Sminshall nak(error) 3197770Ssam int error; 3207770Ssam { 32126094Sminshall register struct tftphdr *tp; 3227770Ssam int length; 3237770Ssam register struct errmsg *pe; 3247770Ssam extern char *sys_errlist[]; 3257770Ssam 32626094Sminshall tp = (struct tftphdr *)ackbuf; 32726094Sminshall tp->th_opcode = htons((u_short)ERROR); 32826094Sminshall tp->th_code = htons((u_short)error); 3297770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 3307770Ssam if (pe->e_code == error) 3317770Ssam break; 33226094Sminshall if (pe->e_code < 0) { 3337770Ssam pe->e_msg = sys_errlist[error - 100]; 33426094Sminshall tp->th_code = EUNDEF; 33526094Sminshall } 33626094Sminshall strcpy(tp->th_msg, pe->e_msg); 3377770Ssam length = strlen(pe->e_msg) + 4; 3387770Ssam if (trace) 33926094Sminshall tpacket("sent", tp, length); 34026094Sminshall if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length) 34126094Sminshall perror("nak"); 3427770Ssam } 3437770Ssam 34426094Sminshall tpacket(s, tp, n) 3457770Ssam struct tftphdr *tp; 3467770Ssam int n; 3477770Ssam { 3487770Ssam static char *opcodes[] = 3497770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3507770Ssam register char *cp, *file; 3517770Ssam u_short op = ntohs(tp->th_opcode); 3527770Ssam char *index(); 3537770Ssam 3547770Ssam if (op < RRQ || op > ERROR) 35526094Sminshall printf("%s opcode=%x ", s, op); 3567770Ssam else 35726094Sminshall printf("%s %s ", s, opcodes[op]); 3587770Ssam switch (op) { 3597770Ssam 3607770Ssam case RRQ: 3617770Ssam case WRQ: 3627770Ssam n -= 2; 3637770Ssam file = cp = tp->th_stuff; 3647770Ssam cp = index(cp, '\0'); 3657770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3667770Ssam break; 3677770Ssam 3687770Ssam case DATA: 3697770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3707770Ssam break; 3717770Ssam 3727770Ssam case ACK: 3737770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3747770Ssam break; 3757770Ssam 3767770Ssam case ERROR: 3777770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3787770Ssam break; 3797770Ssam } 3807770Ssam } 38126094Sminshall 38226094Sminshall struct timeval tstart; 38326094Sminshall struct timeval tstop; 38426094Sminshall struct timezone zone; 38526094Sminshall 38626094Sminshall startclock() { 38726094Sminshall gettimeofday(&tstart, &zone); 38826094Sminshall } 38926094Sminshall 39026094Sminshall stopclock() { 39126094Sminshall gettimeofday(&tstop, &zone); 39226094Sminshall } 39326094Sminshall 39426094Sminshall printstats(direction, amount) 39526094Sminshall char *direction; 39626094Sminshall unsigned long amount; 39726094Sminshall { 39826094Sminshall double delta; 39926094Sminshall /* compute delta in 1/10's second units */ 40026094Sminshall delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 40126094Sminshall ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 40226094Sminshall delta = delta/10.; /* back to seconds */ 40326094Sminshall printf("%s %d bytes in %.1f seconds", direction, amount, delta); 40426094Sminshall if (verbose) 40526094Sminshall printf(" [%.0f bits/sec]", (amount*8.)/delta); 40626094Sminshall putchar('\n'); 40726094Sminshall } 40826094Sminshall 409