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