Deleted Added
sdiff udiff text old ( 213099 ) new ( 246139 )
full compact
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: head/libexec/tftpd/tftp-options.c 213099 2010-09-24 10:40:17Z marius $");
28
29#include <sys/socket.h>
30#include <sys/types.h>
31#include <sys/sysctl.h>
32#include <sys/stat.h>
33
34#include <netinet/in.h>
35#include <arpa/tftp.h>
36
37#include <ctype.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#include "tftp-options.h"
46
47/*
48 * Option handlers
49 */
50
51struct options options[] = {
52 { "tsize", NULL, NULL, NULL /* option_tsize */, 1 },
53 { "timeout", NULL, NULL, option_timeout, 1 },
54 { "blksize", NULL, NULL, option_blksize, 1 },
55 { "blksize2", NULL, NULL, option_blksize2, 0 },
56 { "rollover", NULL, NULL, option_rollover, 0 },
57 { NULL, NULL, NULL, NULL, 0 }
58};
59
60/* By default allow them */
61int options_rfc_enabled = 1;
62int options_extra_enabled = 1;
63
64/*
65 * Rules for the option handlers:
66 * - If there is no o_request, there will be no processing.
67 *
68 * For servers
69 * - Logging is done as warnings.
70 * - The handler exit()s if there is a serious problem with the
71 * values submitted in the option.
72 *
73 * For clients
74 * - Logging is done as errors. After all, the server shouldn't
75 * return rubbish.
76 * - The handler returns if there is a serious problem with the
77 * values submitted in the option.
78 * - Sending the EBADOP packets is done by the handler.
79 */
80
81int
82option_tsize(int peer __unused, struct tftphdr *tp __unused, int mode,
83 struct stat *stbuf)
84{
85
86 if (options[OPT_TSIZE].o_request == NULL)
87 return (0);
88
89 if (mode == RRQ)
90 asprintf(&options[OPT_TSIZE].o_reply,
91 "%ju", stbuf->st_size);
92 else
93 /* XXX Allows writes of all sizes. */
94 options[OPT_TSIZE].o_reply =
95 strdup(options[OPT_TSIZE].o_request);
96 return (0);
97}
98
99int
100option_timeout(int peer)
101{
102
103 if (options[OPT_TIMEOUT].o_request == NULL)
104 return (0);
105
106 int to = atoi(options[OPT_TIMEOUT].o_request);
107 if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) {
108 tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
109 "Received bad value for timeout. "
110 "Should be between %d and %d, received %s",
111 TIMEOUT_MIN, TIMEOUT_MAX);
112 send_error(peer, EBADOP);
113 if (acting_as_client)
114 return (1);
115 exit(1);
116 } else {
117 timeoutpacket = to;
118 options[OPT_TIMEOUT].o_reply =
119 strdup(options[OPT_TIMEOUT].o_request);
120 }
121 settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts);
122
123 if (debug&DEBUG_OPTIONS)
124 tftp_log(LOG_DEBUG, "Setting timeout to '%s'",
125 options[OPT_TIMEOUT].o_reply);
126
127 return (0);
128}
129
130int
131option_rollover(int peer)
132{
133
134 if (options[OPT_ROLLOVER].o_request == NULL)
135 return (0);
136
137 if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0
138 && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) {
139 tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
140 "Bad value for rollover, "
141 "should be either 0 or 1, received '%s', "
142 "ignoring request",
143 options[OPT_ROLLOVER].o_request);
144 if (acting_as_client) {
145 send_error(peer, EBADOP);
146 return (1);
147 }
148 return (0);
149 }
150 options[OPT_ROLLOVER].o_reply =
151 strdup(options[OPT_ROLLOVER].o_request);
152
153 if (debug&DEBUG_OPTIONS)
154 tftp_log(LOG_DEBUG, "Setting rollover to '%s'",
155 options[OPT_ROLLOVER].o_reply);
156
157 return (0);
158}
159
160int
161option_blksize(int peer)
162{
163 u_long maxdgram;
164 size_t len;
165
166 if (options[OPT_BLKSIZE].o_request == NULL)
167 return (0);
168
169 /* maximum size of an UDP packet according to the system */
170 len = sizeof(maxdgram);
171 if (sysctlbyname("net.inet.udp.maxdgram",
172 &maxdgram, &len, NULL, 0) < 0) {
173 tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
174 return (acting_as_client ? 1 : 0);
175 }
176
177 int size = atoi(options[OPT_BLKSIZE].o_request);
178 if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
179 if (acting_as_client) {
180 tftp_log(LOG_ERR,
181 "Invalid blocksize (%d bytes), aborting",
182 size);
183 send_error(peer, EBADOP);
184 return (1);
185 } else {
186 tftp_log(LOG_WARNING,
187 "Invalid blocksize (%d bytes), ignoring request",
188 size);
189 return (0);
190 }
191 }
192
193 if (size > (int)maxdgram) {
194 if (acting_as_client) {
195 tftp_log(LOG_ERR,
196 "Invalid blocksize (%d bytes), "
197 "net.inet.udp.maxdgram sysctl limits it to "
198 "%d bytes.\n", size, maxdgram);
199 send_error(peer, EBADOP);
200 return (1);
201 } else {
202 tftp_log(LOG_WARNING,
203 "Invalid blocksize (%d bytes), "
204 "net.inet.udp.maxdgram sysctl limits it to "
205 "%d bytes.\n", size, maxdgram);
206 size = maxdgram;
207 /* No reason to return */
208 }
209 }
210
211 asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size);
212 segsize = size;
213 pktsize = size + 4;
214 if (debug&DEBUG_OPTIONS)
215 tftp_log(LOG_DEBUG, "Setting blksize to '%s'",
216 options[OPT_BLKSIZE].o_reply);
217
218 return (0);
219}
220
221int
222option_blksize2(int peer __unused)
223{
224 u_long maxdgram;
225 int size, i;
226 size_t len;
227
228 int sizes[] = {
229 8, 16, 32, 64, 128, 256, 512, 1024,
230 2048, 4096, 8192, 16384, 32768, 0
231 };
232
233 if (options[OPT_BLKSIZE2].o_request == NULL)
234 return (0);
235
236 /* maximum size of an UDP packet according to the system */
237 len = sizeof(maxdgram);
238 if (sysctlbyname("net.inet.udp.maxdgram",
239 &maxdgram, &len, NULL, 0) < 0) {
240 tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
241 return (acting_as_client ? 1 : 0);
242 }
243
244 size = atoi(options[OPT_BLKSIZE2].o_request);
245 for (i = 0; sizes[i] != 0; i++) {
246 if (size == sizes[i]) break;
247 }
248 if (sizes[i] == 0) {
249 tftp_log(LOG_INFO,
250 "Invalid blocksize2 (%d bytes), ignoring request", size);
251 return (acting_as_client ? 1 : 0);
252 }
253
254 if (size > (int)maxdgram) {
255 for (i = 0; sizes[i+1] != 0; i++) {
256 if ((int)maxdgram < sizes[i+1]) break;
257 }
258 tftp_log(LOG_INFO,
259 "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram "
260 "sysctl limits it to %d bytes.\n", size, maxdgram);
261 size = sizes[i];
262 /* No need to return */
263 }
264
265 asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size);
266 segsize = size;
267 pktsize = size + 4;
268 if (debug&DEBUG_OPTIONS)
269 tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'",
270 options[OPT_BLKSIZE2].o_reply);
271
272 return (0);
273}
274
275/*
276 * Append the available options to the header
277 */
278uint16_t
279make_options(int peer __unused, char *buffer, uint16_t size) {
280 int i;
281 char *value;
282 const char *option;
283 uint16_t length;
284 uint16_t returnsize = 0;
285
286 if (!options_rfc_enabled) return (0);
287
288 for (i = 0; options[i].o_type != NULL; i++) {
289 if (options[i].rfc == 0 && !options_extra_enabled)
290 continue;
291
292 option = options[i].o_type;
293 if (acting_as_client)
294 value = options[i].o_request;
295 else
296 value = options[i].o_reply;
297 if (value == NULL)
298 continue;
299
300 length = strlen(value) + strlen(option) + 2;
301 if (size <= length) {
302 tftp_log(LOG_ERR,
303 "Running out of option space for "
304 "option '%s' with value '%s': "
305 "needed %d bytes, got %d bytes",
306 option, value, size, length);
307 continue;
308 }
309
310 sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000');
311 size -= length;
312 buffer += length;
313 returnsize += length;
314 }
315
316 return (returnsize);
317}
318
319/*
320 * Parse the received options in the header
321 */
322int
323parse_options(int peer, char *buffer, uint16_t size)
324{
325 int i, options_failed;
326 char *c, *cp, *option, *value;
327
328 if (!options_rfc_enabled) return (0);
329
330 /* Parse the options */
331 cp = buffer;
332 options_failed = 0;
333 while (size > 0) {
334 option = cp;
335 i = get_field(peer, cp, size);
336 cp += i;
337
338 value = cp;
339 i = get_field(peer, cp, size);
340 cp += i;
341
342 /* We are at the end */
343 if (*option == '\0') break;
344
345 if (debug&DEBUG_OPTIONS)
346 tftp_log(LOG_DEBUG,
347 "option: '%s' value: '%s'", option, value);
348
349 for (c = option; *c; c++)
350 if (isupper(*c))
351 *c = tolower(*c);
352 for (i = 0; options[i].o_type != NULL; i++) {
353 if (strcmp(option, options[i].o_type) == 0) {
354 if (!acting_as_client)
355 options[i].o_request = value;
356 if (!options_extra_enabled && !options[i].rfc) {
357 tftp_log(LOG_INFO,
358 "Option '%s' with value '%s' found "
359 "but it is not an RFC option",
360 option, value);
361 continue;
362 }
363 if (options[i].o_handler)
364 options_failed +=
365 (options[i].o_handler)(peer);
366 break;
367 }
368 }
369 if (options[i].o_type == NULL)
370 tftp_log(LOG_WARNING,
371 "Unknown option: '%s'", option);
372
373 size -= strlen(option) + strlen(value) + 2;
374 }
375
376 return (options_failed);
377}
378
379/*
380 * Set some default values in the options
381 */
382void
383init_options(void)
384{
385
386 options[OPT_ROLLOVER].o_request = strdup("0");
387}