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*46861Sbostic static char sccsid[] = "@(#)tftp.c 5.10 (Berkeley) 03/01/91"; 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 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 45*46861Sbostic void 467770Ssam timer() 477770Ssam { 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); 9526104Sminshall send_data: 967770Ssam if (trace) 9726094Sminshall tpacket("sent", dp, size + 4); 98*46861Sbostic n = sendto(f, dp, size + 4, 0, 99*46861Sbostic (struct sockaddr *)&sin, sizeof (sin)); 1009220Ssam if (n != size + 4) { 10113018Ssam perror("tftp: sendto"); 10226094Sminshall goto abort; 1037770Ssam } 10426094Sminshall read_ahead(file, convert); 10526104Sminshall for ( ; ; ) { 10613018Ssam alarm(rexmtval); 10713018Ssam do { 10813018Ssam fromlen = sizeof (from); 10926094Sminshall n = recvfrom(f, ackbuf, sizeof (ackbuf), 0, 110*46861Sbostic (struct sockaddr *)&from, &fromlen); 11113018Ssam } while (n <= 0); 1127770Ssam alarm(0); 11313018Ssam if (n < 0) { 11413018Ssam perror("tftp: recvfrom"); 11526094Sminshall goto abort; 11613018Ssam } 11726094Sminshall sin.sin_port = from.sin_port; /* added */ 11813018Ssam if (trace) 11926094Sminshall tpacket("received", ap, n); 12013018Ssam /* should verify packet came from server */ 12126094Sminshall ap->th_opcode = ntohs(ap->th_opcode); 12226094Sminshall ap->th_block = ntohs(ap->th_block); 12326094Sminshall if (ap->th_opcode == ERROR) { 12426094Sminshall printf("Error code %d: %s\n", ap->th_code, 12526094Sminshall ap->th_msg); 12626094Sminshall goto abort; 12713018Ssam } 12826104Sminshall if (ap->th_opcode == ACK) { 12926113Sminshall int j; 13026113Sminshall 13126104Sminshall if (ap->th_block == block) { 13226104Sminshall break; 13326104Sminshall } 13426113Sminshall /* On an error, try to synchronize 13526113Sminshall * both sides. 13626113Sminshall */ 13726113Sminshall j = synchnet(f); 13826113Sminshall if (j && trace) { 13926113Sminshall printf("discarded %d packets\n", 14026113Sminshall j); 14126113Sminshall } 14226113Sminshall if (ap->th_block == (block-1)) { 14326113Sminshall goto send_data; 14426113Sminshall } 14526104Sminshall } 14626104Sminshall } 1477770Ssam if (block > 0) 1487770Ssam amount += size; 1497770Ssam block++; 1507770Ssam } while (size == SEGSIZE || block == 1); 15126094Sminshall abort: 15226094Sminshall fclose(file); 15326094Sminshall stopclock(); 15426094Sminshall if (amount > 0) 15526094Sminshall printstats("Sent", amount); 1567770Ssam } 1577770Ssam 1587770Ssam /* 1597770Ssam * Receive a file. 1607770Ssam */ 16126094Sminshall recvfile(fd, name, mode) 1627770Ssam int fd; 1637770Ssam char *name; 16426094Sminshall char *mode; 1657770Ssam { 16626094Sminshall register struct tftphdr *ap; 16726094Sminshall struct tftphdr *dp, *w_init(); 16826094Sminshall register int block = 1, n, size; 16926094Sminshall unsigned long amount = 0; 17026094Sminshall struct sockaddr_in from; 17126094Sminshall int fromlen, firsttrip = 1; 17226094Sminshall FILE *file; 17326094Sminshall int convert; /* true if converting crlf -> lf */ 1747770Ssam 17526094Sminshall startclock(); 17626094Sminshall dp = w_init(); 17726094Sminshall ap = (struct tftphdr *)ackbuf; 17826094Sminshall file = fdopen(fd, "w"); 17926094Sminshall convert = !strcmp(mode, "netascii"); 18026094Sminshall 18113018Ssam signal(SIGALRM, timer); 1827770Ssam do { 18313018Ssam if (firsttrip) { 18426094Sminshall size = makerequest(RRQ, name, ap, mode); 18513018Ssam firsttrip = 0; 18613018Ssam } else { 18726094Sminshall ap->th_opcode = htons((u_short)ACK); 18826094Sminshall ap->th_block = htons((u_short)(block)); 18913018Ssam size = 4; 19013018Ssam block++; 19113018Ssam } 1927770Ssam timeout = 0; 19313018Ssam (void) setjmp(timeoutbuf); 19426094Sminshall send_ack: 1957770Ssam if (trace) 19626094Sminshall tpacket("sent", ap, size); 197*46861Sbostic if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&sin, 19826094Sminshall sizeof (sin)) != size) { 19913018Ssam alarm(0); 20013018Ssam perror("tftp: sendto"); 20126094Sminshall goto abort; 2027770Ssam } 20326094Sminshall write_behind(file, convert); 20426094Sminshall for ( ; ; ) { 20513018Ssam alarm(rexmtval); 20626094Sminshall do { 20716384Ssam fromlen = sizeof (from); 20826094Sminshall n = recvfrom(f, dp, PKTSIZE, 0, 209*46861Sbostic (struct sockaddr *)&from, &fromlen); 21016384Ssam } while (n <= 0); 2117770Ssam alarm(0); 21213018Ssam if (n < 0) { 21313018Ssam perror("tftp: recvfrom"); 21426094Sminshall goto abort; 21513018Ssam } 21626094Sminshall sin.sin_port = from.sin_port; /* added */ 21726094Sminshall if (trace) 21826094Sminshall tpacket("received", dp, n); 21926094Sminshall /* should verify client address */ 22026094Sminshall dp->th_opcode = ntohs(dp->th_opcode); 22126094Sminshall dp->th_block = ntohs(dp->th_block); 22226094Sminshall if (dp->th_opcode == ERROR) { 22326094Sminshall printf("Error code %d: %s\n", dp->th_code, 22426094Sminshall dp->th_msg); 22526094Sminshall goto abort; 22616384Ssam } 22726094Sminshall if (dp->th_opcode == DATA) { 22826113Sminshall int j; 22926113Sminshall 23026113Sminshall if (dp->th_block == block) { 23126094Sminshall break; /* have next packet */ 23226113Sminshall } 23326113Sminshall /* On an error, try to synchronize 23426113Sminshall * both sides. 23526113Sminshall */ 23626113Sminshall j = synchnet(f); 23726113Sminshall if (j && trace) { 23826113Sminshall printf("discarded %d packets\n", j); 23926113Sminshall } 24026113Sminshall if (dp->th_block == (block-1)) { 24126094Sminshall goto send_ack; /* resend ack */ 24226113Sminshall } 24316384Ssam } 24426094Sminshall } 24526094Sminshall /* size = write(fd, dp->th_data, n - 4); */ 24626094Sminshall size = writeit(file, &dp, n - 4, convert); 2477770Ssam if (size < 0) { 24826094Sminshall nak(errno + 100); 24926094Sminshall break; 2507770Ssam } 2517770Ssam amount += size; 2527770Ssam } while (size == SEGSIZE); 25326094Sminshall abort: /* ok to ack, since user */ 25426094Sminshall ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ 25526094Sminshall ap->th_block = htons((u_short)block); 256*46861Sbostic (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&sin, sizeof (sin)); 25726094Sminshall write_behind(file, convert); /* flush last buffer */ 25826094Sminshall fclose(file); 25926094Sminshall stopclock(); 26026094Sminshall if (amount > 0) 26126094Sminshall printstats("Received", amount); 2627770Ssam } 2637770Ssam 26426094Sminshall makerequest(request, name, tp, mode) 2657770Ssam int request; 26626094Sminshall char *name, *mode; 26726094Sminshall struct tftphdr *tp; 2687770Ssam { 2697770Ssam register char *cp; 2707770Ssam 27126094Sminshall tp->th_opcode = htons((u_short)request); 27226094Sminshall cp = tp->th_stuff; 27326094Sminshall strcpy(cp, name); 27426094Sminshall cp += strlen(name); 2757770Ssam *cp++ = '\0'; 2767770Ssam strcpy(cp, mode); 27726094Sminshall cp += strlen(mode); 2787770Ssam *cp++ = '\0'; 27926094Sminshall return (cp - (char *)tp); 2807770Ssam } 2817770Ssam 2827770Ssam struct errmsg { 2837770Ssam int e_code; 2847770Ssam char *e_msg; 2857770Ssam } errmsgs[] = { 2867770Ssam { EUNDEF, "Undefined error code" }, 2877770Ssam { ENOTFOUND, "File not found" }, 2887770Ssam { EACCESS, "Access violation" }, 2897770Ssam { ENOSPACE, "Disk full or allocation exceeded" }, 2907770Ssam { EBADOP, "Illegal TFTP operation" }, 2917770Ssam { EBADID, "Unknown transfer ID" }, 2927770Ssam { EEXISTS, "File already exists" }, 2937770Ssam { ENOUSER, "No such user" }, 2947770Ssam { -1, 0 } 2957770Ssam }; 2967770Ssam 2977770Ssam /* 2987770Ssam * Send a nak packet (error message). 2997770Ssam * Error code passed in is one of the 3007770Ssam * standard TFTP codes, or a UNIX errno 3017770Ssam * offset by 100. 3027770Ssam */ 30326094Sminshall nak(error) 3047770Ssam int error; 3057770Ssam { 30642423Sbostic register struct errmsg *pe; 30726094Sminshall register struct tftphdr *tp; 3087770Ssam int length; 30942423Sbostic char *strerror(); 3107770Ssam 31126094Sminshall tp = (struct tftphdr *)ackbuf; 31226094Sminshall tp->th_opcode = htons((u_short)ERROR); 31326094Sminshall tp->th_code = htons((u_short)error); 3147770Ssam for (pe = errmsgs; pe->e_code >= 0; pe++) 3157770Ssam if (pe->e_code == error) 3167770Ssam break; 31726094Sminshall if (pe->e_code < 0) { 31842423Sbostic pe->e_msg = strerror(error - 100); 31926094Sminshall tp->th_code = EUNDEF; 32026094Sminshall } 32126094Sminshall strcpy(tp->th_msg, pe->e_msg); 3227770Ssam length = strlen(pe->e_msg) + 4; 3237770Ssam if (trace) 32426094Sminshall tpacket("sent", tp, length); 325*46861Sbostic if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&sin, 326*46861Sbostic sizeof (sin)) != length) 32726094Sminshall perror("nak"); 3287770Ssam } 3297770Ssam 33026094Sminshall tpacket(s, tp, n) 33126111Sminshall char *s; 3327770Ssam struct tftphdr *tp; 3337770Ssam int n; 3347770Ssam { 3357770Ssam static char *opcodes[] = 3367770Ssam { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; 3377770Ssam register char *cp, *file; 3387770Ssam u_short op = ntohs(tp->th_opcode); 3397770Ssam char *index(); 3407770Ssam 3417770Ssam if (op < RRQ || op > ERROR) 34226094Sminshall printf("%s opcode=%x ", s, op); 3437770Ssam else 34426094Sminshall printf("%s %s ", s, opcodes[op]); 3457770Ssam switch (op) { 3467770Ssam 3477770Ssam case RRQ: 3487770Ssam case WRQ: 3497770Ssam n -= 2; 3507770Ssam file = cp = tp->th_stuff; 3517770Ssam cp = index(cp, '\0'); 3527770Ssam printf("<file=%s, mode=%s>\n", file, cp + 1); 3537770Ssam break; 3547770Ssam 3557770Ssam case DATA: 3567770Ssam printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); 3577770Ssam break; 3587770Ssam 3597770Ssam case ACK: 3607770Ssam printf("<block=%d>\n", ntohs(tp->th_block)); 3617770Ssam break; 3627770Ssam 3637770Ssam case ERROR: 3647770Ssam printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); 3657770Ssam break; 3667770Ssam } 3677770Ssam } 36826094Sminshall 36926094Sminshall struct timeval tstart; 37026094Sminshall struct timeval tstop; 37126094Sminshall struct timezone zone; 37226094Sminshall 37326094Sminshall startclock() { 37426094Sminshall gettimeofday(&tstart, &zone); 37526094Sminshall } 37626094Sminshall 37726094Sminshall stopclock() { 37826094Sminshall gettimeofday(&tstop, &zone); 37926094Sminshall } 38026094Sminshall 38126094Sminshall printstats(direction, amount) 38226094Sminshall char *direction; 38326094Sminshall unsigned long amount; 38426094Sminshall { 38526094Sminshall double delta; 38626094Sminshall /* compute delta in 1/10's second units */ 38726094Sminshall delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - 38826094Sminshall ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); 38926094Sminshall delta = delta/10.; /* back to seconds */ 39026094Sminshall printf("%s %d bytes in %.1f seconds", direction, amount, delta); 39126094Sminshall if (verbose) 39226094Sminshall printf(" [%.0f bits/sec]", (amount*8.)/delta); 39326094Sminshall putchar('\n'); 39426094Sminshall } 39526094Sminshall 396