options.c revision 149399
1147072Sbrooks/* $OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $ */ 2147072Sbrooks 3147072Sbrooks/* DHCP options parsing and reassembly. */ 4147072Sbrooks 5147072Sbrooks/* 6147072Sbrooks * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. 7147072Sbrooks * All rights reserved. 8147072Sbrooks * 9147072Sbrooks * Redistribution and use in source and binary forms, with or without 10147072Sbrooks * modification, are permitted provided that the following conditions 11147072Sbrooks * are met: 12147072Sbrooks * 13147072Sbrooks * 1. Redistributions of source code must retain the above copyright 14147072Sbrooks * notice, this list of conditions and the following disclaimer. 15147072Sbrooks * 2. Redistributions in binary form must reproduce the above copyright 16147072Sbrooks * notice, this list of conditions and the following disclaimer in the 17147072Sbrooks * documentation and/or other materials provided with the distribution. 18147072Sbrooks * 3. Neither the name of The Internet Software Consortium nor the names 19147072Sbrooks * of its contributors may be used to endorse or promote products derived 20147072Sbrooks * from this software without specific prior written permission. 21147072Sbrooks * 22147072Sbrooks * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 23147072Sbrooks * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 24147072Sbrooks * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25147072Sbrooks * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26147072Sbrooks * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 27147072Sbrooks * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28147072Sbrooks * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29147072Sbrooks * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 30147072Sbrooks * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31147072Sbrooks * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32147072Sbrooks * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33147072Sbrooks * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34147072Sbrooks * SUCH DAMAGE. 35147072Sbrooks * 36147072Sbrooks * This software has been written for the Internet Software Consortium 37147072Sbrooks * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 38147072Sbrooks * Enterprises. To learn more about the Internet Software Consortium, 39147072Sbrooks * see ``http://www.vix.com/isc''. To learn more about Vixie 40147072Sbrooks * Enterprises, see ``http://www.vix.com''. 41147072Sbrooks */ 42147072Sbrooks 43149399Sbrooks#include <sys/cdefs.h> 44149399Sbrooks__FBSDID("$FreeBSD: head/sbin/dhclient/options.c 149399 2005-08-23 23:59:55Z brooks $"); 45149399Sbrooks 46147072Sbrooks#include <ctype.h> 47147072Sbrooks 48147072Sbrooks#define DHCP_OPTION_DATA 49147072Sbrooks#include "dhcpd.h" 50147072Sbrooks 51147072Sbrooksint bad_options = 0; 52147072Sbrooksint bad_options_max = 5; 53147072Sbrooks 54147072Sbrooksvoid parse_options(struct packet *); 55147072Sbrooksvoid parse_option_buffer(struct packet *, unsigned char *, int); 56147072Sbrooksint store_options(unsigned char *, int, struct tree_cache **, 57147072Sbrooks unsigned char *, int, int, int, int); 58147072Sbrooks 59147072Sbrooks 60147072Sbrooks/* 61147072Sbrooks * Parse all available options out of the specified packet. 62147072Sbrooks */ 63147072Sbrooksvoid 64147072Sbrooksparse_options(struct packet *packet) 65147072Sbrooks{ 66147072Sbrooks /* Initially, zero all option pointers. */ 67147072Sbrooks memset(packet->options, 0, sizeof(packet->options)); 68147072Sbrooks 69147072Sbrooks /* If we don't see the magic cookie, there's nothing to parse. */ 70147072Sbrooks if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) { 71147072Sbrooks packet->options_valid = 0; 72147072Sbrooks return; 73147072Sbrooks } 74147072Sbrooks 75147072Sbrooks /* 76147072Sbrooks * Go through the options field, up to the end of the packet or 77147072Sbrooks * the End field. 78147072Sbrooks */ 79147072Sbrooks parse_option_buffer(packet, &packet->raw->options[4], 80147072Sbrooks packet->packet_length - DHCP_FIXED_NON_UDP - 4); 81147072Sbrooks 82147072Sbrooks /* 83147072Sbrooks * If we parsed a DHCP Option Overload option, parse more 84147072Sbrooks * options out of the buffer(s) containing them. 85147072Sbrooks */ 86147072Sbrooks if (packet->options_valid && 87147072Sbrooks packet->options[DHO_DHCP_OPTION_OVERLOAD].data) { 88147072Sbrooks if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) 89147072Sbrooks parse_option_buffer(packet, 90147072Sbrooks (unsigned char *)packet->raw->file, 91147072Sbrooks sizeof(packet->raw->file)); 92147072Sbrooks if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) 93147072Sbrooks parse_option_buffer(packet, 94147072Sbrooks (unsigned char *)packet->raw->sname, 95147072Sbrooks sizeof(packet->raw->sname)); 96147072Sbrooks } 97147072Sbrooks} 98147072Sbrooks 99147072Sbrooks/* 100147072Sbrooks * Parse options out of the specified buffer, storing addresses of 101147072Sbrooks * option values in packet->options and setting packet->options_valid if 102147072Sbrooks * no errors are encountered. 103147072Sbrooks */ 104147072Sbrooksvoid 105147072Sbrooksparse_option_buffer(struct packet *packet, 106147072Sbrooks unsigned char *buffer, int length) 107147072Sbrooks{ 108147072Sbrooks unsigned char *s, *t, *end = buffer + length; 109147072Sbrooks int len, code; 110147072Sbrooks 111147072Sbrooks for (s = buffer; *s != DHO_END && s < end; ) { 112147072Sbrooks code = s[0]; 113147072Sbrooks 114147072Sbrooks /* Pad options don't have a length - just skip them. */ 115147072Sbrooks if (code == DHO_PAD) { 116147072Sbrooks s++; 117147072Sbrooks continue; 118147072Sbrooks } 119147072Sbrooks if (s + 2 > end) { 120147072Sbrooks len = 65536; 121147072Sbrooks goto bogus; 122147072Sbrooks } 123147072Sbrooks 124147072Sbrooks /* 125147072Sbrooks * All other fields (except end, see above) have a 126147072Sbrooks * one-byte length. 127147072Sbrooks */ 128147072Sbrooks len = s[1]; 129147072Sbrooks 130147072Sbrooks /* 131147072Sbrooks * If the length is outrageous, silently skip the rest, 132147072Sbrooks * and mark the packet bad. Unfortunately some crappy 133147072Sbrooks * dhcp servers always seem to give us garbage on the 134147072Sbrooks * end of a packet. so rather than keep refusing, give 135147072Sbrooks * up and try to take one after seeing a few without 136147072Sbrooks * anything good. 137147072Sbrooks */ 138147072Sbrooks if (s + len + 2 > end) { 139147072Sbrooks bogus: 140147072Sbrooks bad_options++; 141147072Sbrooks warning("option %s (%d) %s.", 142147072Sbrooks dhcp_options[code].name, len, 143147072Sbrooks "larger than buffer"); 144147072Sbrooks if (bad_options == bad_options_max) { 145147072Sbrooks packet->options_valid = 1; 146147072Sbrooks bad_options = 0; 147147072Sbrooks warning("Many bogus options seen in offers. " 148147072Sbrooks "Taking this offer in spite of bogus " 149147072Sbrooks "options - hope for the best!"); 150147072Sbrooks } else { 151147072Sbrooks warning("rejecting bogus offer."); 152147072Sbrooks packet->options_valid = 0; 153147072Sbrooks } 154147072Sbrooks return; 155147072Sbrooks } 156147072Sbrooks /* 157147072Sbrooks * If we haven't seen this option before, just make 158147072Sbrooks * space for it and copy it there. 159147072Sbrooks */ 160147072Sbrooks if (!packet->options[code].data) { 161147072Sbrooks if (!(t = calloc(1, len + 1))) 162147072Sbrooks error("Can't allocate storage for option %s.", 163147072Sbrooks dhcp_options[code].name); 164147072Sbrooks /* 165147072Sbrooks * Copy and NUL-terminate the option (in case 166147072Sbrooks * it's an ASCII string. 167147072Sbrooks */ 168147072Sbrooks memcpy(t, &s[2], len); 169147072Sbrooks t[len] = 0; 170147072Sbrooks packet->options[code].len = len; 171147072Sbrooks packet->options[code].data = t; 172147072Sbrooks } else { 173147072Sbrooks /* 174147072Sbrooks * If it's a repeat, concatenate it to whatever 175147072Sbrooks * we last saw. This is really only required 176147072Sbrooks * for clients, but what the heck... 177147072Sbrooks */ 178147072Sbrooks t = calloc(1, len + packet->options[code].len + 1); 179147072Sbrooks if (!t) 180147072Sbrooks error("Can't expand storage for option %s.", 181147072Sbrooks dhcp_options[code].name); 182147072Sbrooks memcpy(t, packet->options[code].data, 183147072Sbrooks packet->options[code].len); 184147072Sbrooks memcpy(t + packet->options[code].len, 185147072Sbrooks &s[2], len); 186147072Sbrooks packet->options[code].len += len; 187147072Sbrooks t[packet->options[code].len] = 0; 188147072Sbrooks free(packet->options[code].data); 189147072Sbrooks packet->options[code].data = t; 190147072Sbrooks } 191147072Sbrooks s += len + 2; 192147072Sbrooks } 193147072Sbrooks packet->options_valid = 1; 194147072Sbrooks} 195147072Sbrooks 196147072Sbrooks/* 197147072Sbrooks * cons options into a big buffer, and then split them out into the 198147072Sbrooks * three separate buffers if needed. This allows us to cons up a set of 199147072Sbrooks * vendor options using the same routine. 200147072Sbrooks */ 201147072Sbrooksint 202147072Sbrookscons_options(struct packet *inpacket, struct dhcp_packet *outpacket, 203147072Sbrooks int mms, struct tree_cache **options, 204147072Sbrooks int overload, /* Overload flags that may be set. */ 205147072Sbrooks int terminate, int bootpp, u_int8_t *prl, int prl_len) 206147072Sbrooks{ 207147072Sbrooks unsigned char priority_list[300], buffer[4096]; 208147072Sbrooks int priority_len, main_buffer_size, mainbufix, bufix; 209147072Sbrooks int option_size, length; 210147072Sbrooks 211147072Sbrooks /* 212147072Sbrooks * If the client has provided a maximum DHCP message size, use 213147072Sbrooks * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use 214147072Sbrooks * up to the minimum IP MTU size (576 bytes). 215147072Sbrooks * 216147072Sbrooks * XXX if a BOOTP client specifies a max message size, we will 217147072Sbrooks * honor it. 218147072Sbrooks */ 219147072Sbrooks if (!mms && 220147072Sbrooks inpacket && 221147072Sbrooks inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data && 222147072Sbrooks (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >= 223147072Sbrooks sizeof(u_int16_t))) 224147072Sbrooks mms = getUShort( 225147072Sbrooks inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data); 226147072Sbrooks 227147072Sbrooks if (mms) 228147072Sbrooks main_buffer_size = mms - DHCP_FIXED_LEN; 229147072Sbrooks else if (bootpp) 230147072Sbrooks main_buffer_size = 64; 231147072Sbrooks else 232147072Sbrooks main_buffer_size = 576 - DHCP_FIXED_LEN; 233147072Sbrooks 234147072Sbrooks if (main_buffer_size > sizeof(buffer)) 235147072Sbrooks main_buffer_size = sizeof(buffer); 236147072Sbrooks 237147072Sbrooks /* Preload the option priority list with mandatory options. */ 238147072Sbrooks priority_len = 0; 239147072Sbrooks priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE; 240147072Sbrooks priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER; 241147072Sbrooks priority_list[priority_len++] = DHO_DHCP_LEASE_TIME; 242147072Sbrooks priority_list[priority_len++] = DHO_DHCP_MESSAGE; 243147072Sbrooks 244147072Sbrooks /* 245147072Sbrooks * If the client has provided a list of options that it wishes 246147072Sbrooks * returned, use it to prioritize. Otherwise, prioritize based 247147072Sbrooks * on the default priority list. 248147072Sbrooks */ 249147072Sbrooks if (inpacket && 250147072Sbrooks inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) { 251147072Sbrooks int prlen = 252147072Sbrooks inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len; 253147072Sbrooks if (prlen + priority_len > sizeof(priority_list)) 254147072Sbrooks prlen = sizeof(priority_list) - priority_len; 255147072Sbrooks 256147072Sbrooks memcpy(&priority_list[priority_len], 257147072Sbrooks inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data, 258147072Sbrooks prlen); 259147072Sbrooks priority_len += prlen; 260147072Sbrooks prl = priority_list; 261147072Sbrooks } else if (prl) { 262147072Sbrooks if (prl_len + priority_len > sizeof(priority_list)) 263147072Sbrooks prl_len = sizeof(priority_list) - priority_len; 264147072Sbrooks 265147072Sbrooks memcpy(&priority_list[priority_len], prl, prl_len); 266147072Sbrooks priority_len += prl_len; 267147072Sbrooks prl = priority_list; 268147072Sbrooks } else { 269147072Sbrooks memcpy(&priority_list[priority_len], 270147072Sbrooks dhcp_option_default_priority_list, 271147072Sbrooks sizeof_dhcp_option_default_priority_list); 272147072Sbrooks priority_len += sizeof_dhcp_option_default_priority_list; 273147072Sbrooks } 274147072Sbrooks 275147072Sbrooks /* Copy the options into the big buffer... */ 276147072Sbrooks option_size = store_options( 277147072Sbrooks buffer, 278147072Sbrooks (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) + 279147072Sbrooks ((overload & 2) ? DHCP_SNAME_LEN : 0)), 280147072Sbrooks options, priority_list, priority_len, main_buffer_size, 281147072Sbrooks (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)), 282147072Sbrooks terminate); 283147072Sbrooks 284147072Sbrooks /* Put the cookie up front... */ 285147072Sbrooks memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4); 286147072Sbrooks mainbufix = 4; 287147072Sbrooks 288147072Sbrooks /* 289147072Sbrooks * If we're going to have to overload, store the overload option 290147072Sbrooks * at the beginning. If we can, though, just store the whole 291147072Sbrooks * thing in the packet's option buffer and leave it at that. 292147072Sbrooks */ 293147072Sbrooks if (option_size <= main_buffer_size - mainbufix) { 294147072Sbrooks memcpy(&outpacket->options[mainbufix], 295147072Sbrooks buffer, option_size); 296147072Sbrooks mainbufix += option_size; 297147072Sbrooks if (mainbufix < main_buffer_size) 298147072Sbrooks outpacket->options[mainbufix++] = DHO_END; 299147072Sbrooks length = DHCP_FIXED_NON_UDP + mainbufix; 300147072Sbrooks } else { 301147072Sbrooks outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD; 302147072Sbrooks outpacket->options[mainbufix++] = 1; 303147072Sbrooks if (option_size > 304147072Sbrooks main_buffer_size - mainbufix + DHCP_FILE_LEN) 305147072Sbrooks outpacket->options[mainbufix++] = 3; 306147072Sbrooks else 307147072Sbrooks outpacket->options[mainbufix++] = 1; 308147072Sbrooks 309147072Sbrooks memcpy(&outpacket->options[mainbufix], 310147072Sbrooks buffer, main_buffer_size - mainbufix); 311147072Sbrooks bufix = main_buffer_size - mainbufix; 312147072Sbrooks length = DHCP_FIXED_NON_UDP + mainbufix; 313147072Sbrooks if (overload & 1) { 314147072Sbrooks if (option_size - bufix <= DHCP_FILE_LEN) { 315147072Sbrooks memcpy(outpacket->file, 316147072Sbrooks &buffer[bufix], option_size - bufix); 317147072Sbrooks mainbufix = option_size - bufix; 318147072Sbrooks if (mainbufix < DHCP_FILE_LEN) 319147072Sbrooks outpacket->file[mainbufix++] = (char)DHO_END; 320147072Sbrooks while (mainbufix < DHCP_FILE_LEN) 321147072Sbrooks outpacket->file[mainbufix++] = (char)DHO_PAD; 322147072Sbrooks } else { 323147072Sbrooks memcpy(outpacket->file, 324147072Sbrooks &buffer[bufix], DHCP_FILE_LEN); 325147072Sbrooks bufix += DHCP_FILE_LEN; 326147072Sbrooks } 327147072Sbrooks } 328147072Sbrooks if ((overload & 2) && option_size < bufix) { 329147072Sbrooks memcpy(outpacket->sname, 330147072Sbrooks &buffer[bufix], option_size - bufix); 331147072Sbrooks 332147072Sbrooks mainbufix = option_size - bufix; 333147072Sbrooks if (mainbufix < DHCP_SNAME_LEN) 334147072Sbrooks outpacket->file[mainbufix++] = (char)DHO_END; 335147072Sbrooks while (mainbufix < DHCP_SNAME_LEN) 336147072Sbrooks outpacket->file[mainbufix++] = (char)DHO_PAD; 337147072Sbrooks } 338147072Sbrooks } 339147072Sbrooks return (length); 340147072Sbrooks} 341147072Sbrooks 342147072Sbrooks/* 343147072Sbrooks * Store all the requested options into the requested buffer. 344147072Sbrooks */ 345147072Sbrooksint 346147072Sbrooksstore_options(unsigned char *buffer, int buflen, struct tree_cache **options, 347147072Sbrooks unsigned char *priority_list, int priority_len, int first_cutoff, 348147072Sbrooks int second_cutoff, int terminate) 349147072Sbrooks{ 350147072Sbrooks int bufix = 0, option_stored[256], i, ix, tto; 351147072Sbrooks 352147072Sbrooks /* Zero out the stored-lengths array. */ 353147072Sbrooks memset(option_stored, 0, sizeof(option_stored)); 354147072Sbrooks 355147072Sbrooks /* 356147072Sbrooks * Copy out the options in the order that they appear in the 357147072Sbrooks * priority list... 358147072Sbrooks */ 359147072Sbrooks for (i = 0; i < priority_len; i++) { 360147072Sbrooks /* Code for next option to try to store. */ 361147072Sbrooks int code = priority_list[i]; 362147072Sbrooks int optstart; 363147072Sbrooks 364147072Sbrooks /* 365147072Sbrooks * Number of bytes left to store (some may already have 366147072Sbrooks * been stored by a previous pass). 367147072Sbrooks */ 368147072Sbrooks int length; 369147072Sbrooks 370147072Sbrooks /* If no data is available for this option, skip it. */ 371147072Sbrooks if (!options[code]) { 372147072Sbrooks continue; 373147072Sbrooks } 374147072Sbrooks 375147072Sbrooks /* 376147072Sbrooks * The client could ask for things that are mandatory, 377147072Sbrooks * in which case we should avoid storing them twice... 378147072Sbrooks */ 379147072Sbrooks if (option_stored[code]) 380147072Sbrooks continue; 381147072Sbrooks option_stored[code] = 1; 382147072Sbrooks 383147072Sbrooks /* We should now have a constant length for the option. */ 384147072Sbrooks length = options[code]->len; 385147072Sbrooks 386147072Sbrooks /* Do we add a NUL? */ 387147072Sbrooks if (terminate && dhcp_options[code].format[0] == 't') { 388147072Sbrooks length++; 389147072Sbrooks tto = 1; 390147072Sbrooks } else 391147072Sbrooks tto = 0; 392147072Sbrooks 393147072Sbrooks /* Try to store the option. */ 394147072Sbrooks 395147072Sbrooks /* 396147072Sbrooks * If the option's length is more than 255, we must 397147072Sbrooks * store it in multiple hunks. Store 255-byte hunks 398147072Sbrooks * first. However, in any case, if the option data will 399147072Sbrooks * cross a buffer boundary, split it across that 400147072Sbrooks * boundary. 401147072Sbrooks */ 402147072Sbrooks ix = 0; 403147072Sbrooks 404147072Sbrooks optstart = bufix; 405147072Sbrooks while (length) { 406147072Sbrooks unsigned char incr = length > 255 ? 255 : length; 407147072Sbrooks 408147072Sbrooks /* 409147072Sbrooks * If this hunk of the buffer will cross a 410147072Sbrooks * boundary, only go up to the boundary in this 411147072Sbrooks * pass. 412147072Sbrooks */ 413147072Sbrooks if (bufix < first_cutoff && 414147072Sbrooks bufix + incr > first_cutoff) 415147072Sbrooks incr = first_cutoff - bufix; 416147072Sbrooks else if (bufix < second_cutoff && 417147072Sbrooks bufix + incr > second_cutoff) 418147072Sbrooks incr = second_cutoff - bufix; 419147072Sbrooks 420147072Sbrooks /* 421147072Sbrooks * If this option is going to overflow the 422147072Sbrooks * buffer, skip it. 423147072Sbrooks */ 424147072Sbrooks if (bufix + 2 + incr > buflen) { 425147072Sbrooks bufix = optstart; 426147072Sbrooks break; 427147072Sbrooks } 428147072Sbrooks 429147072Sbrooks /* Everything looks good - copy it in! */ 430147072Sbrooks buffer[bufix] = code; 431147072Sbrooks buffer[bufix + 1] = incr; 432147072Sbrooks if (tto && incr == length) { 433147072Sbrooks memcpy(buffer + bufix + 2, 434147072Sbrooks options[code]->value + ix, incr - 1); 435147072Sbrooks buffer[bufix + 2 + incr - 1] = 0; 436147072Sbrooks } else 437147072Sbrooks memcpy(buffer + bufix + 2, 438147072Sbrooks options[code]->value + ix, incr); 439147072Sbrooks length -= incr; 440147072Sbrooks ix += incr; 441147072Sbrooks bufix += 2 + incr; 442147072Sbrooks } 443147072Sbrooks } 444147072Sbrooks return (bufix); 445147072Sbrooks} 446147072Sbrooks 447147072Sbrooks/* 448147072Sbrooks * Format the specified option so that a human can easily read it. 449147072Sbrooks */ 450147072Sbrookschar * 451147072Sbrookspretty_print_option(unsigned int code, unsigned char *data, int len, 452147072Sbrooks int emit_commas, int emit_quotes) 453147072Sbrooks{ 454147072Sbrooks static char optbuf[32768]; /* XXX */ 455147072Sbrooks int hunksize = 0, numhunk = -1, numelem = 0; 456147072Sbrooks char fmtbuf[32], *op = optbuf; 457147072Sbrooks int i, j, k, opleft = sizeof(optbuf); 458147072Sbrooks unsigned char *dp = data; 459147072Sbrooks struct in_addr foo; 460147072Sbrooks char comma; 461147072Sbrooks 462147072Sbrooks /* Code should be between 0 and 255. */ 463147072Sbrooks if (code > 255) 464147072Sbrooks error("pretty_print_option: bad code %d", code); 465147072Sbrooks 466147072Sbrooks if (emit_commas) 467147072Sbrooks comma = ','; 468147072Sbrooks else 469147072Sbrooks comma = ' '; 470147072Sbrooks 471147072Sbrooks /* Figure out the size of the data. */ 472147072Sbrooks for (i = 0; dhcp_options[code].format[i]; i++) { 473147072Sbrooks if (!numhunk) { 474147072Sbrooks warning("%s: Excess information in format string: %s", 475147072Sbrooks dhcp_options[code].name, 476147072Sbrooks &(dhcp_options[code].format[i])); 477147072Sbrooks break; 478147072Sbrooks } 479147072Sbrooks numelem++; 480147072Sbrooks fmtbuf[i] = dhcp_options[code].format[i]; 481147072Sbrooks switch (dhcp_options[code].format[i]) { 482147072Sbrooks case 'A': 483147072Sbrooks --numelem; 484147072Sbrooks fmtbuf[i] = 0; 485147072Sbrooks numhunk = 0; 486147072Sbrooks break; 487147072Sbrooks case 'X': 488147072Sbrooks for (k = 0; k < len; k++) 489147072Sbrooks if (!isascii(data[k]) || 490147072Sbrooks !isprint(data[k])) 491147072Sbrooks break; 492147072Sbrooks if (k == len) { 493147072Sbrooks fmtbuf[i] = 't'; 494147072Sbrooks numhunk = -2; 495147072Sbrooks } else { 496147072Sbrooks fmtbuf[i] = 'x'; 497147072Sbrooks hunksize++; 498147072Sbrooks comma = ':'; 499147072Sbrooks numhunk = 0; 500147072Sbrooks } 501147072Sbrooks fmtbuf[i + 1] = 0; 502147072Sbrooks break; 503147072Sbrooks case 't': 504147072Sbrooks fmtbuf[i] = 't'; 505147072Sbrooks fmtbuf[i + 1] = 0; 506147072Sbrooks numhunk = -2; 507147072Sbrooks break; 508147072Sbrooks case 'I': 509147072Sbrooks case 'l': 510147072Sbrooks case 'L': 511147072Sbrooks hunksize += 4; 512147072Sbrooks break; 513147072Sbrooks case 's': 514147072Sbrooks case 'S': 515147072Sbrooks hunksize += 2; 516147072Sbrooks break; 517147072Sbrooks case 'b': 518147072Sbrooks case 'B': 519147072Sbrooks case 'f': 520147072Sbrooks hunksize++; 521147072Sbrooks break; 522147072Sbrooks case 'e': 523147072Sbrooks break; 524147072Sbrooks default: 525147072Sbrooks warning("%s: garbage in format string: %s", 526147072Sbrooks dhcp_options[code].name, 527147072Sbrooks &(dhcp_options[code].format[i])); 528147072Sbrooks break; 529147072Sbrooks } 530147072Sbrooks } 531147072Sbrooks 532147072Sbrooks /* Check for too few bytes... */ 533147072Sbrooks if (hunksize > len) { 534147072Sbrooks warning("%s: expecting at least %d bytes; got %d", 535147072Sbrooks dhcp_options[code].name, hunksize, len); 536147072Sbrooks return ("<error>"); 537147072Sbrooks } 538147072Sbrooks /* Check for too many bytes... */ 539147072Sbrooks if (numhunk == -1 && hunksize < len) 540147072Sbrooks warning("%s: %d extra bytes", 541147072Sbrooks dhcp_options[code].name, len - hunksize); 542147072Sbrooks 543147072Sbrooks /* If this is an array, compute its size. */ 544147072Sbrooks if (!numhunk) 545147072Sbrooks numhunk = len / hunksize; 546147072Sbrooks /* See if we got an exact number of hunks. */ 547147072Sbrooks if (numhunk > 0 && numhunk * hunksize < len) 548147072Sbrooks warning("%s: %d extra bytes at end of array", 549147072Sbrooks dhcp_options[code].name, len - numhunk * hunksize); 550147072Sbrooks 551147072Sbrooks /* A one-hunk array prints the same as a single hunk. */ 552147072Sbrooks if (numhunk < 0) 553147072Sbrooks numhunk = 1; 554147072Sbrooks 555147072Sbrooks /* Cycle through the array (or hunk) printing the data. */ 556147072Sbrooks for (i = 0; i < numhunk; i++) { 557147072Sbrooks for (j = 0; j < numelem; j++) { 558147072Sbrooks int opcount; 559147072Sbrooks switch (fmtbuf[j]) { 560147072Sbrooks case 't': 561147072Sbrooks if (emit_quotes) { 562147072Sbrooks *op++ = '"'; 563147072Sbrooks opleft--; 564147072Sbrooks } 565147072Sbrooks for (; dp < data + len; dp++) { 566147072Sbrooks if (!isascii(*dp) || 567147072Sbrooks !isprint(*dp)) { 568147072Sbrooks if (dp + 1 != data + len || 569147072Sbrooks *dp != 0) { 570147072Sbrooks snprintf(op, opleft, 571147072Sbrooks "\\%03o", *dp); 572147072Sbrooks op += 4; 573147072Sbrooks opleft -= 4; 574147072Sbrooks } 575147072Sbrooks } else if (*dp == '"' || 576147072Sbrooks *dp == '\'' || 577147072Sbrooks *dp == '$' || 578147072Sbrooks *dp == '`' || 579147072Sbrooks *dp == '\\') { 580147072Sbrooks *op++ = '\\'; 581147072Sbrooks *op++ = *dp; 582147072Sbrooks opleft -= 2; 583147072Sbrooks } else { 584147072Sbrooks *op++ = *dp; 585147072Sbrooks opleft--; 586147072Sbrooks } 587147072Sbrooks } 588147072Sbrooks if (emit_quotes) { 589147072Sbrooks *op++ = '"'; 590147072Sbrooks opleft--; 591147072Sbrooks } 592147072Sbrooks 593147072Sbrooks *op = 0; 594147072Sbrooks break; 595147072Sbrooks case 'I': 596147072Sbrooks foo.s_addr = htonl(getULong(dp)); 597147072Sbrooks opcount = strlcpy(op, inet_ntoa(foo), opleft); 598147072Sbrooks if (opcount >= opleft) 599147072Sbrooks goto toobig; 600147072Sbrooks opleft -= opcount; 601147072Sbrooks dp += 4; 602147072Sbrooks break; 603147072Sbrooks case 'l': 604147072Sbrooks opcount = snprintf(op, opleft, "%ld", 605147072Sbrooks (long)getLong(dp)); 606147072Sbrooks if (opcount >= opleft || opcount == -1) 607147072Sbrooks goto toobig; 608147072Sbrooks opleft -= opcount; 609147072Sbrooks dp += 4; 610147072Sbrooks break; 611147072Sbrooks case 'L': 612147072Sbrooks opcount = snprintf(op, opleft, "%ld", 613147072Sbrooks (unsigned long)getULong(dp)); 614147072Sbrooks if (opcount >= opleft || opcount == -1) 615147072Sbrooks goto toobig; 616147072Sbrooks opleft -= opcount; 617147072Sbrooks dp += 4; 618147072Sbrooks break; 619147072Sbrooks case 's': 620147072Sbrooks opcount = snprintf(op, opleft, "%d", 621147072Sbrooks getShort(dp)); 622147072Sbrooks if (opcount >= opleft || opcount == -1) 623147072Sbrooks goto toobig; 624147072Sbrooks opleft -= opcount; 625147072Sbrooks dp += 2; 626147072Sbrooks break; 627147072Sbrooks case 'S': 628147072Sbrooks opcount = snprintf(op, opleft, "%d", 629147072Sbrooks getUShort(dp)); 630147072Sbrooks if (opcount >= opleft || opcount == -1) 631147072Sbrooks goto toobig; 632147072Sbrooks opleft -= opcount; 633147072Sbrooks dp += 2; 634147072Sbrooks break; 635147072Sbrooks case 'b': 636147072Sbrooks opcount = snprintf(op, opleft, "%d", 637147072Sbrooks *(char *)dp++); 638147072Sbrooks if (opcount >= opleft || opcount == -1) 639147072Sbrooks goto toobig; 640147072Sbrooks opleft -= opcount; 641147072Sbrooks break; 642147072Sbrooks case 'B': 643147072Sbrooks opcount = snprintf(op, opleft, "%d", *dp++); 644147072Sbrooks if (opcount >= opleft || opcount == -1) 645147072Sbrooks goto toobig; 646147072Sbrooks opleft -= opcount; 647147072Sbrooks break; 648147072Sbrooks case 'x': 649147072Sbrooks opcount = snprintf(op, opleft, "%x", *dp++); 650147072Sbrooks if (opcount >= opleft || opcount == -1) 651147072Sbrooks goto toobig; 652147072Sbrooks opleft -= opcount; 653147072Sbrooks break; 654147072Sbrooks case 'f': 655147072Sbrooks opcount = strlcpy(op, 656147072Sbrooks *dp++ ? "true" : "false", opleft); 657147072Sbrooks if (opcount >= opleft) 658147072Sbrooks goto toobig; 659147072Sbrooks opleft -= opcount; 660147072Sbrooks break; 661147072Sbrooks default: 662147072Sbrooks warning("Unexpected format code %c", fmtbuf[j]); 663147072Sbrooks } 664147072Sbrooks op += strlen(op); 665147072Sbrooks opleft -= strlen(op); 666147072Sbrooks if (opleft < 1) 667147072Sbrooks goto toobig; 668147072Sbrooks if (j + 1 < numelem && comma != ':') { 669147072Sbrooks *op++ = ' '; 670147072Sbrooks opleft--; 671147072Sbrooks } 672147072Sbrooks } 673147072Sbrooks if (i + 1 < numhunk) { 674147072Sbrooks *op++ = comma; 675147072Sbrooks opleft--; 676147072Sbrooks } 677147072Sbrooks if (opleft < 1) 678147072Sbrooks goto toobig; 679147072Sbrooks 680147072Sbrooks } 681147072Sbrooks return (optbuf); 682147072Sbrooks toobig: 683147072Sbrooks warning("dhcp option too large"); 684147072Sbrooks return ("<error>"); 685147072Sbrooks} 686147072Sbrooks 687147072Sbrooksvoid 688147072Sbrooksdo_packet(struct interface_info *interface, struct dhcp_packet *packet, 689147072Sbrooks int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom) 690147072Sbrooks{ 691147072Sbrooks struct packet tp; 692147072Sbrooks int i; 693147072Sbrooks 694147072Sbrooks if (packet->hlen > sizeof(packet->chaddr)) { 695147072Sbrooks note("Discarding packet with invalid hlen."); 696147072Sbrooks return; 697147072Sbrooks } 698147072Sbrooks 699147072Sbrooks memset(&tp, 0, sizeof(tp)); 700147072Sbrooks tp.raw = packet; 701147072Sbrooks tp.packet_length = len; 702147072Sbrooks tp.client_port = from_port; 703147072Sbrooks tp.client_addr = from; 704147072Sbrooks tp.interface = interface; 705147072Sbrooks tp.haddr = hfrom; 706147072Sbrooks 707147072Sbrooks parse_options(&tp); 708147072Sbrooks if (tp.options_valid && 709147072Sbrooks tp.options[DHO_DHCP_MESSAGE_TYPE].data) 710147072Sbrooks tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0]; 711147072Sbrooks if (tp.packet_type) 712147072Sbrooks dhcp(&tp); 713147072Sbrooks else 714147072Sbrooks bootp(&tp); 715147072Sbrooks 716147072Sbrooks /* Free the data associated with the options. */ 717147072Sbrooks for (i = 0; i < 256; i++) 718147072Sbrooks if (tp.options[i].len && tp.options[i].data) 719147072Sbrooks free(tp.options[i].data); 720147072Sbrooks} 721