xref: /freebsd/libexec/tftpd/tftp-options.c (revision e7ff5475)
1e7ff5475SWarner Losh /*
2e7ff5475SWarner Losh  * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3e7ff5475SWarner Losh  *
4e7ff5475SWarner Losh  * Redistribution and use in source and binary forms, with or without
5e7ff5475SWarner Losh  * modification, are permitted provided that the following conditions
6e7ff5475SWarner Losh  * are met:
7e7ff5475SWarner Losh  * 1. Redistributions of source code must retain the above copyright
8e7ff5475SWarner Losh  *    notice, this list of conditions and the following disclaimer.
9e7ff5475SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
10e7ff5475SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
11e7ff5475SWarner Losh  *    documentation and/or other materials provided with the distribution.
12e7ff5475SWarner Losh  *
13e7ff5475SWarner Losh  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14e7ff5475SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15e7ff5475SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16e7ff5475SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17e7ff5475SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18e7ff5475SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19e7ff5475SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20e7ff5475SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21e7ff5475SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22e7ff5475SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23e7ff5475SWarner Losh  * SUCH DAMAGE.
24e7ff5475SWarner Losh  */
25e7ff5475SWarner Losh 
26e7ff5475SWarner Losh #include <sys/cdefs.h>
27e7ff5475SWarner Losh __FBSDID("$FreeBSD$");
28e7ff5475SWarner Losh 
29e7ff5475SWarner Losh #include <sys/socket.h>
30e7ff5475SWarner Losh #include <sys/types.h>
31e7ff5475SWarner Losh #include <sys/sysctl.h>
32e7ff5475SWarner Losh #include <sys/stat.h>
33e7ff5475SWarner Losh 
34e7ff5475SWarner Losh #include <netinet/in.h>
35e7ff5475SWarner Losh #include <arpa/tftp.h>
36e7ff5475SWarner Losh 
37e7ff5475SWarner Losh #include <ctype.h>
38e7ff5475SWarner Losh #include <stdio.h>
39e7ff5475SWarner Losh #include <stdlib.h>
40e7ff5475SWarner Losh #include <string.h>
41e7ff5475SWarner Losh #include <syslog.h>
42e7ff5475SWarner Losh 
43e7ff5475SWarner Losh #include "tftp-utils.h"
44e7ff5475SWarner Losh #include "tftp-io.h"
45e7ff5475SWarner Losh #include "tftp-options.h"
46e7ff5475SWarner Losh 
47e7ff5475SWarner Losh /*
48e7ff5475SWarner Losh  * Option handlers
49e7ff5475SWarner Losh  */
50e7ff5475SWarner Losh 
51e7ff5475SWarner Losh struct options options[] = {
52e7ff5475SWarner Losh 	{ "tsize",	NULL, NULL, NULL /* option_tsize */, 1 },
53e7ff5475SWarner Losh 	{ "timeout",	NULL, NULL, option_timeout, 1 },
54e7ff5475SWarner Losh 	{ "blksize",	NULL, NULL, option_blksize, 1 },
55e7ff5475SWarner Losh 	{ "blksize2",	NULL, NULL, option_blksize2, 0 },
56e7ff5475SWarner Losh 	{ "rollover",	NULL, NULL, option_rollover, 0 },
57e7ff5475SWarner Losh 	{ NULL,		NULL, NULL, NULL, 0 }
58e7ff5475SWarner Losh };
59e7ff5475SWarner Losh 
60e7ff5475SWarner Losh /* By default allow them */
61e7ff5475SWarner Losh int options_rfc_enabled = 1;
62e7ff5475SWarner Losh int options_extra_enabled = 1;
63e7ff5475SWarner Losh 
64e7ff5475SWarner Losh /*
65e7ff5475SWarner Losh  * Rules for the option handlers:
66e7ff5475SWarner Losh  * - If there is no o_request, there will be no processing.
67e7ff5475SWarner Losh  *
68e7ff5475SWarner Losh  * For servers
69e7ff5475SWarner Losh  * - Logging is done as warnings.
70e7ff5475SWarner Losh  * - The handler exit()s if there is a serious problem with the
71e7ff5475SWarner Losh  *   values submitted in the option.
72e7ff5475SWarner Losh  *
73e7ff5475SWarner Losh  * For clients
74e7ff5475SWarner Losh  * - Logging is done as errors. After all, the server shouldn't
75e7ff5475SWarner Losh  *   return rubbish.
76e7ff5475SWarner Losh  * - The handler returns if there is a serious problem with the
77e7ff5475SWarner Losh  *   values submitted in the option.
78e7ff5475SWarner Losh  * - Sending the EBADOP packets is done by the handler.
79e7ff5475SWarner Losh  */
80e7ff5475SWarner Losh 
81e7ff5475SWarner Losh int
82e7ff5475SWarner Losh option_tsize(int peer, struct tftphdr *tp, int mode, struct stat *stbuf)
83e7ff5475SWarner Losh {
84e7ff5475SWarner Losh 
85e7ff5475SWarner Losh 	if (options[OPT_TSIZE].o_request == NULL)
86e7ff5475SWarner Losh 		return (0);
87e7ff5475SWarner Losh 
88e7ff5475SWarner Losh 	if (mode == RRQ)
89e7ff5475SWarner Losh 		asprintf(&options[OPT_TSIZE].o_reply,
90e7ff5475SWarner Losh 			"%ju", stbuf->st_size);
91e7ff5475SWarner Losh 	else
92e7ff5475SWarner Losh 		/* XXX Allows writes of all sizes. */
93e7ff5475SWarner Losh 		options[OPT_TSIZE].o_reply =
94e7ff5475SWarner Losh 			strdup(options[OPT_TSIZE].o_request);
95e7ff5475SWarner Losh 	return (0);
96e7ff5475SWarner Losh }
97e7ff5475SWarner Losh 
98e7ff5475SWarner Losh int
99e7ff5475SWarner Losh option_timeout(int peer)
100e7ff5475SWarner Losh {
101e7ff5475SWarner Losh 
102e7ff5475SWarner Losh 	if (options[OPT_TIMEOUT].o_request == NULL)
103e7ff5475SWarner Losh 		return (0);
104e7ff5475SWarner Losh 
105e7ff5475SWarner Losh 	int to = atoi(options[OPT_TIMEOUT].o_request);
106e7ff5475SWarner Losh 	if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) {
107e7ff5475SWarner Losh 		tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
108e7ff5475SWarner Losh 		    "Received bad value for timeout. "
109e7ff5475SWarner Losh 		    "Should be between %d and %d, received %s",
110e7ff5475SWarner Losh 		    TIMEOUT_MIN, TIMEOUT_MAX);
111e7ff5475SWarner Losh 		send_error(peer, EBADOP);
112e7ff5475SWarner Losh 		if (acting_as_client)
113e7ff5475SWarner Losh 			return (1);
114e7ff5475SWarner Losh 		exit(1);
115e7ff5475SWarner Losh 	} else {
116e7ff5475SWarner Losh 		timeoutpacket = to;
117e7ff5475SWarner Losh 		options[OPT_TIMEOUT].o_reply =
118e7ff5475SWarner Losh 			strdup(options[OPT_TIMEOUT].o_request);
119e7ff5475SWarner Losh 	}
120e7ff5475SWarner Losh 	settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts);
121e7ff5475SWarner Losh 
122e7ff5475SWarner Losh 	if (debug&DEBUG_OPTIONS)
123e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Setting timeout to '%s'",
124e7ff5475SWarner Losh 			options[OPT_TIMEOUT].o_reply);
125e7ff5475SWarner Losh 
126e7ff5475SWarner Losh 	return (0);
127e7ff5475SWarner Losh }
128e7ff5475SWarner Losh 
129e7ff5475SWarner Losh int
130e7ff5475SWarner Losh option_rollover(int peer)
131e7ff5475SWarner Losh {
132e7ff5475SWarner Losh 
133e7ff5475SWarner Losh 	if (options[OPT_ROLLOVER].o_request == NULL)
134e7ff5475SWarner Losh 		return (0);
135e7ff5475SWarner Losh 
136e7ff5475SWarner Losh 	if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0
137e7ff5475SWarner Losh 	 && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) {
138e7ff5475SWarner Losh 		tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
139e7ff5475SWarner Losh 		    "Bad value for rollover, "
140e7ff5475SWarner Losh 		    "should be either 0 or 1, received '%s', "
141e7ff5475SWarner Losh 		    "ignoring request",
142e7ff5475SWarner Losh 		    options[OPT_ROLLOVER].o_request);
143e7ff5475SWarner Losh 		if (acting_as_client) {
144e7ff5475SWarner Losh 			send_error(peer, EBADOP);
145e7ff5475SWarner Losh 			return (1);
146e7ff5475SWarner Losh 		}
147e7ff5475SWarner Losh 		return (0);
148e7ff5475SWarner Losh 	}
149e7ff5475SWarner Losh 	options[OPT_ROLLOVER].o_reply =
150e7ff5475SWarner Losh 		strdup(options[OPT_ROLLOVER].o_request);
151e7ff5475SWarner Losh 
152e7ff5475SWarner Losh 	if (debug&DEBUG_OPTIONS)
153e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Setting rollover to '%s'",
154e7ff5475SWarner Losh 			options[OPT_ROLLOVER].o_reply);
155e7ff5475SWarner Losh 
156e7ff5475SWarner Losh 	return (0);
157e7ff5475SWarner Losh }
158e7ff5475SWarner Losh 
159e7ff5475SWarner Losh int
160e7ff5475SWarner Losh option_blksize(int peer)
161e7ff5475SWarner Losh {
162e7ff5475SWarner Losh 	int *maxdgram;
163e7ff5475SWarner Losh 	char maxbuffer[100];
164e7ff5475SWarner Losh 	size_t len;
165e7ff5475SWarner Losh 
166e7ff5475SWarner Losh 	if (options[OPT_BLKSIZE].o_request == NULL)
167e7ff5475SWarner Losh 		return (0);
168e7ff5475SWarner Losh 
169e7ff5475SWarner Losh 	/* maximum size of an UDP packet according to the system */
170e7ff5475SWarner Losh 	len = sizeof(maxbuffer);
171e7ff5475SWarner Losh 	if (sysctlbyname("net.inet.udp.maxdgram",
172e7ff5475SWarner Losh 	    maxbuffer, &len, NULL, 0) < 0) {
173e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
174e7ff5475SWarner Losh 		return (acting_as_client ? 1 : 0);
175e7ff5475SWarner Losh 	}
176e7ff5475SWarner Losh 	maxdgram = (int *)maxbuffer;
177e7ff5475SWarner Losh 
178e7ff5475SWarner Losh 	int size = atoi(options[OPT_BLKSIZE].o_request);
179e7ff5475SWarner Losh 	if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
180e7ff5475SWarner Losh 		if (acting_as_client) {
181e7ff5475SWarner Losh 			tftp_log(LOG_ERR,
182e7ff5475SWarner Losh 			    "Invalid blocksize (%d bytes), aborting",
183e7ff5475SWarner Losh 			    size);
184e7ff5475SWarner Losh 			send_error(peer, EBADOP);
185e7ff5475SWarner Losh 			return (1);
186e7ff5475SWarner Losh 		} else {
187e7ff5475SWarner Losh 			tftp_log(LOG_WARNING,
188e7ff5475SWarner Losh 			    "Invalid blocksize (%d bytes), ignoring request",
189e7ff5475SWarner Losh 			    size);
190e7ff5475SWarner Losh 			return (0);
191e7ff5475SWarner Losh 		}
192e7ff5475SWarner Losh 	}
193e7ff5475SWarner Losh 
194e7ff5475SWarner Losh 	if (size > *maxdgram) {
195e7ff5475SWarner Losh 		if (acting_as_client) {
196e7ff5475SWarner Losh 			tftp_log(LOG_ERR,
197e7ff5475SWarner Losh 			    "Invalid blocksize (%d bytes), "
198e7ff5475SWarner Losh 			    "net.inet.udp.maxdgram sysctl limits it to "
199e7ff5475SWarner Losh 			    "%d bytes.\n", size, *maxdgram);
200e7ff5475SWarner Losh 			send_error(peer, EBADOP);
201e7ff5475SWarner Losh 			return (1);
202e7ff5475SWarner Losh 		} else {
203e7ff5475SWarner Losh 			tftp_log(LOG_WARNING,
204e7ff5475SWarner Losh 			    "Invalid blocksize (%d bytes), "
205e7ff5475SWarner Losh 			    "net.inet.udp.maxdgram sysctl limits it to "
206e7ff5475SWarner Losh 			    "%d bytes.\n", size, *maxdgram);
207e7ff5475SWarner Losh 			size = *maxdgram;
208e7ff5475SWarner Losh 			/* No reason to return */
209e7ff5475SWarner Losh 		}
210e7ff5475SWarner Losh 	}
211e7ff5475SWarner Losh 
212e7ff5475SWarner Losh 	asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size);
213e7ff5475SWarner Losh 	segsize = size;
214e7ff5475SWarner Losh 	pktsize = size + 4;
215e7ff5475SWarner Losh 	if (debug&DEBUG_OPTIONS)
216e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Setting blksize to '%s'",
217e7ff5475SWarner Losh 		    options[OPT_BLKSIZE].o_reply);
218e7ff5475SWarner Losh 
219e7ff5475SWarner Losh 	return (0);
220e7ff5475SWarner Losh }
221e7ff5475SWarner Losh 
222e7ff5475SWarner Losh int
223e7ff5475SWarner Losh option_blksize2(int peer)
224e7ff5475SWarner Losh {
225e7ff5475SWarner Losh 	int	*maxdgram;
226e7ff5475SWarner Losh 	char	maxbuffer[100];
227e7ff5475SWarner Losh 	int	size, i;
228e7ff5475SWarner Losh 	size_t	len;
229e7ff5475SWarner Losh 
230e7ff5475SWarner Losh 	int sizes[] = {
231e7ff5475SWarner Losh 		8, 16, 32, 64, 128, 256, 512, 1024,
232e7ff5475SWarner Losh 		2048, 4096, 8192, 16384, 32768, 0
233e7ff5475SWarner Losh 	};
234e7ff5475SWarner Losh 
235e7ff5475SWarner Losh 	if (options[OPT_BLKSIZE2].o_request == NULL)
236e7ff5475SWarner Losh 		return (0);
237e7ff5475SWarner Losh 
238e7ff5475SWarner Losh 	/* maximum size of an UDP packet according to the system */
239e7ff5475SWarner Losh 	len = sizeof(maxbuffer);
240e7ff5475SWarner Losh 	if (sysctlbyname("net.inet.udp.maxdgram",
241e7ff5475SWarner Losh 	    maxbuffer, &len, NULL, 0) < 0) {
242e7ff5475SWarner Losh 		tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
243e7ff5475SWarner Losh 		return (acting_as_client ? 1 : 0);
244e7ff5475SWarner Losh 	}
245e7ff5475SWarner Losh 	maxdgram = (int *)maxbuffer;
246e7ff5475SWarner Losh 
247e7ff5475SWarner Losh 	size = atoi(options[OPT_BLKSIZE2].o_request);
248e7ff5475SWarner Losh 	for (i = 0; sizes[i] != 0; i++) {
249e7ff5475SWarner Losh 		if (size == sizes[i]) break;
250e7ff5475SWarner Losh 	}
251e7ff5475SWarner Losh 	if (sizes[i] == 0) {
252e7ff5475SWarner Losh 		tftp_log(LOG_INFO,
253e7ff5475SWarner Losh 		    "Invalid blocksize2 (%d bytes), ignoring request", size);
254e7ff5475SWarner Losh 		return (acting_as_client ? 1 : 0);
255e7ff5475SWarner Losh 	}
256e7ff5475SWarner Losh 
257e7ff5475SWarner Losh 	if (size > *maxdgram) {
258e7ff5475SWarner Losh 		for (i = 0; sizes[i+1] != 0; i++) {
259e7ff5475SWarner Losh 			if (*maxdgram < sizes[i+1]) break;
260e7ff5475SWarner Losh 		}
261e7ff5475SWarner Losh 		tftp_log(LOG_INFO,
262e7ff5475SWarner Losh 		    "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram "
263e7ff5475SWarner Losh 		    "sysctl limits it to %d bytes.\n", size, *maxdgram);
264e7ff5475SWarner Losh 		size = sizes[i];
265e7ff5475SWarner Losh 		/* No need to return */
266e7ff5475SWarner Losh 	}
267e7ff5475SWarner Losh 
268e7ff5475SWarner Losh 	asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size);
269e7ff5475SWarner Losh 	segsize = size;
270e7ff5475SWarner Losh 	pktsize = size + 4;
271e7ff5475SWarner Losh 	if (debug&DEBUG_OPTIONS)
272e7ff5475SWarner Losh 		tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'",
273e7ff5475SWarner Losh 		    options[OPT_BLKSIZE2].o_reply);
274e7ff5475SWarner Losh 
275e7ff5475SWarner Losh 	return (0);
276e7ff5475SWarner Losh }
277e7ff5475SWarner Losh 
278e7ff5475SWarner Losh /*
279e7ff5475SWarner Losh  * Append the available options to the header
280e7ff5475SWarner Losh  */
281e7ff5475SWarner Losh uint16_t
282e7ff5475SWarner Losh make_options(int peer, char *buffer, uint16_t size) {
283e7ff5475SWarner Losh 	int	i;
284e7ff5475SWarner Losh 	char	*value;
285e7ff5475SWarner Losh 	const char *option;
286e7ff5475SWarner Losh 	uint16_t length;
287e7ff5475SWarner Losh 	uint16_t returnsize = 0;
288e7ff5475SWarner Losh 
289e7ff5475SWarner Losh 	if (!options_rfc_enabled) return (0);
290e7ff5475SWarner Losh 
291e7ff5475SWarner Losh 	for (i = 0; options[i].o_type != NULL; i++) {
292e7ff5475SWarner Losh 		if (options[i].rfc == 0 && !options_extra_enabled)
293e7ff5475SWarner Losh 			continue;
294e7ff5475SWarner Losh 
295e7ff5475SWarner Losh 		option = options[i].o_type;
296e7ff5475SWarner Losh 		if (acting_as_client)
297e7ff5475SWarner Losh 			value = options[i].o_request;
298e7ff5475SWarner Losh 		else
299e7ff5475SWarner Losh 			value = options[i].o_reply;
300e7ff5475SWarner Losh 		if (value == NULL)
301e7ff5475SWarner Losh 			continue;
302e7ff5475SWarner Losh 
303e7ff5475SWarner Losh 		length = strlen(value) + strlen(option) + 2;
304e7ff5475SWarner Losh 		if (size <= length) {
305e7ff5475SWarner Losh 			tftp_log(LOG_ERR,
306e7ff5475SWarner Losh 			    "Running out of option space for "
307e7ff5475SWarner Losh 			    "option '%s' with value '%s': "
308e7ff5475SWarner Losh 			    "needed %d bytes, got %d bytes",
309e7ff5475SWarner Losh 			    option, value, size, length);
310e7ff5475SWarner Losh 			continue;
311e7ff5475SWarner Losh 		}
312e7ff5475SWarner Losh 
313e7ff5475SWarner Losh 		sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000');
314e7ff5475SWarner Losh 		size -= length;
315e7ff5475SWarner Losh 		buffer += length;
316e7ff5475SWarner Losh 		returnsize += length;
317e7ff5475SWarner Losh 	}
318e7ff5475SWarner Losh 
319e7ff5475SWarner Losh 	return (returnsize);
320e7ff5475SWarner Losh }
321e7ff5475SWarner Losh 
322e7ff5475SWarner Losh /*
323e7ff5475SWarner Losh  * Parse the received options in the header
324e7ff5475SWarner Losh  */
325e7ff5475SWarner Losh int
326e7ff5475SWarner Losh parse_options(int peer, char *buffer, uint16_t size)
327e7ff5475SWarner Losh {
328e7ff5475SWarner Losh 	int	i, options_failed;
329e7ff5475SWarner Losh 	char	*c, *cp, *option, *value;
330e7ff5475SWarner Losh 
331e7ff5475SWarner Losh 	if (!options_rfc_enabled) return (0);
332e7ff5475SWarner Losh 
333e7ff5475SWarner Losh 	/* Parse the options */
334e7ff5475SWarner Losh 	cp = buffer;
335e7ff5475SWarner Losh 	options_failed = 0;
336e7ff5475SWarner Losh 	while (size > 0) {
337e7ff5475SWarner Losh 		option = cp;
338e7ff5475SWarner Losh 		i = get_field(peer, cp, size);
339e7ff5475SWarner Losh 		cp += i;
340e7ff5475SWarner Losh 
341e7ff5475SWarner Losh 		value = cp;
342e7ff5475SWarner Losh 		i = get_field(peer, cp, size);
343e7ff5475SWarner Losh 		cp += i;
344e7ff5475SWarner Losh 
345e7ff5475SWarner Losh 		/* We are at the end */
346e7ff5475SWarner Losh 		if (*option == '\0') break;
347e7ff5475SWarner Losh 
348e7ff5475SWarner Losh 		if (debug&DEBUG_OPTIONS)
349e7ff5475SWarner Losh 			tftp_log(LOG_DEBUG,
350e7ff5475SWarner Losh 			    "option: '%s' value: '%s'", option, value);
351e7ff5475SWarner Losh 
352e7ff5475SWarner Losh 		for (c = option; *c; c++)
353e7ff5475SWarner Losh 			if (isupper(*c))
354e7ff5475SWarner Losh 				*c = tolower(*c);
355e7ff5475SWarner Losh 		for (i = 0; options[i].o_type != NULL; i++) {
356e7ff5475SWarner Losh 			if (strcmp(option, options[i].o_type) == 0) {
357e7ff5475SWarner Losh 				if (!acting_as_client)
358e7ff5475SWarner Losh 					options[i].o_request = value;
359e7ff5475SWarner Losh 				if (!options_extra_enabled && !options[i].rfc) {
360e7ff5475SWarner Losh 					tftp_log(LOG_INFO,
361e7ff5475SWarner Losh 					    "Option '%s' with value '%s' found "
362e7ff5475SWarner Losh 					    "but it is not an RFC option",
363e7ff5475SWarner Losh 					    option, value);
364e7ff5475SWarner Losh 					continue;
365e7ff5475SWarner Losh 				}
366e7ff5475SWarner Losh 				if (options[i].o_handler)
367e7ff5475SWarner Losh 					options_failed +=
368e7ff5475SWarner Losh 					    (options[i].o_handler)(peer);
369e7ff5475SWarner Losh 				break;
370e7ff5475SWarner Losh 			}
371e7ff5475SWarner Losh 		}
372e7ff5475SWarner Losh 		if (options[i].o_type == NULL)
373e7ff5475SWarner Losh 			tftp_log(LOG_WARNING,
374e7ff5475SWarner Losh 			    "Unknown option: '%s'", option);
375e7ff5475SWarner Losh 
376e7ff5475SWarner Losh 		size -= strlen(option) + strlen(value) + 2;
377e7ff5475SWarner Losh 	}
378e7ff5475SWarner Losh 
379e7ff5475SWarner Losh 	return (options_failed);
380e7ff5475SWarner Losh }
381e7ff5475SWarner Losh 
382e7ff5475SWarner Losh /*
383e7ff5475SWarner Losh  * Set some default values in the options
384e7ff5475SWarner Losh  */
385e7ff5475SWarner Losh void
386e7ff5475SWarner Losh init_options(void)
387e7ff5475SWarner Losh {
388e7ff5475SWarner Losh 
389e7ff5475SWarner Losh 	options[OPT_ROLLOVER].o_request = strdup("0");
390e7ff5475SWarner Losh }
391