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: stable/12/libexec/tftpd/tftp-utils.c 367305 2020-11-03 20:43:01Z jhb $");
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 */
49 int timeoutpacket = TIMEOUT;
50 int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT;
51 int maxtimeouts = MAX_TIMEOUTS;
52 uint16_t segsize = SEGSIZE;
53 uint16_t pktsize = SEGSIZE + 4;
54 uint16_t windowsize = WINDOWSIZE;
55
56 int 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 */
64 int
settimeouts(int _timeoutpacket,int _timeoutnetwork,int _maxtimeouts __unused)65 settimeouts(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 */
86 void
unmappedaddr(struct sockaddr_in6 * sin6)87 unmappedaddr(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 */
107 ssize_t
get_field(int peer,char * buffer,ssize_t size)108 get_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 */
127 static int _tftp_logtostdout = 1;
128
129 void
tftp_openlog(const char * ident,int logopt,int facility)130 tftp_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
138 void
tftp_closelog(void)139 tftp_closelog(void)
140 {
141
142 if (_tftp_logtostdout == 0)
143 closelog();
144 }
145
146 void
tftp_log(int priority,const char * message,...)147 tftp_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 */
166 struct 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
176 const char *
packettype(int type)177 packettype(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 */
196 int debug = DEBUG_NONE;
197 struct 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 };
204 int packetdroppercentage = 0;
205
206 int
debug_find(char * s)207 debug_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
219 int
debug_finds(char * s)220 debug_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
237 const char *
debug_show(int d)238 debug_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 */
261 struct 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
271 char *
rp_strerror(int error)272 rp_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
294 void
stats_init(struct tftp_stats * ts)295 stats_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
306 void
printstats(const char * direction,int verbose,struct tftp_stats * ts)307 printstats(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