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$");
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 */
47 int timeoutpacket = TIMEOUT;
48 int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT;
49 int maxtimeouts = MAX_TIMEOUTS;
50 uint16_t segsize = SEGSIZE;
51 uint16_t pktsize = SEGSIZE + 4;
52
53 int 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 */
61 int
settimeouts(int _timeoutpacket,int _timeoutnetwork,int _maxtimeouts __unused)62 settimeouts(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 */
83 void
unmappedaddr(struct sockaddr_in6 * sin6)84 unmappedaddr(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 */
104 ssize_t
get_field(int peer,char * buffer,ssize_t size)105 get_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 */
124 static int _tftp_logtostdout = 1;
125
126 void
tftp_openlog(const char * ident,int logopt,int facility)127 tftp_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
135 void
tftp_closelog(void)136 tftp_closelog(void)
137 {
138
139 if (_tftp_logtostdout == 0)
140 closelog();
141 }
142
143 void
tftp_log(int priority,const char * message,...)144 tftp_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 */
163 struct 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
173 const char *
packettype(int type)174 packettype(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 */
193 int debug = DEBUG_NONE;
194 struct 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 };
201 int packetdroppercentage = 0;
202
203 int
debug_find(char * s)204 debug_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
216 int
debug_finds(char * s)217 debug_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
234 const char *
debug_show(int d)235 debug_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 */
257 struct 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
267 char *
rp_strerror(int error)268 rp_strerror(int error)
269 {
270 static char s[100];
271 int i = 0;
272
273 while (rp_errors[i].desc != NULL) {
274 if (rp_errors[i].error == error) {
275 strcpy(s, rp_errors[i].desc);
276 }
277 i++;
278 }
279 if (s[0] == '\0')
280 sprintf(s, "unknown (error=%d)", error);
281 return (s);
282 }
283
284 /*
285 * Performance figures
286 */
287
288 void
stats_init(struct tftp_stats * ts)289 stats_init(struct tftp_stats *ts)
290 {
291
292 ts->amount = 0;
293 ts->rollovers = 0;
294 ts->retries = 0;
295 ts->blocks = 0;
296 ts->amount = 0;
297 gettimeofday(&(ts->tstart), NULL);
298 }
299
300 void
printstats(const char * direction,int verbose,struct tftp_stats * ts)301 printstats(const char *direction, int verbose, struct tftp_stats *ts)
302 {
303 double delta; /* compute delta in 1/10's second units */
304
305 delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) -
306 ((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000));
307 delta = delta/10.; /* back to seconds */
308
309 printf("%s %zu bytes during %.1f seconds in %u blocks",
310 direction, ts->amount, delta, ts->blocks);
311
312 if (ts->rollovers != 0)
313 printf(" with %d rollover%s",
314 ts->rollovers, ts->rollovers != 1 ? "s" : "");
315
316 if (verbose)
317 printf(" [%.0f bits/sec]", (ts->amount*8.)/delta);
318 putchar('\n');
319 }
320