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