122404Sdist /*
2*62309Sbostic * Copyright (c) 1983, 1993
3*62309Sbostic * The Regents of the University of California. All rights reserved.
433821Sbostic *
542770Sbostic * %sccs.include.redist.c%
622404Sdist */
722404Sdist
814554Ssam #ifndef lint
9*62309Sbostic static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 06/06/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
2560062Storek #include <errno.h>
2660062Storek #include <setjmp.h>
277770Ssam #include <signal.h>
287770Ssam #include <stdio.h>
2960062Storek #include <unistd.h>
309220Ssam
3160062Storek #include "extern.h"
3260062Storek #include "tftpsubs.h"
3360062Storek
347770Ssam extern int errno;
3526094Sminshall
3660062Storek extern struct sockaddr_in peeraddr; /* filled in by main */
3760062Storek 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
4960062Storek static void nak __P((int));
5060062Storek static int makerequest __P((int, const char *, struct tftphdr *, const char *));
5160062Storek static void printstats __P((const char *, unsigned long));
5260062Storek static void startclock __P((void));
5360062Storek static void stopclock __P((void));
5460062Storek static void timer __P((int));
5560062Storek static void tpacket __P((const char *, struct tftphdr *, int));
567770Ssam
577770Ssam /*
587770Ssam * Send the requested file.
597770Ssam */
6060062Storek void
sendfile(fd,name,mode)6126094Sminshall sendfile(fd, name, mode)
627770Ssam int fd;
637770Ssam char *name;
6426094Sminshall char *mode;
657770Ssam {
6660062Storek register struct tftphdr *ap; /* data and ack packets */
6726094Sminshall struct tftphdr *r_init(), *dp;
6860062Storek register int n;
6960062Storek volatile int block, size, convert;
7060062Storek volatile unsigned long amount;
7126094Sminshall struct sockaddr_in from;
7226094Sminshall int fromlen;
7326094Sminshall FILE *file;
747770Ssam
7560062Storek startclock(); /* start stat's clock */
7660062Storek dp = r_init(); /* reset fillbuf/read-ahead code */
7726094Sminshall ap = (struct tftphdr *)ackbuf;
7826094Sminshall file = fdopen(fd, "r");
7926094Sminshall convert = !strcmp(mode, "netascii");
8060062Storek block = 0;
8160062Storek amount = 0;
8226094Sminshall
8313018Ssam signal(SIGALRM, timer);
847770Ssam do {
8513018Ssam if (block == 0)
8626094Sminshall size = makerequest(WRQ, name, dp, mode) - 4;
8713018Ssam else {
8860062Storek /* 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,
10360062Storek (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 {
11260062Storek fromlen = sizeof(from);
11360062Storek 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 }
12160062Storek 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 */
16560062Storek void
recvfile(fd,name,mode)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();
17360062Storek register int n;
17460062Storek volatile int block, size, firsttrip;
17560062Storek volatile unsigned long amount;
17626094Sminshall struct sockaddr_in from;
17760062Storek int fromlen;
17826094Sminshall FILE *file;
17960062Storek 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");
18660062Storek block = 1;
18760062Storek firsttrip = 1;
18860062Storek 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);
20660062Storek if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
20760062Storek 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 {
21660062Storek 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 }
22560062Storek 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) {
24060062Storek 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)) {
25060062Storek goto send_ack; /* resend ack */
25126113Sminshall }
25216384Ssam }
25326094Sminshall }
25460062Storek /* 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);
26260062Storek abort: /* ok to ack, since user */
26360062Storek ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
26426094Sminshall ap->th_block = htons((u_short)block);
26560062Storek (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
26660062Storek sizeof(peeraddr));
26760062Storek write_behind(file, convert); /* flush last buffer */
26826094Sminshall fclose(file);
26926094Sminshall stopclock();
27026094Sminshall if (amount > 0)
27126094Sminshall printstats("Received", amount);
2727770Ssam }
2737770Ssam
27460062Storek static int
makerequest(request,name,tp,mode)27526094Sminshall makerequest(request, name, tp, mode)
2767770Ssam int request;
27760062Storek const char *name;
27826094Sminshall struct tftphdr *tp;
27960062Storek 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 */
31560062Storek static void
nak(error)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);
33860062Storek if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
33960062Storek sizeof(peeraddr)) != length)
34026094Sminshall perror("nak");
3417770Ssam }
3427770Ssam
34360062Storek static void
tpacket(s,tp,n)34426094Sminshall tpacket(s, tp, n)
34560062Storek 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
38660062Storek static void
startclock()38760062Storek startclock()
38860062Storek {
38960062Storek
39060062Storek (void)gettimeofday(&tstart, NULL);
39126094Sminshall }
39226094Sminshall
39360062Storek static void
stopclock()39460062Storek stopclock()
39560062Storek {
39660062Storek
39760062Storek (void)gettimeofday(&tstop, NULL);
39826094Sminshall }
39926094Sminshall
40060062Storek static void
printstats(direction,amount)40126094Sminshall printstats(direction, amount)
40260062Storek const char *direction;
40360062Storek 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
41660062Storek static void
timer(sig)41760062Storek timer(sig)
41860062Storek int sig;
41960062Storek {
42060062Storek
42160062Storek timeout += rexmtval;
42260062Storek if (timeout >= maxtimeout) {
42360062Storek printf("Transfer timed out.\n");
42460062Storek longjmp(toplevel, -1);
42560062Storek }
42660062Storek longjmp(timeoutbuf, 1);
42760062Storek }
428