1e6209940SPedro F. Giffuni /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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/types.h>
29eb0292d9SDag-Erling Smørgrav #include <sys/socket.h>
30e7ff5475SWarner Losh #include <sys/stat.h>
31eb0292d9SDag-Erling Smørgrav #include <sys/sysctl.h>
32e7ff5475SWarner Losh
33e7ff5475SWarner Losh #include <netinet/in.h>
34e7ff5475SWarner Losh #include <arpa/tftp.h>
35e7ff5475SWarner Losh
36e7ff5475SWarner Losh #include <ctype.h>
37b15e052eSDag-Erling Smørgrav #include <stdarg.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 },
57fdf929ffSJohn Baldwin { "windowsize", NULL, NULL, option_windowsize, 1 },
58e7ff5475SWarner Losh { NULL, NULL, NULL, NULL, 0 }
59e7ff5475SWarner Losh };
60e7ff5475SWarner Losh
61e7ff5475SWarner Losh /* By default allow them */
62e7ff5475SWarner Losh int options_rfc_enabled = 1;
63e7ff5475SWarner Losh int options_extra_enabled = 1;
64e7ff5475SWarner Losh
65b15e052eSDag-Erling Smørgrav int
options_set_request(enum opt_enum opt,const char * fmt,...)66b15e052eSDag-Erling Smørgrav options_set_request(enum opt_enum opt, const char *fmt, ...)
67b15e052eSDag-Erling Smørgrav {
68b15e052eSDag-Erling Smørgrav va_list ap;
69b15e052eSDag-Erling Smørgrav char *str;
70b15e052eSDag-Erling Smørgrav int ret;
71b15e052eSDag-Erling Smørgrav
72b15e052eSDag-Erling Smørgrav if (fmt == NULL) {
73b15e052eSDag-Erling Smørgrav str = NULL;
74b15e052eSDag-Erling Smørgrav } else {
75b15e052eSDag-Erling Smørgrav va_start(ap, fmt);
76b15e052eSDag-Erling Smørgrav ret = vasprintf(&str, fmt, ap);
77b15e052eSDag-Erling Smørgrav va_end(ap);
78b15e052eSDag-Erling Smørgrav if (ret < 0)
79b15e052eSDag-Erling Smørgrav return (ret);
80b15e052eSDag-Erling Smørgrav }
81b15e052eSDag-Erling Smørgrav if (options[opt].o_request != NULL &&
82b15e052eSDag-Erling Smørgrav options[opt].o_request != options[opt].o_reply)
83b15e052eSDag-Erling Smørgrav free(options[opt].o_request);
84b15e052eSDag-Erling Smørgrav options[opt].o_request = str;
85b15e052eSDag-Erling Smørgrav return (0);
86b15e052eSDag-Erling Smørgrav }
87b15e052eSDag-Erling Smørgrav
88b15e052eSDag-Erling Smørgrav int
options_set_reply(enum opt_enum opt,const char * fmt,...)89b15e052eSDag-Erling Smørgrav options_set_reply(enum opt_enum opt, const char *fmt, ...)
90b15e052eSDag-Erling Smørgrav {
91b15e052eSDag-Erling Smørgrav va_list ap;
92b15e052eSDag-Erling Smørgrav char *str;
93b15e052eSDag-Erling Smørgrav int ret;
94b15e052eSDag-Erling Smørgrav
95b15e052eSDag-Erling Smørgrav if (fmt == NULL) {
96b15e052eSDag-Erling Smørgrav str = NULL;
97b15e052eSDag-Erling Smørgrav } else {
98b15e052eSDag-Erling Smørgrav va_start(ap, fmt);
99b15e052eSDag-Erling Smørgrav ret = vasprintf(&str, fmt, ap);
100b15e052eSDag-Erling Smørgrav va_end(ap);
101b15e052eSDag-Erling Smørgrav if (ret < 0)
102b15e052eSDag-Erling Smørgrav return (ret);
103b15e052eSDag-Erling Smørgrav }
104b15e052eSDag-Erling Smørgrav if (options[opt].o_reply != NULL &&
105b15e052eSDag-Erling Smørgrav options[opt].o_reply != options[opt].o_request)
106b15e052eSDag-Erling Smørgrav free(options[opt].o_reply);
107b15e052eSDag-Erling Smørgrav options[opt].o_reply = str;
108b15e052eSDag-Erling Smørgrav return (0);
109b15e052eSDag-Erling Smørgrav }
110b15e052eSDag-Erling Smørgrav
111b15e052eSDag-Erling Smørgrav static void
options_set_reply_equal_request(enum opt_enum opt)112b15e052eSDag-Erling Smørgrav options_set_reply_equal_request(enum opt_enum opt)
113b15e052eSDag-Erling Smørgrav {
114b15e052eSDag-Erling Smørgrav
115b15e052eSDag-Erling Smørgrav if (options[opt].o_reply != NULL &&
116b15e052eSDag-Erling Smørgrav options[opt].o_reply != options[opt].o_request)
117b15e052eSDag-Erling Smørgrav free(options[opt].o_reply);
118b15e052eSDag-Erling Smørgrav options[opt].o_reply = options[opt].o_request;
119b15e052eSDag-Erling Smørgrav }
120b15e052eSDag-Erling Smørgrav
121e7ff5475SWarner Losh /*
122e7ff5475SWarner Losh * Rules for the option handlers:
123e7ff5475SWarner Losh * - If there is no o_request, there will be no processing.
124e7ff5475SWarner Losh *
125e7ff5475SWarner Losh * For servers
126e7ff5475SWarner Losh * - Logging is done as warnings.
127e7ff5475SWarner Losh * - The handler exit()s if there is a serious problem with the
128e7ff5475SWarner Losh * values submitted in the option.
129e7ff5475SWarner Losh *
130e7ff5475SWarner Losh * For clients
131e7ff5475SWarner Losh * - Logging is done as errors. After all, the server shouldn't
132e7ff5475SWarner Losh * return rubbish.
133e7ff5475SWarner Losh * - The handler returns if there is a serious problem with the
134e7ff5475SWarner Losh * values submitted in the option.
135e7ff5475SWarner Losh * - Sending the EBADOP packets is done by the handler.
136e7ff5475SWarner Losh */
137e7ff5475SWarner Losh
138e7ff5475SWarner Losh int
option_tsize(int peer __unused,struct tftphdr * tp __unused,int mode,struct stat * stbuf)13904ebad38SMarius Strobl option_tsize(int peer __unused, struct tftphdr *tp __unused, int mode,
14004ebad38SMarius Strobl struct stat *stbuf)
141e7ff5475SWarner Losh {
142e7ff5475SWarner Losh
143e7ff5475SWarner Losh if (options[OPT_TSIZE].o_request == NULL)
144e7ff5475SWarner Losh return (0);
145e7ff5475SWarner Losh
146e7ff5475SWarner Losh if (mode == RRQ)
147cf325fdaSDag-Erling Smørgrav options_set_reply(OPT_TSIZE, "%ju", (uintmax_t)stbuf->st_size);
148e7ff5475SWarner Losh else
149e7ff5475SWarner Losh /* XXX Allows writes of all sizes. */
150b15e052eSDag-Erling Smørgrav options_set_reply_equal_request(OPT_TSIZE);
151e7ff5475SWarner Losh return (0);
152e7ff5475SWarner Losh }
153e7ff5475SWarner Losh
154e7ff5475SWarner Losh int
option_timeout(int peer)155e7ff5475SWarner Losh option_timeout(int peer)
156e7ff5475SWarner Losh {
157b713097aSMarius Strobl int to;
158e7ff5475SWarner Losh
159e7ff5475SWarner Losh if (options[OPT_TIMEOUT].o_request == NULL)
160e7ff5475SWarner Losh return (0);
161e7ff5475SWarner Losh
162b713097aSMarius Strobl to = atoi(options[OPT_TIMEOUT].o_request);
163e7ff5475SWarner Losh if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) {
164e7ff5475SWarner Losh tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
165e7ff5475SWarner Losh "Received bad value for timeout. "
166b713097aSMarius Strobl "Should be between %d and %d, received %d",
167b713097aSMarius Strobl TIMEOUT_MIN, TIMEOUT_MAX, to);
168e7ff5475SWarner Losh send_error(peer, EBADOP);
169e7ff5475SWarner Losh if (acting_as_client)
170e7ff5475SWarner Losh return (1);
171e7ff5475SWarner Losh exit(1);
172e7ff5475SWarner Losh } else {
173e7ff5475SWarner Losh timeoutpacket = to;
174b15e052eSDag-Erling Smørgrav options_set_reply_equal_request(OPT_TIMEOUT);
175e7ff5475SWarner Losh }
176e7ff5475SWarner Losh settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts);
177e7ff5475SWarner Losh
178e7ff5475SWarner Losh if (debug & DEBUG_OPTIONS)
179e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Setting timeout to '%s'",
180e7ff5475SWarner Losh options[OPT_TIMEOUT].o_reply);
181e7ff5475SWarner Losh
182e7ff5475SWarner Losh return (0);
183e7ff5475SWarner Losh }
184e7ff5475SWarner Losh
185e7ff5475SWarner Losh int
option_rollover(int peer)186e7ff5475SWarner Losh option_rollover(int peer)
187e7ff5475SWarner Losh {
188e7ff5475SWarner Losh
189e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL)
190e7ff5475SWarner Losh return (0);
191e7ff5475SWarner Losh
192e7ff5475SWarner Losh if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0
193e7ff5475SWarner Losh && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) {
194e7ff5475SWarner Losh tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
195e7ff5475SWarner Losh "Bad value for rollover, "
196e7ff5475SWarner Losh "should be either 0 or 1, received '%s', "
197e7ff5475SWarner Losh "ignoring request",
198e7ff5475SWarner Losh options[OPT_ROLLOVER].o_request);
199e7ff5475SWarner Losh if (acting_as_client) {
200e7ff5475SWarner Losh send_error(peer, EBADOP);
201e7ff5475SWarner Losh return (1);
202e7ff5475SWarner Losh }
203e7ff5475SWarner Losh return (0);
204e7ff5475SWarner Losh }
205b15e052eSDag-Erling Smørgrav options_set_reply_equal_request(OPT_ROLLOVER);
206e7ff5475SWarner Losh
207e7ff5475SWarner Losh if (debug & DEBUG_OPTIONS)
208e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Setting rollover to '%s'",
209e7ff5475SWarner Losh options[OPT_ROLLOVER].o_reply);
210e7ff5475SWarner Losh
211e7ff5475SWarner Losh return (0);
212e7ff5475SWarner Losh }
213e7ff5475SWarner Losh
214e7ff5475SWarner Losh int
option_blksize(int peer)215e7ff5475SWarner Losh option_blksize(int peer)
216e7ff5475SWarner Losh {
21704ebad38SMarius Strobl u_long maxdgram;
218e7ff5475SWarner Losh size_t len;
219e7ff5475SWarner Losh
220e7ff5475SWarner Losh if (options[OPT_BLKSIZE].o_request == NULL)
221e7ff5475SWarner Losh return (0);
222e7ff5475SWarner Losh
223e7ff5475SWarner Losh /* maximum size of an UDP packet according to the system */
22404ebad38SMarius Strobl len = sizeof(maxdgram);
225e7ff5475SWarner Losh if (sysctlbyname("net.inet.udp.maxdgram",
22604ebad38SMarius Strobl &maxdgram, &len, NULL, 0) < 0) {
227e7ff5475SWarner Losh tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
228e7ff5475SWarner Losh return (acting_as_client ? 1 : 0);
229e7ff5475SWarner Losh }
230a6dfd201SDag-Erling Smørgrav maxdgram -= 4; /* leave room for header */
231e7ff5475SWarner Losh
232e7ff5475SWarner Losh int size = atoi(options[OPT_BLKSIZE].o_request);
233e7ff5475SWarner Losh if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
234e7ff5475SWarner Losh if (acting_as_client) {
235e7ff5475SWarner Losh tftp_log(LOG_ERR,
236e7ff5475SWarner Losh "Invalid blocksize (%d bytes), aborting",
237e7ff5475SWarner Losh size);
238e7ff5475SWarner Losh send_error(peer, EBADOP);
239e7ff5475SWarner Losh return (1);
240e7ff5475SWarner Losh } else {
241e7ff5475SWarner Losh tftp_log(LOG_WARNING,
242e7ff5475SWarner Losh "Invalid blocksize (%d bytes), ignoring request",
243e7ff5475SWarner Losh size);
244e7ff5475SWarner Losh return (0);
245e7ff5475SWarner Losh }
246e7ff5475SWarner Losh }
247e7ff5475SWarner Losh
24804ebad38SMarius Strobl if (size > (int)maxdgram) {
249e7ff5475SWarner Losh if (acting_as_client) {
250e7ff5475SWarner Losh tftp_log(LOG_ERR,
251e7ff5475SWarner Losh "Invalid blocksize (%d bytes), "
252e7ff5475SWarner Losh "net.inet.udp.maxdgram sysctl limits it to "
253b713097aSMarius Strobl "%ld bytes.\n", size, maxdgram);
254e7ff5475SWarner Losh send_error(peer, EBADOP);
255e7ff5475SWarner Losh return (1);
256e7ff5475SWarner Losh } else {
257e7ff5475SWarner Losh tftp_log(LOG_WARNING,
258e7ff5475SWarner Losh "Invalid blocksize (%d bytes), "
259e7ff5475SWarner Losh "net.inet.udp.maxdgram sysctl limits it to "
260b713097aSMarius Strobl "%ld bytes.\n", size, maxdgram);
26104ebad38SMarius Strobl size = maxdgram;
262e7ff5475SWarner Losh /* No reason to return */
263e7ff5475SWarner Losh }
264e7ff5475SWarner Losh }
265e7ff5475SWarner Losh
266b15e052eSDag-Erling Smørgrav options_set_reply(OPT_BLKSIZE, "%d", size);
267e7ff5475SWarner Losh segsize = size;
268e7ff5475SWarner Losh pktsize = size + 4;
269e7ff5475SWarner Losh if (debug & DEBUG_OPTIONS)
270e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Setting blksize to '%s'",
271e7ff5475SWarner Losh options[OPT_BLKSIZE].o_reply);
272e7ff5475SWarner Losh
273e7ff5475SWarner Losh return (0);
274e7ff5475SWarner Losh }
275e7ff5475SWarner Losh
276e7ff5475SWarner Losh int
option_blksize2(int peer __unused)27704ebad38SMarius Strobl option_blksize2(int peer __unused)
278e7ff5475SWarner Losh {
27904ebad38SMarius Strobl u_long maxdgram;
280e7ff5475SWarner Losh int size, i;
281e7ff5475SWarner Losh size_t len;
282e7ff5475SWarner Losh
283e7ff5475SWarner Losh int sizes[] = {
284e7ff5475SWarner Losh 8, 16, 32, 64, 128, 256, 512, 1024,
285e7ff5475SWarner Losh 2048, 4096, 8192, 16384, 32768, 0
286e7ff5475SWarner Losh };
287e7ff5475SWarner Losh
288e7ff5475SWarner Losh if (options[OPT_BLKSIZE2].o_request == NULL)
289e7ff5475SWarner Losh return (0);
290e7ff5475SWarner Losh
291e7ff5475SWarner Losh /* maximum size of an UDP packet according to the system */
29204ebad38SMarius Strobl len = sizeof(maxdgram);
293e7ff5475SWarner Losh if (sysctlbyname("net.inet.udp.maxdgram",
29404ebad38SMarius Strobl &maxdgram, &len, NULL, 0) < 0) {
295e7ff5475SWarner Losh tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
296e7ff5475SWarner Losh return (acting_as_client ? 1 : 0);
297e7ff5475SWarner Losh }
298e7ff5475SWarner Losh
299e7ff5475SWarner Losh size = atoi(options[OPT_BLKSIZE2].o_request);
300e7ff5475SWarner Losh for (i = 0; sizes[i] != 0; i++) {
301e7ff5475SWarner Losh if (size == sizes[i]) break;
302e7ff5475SWarner Losh }
303e7ff5475SWarner Losh if (sizes[i] == 0) {
304e7ff5475SWarner Losh tftp_log(LOG_INFO,
305e7ff5475SWarner Losh "Invalid blocksize2 (%d bytes), ignoring request", size);
306e7ff5475SWarner Losh return (acting_as_client ? 1 : 0);
307e7ff5475SWarner Losh }
308e7ff5475SWarner Losh
30904ebad38SMarius Strobl if (size > (int)maxdgram) {
310e7ff5475SWarner Losh for (i = 0; sizes[i+1] != 0; i++) {
31104ebad38SMarius Strobl if ((int)maxdgram < sizes[i+1]) break;
312e7ff5475SWarner Losh }
313e7ff5475SWarner Losh tftp_log(LOG_INFO,
314e7ff5475SWarner Losh "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram "
315b713097aSMarius Strobl "sysctl limits it to %ld bytes.\n", size, maxdgram);
316e7ff5475SWarner Losh size = sizes[i];
317e7ff5475SWarner Losh /* No need to return */
318e7ff5475SWarner Losh }
319e7ff5475SWarner Losh
320b15e052eSDag-Erling Smørgrav options_set_reply(OPT_BLKSIZE2, "%d", size);
321e7ff5475SWarner Losh segsize = size;
322e7ff5475SWarner Losh pktsize = size + 4;
323e7ff5475SWarner Losh if (debug & DEBUG_OPTIONS)
324e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'",
325e7ff5475SWarner Losh options[OPT_BLKSIZE2].o_reply);
326e7ff5475SWarner Losh
327e7ff5475SWarner Losh return (0);
328e7ff5475SWarner Losh }
329e7ff5475SWarner Losh
330fdf929ffSJohn Baldwin int
option_windowsize(int peer)331fdf929ffSJohn Baldwin option_windowsize(int peer)
332fdf929ffSJohn Baldwin {
333fdf929ffSJohn Baldwin int size;
334fdf929ffSJohn Baldwin
335fdf929ffSJohn Baldwin if (options[OPT_WINDOWSIZE].o_request == NULL)
336fdf929ffSJohn Baldwin return (0);
337fdf929ffSJohn Baldwin
338fdf929ffSJohn Baldwin size = atoi(options[OPT_WINDOWSIZE].o_request);
339fdf929ffSJohn Baldwin if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) {
340fdf929ffSJohn Baldwin if (acting_as_client) {
341fdf929ffSJohn Baldwin tftp_log(LOG_ERR,
342fdf929ffSJohn Baldwin "Invalid windowsize (%d blocks), aborting",
343fdf929ffSJohn Baldwin size);
344fdf929ffSJohn Baldwin send_error(peer, EBADOP);
345fdf929ffSJohn Baldwin return (1);
346fdf929ffSJohn Baldwin } else {
347fdf929ffSJohn Baldwin tftp_log(LOG_WARNING,
348fdf929ffSJohn Baldwin "Invalid windowsize (%d blocks), ignoring request",
349fdf929ffSJohn Baldwin size);
350fdf929ffSJohn Baldwin return (0);
351fdf929ffSJohn Baldwin }
352fdf929ffSJohn Baldwin }
353fdf929ffSJohn Baldwin
354fdf929ffSJohn Baldwin /* XXX: Should force a windowsize of 1 for non-seekable files. */
355b15e052eSDag-Erling Smørgrav options_set_reply(OPT_WINDOWSIZE, "%d", size);
356fdf929ffSJohn Baldwin windowsize = size;
357fdf929ffSJohn Baldwin
358fdf929ffSJohn Baldwin if (debug & DEBUG_OPTIONS)
359fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, "Setting windowsize to '%s'",
360fdf929ffSJohn Baldwin options[OPT_WINDOWSIZE].o_reply);
361fdf929ffSJohn Baldwin
362fdf929ffSJohn Baldwin return (0);
363fdf929ffSJohn Baldwin }
364fdf929ffSJohn Baldwin
365e7ff5475SWarner Losh /*
366e7ff5475SWarner Losh * Append the available options to the header
367e7ff5475SWarner Losh */
368e7ff5475SWarner Losh uint16_t
make_options(int peer __unused,char * buffer,uint16_t size)36904ebad38SMarius Strobl make_options(int peer __unused, char *buffer, uint16_t size) {
370e7ff5475SWarner Losh int i;
371e7ff5475SWarner Losh char *value;
372e7ff5475SWarner Losh const char *option;
373e7ff5475SWarner Losh uint16_t length;
374e7ff5475SWarner Losh uint16_t returnsize = 0;
375e7ff5475SWarner Losh
376e7ff5475SWarner Losh if (!options_rfc_enabled) return (0);
377e7ff5475SWarner Losh
378e7ff5475SWarner Losh for (i = 0; options[i].o_type != NULL; i++) {
379e7ff5475SWarner Losh if (options[i].rfc == 0 && !options_extra_enabled)
380e7ff5475SWarner Losh continue;
381e7ff5475SWarner Losh
382e7ff5475SWarner Losh option = options[i].o_type;
383e7ff5475SWarner Losh if (acting_as_client)
384e7ff5475SWarner Losh value = options[i].o_request;
385e7ff5475SWarner Losh else
386e7ff5475SWarner Losh value = options[i].o_reply;
387e7ff5475SWarner Losh if (value == NULL)
388e7ff5475SWarner Losh continue;
389e7ff5475SWarner Losh
390e7ff5475SWarner Losh length = strlen(value) + strlen(option) + 2;
391e7ff5475SWarner Losh if (size <= length) {
392e7ff5475SWarner Losh tftp_log(LOG_ERR,
393e7ff5475SWarner Losh "Running out of option space for "
394e7ff5475SWarner Losh "option '%s' with value '%s': "
395e7ff5475SWarner Losh "needed %d bytes, got %d bytes",
396e7ff5475SWarner Losh option, value, size, length);
397e7ff5475SWarner Losh continue;
398e7ff5475SWarner Losh }
399e7ff5475SWarner Losh
400e7ff5475SWarner Losh sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000');
401e7ff5475SWarner Losh size -= length;
402e7ff5475SWarner Losh buffer += length;
403e7ff5475SWarner Losh returnsize += length;
404e7ff5475SWarner Losh }
405e7ff5475SWarner Losh
406e7ff5475SWarner Losh return (returnsize);
407e7ff5475SWarner Losh }
408e7ff5475SWarner Losh
409e7ff5475SWarner Losh /*
410e7ff5475SWarner Losh * Parse the received options in the header
411e7ff5475SWarner Losh */
412e7ff5475SWarner Losh int
parse_options(int peer,char * buffer,uint16_t size)413e7ff5475SWarner Losh parse_options(int peer, char *buffer, uint16_t size)
414e7ff5475SWarner Losh {
415e7ff5475SWarner Losh int i, options_failed;
416e7ff5475SWarner Losh char *c, *cp, *option, *value;
417e7ff5475SWarner Losh
418e7ff5475SWarner Losh if (!options_rfc_enabled) return (0);
419e7ff5475SWarner Losh
420e7ff5475SWarner Losh /* Parse the options */
421e7ff5475SWarner Losh cp = buffer;
422e7ff5475SWarner Losh options_failed = 0;
423e7ff5475SWarner Losh while (size > 0) {
424e7ff5475SWarner Losh option = cp;
425e7ff5475SWarner Losh i = get_field(peer, cp, size);
426e7ff5475SWarner Losh cp += i;
427e7ff5475SWarner Losh
428e7ff5475SWarner Losh value = cp;
429e7ff5475SWarner Losh i = get_field(peer, cp, size);
430e7ff5475SWarner Losh cp += i;
431e7ff5475SWarner Losh
432e7ff5475SWarner Losh /* We are at the end */
433e7ff5475SWarner Losh if (*option == '\0') break;
434e7ff5475SWarner Losh
435e7ff5475SWarner Losh if (debug & DEBUG_OPTIONS)
436e7ff5475SWarner Losh tftp_log(LOG_DEBUG,
437e7ff5475SWarner Losh "option: '%s' value: '%s'", option, value);
438e7ff5475SWarner Losh
439e7ff5475SWarner Losh for (c = option; *c; c++)
440e7ff5475SWarner Losh if (isupper(*c))
441e7ff5475SWarner Losh *c = tolower(*c);
442e7ff5475SWarner Losh for (i = 0; options[i].o_type != NULL; i++) {
443e7ff5475SWarner Losh if (strcmp(option, options[i].o_type) == 0) {
444e7ff5475SWarner Losh if (!acting_as_client)
445b15e052eSDag-Erling Smørgrav options_set_request(i, "%s", value);
446e7ff5475SWarner Losh if (!options_extra_enabled && !options[i].rfc) {
447e7ff5475SWarner Losh tftp_log(LOG_INFO,
448e7ff5475SWarner Losh "Option '%s' with value '%s' found "
449e7ff5475SWarner Losh "but it is not an RFC option",
450e7ff5475SWarner Losh option, value);
451e7ff5475SWarner Losh continue;
452e7ff5475SWarner Losh }
453e7ff5475SWarner Losh if (options[i].o_handler)
454e7ff5475SWarner Losh options_failed +=
455e7ff5475SWarner Losh (options[i].o_handler)(peer);
456e7ff5475SWarner Losh break;
457e7ff5475SWarner Losh }
458e7ff5475SWarner Losh }
459e7ff5475SWarner Losh if (options[i].o_type == NULL)
460e7ff5475SWarner Losh tftp_log(LOG_WARNING,
461e7ff5475SWarner Losh "Unknown option: '%s'", option);
462e7ff5475SWarner Losh
463e7ff5475SWarner Losh size -= strlen(option) + strlen(value) + 2;
464e7ff5475SWarner Losh }
465e7ff5475SWarner Losh
466e7ff5475SWarner Losh return (options_failed);
467e7ff5475SWarner Losh }
468e7ff5475SWarner Losh
469e7ff5475SWarner Losh /*
470e7ff5475SWarner Losh * Set some default values in the options
471e7ff5475SWarner Losh */
472e7ff5475SWarner Losh void
init_options(void)473e7ff5475SWarner Losh init_options(void)
474e7ff5475SWarner Losh {
475e7ff5475SWarner Losh
476b15e052eSDag-Erling Smørgrav options_set_request(OPT_ROLLOVER, "0");
477e7ff5475SWarner Losh }
478