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