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