2 * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 #include <sys/sysctl.h>
34 #include <netinet/in.h>
35 #include <arpa/tftp.h>
43 #include "tftp-utils.h"
45 #include "tftp-options.h"
51 struct 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 }
60 /* By default allow them */
61 int options_rfc_enabled = 1;
62 int options_extra_enabled = 1;
65 * Rules for the option handlers:
66 * - If there is no o_request, there will be no processing.
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.
74 * - Logging is done as errors. After all, the server shouldn't
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.
82 option_tsize(int peer, struct tftphdr *tp, int mode, struct stat *stbuf)
85 if (options[OPT_TSIZE].o_request == NULL)
89 asprintf(&options[OPT_TSIZE].o_reply,
90 "%ju", stbuf->st_size);
92 /* XXX Allows writes of all sizes. */
93 options[OPT_TSIZE].o_reply =
94 strdup(options[OPT_TSIZE].o_request);
99 option_timeout(int peer)
102 if (options[OPT_TIMEOUT].o_request == NULL)
105 int to = atoi(options[OPT_TIMEOUT].o_request);
106 if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) {
107 tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
108 "Received bad value for timeout. "
109 "Should be between %d and %d, received %s",
110 TIMEOUT_MIN, TIMEOUT_MAX);
111 send_error(peer, EBADOP);
112 if (acting_as_client)
117 options[OPT_TIMEOUT].o_reply =
118 strdup(options[OPT_TIMEOUT].o_request);
120 settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts);
122 if (debug&DEBUG_OPTIONS)
123 tftp_log(LOG_DEBUG, "Setting timeout to '%s'",
124 options[OPT_TIMEOUT].o_reply);
130 option_rollover(int peer)
133 if (options[OPT_ROLLOVER].o_request == NULL)
136 if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0
137 && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) {
138 tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
139 "Bad value for rollover, "
140 "should be either 0 or 1, received '%s', "
142 options[OPT_ROLLOVER].o_request);
143 if (acting_as_client) {
144 send_error(peer, EBADOP);
149 options[OPT_ROLLOVER].o_reply =
150 strdup(options[OPT_ROLLOVER].o_request);
152 if (debug&DEBUG_OPTIONS)
153 tftp_log(LOG_DEBUG, "Setting rollover to '%s'",
154 options[OPT_ROLLOVER].o_reply);
160 option_blksize(int peer)
166 if (options[OPT_BLKSIZE].o_request == NULL)
169 /* maximum size of an UDP packet according to the system */
170 len = sizeof(maxbuffer);
171 if (sysctlbyname("net.inet.udp.maxdgram",
172 maxbuffer, &len, NULL, 0) < 0) {
173 tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
174 return (acting_as_client ? 1 : 0);
176 maxdgram = (int *)maxbuffer;
178 int size = atoi(options[OPT_BLKSIZE].o_request);
179 if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
180 if (acting_as_client) {
182 "Invalid blocksize (%d bytes), aborting",
184 send_error(peer, EBADOP);
187 tftp_log(LOG_WARNING,
188 "Invalid blocksize (%d bytes), ignoring request",
194 if (size > *maxdgram) {
195 if (acting_as_client) {
197 "Invalid blocksize (%d bytes), "
198 "net.inet.udp.maxdgram sysctl limits it to "
199 "%d bytes.\n", size, *maxdgram);
200 send_error(peer, EBADOP);
203 tftp_log(LOG_WARNING,
204 "Invalid blocksize (%d bytes), "
205 "net.inet.udp.maxdgram sysctl limits it to "
206 "%d bytes.\n", size, *maxdgram);
208 /* No reason to return */
212 asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size);
215 if (debug&DEBUG_OPTIONS)
216 tftp_log(LOG_DEBUG, "Setting blksize to '%s'",
217 options[OPT_BLKSIZE].o_reply);
223 option_blksize2(int peer)
231 8, 16, 32, 64, 128, 256, 512, 1024,
232 2048, 4096, 8192, 16384, 32768, 0
235 if (options[OPT_BLKSIZE2].o_request == NULL)
238 /* maximum size of an UDP packet according to the system */
239 len = sizeof(maxbuffer);
240 if (sysctlbyname("net.inet.udp.maxdgram",
241 maxbuffer, &len, NULL, 0) < 0) {
242 tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
243 return (acting_as_client ? 1 : 0);
245 maxdgram = (int *)maxbuffer;
247 size = atoi(options[OPT_BLKSIZE2].o_request);
248 for (i = 0; sizes[i] != 0; i++) {
249 if (size == sizes[i]) break;
253 "Invalid blocksize2 (%d bytes), ignoring request", size);
254 return (acting_as_client ? 1 : 0);
257 if (size > *maxdgram) {
258 for (i = 0; sizes[i+1] != 0; i++) {
259 if (*maxdgram < sizes[i+1]) break;
262 "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram "
263 "sysctl limits it to %d bytes.\n", size, *maxdgram);
265 /* No need to return */
268 asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size);
271 if (debug&DEBUG_OPTIONS)
272 tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'",
273 options[OPT_BLKSIZE2].o_reply);
279 * Append the available options to the header
282 make_options(int peer, char *buffer, uint16_t size) {
287 uint16_t returnsize = 0;
289 if (!options_rfc_enabled) return (0);
291 for (i = 0; options[i].o_type != NULL; i++) {
292 if (options[i].rfc == 0 && !options_extra_enabled)
295 option = options[i].o_type;
296 if (acting_as_client)
297 value = options[i].o_request;
299 value = options[i].o_reply;
303 length = strlen(value) + strlen(option) + 2;
304 if (size <= length) {
306 "Running out of option space for "
307 "option '%s' with value '%s': "
308 "needed %d bytes, got %d bytes",
309 option, value, size, length);
313 sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000');
316 returnsize += length;
323 * Parse the received options in the header
326 parse_options(int peer, char *buffer, uint16_t size)
328 int i, options_failed;
329 char *c, *cp, *option, *value;
331 if (!options_rfc_enabled) return (0);
333 /* Parse the options */
338 i = get_field(peer, cp, size);
342 i = get_field(peer, cp, size);
345 /* We are at the end */
346 if (*option == '\0') break;
348 if (debug&DEBUG_OPTIONS)
350 "option: '%s' value: '%s'", option, value);
352 for (c = option; *c; c++)
355 for (i = 0; options[i].o_type != NULL; i++) {
356 if (strcmp(option, options[i].o_type) == 0) {
357 if (!acting_as_client)
358 options[i].o_request = value;
359 if (!options_extra_enabled && !options[i].rfc) {
361 "Option '%s' with value '%s' found "
362 "but it is not an RFC option",
366 if (options[i].o_handler)
368 (options[i].o_handler)(peer);
372 if (options[i].o_type == NULL)
373 tftp_log(LOG_WARNING,
374 "Unknown option: '%s'", option);
376 size -= strlen(option) + strlen(value) + 2;
379 return (options_failed);
383 * Set some default values in the options
389 options[OPT_ROLLOVER].o_request = strdup("0");