GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tftp/tftp.c Lines: 0 307 0.0 %
Date: 2017-11-07 Branches: 0 197 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: tftp.c,v 1.25 2017/09/10 07:29:39 tb Exp $	*/
2
/*	$NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $	*/
3
4
/*
5
 * Copyright (c) 1983, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
/*
34
 * TFTP User Program -- Protocol Machines
35
 *
36
 * This version includes many modifications by Jim Guyton <guyton@rand-unix>
37
 */
38
39
#include <sys/types.h>
40
#include <sys/socket.h>
41
#include <sys/time.h>
42
#include <sys/stat.h>
43
44
#include <netinet/in.h>
45
#include <arpa/tftp.h>
46
47
#include <err.h>
48
#include <errno.h>
49
#include <poll.h>
50
#include <signal.h>
51
#include <stdio.h>
52
#include <stddef.h>
53
#include <stdlib.h>
54
#include <string.h>
55
#include <time.h>
56
#include <unistd.h>
57
#include <netdb.h>
58
59
#include "extern.h"
60
#include "tftpsubs.h"
61
62
static int	cmpport(struct sockaddr *, struct sockaddr *);
63
static int	makerequest(int, const char *, struct tftphdr *, const char *);
64
static void	nak(int, struct sockaddr *);
65
static void 	tpacket(const char *, struct tftphdr *, int);
66
static void	startclock(void);
67
static void	stopclock(void);
68
static void	printstats(const char *, unsigned long);
69
static void	printtimeout(void);
70
static void	oack(struct tftphdr *, int, int);
71
static int	oack_set(const char *, const char *);
72
73
extern struct sockaddr_storage	 peeraddr;	/* filled in by main */
74
extern int			 f;		/* the opened socket */
75
extern int			 trace;
76
extern int			 verbose;
77
extern int			 rexmtval;
78
extern int			 maxtimeout;
79
extern FILE			*file;
80
extern volatile sig_atomic_t	 intrflag;
81
extern char			*ackbuf;
82
extern int			 has_options;
83
extern int			 opt_tsize;
84
extern int			 opt_tout;
85
extern int			 opt_blksize;
86
87
struct timespec	tstart;
88
struct timespec	tstop;
89
unsigned int	segment_size = SEGSIZE;
90
unsigned int	packet_size = SEGSIZE + 4;
91
92
struct errmsg {
93
	int	 e_code;
94
	char	*e_msg;
95
} errmsgs[] = {
96
	{ EUNDEF,	"Undefined error code" },
97
	{ ENOTFOUND,	"File not found" },
98
	{ EACCESS,	"Access violation" },
99
	{ ENOSPACE,	"Disk full or allocation exceeded" },
100
	{ EBADOP,	"Illegal TFTP operation" },
101
	{ EBADID,	"Unknown transfer ID" },
102
	{ EEXISTS,	"File already exists" },
103
	{ ENOUSER,	"No such user" },
104
	{ EOPTNEG,	"Option negotiation failed" },
105
	{ -1,		NULL }
106
};
107
108
struct options {
109
	const char      *o_type;
110
} options[] = {
111
	{ "tsize" },
112
	{ "timeout" },
113
	{ "blksize" },
114
	{ NULL }
115
};
116
117
enum opt_enum {
118
	OPT_TSIZE = 0,
119
	OPT_TIMEOUT,
120
	OPT_BLKSIZE
121
};
122
123
/*
124
 * Send the requested file.
125
 */
126
void
127
sendfile(int fd, char *name, char *mode)
128
{
129
	struct tftphdr		*dp, *ap; /* data and ack packets */
130
	struct sockaddr_storage	 from, peer;
131
	struct sockaddr_storage	 serv; /* valid server port number */
132
	struct pollfd		 pfd[1];
133
	unsigned long		 amount;
134
	socklen_t		 fromlen;
135
	int			 convert; /* true if converting crlf -> lf */
136
	int			 n, nfds, error, timeouts, size;
137
	uint16_t		 block = 0;
138
	int			 firsttrip = 1;
139
140
	startclock();		/* start stat's clock */
141
	dp = r_init();		/* reset fillbuf/read-ahead code */
142
	ap = (struct tftphdr *)ackbuf;
143
	file = fdopen(fd, "r");
144
	convert = !strcmp(mode, "netascii");
145
	amount = 0;
146
	memcpy(&peer, &peeraddr, peeraddr.ss_len);
147
	memset(&serv, 0, sizeof(serv));
148
149
	do {
150
		/* read data from file */
151
		if (firsttrip)
152
			size = makerequest(WRQ, name, dp, mode) - 4;
153
		else {
154
			size = readit(file, &dp, convert, segment_size);
155
			if (size < 0) {
156
				nak(errno + 100, (struct sockaddr *)&peer);
157
				break;
158
			}
159
			dp->th_opcode = htons((u_short)DATA);
160
			dp->th_block = htons(block);
161
		}
162
163
		/* send data to server and wait for server ACK */
164
		for (timeouts = 0, error = 0; !intrflag;) {
165
			if (timeouts >= maxtimeout) {
166
				printtimeout();
167
				goto abort;
168
			}
169
170
			if (!error) {
171
				if (trace)
172
					tpacket("sent", dp, size + 4);
173
				if (sendto(f, dp, size + 4, 0,
174
				    (struct sockaddr *)&peer,
175
				    peer.ss_len) != size + 4) {
176
					warn("sendto");
177
					goto abort;
178
				}
179
				if (!firsttrip)
180
					read_ahead(file, convert, segment_size);
181
			}
182
			error = 0;
183
184
			pfd[0].fd = f;
185
			pfd[0].events = POLLIN;
186
			nfds = poll(pfd, 1, rexmtval * 1000);
187
			if (nfds == 0) {
188
				timeouts += rexmtval;
189
				continue;
190
			}
191
			if (nfds == -1) {
192
				error = 1;
193
				if (errno == EINTR)
194
					continue;
195
				warn("poll");
196
				goto abort;
197
			}
198
			fromlen = sizeof(from);
199
			n = recvfrom(f, ackbuf, packet_size, 0,
200
			    (struct sockaddr *)&from, &fromlen);
201
			if (n == 0) {
202
				warn("recvfrom");
203
				goto abort;
204
			}
205
			if (n == -1) {
206
				error = 1;
207
				if (errno == EINTR)
208
					continue;
209
				warn("recvfrom");
210
				goto abort;
211
			}
212
			if (!serv.ss_family)
213
				serv = from;
214
			else if (!cmpport((struct sockaddr *)&serv,
215
			    (struct sockaddr *)&from)) {
216
				warn("server port mismatch");
217
				goto abort;
218
			}
219
			peer = from;
220
			if (trace)
221
				tpacket("received", ap, n);
222
223
			ap->th_opcode = ntohs(ap->th_opcode);
224
225
			if (ap->th_opcode == OACK) {
226
				oack(ap, n, 0);
227
				break;
228
			}
229
230
			ap->th_block = ntohs(ap->th_block);
231
232
			if (ap->th_opcode == ERROR) {
233
				printf("Error code %d: %s\n",
234
				    ap->th_code, ap->th_msg);
235
				goto abort;
236
			}
237
			if (ap->th_opcode == ACK) {
238
				int j;
239
				if (ap->th_block == block)
240
					break;
241
				/* re-synchronize with other side */
242
				j = synchnet(f);
243
				if (j && trace)
244
					printf("discarded %d packets\n", j);
245
				if (ap->th_block == (block - 1))
246
					continue;
247
			}
248
			error = 1;	/* received packet does not match */
249
		}
250
251
		if (firsttrip) {
252
			size = segment_size;
253
			firsttrip = 0;
254
		} else
255
			amount += size;
256
		block++;
257
	} while ((size == segment_size) && !intrflag);
258
259
abort:
260
	fclose(file);
261
	stopclock();
262
	if (amount > 0) {
263
		if (intrflag)
264
			putchar('\n');
265
		printstats("Sent", amount);
266
	}
267
}
268
269
/*
270
 * Receive a file.
271
 */
272
void
273
recvfile(int fd, char *name, char *mode)
274
{
275
	struct tftphdr		*dp, *ap; /* data and ack packets */
276
	struct sockaddr_storage	 from, peer;
277
	struct sockaddr_storage	 serv; /* valid server port number */
278
	struct pollfd		 pfd[1];
279
	unsigned long		 amount;
280
	socklen_t		 fromlen;
281
	int			 convert; /* true if converting crlf -> lf */
282
	int			 n, nfds, error, timeouts, size;
283
	int			 firsttrip;
284
	uint16_t		 block;
285
286
	startclock();		/* start stat's clock */
287
	dp = w_init();		/* reset fillbuf/read-ahead code */
288
	ap = (struct tftphdr *)ackbuf;
289
	file = fdopen(fd, "w");
290
	convert = !strcmp(mode, "netascii");
291
	n = 0;
292
	block = 1;
293
	amount = 0;
294
	firsttrip = 1;
295
	memcpy(&peer, &peeraddr, peeraddr.ss_len);
296
	memset(&serv, 0, sizeof(serv));
297
298
options:
299
	do {
300
		/* create new ACK packet */
301
		if (firsttrip) {
302
			size = makerequest(RRQ, name, ap, mode);
303
			firsttrip = 0;
304
		} else {
305
			ap->th_opcode = htons((u_short)ACK);
306
			ap->th_block = htons(block);
307
			size = 4;
308
			block++;
309
		}
310
311
		/* send ACK to server and wait for server data */
312
		for (timeouts = 0, error = 0; !intrflag;) {
313
			if (timeouts >= maxtimeout) {
314
				printtimeout();
315
				goto abort;
316
			}
317
318
			if (!error) {
319
				if (trace)
320
					tpacket("sent", ap, size);
321
				if (sendto(f, ackbuf, size, 0,
322
				    (struct sockaddr *)&peer,
323
				    peer.ss_len) != size) {
324
					warn("sendto");
325
					goto abort;
326
				}
327
				write_behind(file, convert);
328
			}
329
			error = 0;
330
331
			pfd[0].fd = f;
332
			pfd[0].events = POLLIN;
333
			nfds = poll(pfd, 1, rexmtval * 1000);
334
			if (nfds == 0) {
335
				timeouts += rexmtval;
336
				continue;
337
			}
338
			if (nfds == -1) {
339
				error = 1;
340
				if (errno == EINTR)
341
					continue;
342
				warn("poll");
343
				goto abort;
344
			}
345
			fromlen = sizeof(from);
346
			n = recvfrom(f, dp, packet_size, 0,
347
			    (struct sockaddr *)&from, &fromlen);
348
			if (n == 0) {
349
				warn("recvfrom");
350
				goto abort;
351
			}
352
			if (n == -1) {
353
				error = 1;
354
				if (errno == EINTR)
355
					continue;
356
				warn("recvfrom");
357
				goto abort;
358
			}
359
			if (!serv.ss_family)
360
				serv = from;
361
			else if (!cmpport((struct sockaddr *)&serv,
362
			    (struct sockaddr *)&from)) {
363
				warn("server port mismatch");
364
				goto abort;
365
			}
366
			peer = from;
367
			if (trace)
368
				tpacket("received", dp, n);
369
370
			dp->th_opcode = ntohs(dp->th_opcode);
371
372
			if (dp->th_opcode == OACK) {
373
				oack(dp, n, 0);
374
				block = 0;
375
				goto options;
376
			}
377
378
			dp->th_block = ntohs(dp->th_block);
379
380
			if (dp->th_opcode == ERROR) {
381
				printf("Error code %d: %s\n",
382
				    dp->th_code, dp->th_msg);
383
				goto abort;
384
			}
385
			if (dp->th_opcode == DATA) {
386
				int j;
387
				if (dp->th_block == block)
388
					break;
389
				/* re-synchronize with other side */
390
				j = synchnet(f);
391
				if (j && trace)
392
					printf("discarded %d packets\n", j);
393
				if (dp->th_block == (block - 1))
394
					continue;
395
			}
396
			error = 1;	/* received packet does not match */
397
		}
398
399
		/* write data to file */
400
		size = writeit(file, &dp, n - 4, convert);
401
		if (size < 0) {
402
			nak(errno + 100, (struct sockaddr *)&peer);
403
			break;
404
		}
405
		amount += size;
406
	} while (size == segment_size && !intrflag);
407
408
abort:
409
	/* ok to ack, since user has seen err msg */
410
	ap->th_opcode = htons((u_short)ACK);
411
	ap->th_block = htons(block);
412
	(void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer,
413
	    peer.ss_len);
414
	write_behind(file, convert);	/* flush last buffer */
415
416
	fclose(file);
417
	stopclock();
418
	if (amount > 0) {
419
		if (intrflag)
420
			putchar('\n');
421
		printstats("Received", amount);
422
	}
423
}
424
425
static int
426
cmpport(struct sockaddr *sa, struct sockaddr *sb)
427
{
428
	char a[NI_MAXSERV], b[NI_MAXSERV];
429
	if (getnameinfo(sa, sa->sa_len, NULL, 0, a, sizeof(a), NI_NUMERICSERV))
430
		return (0);
431
	if (getnameinfo(sb, sb->sa_len, NULL, 0, b, sizeof(b), NI_NUMERICSERV))
432
		return (0);
433
	if (strcmp(a, b) != 0)
434
		return (0);
435
436
	return (1);
437
}
438
439
static int
440
makerequest(int request, const char *name, struct tftphdr *tp,
441
    const char *mode)
442
{
443
	char		*cp;
444
	int		 len, pktlen;
445
	off_t		 fsize = 0;
446
	struct stat	 st;
447
448
	tp->th_opcode = htons((u_short)request);
449
	cp = tp->th_stuff;
450
	pktlen = packet_size - offsetof(struct tftphdr, th_stuff);
451
	len = strlen(name) + 1;
452
	strlcpy(cp, name, pktlen);
453
	strlcpy(cp + len, mode, pktlen - len);
454
	len += strlen(mode) + 1;
455
456
	if (opt_tsize) {
457
		if (request == WRQ) {
458
			stat(name, &st);
459
			fsize = st.st_size;
460
		}
461
		len += snprintf(cp + len, pktlen - len, "%s%c%lld%c",
462
		    options[OPT_TSIZE].o_type, 0, fsize, 0);
463
	}
464
	if (opt_tout)
465
		len += snprintf(cp + len, pktlen - len, "%s%c%d%c",
466
		    options[OPT_TIMEOUT].o_type, 0, rexmtval, 0);
467
	if (opt_blksize)
468
		len += snprintf(cp + len, pktlen - len, "%s%c%d%c",
469
		    options[OPT_BLKSIZE].o_type, 0, opt_blksize, 0);
470
471
	return (cp + len - (char *)tp);
472
}
473
474
/*
475
 * Send a nak packet (error message).
476
 * Error code passed in is one of the
477
 * standard TFTP codes, or a UNIX errno
478
 * offset by 100.
479
 */
480
static void
481
nak(int error, struct sockaddr *peer)
482
{
483
	struct errmsg	*pe;
484
	struct tftphdr	*tp;
485
	int		 length;
486
487
	tp = (struct tftphdr *)ackbuf;
488
	tp->th_opcode = htons((u_short)ERROR);
489
	tp->th_code = htons((u_short)error);
490
	for (pe = errmsgs; pe->e_code >= 0; pe++)
491
		if (pe->e_code == error)
492
			break;
493
	if (pe->e_code < 0) {
494
		pe->e_msg = strerror(error - 100);
495
		tp->th_code = EUNDEF;
496
	}
497
	length = strlcpy(tp->th_msg, pe->e_msg, packet_size) + 5;
498
	if (length > packet_size)
499
		length = packet_size;
500
	if (trace)
501
		tpacket("sent", tp, length);
502
	if (sendto(f, ackbuf, length, 0, peer,
503
	    peer->sa_len) != length)
504
		warn("nak");
505
}
506
507
static void
508
tpacket(const char *s, struct tftphdr *tp, int n)
509
{
510
	char		*cp, *file;
511
	static char	*opcodes[] =
512
	    { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
513
514
	u_short op = ntohs(tp->th_opcode);
515
516
	if (op < RRQ || op > OACK)
517
		printf("%s opcode=%x ", s, op);
518
	else
519
		printf("%s %s ", s, opcodes[op]);
520
521
	switch (op) {
522
	case RRQ:
523
	case WRQ:
524
		n -= 2;
525
		file = cp = tp->th_stuff;
526
		cp = strchr(cp, '\0');
527
		printf("<file=%s, mode=%s", file, cp + 1);
528
		if (has_options)
529
			oack(tp, n, 1);
530
		printf(">\n");
531
		break;
532
	case DATA:
533
		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
534
		break;
535
	case ACK:
536
		printf("<block=%d>\n", ntohs(tp->th_block));
537
		break;
538
	case ERROR:
539
		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
540
		break;
541
	case OACK:
542
		printf("<");
543
		oack(tp, n, 1);
544
		printf(">\n");
545
		break;
546
	}
547
}
548
549
static void
550
startclock(void)
551
{
552
	clock_gettime(CLOCK_MONOTONIC, &tstart);
553
}
554
555
static void
556
stopclock(void)
557
{
558
	clock_gettime(CLOCK_MONOTONIC, &tstop);
559
}
560
561
static void
562
printstats(const char *direction, unsigned long amount)
563
{
564
	double	delta;
565
566
	/* compute delta in 1/10's second units */
567
	delta = ((tstop.tv_sec * 10.) + (tstop.tv_nsec / 100000000)) -
568
	    ((tstart.tv_sec * 10.) + (tstart.tv_nsec / 100000000));
569
	delta = delta / 10.;	/* back to seconds */
570
	printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
571
	if (verbose)
572
		printf(" [%.0f bits/sec]", (amount * 8.) / delta);
573
	putchar('\n');
574
}
575
576
static void
577
printtimeout(void)
578
{
579
	printf("Transfer timed out.\n");
580
}
581
582
static void
583
oack(struct tftphdr *tp, int size, int trace)
584
{
585
	int	 i, len, off;
586
	char	*opt, *val;
587
588
	u_short op = ntohs(tp->th_opcode);
589
590
	opt = tp->th_u.tu_stuff;
591
	val = tp->th_u.tu_stuff;
592
593
	if (op == RRQ || op == WRQ) {
594
		len = strlen(opt) + 1;
595
		opt = strchr(opt, '\0');
596
		opt++;
597
		len += strlen(opt) + 1;
598
		opt = strchr(opt, '\0');
599
		opt++;
600
		val = opt;
601
		off = len;
602
		if (trace)
603
			printf(", ");
604
	} else
605
		off = 2;
606
607
	for (i = off, len = 0; i < size - 1; i++) {
608
		if (*val != '\0') {
609
			val++;
610
			continue;
611
		}
612
		/* got option and value */
613
		val++;
614
		if (trace)
615
			printf("%s=%s", opt, val);
616
		else
617
			if (oack_set(opt, val) == -1)
618
				break;
619
		len = strlen(val) + 1;
620
		val += len;
621
		opt = val;
622
		i += len;
623
		if (trace && i < size - 1)
624
			printf(", ");
625
	}
626
}
627
628
int
629
oack_set(const char *option, const char *value)
630
{
631
	int		 i, n;
632
	const char	*errstr;
633
	struct sockaddr_storage peer;
634
	memcpy(&peer, &peeraddr, peeraddr.ss_len);
635
636
	for (i = 0; options[i].o_type != NULL; i++) {
637
		if (!strcasecmp(options[i].o_type, option)) {
638
			if (i == OPT_TSIZE) {
639
				/* XXX verify OACK response */
640
			}
641
			if (i == OPT_TIMEOUT) {
642
				/* verify OACK response */
643
				n = strtonum(value, TIMEOUT_MIN, TIMEOUT_MAX,
644
				    &errstr);
645
				if (errstr || rexmtval != n ||
646
				    opt_tout == 0) {
647
					nak(EOPTNEG, (struct sockaddr *)&peer);
648
					intrflag = 1;
649
					return (-1);
650
				}
651
				/* OK */
652
			}
653
			if (i == OPT_BLKSIZE) {
654
				/* verify OACK response */
655
				n = strtonum(value, SEGSIZE_MIN, SEGSIZE_MAX,
656
				    &errstr);
657
				if (errstr || opt_blksize != n ||
658
				    opt_blksize == 0) {
659
					nak(EOPTNEG, (struct sockaddr *)&peer);
660
					intrflag = 1;
661
					return (-1);
662
				}
663
				/* OK, set option */
664
				segment_size = n;
665
				packet_size = segment_size + 4;
666
			}
667
		}
668
	}
669
670
	return (1);
671
}