1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
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#include <sys/socket.h>
30#include <sys/stat.h>
31#include <sys/time.h>
32
33#include <netinet/in.h>
34#include <arpa/tftp.h>
35
36#include <errno.h>
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 */
107size_t
108get_field(int peer, char *buffer, size_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	int serrno;
151	char *s;
152
153	serrno = errno;
154	va_start(ap, message);
155	if (_tftp_logtostdout == 0) {
156		vasprintf(&s, message, ap);
157		syslog(priority, "%s", s);
158	} else {
159		vprintf(message, ap);
160		printf("\n");
161	}
162	va_end(ap);
163	errno = serrno;
164}
165
166/*
167 * Packet types
168 */
169struct packettypes packettypes[] = {
170	{ RRQ,		"RRQ"	},
171	{ WRQ,		"WRQ"	},
172	{ DATA,		"DATA"	},
173	{ ACK,		"ACK"	},
174	{ ERROR,	"ERROR"	},
175	{ OACK,		"OACK"	},
176	{ 0,		NULL	},
177};
178
179const char *
180packettype(int type)
181{
182	static char failed[100];
183	int i = 0;
184
185	while (packettypes[i].name != NULL) {
186		if (packettypes[i].value == type)
187			break;
188		i++;
189	}
190	if (packettypes[i].name != NULL)
191		return packettypes[i].name;
192	sprintf(failed, "unknown (type: %d)", type);
193	return (failed);
194}
195
196/*
197 * Debugs
198 */
199int	debug = DEBUG_NONE;
200struct debugs debugs[] = {
201	{ DEBUG_PACKETS,	"packet",	"Packet debugging"	},
202	{ DEBUG_SIMPLE,		"simple",	"Simple debugging"	},
203	{ DEBUG_OPTIONS,	"options",	"Options debugging"	},
204	{ DEBUG_ACCESS,		"access",	"TCPd access debugging"	},
205	{ DEBUG_NONE,		NULL,		"No debugging"		},
206};
207unsigned int packetdroppercentage = 0;
208
209int
210debug_find(char *s)
211{
212	int i = 0;
213
214	while (debugs[i].name != NULL) {
215		if (strcasecmp(debugs[i].name, s) == 0)
216			break;
217		i++;
218	}
219	return (debugs[i].value);
220}
221
222int
223debug_finds(char *s)
224{
225	int i = 0;
226	char *ps = s;
227
228	while (s != NULL) {
229		ps = strchr(s, ' ');
230		if (ps != NULL)
231			*ps = '\0';
232		i += debug_find(s);
233		if (ps != NULL)
234			*ps = ' ';
235		s = ps;
236	}
237	return (i);
238}
239
240const char *
241debug_show(int d)
242{
243	static char s[100];
244	size_t space = sizeof(s);
245	int i = 0;
246
247	s[0] = '\0';
248	while (debugs[i].name != NULL) {
249		if (d&debugs[i].value) {
250			if (s[0] != '\0')
251				strlcat(s, " ", space);
252			strlcat(s, debugs[i].name, space);
253		}
254		i++;
255	}
256	if (s[0] != '\0')
257		return (s);
258	return ("none");
259}
260
261/*
262 * RP_
263 */
264struct rp_errors rp_errors[] = {
265	{ RP_TIMEOUT,		"Network timeout" },
266	{ RP_TOOSMALL,		"Not enough data bytes" },
267	{ RP_WRONGSOURCE,	"Invalid IP address of UDP port" },
268	{ RP_ERROR,		"Error packet" },
269	{ RP_RECVFROM,		"recvfrom() complained" },
270	{ RP_TOOBIG,		"Too many data bytes" },
271	{ RP_NONE,		NULL }
272};
273
274char *
275rp_strerror(int error)
276{
277	static char s[100];
278	size_t space = sizeof(s);
279	int i = 0;
280
281	while (rp_errors[i].desc != NULL) {
282		if (rp_errors[i].error == error) {
283			strlcpy(s, rp_errors[i].desc, space);
284			space -= strlen(rp_errors[i].desc);
285		}
286		i++;
287	}
288	if (s[0] == '\0')
289		sprintf(s, "unknown (error=%d)", error);
290	return (s);
291}
292
293/*
294 * Performance figures
295 */
296
297void
298stats_init(struct tftp_stats *ts)
299{
300
301	ts->amount = 0;
302	ts->rollovers = 0;
303	ts->retries = 0;
304	ts->blocks = 0;
305	ts->amount = 0;
306	gettimeofday(&(ts->tstart), NULL);
307}
308
309void
310printstats(const char *direction, int verbose, struct tftp_stats *ts)
311{
312	double delta;	/* compute delta in 1/10's second units */
313
314	delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) -
315		((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000));
316	delta = delta/10.;      /* back to seconds */
317
318	printf("%s %zu bytes during %.1f seconds in %u blocks",
319	    direction, ts->amount, delta, ts->blocks);
320
321	if (ts->rollovers != 0)
322		printf(" with %d rollover%s",
323		    ts->rollovers, ts->rollovers != 1 ? "s" : "");
324
325	if (verbose)
326		printf(" [%.0f bits/sec]", (ts->amount*8.)/delta);
327	putchar('\n');
328}
329