tftp-utils.c revision 339059
1/*
2 * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: stable/10/libexec/tftpd/tftp-utils.c 339059 2018-10-01 16:08:27Z asomers $");
28
29#include <sys/socket.h>
30#include <sys/stat.h>
31
32#include <netinet/in.h>
33#include <arpa/tftp.h>
34
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <syslog.h>
40
41#include "tftp-utils.h"
42#include "tftp-io.h"
43
44/*
45 * Default values, can be changed later via the TFTP Options
46 */
47int		timeoutpacket = TIMEOUT;
48int		timeoutnetwork = MAX_TIMEOUTS * TIMEOUT;
49int		maxtimeouts = MAX_TIMEOUTS;
50uint16_t	segsize = SEGSIZE;
51uint16_t	pktsize = SEGSIZE + 4;
52
53int	acting_as_client;
54
55
56/*
57 * Set timeout values for packet reception. The idea is that you
58 * get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the
59 * first timeout) to 'timeoutnetwork' (i.e. the last timeout)
60 */
61int
62settimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts __unused)
63{
64	int i;
65
66	/* We cannot do impossible things */
67	if (_timeoutpacket >= _timeoutnetwork)
68		return (0);
69
70	maxtimeouts = 0;
71	i = _timeoutpacket;
72	while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) {
73		maxtimeouts++;
74		i += 5;
75	}
76
77	timeoutpacket = _timeoutpacket;
78	timeoutnetwork = i;
79	return (1);
80}
81
82/* translate IPv4 mapped IPv6 address to IPv4 address */
83void
84unmappedaddr(struct sockaddr_in6 *sin6)
85{
86	struct sockaddr_in *sin4;
87	u_int32_t addr;
88	int port;
89
90	if (sin6->sin6_family != AF_INET6 ||
91	    !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
92		return;
93	sin4 = (struct sockaddr_in *)sin6;
94	memcpy(&addr, &sin6->sin6_addr.s6_addr[12], sizeof(addr));
95	port = sin6->sin6_port;
96	memset(sin4, 0, sizeof(struct sockaddr_in));
97	sin4->sin_addr.s_addr = addr;
98	sin4->sin_port = port;
99	sin4->sin_family = AF_INET;
100	sin4->sin_len = sizeof(struct sockaddr_in);
101}
102
103/* Get a field from a \0 separated string */
104ssize_t
105get_field(int peer, char *buffer, ssize_t size)
106{
107	char *cp = buffer;
108
109	while (cp < buffer + size) {
110		if (*cp == '\0') break;
111		cp++;
112	}
113	if (*cp != '\0') {
114		tftp_log(LOG_ERR, "Bad option - no trailing \\0 found");
115		send_error(peer, EBADOP);
116		exit(1);
117	}
118	return (cp - buffer + 1);
119}
120
121/*
122 * Logging functions
123 */
124static int _tftp_logtostdout = 1;
125
126void
127tftp_openlog(const char *ident, int logopt, int facility)
128{
129
130	_tftp_logtostdout = (ident == NULL);
131	if (_tftp_logtostdout == 0)
132		openlog(ident, logopt, facility);
133}
134
135void
136tftp_closelog(void)
137{
138
139	if (_tftp_logtostdout == 0)
140		closelog();
141}
142
143void
144tftp_log(int priority, const char *message, ...)
145{
146	va_list ap;
147	char *s;
148
149	va_start(ap, message);
150	if (_tftp_logtostdout == 0) {
151		vasprintf(&s, message, ap);
152		syslog(priority, "%s", s);
153	} else {
154		vprintf(message, ap);
155		printf("\n");
156	}
157	va_end(ap);
158}
159
160/*
161 * Packet types
162 */
163struct packettypes packettypes[] = {
164	{ RRQ,		"RRQ"	},
165	{ WRQ,		"WRQ"	},
166	{ DATA,		"DATA"	},
167	{ ACK,		"ACK"	},
168	{ ERROR,	"ERROR"	},
169	{ OACK,		"OACK"	},
170	{ 0,		NULL	},
171};
172
173const char *
174packettype(int type)
175{
176	static char failed[100];
177	int i = 0;
178
179	while (packettypes[i].name != NULL) {
180		if (packettypes[i].value == type)
181			break;
182		i++;
183	}
184	if (packettypes[i].name != NULL)
185		return packettypes[i].name;
186	sprintf(failed, "unknown (type: %d)", type);
187	return (failed);
188}
189
190/*
191 * Debugs
192 */
193int	debug = DEBUG_NONE;
194struct debugs debugs[] = {
195	{ DEBUG_PACKETS,	"packet",	"Packet debugging"	},
196	{ DEBUG_SIMPLE,		"simple",	"Simple debugging"	},
197	{ DEBUG_OPTIONS,	"options",	"Options debugging"	},
198	{ DEBUG_ACCESS,		"access",	"TCPd access debugging"	},
199	{ DEBUG_NONE,		NULL,		"No debugging"		},
200};
201int	packetdroppercentage = 0;
202
203int
204debug_find(char *s)
205{
206	int i = 0;
207
208	while (debugs[i].name != NULL) {
209		if (strcasecmp(debugs[i].name, s) == 0)
210			break;
211		i++;
212	}
213	return (debugs[i].value);
214}
215
216int
217debug_finds(char *s)
218{
219	int i = 0;
220	char *ps = s;
221
222	while (s != NULL) {
223		ps = strchr(s, ' ');
224		if (ps != NULL)
225			*ps = '\0';
226		i += debug_find(s);
227		if (ps != NULL)
228			*ps = ' ';
229		s = ps;
230	}
231	return (i);
232}
233
234const char *
235debug_show(int d)
236{
237	static char s[100];
238	int i = 0;
239
240	s[0] = '\0';
241	while (debugs[i].name != NULL) {
242		if (d&debugs[i].value) {
243			if (s[0] != '\0')
244				strcat(s, " ");
245			strcat(s, debugs[i].name);
246		}
247		i++;
248	}
249	if (s[0] != '\0')
250		return (s);
251	return ("none");
252}
253
254/*
255 * RP_
256 */
257struct rp_errors rp_errors[] = {
258	{ RP_TIMEOUT,		"Network timeout" },
259	{ RP_TOOSMALL,		"Not enough data bytes" },
260	{ RP_WRONGSOURCE,	"Invalid IP address of UDP port" },
261	{ RP_ERROR,		"Error packet" },
262	{ RP_RECVFROM,		"recvfrom() complained" },
263	{ RP_TOOBIG,		"Too many data bytes" },
264	{ RP_NONE,		NULL }
265};
266
267char *
268rp_strerror(int error)
269{
270	static char s[100];
271	size_t space = sizeof(s);
272	int i = 0;
273
274	while (rp_errors[i].desc != NULL) {
275		if (rp_errors[i].error == error) {
276			strlcpy(s, rp_errors[i].desc, space);
277			space -= strlen(rp_errors[i].desc);
278		}
279		i++;
280	}
281	if (s[0] == '\0')
282		sprintf(s, "unknown (error=%d)", error);
283	return (s);
284}
285
286/*
287 * Performance figures
288 */
289
290void
291stats_init(struct tftp_stats *ts)
292{
293
294	ts->amount = 0;
295	ts->rollovers = 0;
296	ts->retries = 0;
297	ts->blocks = 0;
298	ts->amount = 0;
299	gettimeofday(&(ts->tstart), NULL);
300}
301
302void
303printstats(const char *direction, int verbose, struct tftp_stats *ts)
304{
305	double delta;	/* compute delta in 1/10's second units */
306
307	delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) -
308		((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000));
309	delta = delta/10.;      /* back to seconds */
310
311	printf("%s %zu bytes during %.1f seconds in %u blocks",
312	    direction, ts->amount, delta, ts->blocks);
313
314	if (ts->rollovers != 0)
315		printf(" with %d rollover%s",
316		    ts->rollovers, ts->rollovers != 1 ? "s" : "");
317
318	if (verbose)
319		printf(" [%.0f bits/sec]", (ts->amount*8.)/delta);
320	putchar('\n');
321}
322