Home | History | Annotate | Line # | Download | only in tftp
tftp.c revision 1.3
      1 /*
      2  * Copyright (c) 1983 Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #ifndef lint
     35 /*static char sccsid[] = "from: @(#)tftp.c	5.10 (Berkeley) 3/1/91";*/
     36 static char rcsid[] = "$Id: tftp.c,v 1.3 1993/08/01 18:07:06 mycroft Exp $";
     37 #endif /* not lint */
     38 
     39 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
     40 
     41 /*
     42  * TFTP User Program -- Protocol Machines
     43  */
     44 #include <sys/types.h>
     45 #include <sys/socket.h>
     46 #include <sys/time.h>
     47 
     48 #include <netinet/in.h>
     49 
     50 #include <arpa/tftp.h>
     51 
     52 #include <signal.h>
     53 #include <stdio.h>
     54 #include <errno.h>
     55 #include <setjmp.h>
     56 
     57 extern	int errno;
     58 
     59 extern  struct sockaddr_in s_in;         /* filled in by main */
     60 extern  int     f;                      /* the opened socket */
     61 extern  int     trace;
     62 extern  int     verbose;
     63 extern  int     rexmtval;
     64 extern  int     maxtimeout;
     65 
     66 #define PKTSIZE    SEGSIZE+4
     67 char    ackbuf[PKTSIZE];
     68 int	timeout;
     69 jmp_buf	toplevel;
     70 jmp_buf	timeoutbuf;
     71 
     72 void
     73 timer()
     74 {
     75 	timeout += rexmtval;
     76 	if (timeout >= maxtimeout) {
     77 		printf("Transfer timed out.\n");
     78 		longjmp(toplevel, -1);
     79 	}
     80 	longjmp(timeoutbuf, 1);
     81 }
     82 
     83 /*
     84  * Send the requested file.
     85  */
     86 sendfile(fd, name, mode)
     87 	int fd;
     88 	char *name;
     89 	char *mode;
     90 {
     91 	register struct tftphdr *ap;       /* data and ack packets */
     92 	struct tftphdr *r_init(), *dp;
     93 	register int block = 0, size, n;
     94 	register unsigned long amount = 0;
     95 	struct sockaddr_in from;
     96 	int fromlen;
     97 	int convert;            /* true if doing nl->crlf conversion */
     98 	FILE *file;
     99 
    100 	startclock();           /* start stat's clock */
    101 	dp = r_init();          /* reset fillbuf/read-ahead code */
    102 	ap = (struct tftphdr *)ackbuf;
    103 	file = fdopen(fd, "r");
    104 	convert = !strcmp(mode, "netascii");
    105 
    106 	signal(SIGALRM, timer);
    107 	do {
    108 		if (block == 0)
    109 			size = makerequest(WRQ, name, dp, mode) - 4;
    110 		else {
    111 		/*      size = read(fd, dp->th_data, SEGSIZE);   */
    112 			size = readit(file, &dp, convert);
    113 			if (size < 0) {
    114 				nak(errno + 100);
    115 				break;
    116 			}
    117 			dp->th_opcode = htons((u_short)DATA);
    118 			dp->th_block = htons((u_short)block);
    119 		}
    120 		timeout = 0;
    121 		(void) setjmp(timeoutbuf);
    122 send_data:
    123 		if (trace)
    124 			tpacket("sent", dp, size + 4);
    125 		n = sendto(f, dp, size + 4, 0,
    126 		    (struct sockaddr *)&s_in, sizeof (s_in));
    127 		if (n != size + 4) {
    128 			perror("tftp: sendto");
    129 			goto abort;
    130 		}
    131 		read_ahead(file, convert);
    132 		for ( ; ; ) {
    133 			alarm(rexmtval);
    134 			do {
    135 				fromlen = sizeof (from);
    136 				n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
    137 				    (struct sockaddr *)&from, &fromlen);
    138 			} while (n <= 0);
    139 			alarm(0);
    140 			if (n < 0) {
    141 				perror("tftp: recvfrom");
    142 				goto abort;
    143 			}
    144 			s_in.sin_port = from.sin_port;   /* added */
    145 			if (trace)
    146 				tpacket("received", ap, n);
    147 			/* should verify packet came from server */
    148 			ap->th_opcode = ntohs(ap->th_opcode);
    149 			ap->th_block = ntohs(ap->th_block);
    150 			if (ap->th_opcode == ERROR) {
    151 				printf("Error code %d: %s\n", ap->th_code,
    152 					ap->th_msg);
    153 				goto abort;
    154 			}
    155 			if (ap->th_opcode == ACK) {
    156 				int j;
    157 
    158 				if (ap->th_block == block) {
    159 					break;
    160 				}
    161 				/* On an error, try to synchronize
    162 				 * both sides.
    163 				 */
    164 				j = synchnet(f);
    165 				if (j && trace) {
    166 					printf("discarded %d packets\n",
    167 							j);
    168 				}
    169 				if (ap->th_block == (block-1)) {
    170 					goto send_data;
    171 				}
    172 			}
    173 		}
    174 		if (block > 0)
    175 			amount += size;
    176 		block++;
    177 	} while (size == SEGSIZE || block == 1);
    178 abort:
    179 	fclose(file);
    180 	stopclock();
    181 	if (amount > 0)
    182 		printstats("Sent", amount);
    183 }
    184 
    185 /*
    186  * Receive a file.
    187  */
    188 recvfile(fd, name, mode)
    189 	int fd;
    190 	char *name;
    191 	char *mode;
    192 {
    193 	register struct tftphdr *ap;
    194 	struct tftphdr *dp, *w_init();
    195 	register int block = 1, n, size;
    196 	unsigned long amount = 0;
    197 	struct sockaddr_in from;
    198 	int fromlen, firsttrip = 1;
    199 	FILE *file;
    200 	int convert;                    /* true if converting crlf -> lf */
    201 
    202 	startclock();
    203 	dp = w_init();
    204 	ap = (struct tftphdr *)ackbuf;
    205 	file = fdopen(fd, "w");
    206 	convert = !strcmp(mode, "netascii");
    207 
    208 	signal(SIGALRM, timer);
    209 	do {
    210 		if (firsttrip) {
    211 			size = makerequest(RRQ, name, ap, mode);
    212 			firsttrip = 0;
    213 		} else {
    214 			ap->th_opcode = htons((u_short)ACK);
    215 			ap->th_block = htons((u_short)(block));
    216 			size = 4;
    217 			block++;
    218 		}
    219 		timeout = 0;
    220 		(void) setjmp(timeoutbuf);
    221 send_ack:
    222 		if (trace)
    223 			tpacket("sent", ap, size);
    224 		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&s_in,
    225 		    sizeof (s_in)) != size) {
    226 			alarm(0);
    227 			perror("tftp: sendto");
    228 			goto abort;
    229 		}
    230 		write_behind(file, convert);
    231 		for ( ; ; ) {
    232 			alarm(rexmtval);
    233 			do  {
    234 				fromlen = sizeof (from);
    235 				n = recvfrom(f, dp, PKTSIZE, 0,
    236 				    (struct sockaddr *)&from, &fromlen);
    237 			} while (n <= 0);
    238 			alarm(0);
    239 			if (n < 0) {
    240 				perror("tftp: recvfrom");
    241 				goto abort;
    242 			}
    243 			s_in.sin_port = from.sin_port;   /* added */
    244 			if (trace)
    245 				tpacket("received", dp, n);
    246 			/* should verify client address */
    247 			dp->th_opcode = ntohs(dp->th_opcode);
    248 			dp->th_block = ntohs(dp->th_block);
    249 			if (dp->th_opcode == ERROR) {
    250 				printf("Error code %d: %s\n", dp->th_code,
    251 					dp->th_msg);
    252 				goto abort;
    253 			}
    254 			if (dp->th_opcode == DATA) {
    255 				int j;
    256 
    257 				if (dp->th_block == block) {
    258 					break;          /* have next packet */
    259 				}
    260 				/* On an error, try to synchronize
    261 				 * both sides.
    262 				 */
    263 				j = synchnet(f);
    264 				if (j && trace) {
    265 					printf("discarded %d packets\n", j);
    266 				}
    267 				if (dp->th_block == (block-1)) {
    268 					goto send_ack;  /* resend ack */
    269 				}
    270 			}
    271 		}
    272 	/*      size = write(fd, dp->th_data, n - 4); */
    273 		size = writeit(file, &dp, n - 4, convert);
    274 		if (size < 0) {
    275 			nak(errno + 100);
    276 			break;
    277 		}
    278 		amount += size;
    279 	} while (size == SEGSIZE);
    280 abort:                                          /* ok to ack, since user */
    281 	ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
    282 	ap->th_block = htons((u_short)block);
    283 	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&s_in, sizeof (s_in));
    284 	write_behind(file, convert);            /* flush last buffer */
    285 	fclose(file);
    286 	stopclock();
    287 	if (amount > 0)
    288 		printstats("Received", amount);
    289 }
    290 
    291 makerequest(request, name, tp, mode)
    292 	int request;
    293 	char *name, *mode;
    294 	struct tftphdr *tp;
    295 {
    296 	register char *cp;
    297 
    298 	tp->th_opcode = htons((u_short)request);
    299 	cp = tp->th_stuff;
    300 	strcpy(cp, name);
    301 	cp += strlen(name);
    302 	*cp++ = '\0';
    303 	strcpy(cp, mode);
    304 	cp += strlen(mode);
    305 	*cp++ = '\0';
    306 	return (cp - (char *)tp);
    307 }
    308 
    309 struct errmsg {
    310 	int	e_code;
    311 	char	*e_msg;
    312 } errmsgs[] = {
    313 	{ EUNDEF,	"Undefined error code" },
    314 	{ ENOTFOUND,	"File not found" },
    315 	{ EACCESS,	"Access violation" },
    316 	{ ENOSPACE,	"Disk full or allocation exceeded" },
    317 	{ EBADOP,	"Illegal TFTP operation" },
    318 	{ EBADID,	"Unknown transfer ID" },
    319 	{ EEXISTS,	"File already exists" },
    320 	{ ENOUSER,	"No such user" },
    321 	{ -1,		0 }
    322 };
    323 
    324 /*
    325  * Send a nak packet (error message).
    326  * Error code passed in is one of the
    327  * standard TFTP codes, or a UNIX errno
    328  * offset by 100.
    329  */
    330 nak(error)
    331 	int error;
    332 {
    333 	register struct errmsg *pe;
    334 	register struct tftphdr *tp;
    335 	int length;
    336 	char *strerror();
    337 
    338 	tp = (struct tftphdr *)ackbuf;
    339 	tp->th_opcode = htons((u_short)ERROR);
    340 	tp->th_code = htons((u_short)error);
    341 	for (pe = errmsgs; pe->e_code >= 0; pe++)
    342 		if (pe->e_code == error)
    343 			break;
    344 	if (pe->e_code < 0) {
    345 		pe->e_msg = strerror(error - 100);
    346 		tp->th_code = EUNDEF;
    347 	}
    348 	strcpy(tp->th_msg, pe->e_msg);
    349 	length = strlen(pe->e_msg) + 4;
    350 	if (trace)
    351 		tpacket("sent", tp, length);
    352 	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&s_in,
    353 	    sizeof (s_in)) != length)
    354 		perror("nak");
    355 }
    356 
    357 tpacket(s, tp, n)
    358 	char *s;
    359 	struct tftphdr *tp;
    360 	int n;
    361 {
    362 	static char *opcodes[] =
    363 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
    364 	register char *cp, *file;
    365 	u_short op = ntohs(tp->th_opcode);
    366 	char *index();
    367 
    368 	if (op < RRQ || op > ERROR)
    369 		printf("%s opcode=%x ", s, op);
    370 	else
    371 		printf("%s %s ", s, opcodes[op]);
    372 	switch (op) {
    373 
    374 	case RRQ:
    375 	case WRQ:
    376 		n -= 2;
    377 		file = cp = tp->th_stuff;
    378 		cp = index(cp, '\0');
    379 		printf("<file=%s, mode=%s>\n", file, cp + 1);
    380 		break;
    381 
    382 	case DATA:
    383 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
    384 		break;
    385 
    386 	case ACK:
    387 		printf("<block=%d>\n", ntohs(tp->th_block));
    388 		break;
    389 
    390 	case ERROR:
    391 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
    392 		break;
    393 	}
    394 }
    395 
    396 struct timeval tstart;
    397 struct timeval tstop;
    398 struct timezone zone;
    399 
    400 startclock() {
    401 	gettimeofday(&tstart, &zone);
    402 }
    403 
    404 stopclock() {
    405 	gettimeofday(&tstop, &zone);
    406 }
    407 
    408 printstats(direction, amount)
    409 char *direction;
    410 unsigned long amount;
    411 {
    412 	double delta;
    413 			/* compute delta in 1/10's second units */
    414 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
    415 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
    416 	delta = delta/10.;      /* back to seconds */
    417 	printf("%s %d bytes in %.1f seconds", direction, amount, delta);
    418 	if (verbose)
    419 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
    420 	putchar('\n');
    421 }
    422 
    423