tftp-options.c revision 213099
138136Sdfr/* 238136Sdfr * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 338136Sdfr * 438136Sdfr * Redistribution and use in source and binary forms, with or without 538136Sdfr * modification, are permitted provided that the following conditions 638136Sdfr * are met: 738136Sdfr * 1. Redistributions of source code must retain the above copyright 838136Sdfr * notice, this list of conditions and the following disclaimer. 938136Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1038136Sdfr * notice, this list of conditions and the following disclaimer in the 1138136Sdfr * documentation and/or other materials provided with the distribution. 1238136Sdfr * 1338136Sdfr * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1438136Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1538136Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1638136Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 1738136Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1838136Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1938136Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2038136Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2138136Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2238136Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2338136Sdfr * SUCH DAMAGE. 2438136Sdfr */ 2538136Sdfr 2650477Speter#include <sys/cdefs.h> 2738136Sdfr__FBSDID("$FreeBSD: head/libexec/tftpd/tftp-options.c 213099 2010-09-24 10:40:17Z marius $"); 2838136Sdfr 2947613Sdfr#include <sys/socket.h> 3047613Sdfr#include <sys/types.h> 3147613Sdfr#include <sys/sysctl.h> 3250769Sdfr#include <sys/stat.h> 3350769Sdfr 3450769Sdfr#include <netinet/in.h> 3550769Sdfr#include <arpa/tftp.h> 3647398Sdfr 3750769Sdfr#include <ctype.h> 3847398Sdfr#include <stdio.h> 3955206Speter#include <stdlib.h> 4047613Sdfr#include <string.h> 4147578Sdfr#include <syslog.h> 4247578Sdfr 4347578Sdfr#include "tftp-utils.h" 4447578Sdfr#include "tftp-io.h" 4547578Sdfr#include "tftp-options.h" 4647578Sdfr 4747578Sdfr/* 4847578Sdfr * Option handlers 4947578Sdfr */ 5047578Sdfr 5147578Sdfrstruct options options[] = { 5266840Smsmith { "tsize", NULL, NULL, NULL /* option_tsize */, 1 }, 5366840Smsmith { "timeout", NULL, NULL, option_timeout, 1 }, 5466840Smsmith { "blksize", NULL, NULL, option_blksize, 1 }, 5557368Sgj { "blksize2", NULL, NULL, option_blksize2, 0 }, 5666840Smsmith { "rollover", NULL, NULL, option_rollover, 0 }, 5766840Smsmith { NULL, NULL, NULL, NULL, 0 } 5866840Smsmith}; 5941181Sdfr 6066840Smsmith/* By default allow them */ 6166840Smsmithint options_rfc_enabled = 1; 6266840Smsmithint options_extra_enabled = 1; 6366840Smsmith 6466840Smsmith/* 6566840Smsmith * Rules for the option handlers: 6666840Smsmith * - If there is no o_request, there will be no processing. 6766840Smsmith * 6857973Sphk * For servers 6957973Sphk * - Logging is done as warnings. 7057973Sphk * - The handler exit()s if there is a serious problem with the 7150769Sdfr * values submitted in the option. 7250769Sdfr * 7350769Sdfr * For clients 7450769Sdfr * - Logging is done as errors. After all, the server shouldn't 7550769Sdfr * return rubbish. 7650769Sdfr * - The handler returns if there is a serious problem with the 7750769Sdfr * values submitted in the option. 7850769Sdfr * - Sending the EBADOP packets is done by the handler. 7950769Sdfr */ 8050769Sdfr 8150769Sdfrint 8250769Sdfroption_tsize(int peer __unused, struct tftphdr *tp __unused, int mode, 8350769Sdfr struct stat *stbuf) 8450769Sdfr{ 8550769Sdfr 8650769Sdfr if (options[OPT_TSIZE].o_request == NULL) 8750769Sdfr return (0); 8850769Sdfr 8950769Sdfr if (mode == RRQ) 9050769Sdfr asprintf(&options[OPT_TSIZE].o_reply, 9150769Sdfr "%ju", stbuf->st_size); 9250769Sdfr else 9350769Sdfr /* XXX Allows writes of all sizes. */ 9450769Sdfr options[OPT_TSIZE].o_reply = 9550769Sdfr strdup(options[OPT_TSIZE].o_request); 9650769Sdfr return (0); 9750769Sdfr} 9850769Sdfr 9950769Sdfrint 10050769Sdfroption_timeout(int peer) 10150769Sdfr{ 10250769Sdfr 10350769Sdfr if (options[OPT_TIMEOUT].o_request == NULL) 10438136Sdfr return (0); 10541181Sdfr 10641181Sdfr int to = atoi(options[OPT_TIMEOUT].o_request); 10741181Sdfr if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) { 10841181Sdfr tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING, 10941181Sdfr "Received bad value for timeout. " 11041181Sdfr "Should be between %d and %d, received %s", 11141181Sdfr TIMEOUT_MIN, TIMEOUT_MAX); 11241181Sdfr send_error(peer, EBADOP); 11341181Sdfr if (acting_as_client) 114105139Sjhb return (1); 115105139Sjhb exit(1); 116105139Sjhb } else { 11741181Sdfr timeoutpacket = to; 11841181Sdfr options[OPT_TIMEOUT].o_reply = 11941181Sdfr strdup(options[OPT_TIMEOUT].o_request); 12041181Sdfr } 12141181Sdfr settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts); 12247613Sdfr 12347613Sdfr if (debug&DEBUG_OPTIONS) 12447613Sdfr tftp_log(LOG_DEBUG, "Setting timeout to '%s'", 12547613Sdfr options[OPT_TIMEOUT].o_reply); 12682863Syokota 12782863Syokota return (0); 12838136Sdfr} 12938136Sdfr 13038136Sdfrint 13183051Syokotaoption_rollover(int peer) 13283051Syokota{ 13383051Syokota 13483051Syokota if (options[OPT_ROLLOVER].o_request == NULL) 13583051Syokota return (0); 13683051Syokota 13783051Syokota if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0 13838136Sdfr && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) { 13938136Sdfr tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING, 140105139Sjhb "Bad value for rollover, " 141105139Sjhb "should be either 0 or 1, received '%s', " 14238136Sdfr "ignoring request", 14338136Sdfr options[OPT_ROLLOVER].o_request); 14438136Sdfr if (acting_as_client) { 14538136Sdfr send_error(peer, EBADOP); 14645720Speter return (1); 14745720Speter } 148105139Sjhb return (0); 14947613Sdfr } 15047613Sdfr options[OPT_ROLLOVER].o_reply = 15147613Sdfr strdup(options[OPT_ROLLOVER].o_request); 15247613Sdfr 15382863Syokota if (debug&DEBUG_OPTIONS) 15447613Sdfr tftp_log(LOG_DEBUG, "Setting rollover to '%s'", 155117337Sjhb options[OPT_ROLLOVER].o_reply); 156117337Sjhb 157117337Sjhb return (0); 15850769Sdfr} 15950769Sdfr 16047613Sdfrint 16192756Salfredoption_blksize(int peer) 16292756Salfred{ 163135262Sphk u_long maxdgram; 16492756Salfred size_t len; 16592756Salfred 16692756Salfred if (options[OPT_BLKSIZE].o_request == NULL) 16792756Salfred return (0); 16892756Salfred 16950769Sdfr /* maximum size of an UDP packet according to the system */ 170135262Sphk len = sizeof(maxdgram); 171135262Sphk if (sysctlbyname("net.inet.udp.maxdgram", 172135262Sphk &maxdgram, &len, NULL, 0) < 0) { 173135262Sphk tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram"); 174135262Sphk return (acting_as_client ? 1 : 0); 175135262Sphk } 176117337Sjhb 177117337Sjhb int size = atoi(options[OPT_BLKSIZE].o_request); 17867442Snyan if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { 17967442Snyan if (acting_as_client) { 18067442Snyan tftp_log(LOG_ERR, 18167442Snyan "Invalid blocksize (%d bytes), aborting", 18267442Snyan size); 18367442Snyan send_error(peer, EBADOP); 18467442Snyan return (1); 18567442Snyan } else { 18667442Snyan tftp_log(LOG_WARNING, 18767442Snyan "Invalid blocksize (%d bytes), ignoring request", 18867442Snyan size); 18967442Snyan return (0); 19067442Snyan } 19155206Speter } 19247613Sdfr 19347613Sdfr if (size > (int)maxdgram) { 194 if (acting_as_client) { 195 tftp_log(LOG_ERR, 196 "Invalid blocksize (%d bytes), " 197 "net.inet.udp.maxdgram sysctl limits it to " 198 "%d bytes.\n", size, maxdgram); 199 send_error(peer, EBADOP); 200 return (1); 201 } else { 202 tftp_log(LOG_WARNING, 203 "Invalid blocksize (%d bytes), " 204 "net.inet.udp.maxdgram sysctl limits it to " 205 "%d bytes.\n", size, maxdgram); 206 size = maxdgram; 207 /* No reason to return */ 208 } 209 } 210 211 asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size); 212 segsize = size; 213 pktsize = size + 4; 214 if (debug&DEBUG_OPTIONS) 215 tftp_log(LOG_DEBUG, "Setting blksize to '%s'", 216 options[OPT_BLKSIZE].o_reply); 217 218 return (0); 219} 220 221int 222option_blksize2(int peer __unused) 223{ 224 u_long maxdgram; 225 int size, i; 226 size_t len; 227 228 int sizes[] = { 229 8, 16, 32, 64, 128, 256, 512, 1024, 230 2048, 4096, 8192, 16384, 32768, 0 231 }; 232 233 if (options[OPT_BLKSIZE2].o_request == NULL) 234 return (0); 235 236 /* maximum size of an UDP packet according to the system */ 237 len = sizeof(maxdgram); 238 if (sysctlbyname("net.inet.udp.maxdgram", 239 &maxdgram, &len, NULL, 0) < 0) { 240 tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram"); 241 return (acting_as_client ? 1 : 0); 242 } 243 244 size = atoi(options[OPT_BLKSIZE2].o_request); 245 for (i = 0; sizes[i] != 0; i++) { 246 if (size == sizes[i]) break; 247 } 248 if (sizes[i] == 0) { 249 tftp_log(LOG_INFO, 250 "Invalid blocksize2 (%d bytes), ignoring request", size); 251 return (acting_as_client ? 1 : 0); 252 } 253 254 if (size > (int)maxdgram) { 255 for (i = 0; sizes[i+1] != 0; i++) { 256 if ((int)maxdgram < sizes[i+1]) break; 257 } 258 tftp_log(LOG_INFO, 259 "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram " 260 "sysctl limits it to %d bytes.\n", size, maxdgram); 261 size = sizes[i]; 262 /* No need to return */ 263 } 264 265 asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size); 266 segsize = size; 267 pktsize = size + 4; 268 if (debug&DEBUG_OPTIONS) 269 tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'", 270 options[OPT_BLKSIZE2].o_reply); 271 272 return (0); 273} 274 275/* 276 * Append the available options to the header 277 */ 278uint16_t 279make_options(int peer __unused, char *buffer, uint16_t size) { 280 int i; 281 char *value; 282 const char *option; 283 uint16_t length; 284 uint16_t returnsize = 0; 285 286 if (!options_rfc_enabled) return (0); 287 288 for (i = 0; options[i].o_type != NULL; i++) { 289 if (options[i].rfc == 0 && !options_extra_enabled) 290 continue; 291 292 option = options[i].o_type; 293 if (acting_as_client) 294 value = options[i].o_request; 295 else 296 value = options[i].o_reply; 297 if (value == NULL) 298 continue; 299 300 length = strlen(value) + strlen(option) + 2; 301 if (size <= length) { 302 tftp_log(LOG_ERR, 303 "Running out of option space for " 304 "option '%s' with value '%s': " 305 "needed %d bytes, got %d bytes", 306 option, value, size, length); 307 continue; 308 } 309 310 sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000'); 311 size -= length; 312 buffer += length; 313 returnsize += length; 314 } 315 316 return (returnsize); 317} 318 319/* 320 * Parse the received options in the header 321 */ 322int 323parse_options(int peer, char *buffer, uint16_t size) 324{ 325 int i, options_failed; 326 char *c, *cp, *option, *value; 327 328 if (!options_rfc_enabled) return (0); 329 330 /* Parse the options */ 331 cp = buffer; 332 options_failed = 0; 333 while (size > 0) { 334 option = cp; 335 i = get_field(peer, cp, size); 336 cp += i; 337 338 value = cp; 339 i = get_field(peer, cp, size); 340 cp += i; 341 342 /* We are at the end */ 343 if (*option == '\0') break; 344 345 if (debug&DEBUG_OPTIONS) 346 tftp_log(LOG_DEBUG, 347 "option: '%s' value: '%s'", option, value); 348 349 for (c = option; *c; c++) 350 if (isupper(*c)) 351 *c = tolower(*c); 352 for (i = 0; options[i].o_type != NULL; i++) { 353 if (strcmp(option, options[i].o_type) == 0) { 354 if (!acting_as_client) 355 options[i].o_request = value; 356 if (!options_extra_enabled && !options[i].rfc) { 357 tftp_log(LOG_INFO, 358 "Option '%s' with value '%s' found " 359 "but it is not an RFC option", 360 option, value); 361 continue; 362 } 363 if (options[i].o_handler) 364 options_failed += 365 (options[i].o_handler)(peer); 366 break; 367 } 368 } 369 if (options[i].o_type == NULL) 370 tftp_log(LOG_WARNING, 371 "Unknown option: '%s'", option); 372 373 size -= strlen(option) + strlen(value) + 2; 374 } 375 376 return (options_failed); 377} 378 379/* 380 * Set some default values in the options 381 */ 382void 383init_options(void) 384{ 385 386 options[OPT_ROLLOVER].o_request = strdup("0"); 387} 388