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