1207614Simp/*
2207614Simp * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3207614Simp *
4207614Simp * Redistribution and use in source and binary forms, with or without
5207614Simp * modification, are permitted provided that the following conditions
6207614Simp * are met:
7207614Simp * 1. Redistributions of source code must retain the above copyright
8207614Simp *    notice, this list of conditions and the following disclaimer.
9207614Simp * 2. Redistributions in binary form must reproduce the above copyright
10207614Simp *    notice, this list of conditions and the following disclaimer in the
11207614Simp *    documentation and/or other materials provided with the distribution.
12207614Simp *
13207614Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14207614Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15207614Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16207614Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17207614Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18207614Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19207614Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20207614Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21207614Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22207614Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23207614Simp * SUCH DAMAGE.
24207614Simp */
25207614Simp
26207614Simp#include <sys/cdefs.h>
27207614Simp__FBSDID("$FreeBSD: stable/11/libexec/tftpd/tftp-utils.c 345389 2019-03-21 21:45:18Z asomers $");
28207614Simp
29207614Simp#include <sys/socket.h>
30207614Simp#include <sys/stat.h>
31207614Simp
32207614Simp#include <netinet/in.h>
33207614Simp#include <arpa/tftp.h>
34207614Simp
35207614Simp#include <stdarg.h>
36207614Simp#include <stdio.h>
37207614Simp#include <stdlib.h>
38207614Simp#include <string.h>
39207614Simp#include <syslog.h>
40207614Simp
41207614Simp#include "tftp-utils.h"
42207614Simp#include "tftp-io.h"
43207614Simp
44207614Simp/*
45207614Simp * Default values, can be changed later via the TFTP Options
46207614Simp */
47207614Simpint		timeoutpacket = TIMEOUT;
48207614Simpint		timeoutnetwork = MAX_TIMEOUTS * TIMEOUT;
49207614Simpint		maxtimeouts = MAX_TIMEOUTS;
50207614Simpuint16_t	segsize = SEGSIZE;
51207614Simpuint16_t	pktsize = SEGSIZE + 4;
52207614Simp
53207614Simpint	acting_as_client;
54207614Simp
55207614Simp
56207614Simp/*
57207614Simp * Set timeout values for packet reception. The idea is that you
58207614Simp * get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the
59207614Simp * first timeout) to 'timeoutnetwork' (i.e. the last timeout)
60207614Simp */
61207614Simpint
62213099Smariussettimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts __unused)
63207614Simp{
64207614Simp	int i;
65207614Simp
66207614Simp	/* We cannot do impossible things */
67207614Simp	if (_timeoutpacket >= _timeoutnetwork)
68207614Simp		return (0);
69207614Simp
70207614Simp	maxtimeouts = 0;
71207614Simp	i = _timeoutpacket;
72207614Simp	while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) {
73207614Simp		maxtimeouts++;
74207614Simp		i += 5;
75207614Simp	}
76207614Simp
77207614Simp	timeoutpacket = _timeoutpacket;
78207614Simp	timeoutnetwork = i;
79207614Simp	return (1);
80207614Simp}
81207614Simp
82207614Simp/* translate IPv4 mapped IPv6 address to IPv4 address */
83207614Simpvoid
84207614Simpunmappedaddr(struct sockaddr_in6 *sin6)
85207614Simp{
86207614Simp	struct sockaddr_in *sin4;
87207614Simp	u_int32_t addr;
88207614Simp	int port;
89207614Simp
90207614Simp	if (sin6->sin6_family != AF_INET6 ||
91207614Simp	    !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
92207614Simp		return;
93207614Simp	sin4 = (struct sockaddr_in *)sin6;
94213099Smarius	memcpy(&addr, &sin6->sin6_addr.s6_addr[12], sizeof(addr));
95207614Simp	port = sin6->sin6_port;
96207614Simp	memset(sin4, 0, sizeof(struct sockaddr_in));
97207614Simp	sin4->sin_addr.s_addr = addr;
98207614Simp	sin4->sin_port = port;
99207614Simp	sin4->sin_family = AF_INET;
100207614Simp	sin4->sin_len = sizeof(struct sockaddr_in);
101207614Simp}
102207614Simp
103215034Sbrucec/* Get a field from a \0 separated string */
104207614Simpssize_t
105207614Simpget_field(int peer, char *buffer, ssize_t size)
106207614Simp{
107207614Simp	char *cp = buffer;
108207614Simp
109207614Simp	while (cp < buffer + size) {
110207614Simp		if (*cp == '\0') break;
111207614Simp		cp++;
112207614Simp	}
113207614Simp	if (*cp != '\0') {
114207614Simp		tftp_log(LOG_ERR, "Bad option - no trailing \\0 found");
115207614Simp		send_error(peer, EBADOP);
116207614Simp		exit(1);
117207614Simp	}
118207614Simp	return (cp - buffer + 1);
119207614Simp}
120207614Simp
121207614Simp/*
122207614Simp * Logging functions
123207614Simp */
124241720Sedstatic int _tftp_logtostdout = 1;
125207614Simp
126207614Simpvoid
127207614Simptftp_openlog(const char *ident, int logopt, int facility)
128207614Simp{
129207614Simp
130207614Simp	_tftp_logtostdout = (ident == NULL);
131207614Simp	if (_tftp_logtostdout == 0)
132207614Simp		openlog(ident, logopt, facility);
133207614Simp}
134207614Simp
135207614Simpvoid
136207614Simptftp_closelog(void)
137207614Simp{
138207614Simp
139207614Simp	if (_tftp_logtostdout == 0)
140207614Simp		closelog();
141207614Simp}
142207614Simp
143207614Simpvoid
144207614Simptftp_log(int priority, const char *message, ...)
145207614Simp{
146207614Simp	va_list ap;
147207614Simp	char *s;
148207614Simp
149207614Simp	va_start(ap, message);
150207614Simp	if (_tftp_logtostdout == 0) {
151207614Simp		vasprintf(&s, message, ap);
152207614Simp		syslog(priority, "%s", s);
153207614Simp	} else {
154207614Simp		vprintf(message, ap);
155207614Simp		printf("\n");
156207614Simp	}
157207614Simp	va_end(ap);
158207614Simp}
159207614Simp
160207614Simp/*
161207614Simp * Packet types
162207614Simp */
163207614Simpstruct packettypes packettypes[] = {
164207614Simp	{ RRQ,		"RRQ"	},
165207614Simp	{ WRQ,		"WRQ"	},
166207614Simp	{ DATA,		"DATA"	},
167207614Simp	{ ACK,		"ACK"	},
168207614Simp	{ ERROR,	"ERROR"	},
169207614Simp	{ OACK,		"OACK"	},
170207614Simp	{ 0,		NULL	},
171207614Simp};
172207614Simp
173213099Smariusconst char *
174207614Simppackettype(int type)
175207614Simp{
176207614Simp	static char failed[100];
177207614Simp	int i = 0;
178207614Simp
179207614Simp	while (packettypes[i].name != NULL) {
180207614Simp		if (packettypes[i].value == type)
181207614Simp			break;
182207614Simp		i++;
183207614Simp	}
184207614Simp	if (packettypes[i].name != NULL)
185207614Simp		return packettypes[i].name;
186207614Simp	sprintf(failed, "unknown (type: %d)", type);
187207614Simp	return (failed);
188207614Simp}
189207614Simp
190207614Simp/*
191207614Simp * Debugs
192207614Simp */
193207614Simpint	debug = DEBUG_NONE;
194207614Simpstruct debugs debugs[] = {
195207614Simp	{ DEBUG_PACKETS,	"packet",	"Packet debugging"	},
196207614Simp	{ DEBUG_SIMPLE,		"simple",	"Simple debugging"	},
197207614Simp	{ DEBUG_OPTIONS,	"options",	"Options debugging"	},
198207614Simp	{ DEBUG_ACCESS,		"access",	"TCPd access debugging"	},
199207614Simp	{ DEBUG_NONE,		NULL,		"No debugging"		},
200207614Simp};
201207614Simpint	packetdroppercentage = 0;
202207614Simp
203207614Simpint
204207614Simpdebug_find(char *s)
205207614Simp{
206207614Simp	int i = 0;
207207614Simp
208207614Simp	while (debugs[i].name != NULL) {
209207614Simp		if (strcasecmp(debugs[i].name, s) == 0)
210207614Simp			break;
211207614Simp		i++;
212207614Simp	}
213207614Simp	return (debugs[i].value);
214207614Simp}
215207614Simp
216207614Simpint
217207614Simpdebug_finds(char *s)
218207614Simp{
219207614Simp	int i = 0;
220207614Simp	char *ps = s;
221207614Simp
222207614Simp	while (s != NULL) {
223207614Simp		ps = strchr(s, ' ');
224207614Simp		if (ps != NULL)
225207614Simp			*ps = '\0';
226207614Simp		i += debug_find(s);
227207614Simp		if (ps != NULL)
228207614Simp			*ps = ' ';
229207614Simp		s = ps;
230207614Simp	}
231207614Simp	return (i);
232207614Simp}
233207614Simp
234213099Smariusconst char *
235207614Simpdebug_show(int d)
236207614Simp{
237207614Simp	static char s[100];
238345389Sasomers	size_t space = sizeof(s);
239207614Simp	int i = 0;
240207614Simp
241207614Simp	s[0] = '\0';
242207614Simp	while (debugs[i].name != NULL) {
243207614Simp		if (d&debugs[i].value) {
244345389Sasomers			if (s[0] != '\0')
245345389Sasomers				strlcat(s, " ", space);
246345389Sasomers			strlcat(s, debugs[i].name, space);
247207614Simp		}
248207614Simp		i++;
249207614Simp	}
250207614Simp	if (s[0] != '\0')
251207614Simp		return (s);
252207614Simp	return ("none");
253207614Simp}
254207614Simp
255207614Simp/*
256207614Simp * RP_
257207614Simp */
258207614Simpstruct rp_errors rp_errors[] = {
259207614Simp	{ RP_TIMEOUT,		"Network timeout" },
260207614Simp	{ RP_TOOSMALL,		"Not enough data bytes" },
261207614Simp	{ RP_WRONGSOURCE,	"Invalid IP address of UDP port" },
262207614Simp	{ RP_ERROR,		"Error packet" },
263207614Simp	{ RP_RECVFROM,		"recvfrom() complained" },
264207614Simp	{ RP_TOOBIG,		"Too many data bytes" },
265207614Simp	{ RP_NONE,		NULL }
266207614Simp};
267207614Simp
268207614Simpchar *
269207614Simprp_strerror(int error)
270207614Simp{
271207614Simp	static char s[100];
272339051Sasomers	size_t space = sizeof(s);
273207614Simp	int i = 0;
274207614Simp
275207614Simp	while (rp_errors[i].desc != NULL) {
276207614Simp		if (rp_errors[i].error == error) {
277339051Sasomers			strlcpy(s, rp_errors[i].desc, space);
278339051Sasomers			space -= strlen(rp_errors[i].desc);
279207614Simp		}
280207614Simp		i++;
281207614Simp	}
282207614Simp	if (s[0] == '\0')
283207614Simp		sprintf(s, "unknown (error=%d)", error);
284207614Simp	return (s);
285207614Simp}
286207614Simp
287207614Simp/*
288207614Simp * Performance figures
289207614Simp */
290207614Simp
291207614Simpvoid
292207614Simpstats_init(struct tftp_stats *ts)
293207614Simp{
294207614Simp
295207614Simp	ts->amount = 0;
296207614Simp	ts->rollovers = 0;
297207614Simp	ts->retries = 0;
298207614Simp	ts->blocks = 0;
299207614Simp	ts->amount = 0;
300207614Simp	gettimeofday(&(ts->tstart), NULL);
301207614Simp}
302207614Simp
303207614Simpvoid
304207614Simpprintstats(const char *direction, int verbose, struct tftp_stats *ts)
305207614Simp{
306207614Simp	double delta;	/* compute delta in 1/10's second units */
307207614Simp
308207614Simp	delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) -
309207614Simp		((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000));
310207614Simp	delta = delta/10.;      /* back to seconds */
311207614Simp
312207614Simp	printf("%s %zu bytes during %.1f seconds in %u blocks",
313207614Simp	    direction, ts->amount, delta, ts->blocks);
314207614Simp
315207614Simp	if (ts->rollovers != 0)
316207614Simp		printf(" with %d rollover%s",
317207614Simp		    ts->rollovers, ts->rollovers != 1 ? "s" : "");
318207614Simp
319207614Simp	if (verbose)
320207614Simp		printf(" [%.0f bits/sec]", (ts->amount*8.)/delta);
321207614Simp	putchar('\n');
322207614Simp}
323