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*26113Sminshall static char sccsid[] = "@(#)tftp.c 5.5 (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> 1826094Sminshall #include <sys/time.h> 199220Ssam 209220Ssam #include <netinet/in.h> 2126094Sminshall 2212399Ssam #include <arpa/tftp.h> 2312399Ssam 247770Ssam #include <signal.h> 257770Ssam #include <stdio.h> 267770Ssam #include <errno.h> 277770Ssam #include <setjmp.h> 289220Ssam 297770Ssam extern int errno; 3026094Sminshall 3126094Sminshall extern struct sockaddr_in sin; /* filled in by main */ 3226094Sminshall extern int f; /* the opened socket */ 3326094Sminshall extern int trace; 3426094Sminshall extern int verbose; 3526094Sminshall extern int rexmtval; 3626094Sminshall extern int maxtimeout; 3726094Sminshall 3826094Sminshall #define PKTSIZE SEGSIZE+4 3926094Sminshall char ackbuf[PKTSIZE]; 407770Ssam int timeout; 417770Ssam jmp_buf toplevel; 4213018Ssam jmp_buf timeoutbuf; 437770Ssam 447770Ssam timer() 457770Ssam { 4613018Ssam 4713018Ssam timeout += rexmtval; 4813018Ssam if (timeout >= maxtimeout) { 497770Ssam printf("Transfer timed out.\n"); 507770Ssam longjmp(toplevel, -1); 517770Ssam } 5213018Ssam longjmp(timeoutbuf, 1); 537770Ssam } 547770Ssam 557770Ssam /* 567770Ssam * Send the requested file. 577770Ssam */ 5826094Sminshall sendfile(fd, name, mode) 597770Ssam int fd; 607770Ssam char *name; 6126094Sminshall char *mode; 627770Ssam { 6326094Sminshall register struct tftphdr *ap; /* data and ack packets */ 6426094Sminshall struct tftphdr *r_init(), *dp; 6526094Sminshall register int block = 0, size, n; 6626094Sminshall register unsigned long amount = 0; 6726094Sminshall struct sockaddr_in from; 6826094Sminshall int fromlen; 6926094Sminshall int convert; /* true if doing nl->crlf conversion */ 7026094Sminshall FILE *file; 717770Ssam 7226094Sminshall startclock(); /* start stat's clock */ 7326094Sminshall dp = r_init(); /* reset fillbuf/read-ahead code */ 7426094Sminshall ap = (struct tftphdr *)ackbuf; 7526094Sminshall file = fdopen(fd, "r"); 7626094Sminshall convert = !strcmp(mode, "netascii"); 7726094Sminshall 7813018Ssam signal(SIGALRM, timer); 797770Ssam do { 8013018Ssam if (block == 0) 8126094Sminshall size = makerequest(WRQ, name, dp, mode) - 4; 8213018Ssam else { 8326094Sminshall /* size = read(fd, dp->th_data, SEGSIZE); */ 8426094Sminshall size = readit(file, &dp, convert); 857770Ssam if (size < 0) { 8626094Sminshall nak(errno + 100); 877770Ssam break; 887770Ssam } 8926094Sminshall dp->th_opcode = htons((u_short)DATA); 9026094Sminshall dp->th_block = htons((u_short)block); 917770Ssam } 927770Ssam timeout = 0; 9313018Ssam (void) setjmp(timeoutbuf); 9426104Sminshall send_data: 957770Ssam if (trace) 9626094Sminshall tpacket("sent", dp, size + 4); 9726094Sminshall n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin)); 989220Ssam if (n != size + 4) { 9913018Ssam perror("tftp: sendto"); 10026094Sminshall goto abort; 1017770Ssam } 10226094Sminshall read_ahead(file, convert); 10326104Sminshall for ( ; ; ) { 10413018Ssam alarm(rexmtval); 10513018Ssam do { 10613018Ssam fromlen = sizeof (from); 10726094Sminshall n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 10813018Ssam (caddr_t)&from, &fromlen); 10913018Ssam } while (n <= 0); 1107770Ssam alarm(0); 11113018Ssam if (n < 0) { 11213018Ssam perror("tftp: recvfrom"); 11326094Sminshall goto abort; 11413018Ssam } 11526094Sminshall sin.sin_port = from.sin_port; /* added */ 11613018Ssam if (trace) 11726094Sminshall tpacket("received", ap, n); 11813018Ssam /* should verify packet came from server */ 11926094Sminshall ap->th_opcode = ntohs(ap->th_opcode); 12026094Sminshall ap->th_block = ntohs(ap->th_block); 12126094Sminshall if (ap->th_opcode == ERROR) { 12226094Sminshall printf("Error code %d: %s\n", ap->th_code, 12326094Sminshall ap->th_msg); 12426094Sminshall goto abort; 12513018Ssam } 12626104Sminshall if (ap->th_opcode == ACK) { 127*26113Sminshall int j; 128*26113Sminshall 12926104Sminshall if (ap->th_block == block) { 13026104Sminshall break; 13126104Sminshall } 132*26113Sminshall /* On an error, try to synchronize 133*26113Sminshall * both sides. 134*26113Sminshall */ 135*26113Sminshall j = synchnet(f); 136*26113Sminshall if (j && trace) { 137*26113Sminshall printf("discarded %d packets\n", 138*26113Sminshall j); 139*26113Sminshall } 140*26113Sminshall if (ap->th_block == (block-1)) { 141*26113Sminshall goto send_data; 142*26113Sminshall } 14326104Sminshall } 14426104Sminshall } 1457770Ssam if (block > 0) 1467770Ssam amount += size; 1477770Ssam block++; 1487770Ssam } while (size == SEGSIZE || block == 1); 14926094Sminshall abort: 15026094Sminshall fclose(file); 15126094Sminshall stopclock(); 15226094Sminshall if (amount > 0) 15326094Sminshall printstats("Sent", amount); 1547770Ssam } 1557770Ssam 1567770Ssam /* 1577770Ssam * Receive a file. 1587770Ssam */ 15926094Sminshall recvfile(fd, name, mode) 1607770Ssam int fd; 1617770Ssam char *name; 16226094Sminshall char *mode; 1637770Ssam { 16426094Sminshall register struct tftphdr *ap; 16526094Sminshall struct tftphdr *dp, *w_init(); 16626094Sminshall register int block = 1, n, size; 16726094Sminshall unsigned long amount = 0; 16826094Sminshall struct sockaddr_in from; 16926094Sminshall int fromlen, firsttrip = 1; 17026094Sminshall FILE *file; 17126094Sminshall int convert; /* true if converting crlf -> lf */ 1727770Ssam 17326094Sminshall startclock(); 17426094Sminshall dp = w_init(); 17526094Sminshall ap = (struct tftphdr *)ackbuf; 17626094Sminshall file = fdopen(fd, "w"); 17726094Sminshall convert = !strcmp(mode, "netascii"); 17826094Sminshall 17913018Ssam signal(SIGALRM, timer); 1807770Ssam do { 18113018Ssam if (firsttrip) { 18226094Sminshall size = makerequest(RRQ, name, ap, mode); 18313018Ssam firsttrip = 0; 18413018Ssam } else { 18526094Sminshall ap->th_opcode = htons((u_short)ACK); 18626094Sminshall ap->th_block = htons((u_short)(block)); 18713018Ssam size = 4; 18813018Ssam block++; 18913018Ssam } 1907770Ssam timeout = 0; 19113018Ssam (void) setjmp(timeoutbuf); 19226094Sminshall send_ack: 1937770Ssam if (trace) 19426094Sminshall tpacket("sent", ap, size); 19526094Sminshall if (sendto(f, ackbuf, size, 0, (caddr_t)&sin, 19626094Sminshall sizeof (sin)) != size) { 19713018Ssam alarm(0); 19813018Ssam perror("tftp: sendto"); 19926094Sminshall goto abort; 2007770Ssam } 20126094Sminshall write_behind(file, convert); 20226094Sminshall for ( ; ; ) { 20313018Ssam alarm(rexmtval); 20426094Sminshall do { 20516384Ssam fromlen = sizeof (from); 20626094Sminshall n = recvfrom(f, dp, PKTSIZE, 0, 20713018Ssam (caddr_t)&from, &fromlen); 20816384Ssam } while (n <= 0); 2097770Ssam alarm(0); 21013018Ssam if (n < 0) { 21113018Ssam perror("tftp: recvfrom"); 21226094Sminshall goto abort; 21313018Ssam } 21426094Sminshall sin.sin_port = from.sin_port; /* added */ 21526094Sminshall if (trace) 21626094Sminshall tpacket("received", dp, n); 21726094Sminshall /* should verify client address */ 21826094Sminshall dp->th_opcode = ntohs(dp->th_opcode); 21926094Sminshall dp->th_block = ntohs(dp->th_block); 22026094Sminshall if (dp->th_opcode == ERROR) { 22126094Sminshall printf("Error code %d: %s\n", dp->th_code, 22226094Sminshall dp->th_msg); 22326094Sminshall goto abort; 22416384Ssam } 22526094Sminshall if (dp->th_opcode == DATA) { 226*26113Sminshall int j; 227*26113Sminshall 228*26113Sminshall if (dp->th_block == block) { 22926094Sminshall break; /* have next packet */ 230*26113Sminshall } 231*26113Sminshall /* On an error, try to synchronize 232*26113Sminshall * both sides. 233*26113Sminshall */ 234*26113Sminshall j = synchnet(f); 235*26113Sminshall if (j && trace) { 236*26113Sminshall printf("discarded %d packets\n", j); 237*26113Sminshall } 238*26113Sminshall if (dp->th_block == (block-1)) { 23926094Sminshall goto send_ack; /* resend ack */ 240*26113Sminshall } 24116384Ssam } 24226094Sminshall } 24326094Sminshall /* size = write(fd, dp->th_data, n - 4); */ 24426094Sminshall size = writeit(file, &dp, n - 4, convert); 2457770Ssam if (size < 0) { 24626094Sminshall nak(errno + 100); 24726094Sminshall break; 2487770Ssam } 2497770Ssam amount += size; 2507770Ssam } while (size == SEGSIZE); 25126094Sminshall abort: /* ok to ack, since user */ 25226094Sminshall ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 25326094Sminshall ap->th_block = htons((u_short)block); 25426094Sminshall (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin)); 25526094Sminshall write_behind(file, convert); /* flush last buffer */ 25626094Sminshall fclose(file); 25726094Sminshall stopclock(); 25826094Sminshall if (amount > 0) 25926094Sminshall printstats("Received", amount); 2607770Ssam } 2617770Ssam 26226094Sminshall makerequest(request, name, tp, mode) 2637770Ssam int request; 26426094Sminshall char *name, *mode; 26526094Sminshall struct tftphdr *tp; 2667770Ssam { 2677770Ssam register char *cp; 2687770Ssam 26926094Sminshall tp->th_opcode = htons((u_short)request); 27026094Sminshall cp = tp->th_stuff; 27126094Sminshall strcpy(cp, name); 27226094Sminshall cp += strlen(name); 2737770Ssam *cp++ = '\0'; 2747770Ssam strcpy(cp, mode); 27526094Sminshall cp += strlen(mode); 2767770Ssam *cp++ = '\0'; 27726094Sminshall return (cp - (char *)tp); 2787770Ssam } 2797770Ssam 2807770Ssam struct errmsg { 2817770Ssam int e_code; 2827770Ssam char *e_msg; 2837770Ssam } errmsgs[] = { 2847770Ssam { EUNDEF, "Undefined error code" }, 2857770Ssam { ENOTFOUND, "File not found" }, 2867770Ssam { EACCESS, "Access violation" }, 2877770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2887770Ssam { EBADOP, "Illegal TFTP operation" }, 2897770Ssam { EBADID, "Unknown transfer ID" }, 2907770Ssam { EEXISTS, "File already exists" }, 2917770Ssam { ENOUSER, "No such user" }, 2927770Ssam { -1, 0 } 2937770Ssam }; 2947770Ssam 2957770Ssam /* 2967770Ssam * Send a nak packet (error message). 2977770Ssam * Error code passed in is one of the 2987770Ssam * standard TFTP codes, or a UNIX errno 2997770Ssam * offset by 100. 3007770Ssam */ 30126094Sminshall nak(error) 3027770Ssam int error; 3037770Ssam { 30426094Sminshall register struct tftphdr *tp; 3057770Ssam int length; 3067770Ssam register struct errmsg *pe; 3077770Ssam extern char *sys_errlist[]; 3087770Ssam 30926094Sminshall tp = (struct tftphdr *)ackbuf; 31026094Sminshall tp->th_opcode = htons((u_short)ERROR); 31126094Sminshall tp->th_code = htons((u_short)error); 3127770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 3137770Ssam if (pe->e_code == error) 3147770Ssam break; 31526094Sminshall if (pe->e_code < 0) { 3167770Ssam pe->e_msg = sys_errlist[error - 100]; 31726094Sminshall tp->th_code = EUNDEF; 31826094Sminshall } 31926094Sminshall strcpy(tp->th_msg, pe->e_msg); 3207770Ssam length = strlen(pe->e_msg) + 4; 3217770Ssam if (trace) 32226094Sminshall tpacket("sent", tp, length); 32326094Sminshall if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length) 32426094Sminshall perror("nak"); 3257770Ssam } 3267770Ssam 32726094Sminshall tpacket(s, tp, n) 32826111Sminshall char *s; 3297770Ssam struct tftphdr *tp; 3307770Ssam int n; 3317770Ssam { 3327770Ssam static char *opcodes[] = 3337770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3347770Ssam register char *cp, *file; 3357770Ssam u_short op = ntohs(tp->th_opcode); 3367770Ssam char *index(); 3377770Ssam 3387770Ssam if (op < RRQ || op > ERROR) 33926094Sminshall printf("%s opcode=%x ", s, op); 3407770Ssam else 34126094Sminshall printf("%s %s ", s, opcodes[op]); 3427770Ssam switch (op) { 3437770Ssam 3447770Ssam case RRQ: 3457770Ssam case WRQ: 3467770Ssam n -= 2; 3477770Ssam file = cp = tp->th_stuff; 3487770Ssam cp = index(cp, '\0'); 3497770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3507770Ssam break; 3517770Ssam 3527770Ssam case DATA: 3537770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3547770Ssam break; 3557770Ssam 3567770Ssam case ACK: 3577770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3587770Ssam break; 3597770Ssam 3607770Ssam case ERROR: 3617770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3627770Ssam break; 3637770Ssam } 3647770Ssam } 36526094Sminshall 36626094Sminshall struct timeval tstart; 36726094Sminshall struct timeval tstop; 36826094Sminshall struct timezone zone; 36926094Sminshall 37026094Sminshall startclock() { 37126094Sminshall gettimeofday(&tstart, &zone); 37226094Sminshall } 37326094Sminshall 37426094Sminshall stopclock() { 37526094Sminshall gettimeofday(&tstop, &zone); 37626094Sminshall } 37726094Sminshall 37826094Sminshall printstats(direction, amount) 37926094Sminshall char *direction; 38026094Sminshall unsigned long amount; 38126094Sminshall { 38226094Sminshall double delta; 38326094Sminshall /* compute delta in 1/10's second units */ 38426094Sminshall delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 38526094Sminshall ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 38626094Sminshall delta = delta/10.; /* back to seconds */ 38726094Sminshall printf("%s %d bytes in %.1f seconds", direction, amount, delta); 38826094Sminshall if (verbose) 38926094Sminshall printf(" [%.0f bits/sec]", (amount*8.)/delta); 39026094Sminshall putchar('\n'); 39126094Sminshall } 39226094Sminshall 393