1*9a2590e5Sderaadt /* options.c 2*9a2590e5Sderaadt 3*9a2590e5Sderaadt DHCP options parsing and reassembly. */ 4*9a2590e5Sderaadt 5*9a2590e5Sderaadt /* 6*9a2590e5Sderaadt * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. 7*9a2590e5Sderaadt * All rights reserved. 8*9a2590e5Sderaadt * 9*9a2590e5Sderaadt * Redistribution and use in source and binary forms, with or without 10*9a2590e5Sderaadt * modification, are permitted provided that the following conditions 11*9a2590e5Sderaadt * are met: 12*9a2590e5Sderaadt * 13*9a2590e5Sderaadt * 1. Redistributions of source code must retain the above copyright 14*9a2590e5Sderaadt * notice, this list of conditions and the following disclaimer. 15*9a2590e5Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 16*9a2590e5Sderaadt * notice, this list of conditions and the following disclaimer in the 17*9a2590e5Sderaadt * documentation and/or other materials provided with the distribution. 18*9a2590e5Sderaadt * 3. Neither the name of The Internet Software Consortium nor the names 19*9a2590e5Sderaadt * of its contributors may be used to endorse or promote products derived 20*9a2590e5Sderaadt * from this software without specific prior written permission. 21*9a2590e5Sderaadt * 22*9a2590e5Sderaadt * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 23*9a2590e5Sderaadt * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 24*9a2590e5Sderaadt * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25*9a2590e5Sderaadt * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26*9a2590e5Sderaadt * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 27*9a2590e5Sderaadt * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28*9a2590e5Sderaadt * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29*9a2590e5Sderaadt * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 30*9a2590e5Sderaadt * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31*9a2590e5Sderaadt * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32*9a2590e5Sderaadt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33*9a2590e5Sderaadt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34*9a2590e5Sderaadt * SUCH DAMAGE. 35*9a2590e5Sderaadt * 36*9a2590e5Sderaadt * This software has been written for the Internet Software Consortium 37*9a2590e5Sderaadt * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 38*9a2590e5Sderaadt * Enterprises. To learn more about the Internet Software Consortium, 39*9a2590e5Sderaadt * see ``http://www.vix.com/isc''. To learn more about Vixie 40*9a2590e5Sderaadt * Enterprises, see ``http://www.vix.com''. 41*9a2590e5Sderaadt */ 42*9a2590e5Sderaadt 43*9a2590e5Sderaadt #define DHCP_OPTION_DATA 44*9a2590e5Sderaadt #include "dhcpd.h" 45*9a2590e5Sderaadt #include <ctype.h> 46*9a2590e5Sderaadt 47*9a2590e5Sderaadt int bad_options = 0; 48*9a2590e5Sderaadt int bad_options_max = 5; 49*9a2590e5Sderaadt 50*9a2590e5Sderaadt /* Parse all available options out of the specified packet. */ 51*9a2590e5Sderaadt 52*9a2590e5Sderaadt void parse_options (packet) 53*9a2590e5Sderaadt struct packet *packet; 54*9a2590e5Sderaadt { 55*9a2590e5Sderaadt /* Initially, zero all option pointers. */ 56*9a2590e5Sderaadt memset (packet -> options, 0, sizeof (packet -> options)); 57*9a2590e5Sderaadt 58*9a2590e5Sderaadt /* If we don't see the magic cookie, there's nothing to parse. */ 59*9a2590e5Sderaadt if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) { 60*9a2590e5Sderaadt packet -> options_valid = 0; 61*9a2590e5Sderaadt return; 62*9a2590e5Sderaadt } 63*9a2590e5Sderaadt 64*9a2590e5Sderaadt /* Go through the options field, up to the end of the packet 65*9a2590e5Sderaadt or the End field. */ 66*9a2590e5Sderaadt parse_option_buffer (packet, &packet -> raw -> options [4], 67*9a2590e5Sderaadt packet -> packet_length - DHCP_FIXED_NON_UDP - 4); 68*9a2590e5Sderaadt /* If we parsed a DHCP Option Overload option, parse more 69*9a2590e5Sderaadt options out of the buffer(s) containing them. */ 70*9a2590e5Sderaadt if (packet -> options_valid 71*9a2590e5Sderaadt && packet -> options [DHO_DHCP_OPTION_OVERLOAD].data) { 72*9a2590e5Sderaadt if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1) 73*9a2590e5Sderaadt parse_option_buffer (packet, 74*9a2590e5Sderaadt (unsigned char *) 75*9a2590e5Sderaadt packet -> raw -> file, 76*9a2590e5Sderaadt sizeof packet -> raw -> file); 77*9a2590e5Sderaadt if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2) 78*9a2590e5Sderaadt parse_option_buffer (packet, 79*9a2590e5Sderaadt (unsigned char *) 80*9a2590e5Sderaadt packet -> raw -> sname, 81*9a2590e5Sderaadt sizeof packet -> raw -> sname); 82*9a2590e5Sderaadt } 83*9a2590e5Sderaadt } 84*9a2590e5Sderaadt 85*9a2590e5Sderaadt /* Parse options out of the specified buffer, storing addresses of option 86*9a2590e5Sderaadt values in packet -> options and setting packet -> options_valid if no 87*9a2590e5Sderaadt errors are encountered. */ 88*9a2590e5Sderaadt 89*9a2590e5Sderaadt void parse_option_buffer (packet, buffer, length) 90*9a2590e5Sderaadt struct packet *packet; 91*9a2590e5Sderaadt unsigned char *buffer; 92*9a2590e5Sderaadt int length; 93*9a2590e5Sderaadt { 94*9a2590e5Sderaadt unsigned char *s, *t; 95*9a2590e5Sderaadt unsigned char *end = buffer + length; 96*9a2590e5Sderaadt int len; 97*9a2590e5Sderaadt int code; 98*9a2590e5Sderaadt 99*9a2590e5Sderaadt for (s = buffer; *s != DHO_END && s < end; ) { 100*9a2590e5Sderaadt code = s [0]; 101*9a2590e5Sderaadt 102*9a2590e5Sderaadt /* Pad options don't have a length - just skip them. */ 103*9a2590e5Sderaadt if (code == DHO_PAD) { 104*9a2590e5Sderaadt ++s; 105*9a2590e5Sderaadt continue; 106*9a2590e5Sderaadt } 107*9a2590e5Sderaadt if (s + 2 > end) { 108*9a2590e5Sderaadt len = 65536; 109*9a2590e5Sderaadt goto bogus; 110*9a2590e5Sderaadt } 111*9a2590e5Sderaadt 112*9a2590e5Sderaadt /* All other fields (except end, see above) have a 113*9a2590e5Sderaadt one-byte length. */ 114*9a2590e5Sderaadt len = s [1]; 115*9a2590e5Sderaadt 116*9a2590e5Sderaadt /* If the length is outrageous, silently skip the 117*9a2590e5Sderaadt * rest, and mark the packet bad. Unfortuntely 118*9a2590e5Sderaadt * some crappy dhcp servers always seem to give 119*9a2590e5Sderaadt * us garbage on the end of a packet. so rather than 120*9a2590e5Sderaadt * keep refusing, give up and try to take one after 121*9a2590e5Sderaadt * seeing a few without anything good. 122*9a2590e5Sderaadt */ 123*9a2590e5Sderaadt if (s + len + 2 > end) { 124*9a2590e5Sderaadt bogus: 125*9a2590e5Sderaadt bad_options++; 126*9a2590e5Sderaadt warn ("option %s (%d) %s.", 127*9a2590e5Sderaadt dhcp_options [code].name, len, 128*9a2590e5Sderaadt "larger than buffer"); 129*9a2590e5Sderaadt if (bad_options == bad_options_max) { 130*9a2590e5Sderaadt packet -> options_valid = 1; 131*9a2590e5Sderaadt bad_options = 0; 132*9a2590e5Sderaadt warn ("Many bogus options seen in offers."); 133*9a2590e5Sderaadt warn ("Taking this offer in spite of bogus"); 134*9a2590e5Sderaadt warn ("options - hope for the best!"); 135*9a2590e5Sderaadt } else { 136*9a2590e5Sderaadt warn ("rejecting bogus offer."); 137*9a2590e5Sderaadt packet -> options_valid = 0; 138*9a2590e5Sderaadt } 139*9a2590e5Sderaadt return; 140*9a2590e5Sderaadt } 141*9a2590e5Sderaadt /* If we haven't seen this option before, just make 142*9a2590e5Sderaadt space for it and copy it there. */ 143*9a2590e5Sderaadt if (!packet -> options [code].data) { 144*9a2590e5Sderaadt if (!(t = ((unsigned char *) 145*9a2590e5Sderaadt dmalloc (len + 1, "parse_option_buffer")))) 146*9a2590e5Sderaadt error ("Can't allocate storage for option %s.", 147*9a2590e5Sderaadt dhcp_options [code].name); 148*9a2590e5Sderaadt /* Copy and NUL-terminate the option (in case it's an 149*9a2590e5Sderaadt ASCII string. */ 150*9a2590e5Sderaadt memcpy (t, &s [2], len); 151*9a2590e5Sderaadt t [len] = 0; 152*9a2590e5Sderaadt packet -> options [code].len = len; 153*9a2590e5Sderaadt packet -> options [code].data = t; 154*9a2590e5Sderaadt } else { 155*9a2590e5Sderaadt /* If it's a repeat, concatenate it to whatever 156*9a2590e5Sderaadt we last saw. This is really only required 157*9a2590e5Sderaadt for clients, but what the heck... */ 158*9a2590e5Sderaadt t = ((unsigned char *) 159*9a2590e5Sderaadt dmalloc (len + packet -> options [code].len + 1, 160*9a2590e5Sderaadt "parse_option_buffer")); 161*9a2590e5Sderaadt if (!t) 162*9a2590e5Sderaadt error ("Can't expand storage for option %s.", 163*9a2590e5Sderaadt dhcp_options [code].name); 164*9a2590e5Sderaadt memcpy (t, packet -> options [code].data, 165*9a2590e5Sderaadt packet -> options [code].len); 166*9a2590e5Sderaadt memcpy (t + packet -> options [code].len, 167*9a2590e5Sderaadt &s [2], len); 168*9a2590e5Sderaadt packet -> options [code].len += len; 169*9a2590e5Sderaadt t [packet -> options [code].len] = 0; 170*9a2590e5Sderaadt dfree (packet -> options [code].data, 171*9a2590e5Sderaadt "parse_option_buffer"); 172*9a2590e5Sderaadt packet -> options [code].data = t; 173*9a2590e5Sderaadt } 174*9a2590e5Sderaadt s += len + 2; 175*9a2590e5Sderaadt } 176*9a2590e5Sderaadt packet -> options_valid = 1; 177*9a2590e5Sderaadt } 178*9a2590e5Sderaadt 179*9a2590e5Sderaadt /* cons options into a big buffer, and then split them out into the 180*9a2590e5Sderaadt three separate buffers if needed. This allows us to cons up a set 181*9a2590e5Sderaadt of vendor options using the same routine. */ 182*9a2590e5Sderaadt 183*9a2590e5Sderaadt int cons_options (inpacket, outpacket, mms, 184*9a2590e5Sderaadt options, overload, terminate, bootpp, prl, prl_len) 185*9a2590e5Sderaadt struct packet *inpacket; 186*9a2590e5Sderaadt struct dhcp_packet *outpacket; 187*9a2590e5Sderaadt int mms; 188*9a2590e5Sderaadt struct tree_cache **options; 189*9a2590e5Sderaadt int overload; /* Overload flags that may be set. */ 190*9a2590e5Sderaadt int terminate; 191*9a2590e5Sderaadt int bootpp; 192*9a2590e5Sderaadt u_int8_t *prl; 193*9a2590e5Sderaadt int prl_len; 194*9a2590e5Sderaadt { 195*9a2590e5Sderaadt unsigned char priority_list [300]; 196*9a2590e5Sderaadt int priority_len; 197*9a2590e5Sderaadt unsigned char buffer [4096]; /* Really big buffer... */ 198*9a2590e5Sderaadt int main_buffer_size; 199*9a2590e5Sderaadt int mainbufix, bufix; 200*9a2590e5Sderaadt int option_size; 201*9a2590e5Sderaadt int length; 202*9a2590e5Sderaadt 203*9a2590e5Sderaadt /* If the client has provided a maximum DHCP message size, 204*9a2590e5Sderaadt use that; otherwise, if it's BOOTP, only 64 bytes; otherwise 205*9a2590e5Sderaadt use up to the minimum IP MTU size (576 bytes). */ 206*9a2590e5Sderaadt /* XXX if a BOOTP client specifies a max message size, we will 207*9a2590e5Sderaadt honor it. */ 208*9a2590e5Sderaadt if (!mms && 209*9a2590e5Sderaadt inpacket && 210*9a2590e5Sderaadt inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data && 211*9a2590e5Sderaadt (inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].len >= 212*9a2590e5Sderaadt sizeof (u_int16_t))) 213*9a2590e5Sderaadt mms = getUShort (inpacket -> options 214*9a2590e5Sderaadt [DHO_DHCP_MAX_MESSAGE_SIZE].data); 215*9a2590e5Sderaadt 216*9a2590e5Sderaadt /* If the client has provided a maximum DHCP message size, 217*9a2590e5Sderaadt use that; otherwise, if it's BOOTP, only 64 bytes; otherwise 218*9a2590e5Sderaadt use up to the minimum IP MTU size (576 bytes). */ 219*9a2590e5Sderaadt /* XXX if a BOOTP client specifies a max message size, we will 220*9a2590e5Sderaadt honor it. */ 221*9a2590e5Sderaadt if (mms) 222*9a2590e5Sderaadt main_buffer_size = mms - DHCP_FIXED_LEN; 223*9a2590e5Sderaadt else if (bootpp) 224*9a2590e5Sderaadt main_buffer_size = 64; 225*9a2590e5Sderaadt else 226*9a2590e5Sderaadt main_buffer_size = 576 - DHCP_FIXED_LEN; 227*9a2590e5Sderaadt 228*9a2590e5Sderaadt if (main_buffer_size > sizeof buffer) 229*9a2590e5Sderaadt main_buffer_size = sizeof buffer; 230*9a2590e5Sderaadt 231*9a2590e5Sderaadt /* Preload the option priority list with mandatory options. */ 232*9a2590e5Sderaadt priority_len = 0; 233*9a2590e5Sderaadt priority_list [priority_len++] = DHO_DHCP_MESSAGE_TYPE; 234*9a2590e5Sderaadt priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER; 235*9a2590e5Sderaadt priority_list [priority_len++] = DHO_DHCP_LEASE_TIME; 236*9a2590e5Sderaadt priority_list [priority_len++] = DHO_DHCP_MESSAGE; 237*9a2590e5Sderaadt 238*9a2590e5Sderaadt /* If the client has provided a list of options that it wishes 239*9a2590e5Sderaadt returned, use it to prioritize. Otherwise, prioritize 240*9a2590e5Sderaadt based on the default priority list. */ 241*9a2590e5Sderaadt 242*9a2590e5Sderaadt if (inpacket && 243*9a2590e5Sderaadt inpacket -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data) { 244*9a2590e5Sderaadt int prlen = (inpacket -> 245*9a2590e5Sderaadt options [DHO_DHCP_PARAMETER_REQUEST_LIST].len); 246*9a2590e5Sderaadt if (prlen + priority_len > sizeof priority_list) 247*9a2590e5Sderaadt prlen = (sizeof priority_list) - priority_len; 248*9a2590e5Sderaadt 249*9a2590e5Sderaadt memcpy (&priority_list [priority_len], 250*9a2590e5Sderaadt (inpacket -> options 251*9a2590e5Sderaadt [DHO_DHCP_PARAMETER_REQUEST_LIST].data), prlen); 252*9a2590e5Sderaadt priority_len += prlen; 253*9a2590e5Sderaadt prl = priority_list; 254*9a2590e5Sderaadt } else if (prl) { 255*9a2590e5Sderaadt if (prl_len + priority_len > sizeof priority_list) 256*9a2590e5Sderaadt prl_len = (sizeof priority_list) - priority_len; 257*9a2590e5Sderaadt 258*9a2590e5Sderaadt memcpy (&priority_list [priority_len], prl, prl_len); 259*9a2590e5Sderaadt priority_len += prl_len; 260*9a2590e5Sderaadt prl = priority_list; 261*9a2590e5Sderaadt } else { 262*9a2590e5Sderaadt memcpy (&priority_list [priority_len], 263*9a2590e5Sderaadt dhcp_option_default_priority_list, 264*9a2590e5Sderaadt sizeof_dhcp_option_default_priority_list); 265*9a2590e5Sderaadt priority_len += sizeof_dhcp_option_default_priority_list; 266*9a2590e5Sderaadt } 267*9a2590e5Sderaadt 268*9a2590e5Sderaadt /* Copy the options into the big buffer... */ 269*9a2590e5Sderaadt option_size = store_options (buffer, 270*9a2590e5Sderaadt (main_buffer_size - 7 + 271*9a2590e5Sderaadt ((overload & 1) ? DHCP_FILE_LEN : 0) + 272*9a2590e5Sderaadt ((overload & 2) ? DHCP_SNAME_LEN : 0)), 273*9a2590e5Sderaadt options, priority_list, priority_len, 274*9a2590e5Sderaadt main_buffer_size, 275*9a2590e5Sderaadt (main_buffer_size + 276*9a2590e5Sderaadt ((overload & 1) ? DHCP_FILE_LEN : 0)), 277*9a2590e5Sderaadt terminate); 278*9a2590e5Sderaadt 279*9a2590e5Sderaadt /* Put the cookie up front... */ 280*9a2590e5Sderaadt memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4); 281*9a2590e5Sderaadt mainbufix = 4; 282*9a2590e5Sderaadt 283*9a2590e5Sderaadt /* If we're going to have to overload, store the overload 284*9a2590e5Sderaadt option at the beginning. If we can, though, just store the 285*9a2590e5Sderaadt whole thing in the packet's option buffer and leave it at 286*9a2590e5Sderaadt that. */ 287*9a2590e5Sderaadt if (option_size <= main_buffer_size - mainbufix) { 288*9a2590e5Sderaadt memcpy (&outpacket -> options [mainbufix], 289*9a2590e5Sderaadt buffer, option_size); 290*9a2590e5Sderaadt mainbufix += option_size; 291*9a2590e5Sderaadt if (mainbufix < main_buffer_size) 292*9a2590e5Sderaadt outpacket -> options [mainbufix++] 293*9a2590e5Sderaadt = DHO_END; 294*9a2590e5Sderaadt length = DHCP_FIXED_NON_UDP + mainbufix; 295*9a2590e5Sderaadt } else { 296*9a2590e5Sderaadt outpacket -> options [mainbufix++] = 297*9a2590e5Sderaadt DHO_DHCP_OPTION_OVERLOAD; 298*9a2590e5Sderaadt outpacket -> options [mainbufix++] = 1; 299*9a2590e5Sderaadt if (option_size > main_buffer_size - mainbufix + DHCP_FILE_LEN) 300*9a2590e5Sderaadt outpacket -> options [mainbufix++] = 3; 301*9a2590e5Sderaadt else 302*9a2590e5Sderaadt outpacket -> options [mainbufix++] = 1; 303*9a2590e5Sderaadt 304*9a2590e5Sderaadt memcpy (&outpacket -> options [mainbufix], 305*9a2590e5Sderaadt buffer, main_buffer_size - mainbufix); 306*9a2590e5Sderaadt bufix = main_buffer_size - mainbufix; 307*9a2590e5Sderaadt length = DHCP_FIXED_NON_UDP + mainbufix; 308*9a2590e5Sderaadt if (overload & 1) { 309*9a2590e5Sderaadt if (option_size - bufix <= DHCP_FILE_LEN) { 310*9a2590e5Sderaadt memcpy (outpacket -> file, 311*9a2590e5Sderaadt &buffer [bufix], option_size - bufix); 312*9a2590e5Sderaadt mainbufix = option_size - bufix; 313*9a2590e5Sderaadt if (mainbufix < DHCP_FILE_LEN) 314*9a2590e5Sderaadt outpacket -> file [mainbufix++] 315*9a2590e5Sderaadt = DHO_END; 316*9a2590e5Sderaadt while (mainbufix < DHCP_FILE_LEN) 317*9a2590e5Sderaadt outpacket -> file [mainbufix++] 318*9a2590e5Sderaadt = DHO_PAD; 319*9a2590e5Sderaadt } else { 320*9a2590e5Sderaadt memcpy (outpacket -> file, 321*9a2590e5Sderaadt &buffer [bufix], DHCP_FILE_LEN); 322*9a2590e5Sderaadt bufix += DHCP_FILE_LEN; 323*9a2590e5Sderaadt } 324*9a2590e5Sderaadt } 325*9a2590e5Sderaadt if ((overload & 2) && option_size < bufix) { 326*9a2590e5Sderaadt memcpy (outpacket -> sname, 327*9a2590e5Sderaadt &buffer [bufix], option_size - bufix); 328*9a2590e5Sderaadt 329*9a2590e5Sderaadt mainbufix = option_size - bufix; 330*9a2590e5Sderaadt if (mainbufix < DHCP_SNAME_LEN) 331*9a2590e5Sderaadt outpacket -> file [mainbufix++] 332*9a2590e5Sderaadt = DHO_END; 333*9a2590e5Sderaadt while (mainbufix < DHCP_SNAME_LEN) 334*9a2590e5Sderaadt outpacket -> file [mainbufix++] 335*9a2590e5Sderaadt = DHO_PAD; 336*9a2590e5Sderaadt } 337*9a2590e5Sderaadt } 338*9a2590e5Sderaadt return length; 339*9a2590e5Sderaadt } 340*9a2590e5Sderaadt 341*9a2590e5Sderaadt /* Store all the requested options into the requested buffer. */ 342*9a2590e5Sderaadt 343*9a2590e5Sderaadt int store_options (buffer, buflen, options, priority_list, priority_len, 344*9a2590e5Sderaadt first_cutoff, second_cutoff, terminate) 345*9a2590e5Sderaadt unsigned char *buffer; 346*9a2590e5Sderaadt int buflen; 347*9a2590e5Sderaadt struct tree_cache **options; 348*9a2590e5Sderaadt unsigned char *priority_list; 349*9a2590e5Sderaadt int priority_len; 350*9a2590e5Sderaadt int first_cutoff, second_cutoff; 351*9a2590e5Sderaadt int terminate; 352*9a2590e5Sderaadt { 353*9a2590e5Sderaadt int bufix = 0; 354*9a2590e5Sderaadt int option_stored [256]; 355*9a2590e5Sderaadt int i; 356*9a2590e5Sderaadt int ix; 357*9a2590e5Sderaadt int tto; 358*9a2590e5Sderaadt 359*9a2590e5Sderaadt /* Zero out the stored-lengths array. */ 360*9a2590e5Sderaadt memset (option_stored, 0, sizeof option_stored); 361*9a2590e5Sderaadt 362*9a2590e5Sderaadt /* Copy out the options in the order that they appear in the 363*9a2590e5Sderaadt priority list... */ 364*9a2590e5Sderaadt for (i = 0; i < priority_len; i++) { 365*9a2590e5Sderaadt /* Code for next option to try to store. */ 366*9a2590e5Sderaadt int code = priority_list [i]; 367*9a2590e5Sderaadt int optstart; 368*9a2590e5Sderaadt 369*9a2590e5Sderaadt /* Number of bytes left to store (some may already 370*9a2590e5Sderaadt have been stored by a previous pass). */ 371*9a2590e5Sderaadt int length; 372*9a2590e5Sderaadt 373*9a2590e5Sderaadt /* If no data is available for this option, skip it. */ 374*9a2590e5Sderaadt if (!options [code]) { 375*9a2590e5Sderaadt continue; 376*9a2590e5Sderaadt } 377*9a2590e5Sderaadt 378*9a2590e5Sderaadt /* The client could ask for things that are mandatory, 379*9a2590e5Sderaadt in which case we should avoid storing them twice... */ 380*9a2590e5Sderaadt if (option_stored [code]) 381*9a2590e5Sderaadt continue; 382*9a2590e5Sderaadt option_stored [code] = 1; 383*9a2590e5Sderaadt 384*9a2590e5Sderaadt /* Find the value of the option... */ 385*9a2590e5Sderaadt if (!tree_evaluate (options [code])) { 386*9a2590e5Sderaadt continue; 387*9a2590e5Sderaadt } 388*9a2590e5Sderaadt 389*9a2590e5Sderaadt /* We should now have a constant length for the option. */ 390*9a2590e5Sderaadt length = options [code] -> len; 391*9a2590e5Sderaadt 392*9a2590e5Sderaadt /* Do we add a NUL? */ 393*9a2590e5Sderaadt if (terminate && dhcp_options [code].format [0] == 't') { 394*9a2590e5Sderaadt length++; 395*9a2590e5Sderaadt tto = 1; 396*9a2590e5Sderaadt } else { 397*9a2590e5Sderaadt tto = 0; 398*9a2590e5Sderaadt } 399*9a2590e5Sderaadt 400*9a2590e5Sderaadt /* Try to store the option. */ 401*9a2590e5Sderaadt 402*9a2590e5Sderaadt /* If the option's length is more than 255, we must store it 403*9a2590e5Sderaadt in multiple hunks. Store 255-byte hunks first. However, 404*9a2590e5Sderaadt in any case, if the option data will cross a buffer 405*9a2590e5Sderaadt boundary, split it across that boundary. */ 406*9a2590e5Sderaadt 407*9a2590e5Sderaadt ix = 0; 408*9a2590e5Sderaadt 409*9a2590e5Sderaadt optstart = bufix; 410*9a2590e5Sderaadt while (length) { 411*9a2590e5Sderaadt unsigned char incr = length > 255 ? 255 : length; 412*9a2590e5Sderaadt 413*9a2590e5Sderaadt /* If this hunk of the buffer will cross a 414*9a2590e5Sderaadt boundary, only go up to the boundary in this 415*9a2590e5Sderaadt pass. */ 416*9a2590e5Sderaadt if (bufix < first_cutoff && 417*9a2590e5Sderaadt bufix + incr > first_cutoff) 418*9a2590e5Sderaadt incr = first_cutoff - bufix; 419*9a2590e5Sderaadt else if (bufix < second_cutoff && 420*9a2590e5Sderaadt bufix + incr > second_cutoff) 421*9a2590e5Sderaadt incr = second_cutoff - bufix; 422*9a2590e5Sderaadt 423*9a2590e5Sderaadt /* If this option is going to overflow the buffer, 424*9a2590e5Sderaadt skip it. */ 425*9a2590e5Sderaadt if (bufix + 2 + incr > buflen) { 426*9a2590e5Sderaadt bufix = optstart; 427*9a2590e5Sderaadt break; 428*9a2590e5Sderaadt } 429*9a2590e5Sderaadt 430*9a2590e5Sderaadt /* Everything looks good - copy it in! */ 431*9a2590e5Sderaadt buffer [bufix] = code; 432*9a2590e5Sderaadt buffer [bufix + 1] = incr; 433*9a2590e5Sderaadt if (tto && incr == length) { 434*9a2590e5Sderaadt memcpy (buffer + bufix + 2, 435*9a2590e5Sderaadt options [code] -> value + ix, 436*9a2590e5Sderaadt incr - 1); 437*9a2590e5Sderaadt buffer [bufix + 2 + incr - 1] = 0; 438*9a2590e5Sderaadt } else { 439*9a2590e5Sderaadt memcpy (buffer + bufix + 2, 440*9a2590e5Sderaadt options [code] -> value + ix, incr); 441*9a2590e5Sderaadt } 442*9a2590e5Sderaadt length -= incr; 443*9a2590e5Sderaadt ix += incr; 444*9a2590e5Sderaadt bufix += 2 + incr; 445*9a2590e5Sderaadt } 446*9a2590e5Sderaadt } 447*9a2590e5Sderaadt return bufix; 448*9a2590e5Sderaadt } 449*9a2590e5Sderaadt 450*9a2590e5Sderaadt /* Format the specified option so that a human can easily read it. */ 451*9a2590e5Sderaadt 452*9a2590e5Sderaadt char *pretty_print_option (code, data, len, emit_commas, emit_quotes) 453*9a2590e5Sderaadt unsigned int code; 454*9a2590e5Sderaadt unsigned char *data; 455*9a2590e5Sderaadt int len; 456*9a2590e5Sderaadt int emit_commas; 457*9a2590e5Sderaadt int emit_quotes; 458*9a2590e5Sderaadt { 459*9a2590e5Sderaadt static char optbuf [32768]; /* XXX */ 460*9a2590e5Sderaadt int hunksize = 0; 461*9a2590e5Sderaadt int numhunk = -1; 462*9a2590e5Sderaadt int numelem = 0; 463*9a2590e5Sderaadt char fmtbuf [32]; 464*9a2590e5Sderaadt int i, j, k; 465*9a2590e5Sderaadt char *op = optbuf; 466*9a2590e5Sderaadt int opleft = sizeof(optbuf); 467*9a2590e5Sderaadt unsigned char *dp = data; 468*9a2590e5Sderaadt struct in_addr foo; 469*9a2590e5Sderaadt char comma; 470*9a2590e5Sderaadt 471*9a2590e5Sderaadt 472*9a2590e5Sderaadt /* Code should be between 0 and 255. */ 473*9a2590e5Sderaadt if (code > 255) 474*9a2590e5Sderaadt error ("pretty_print_option: bad code %d\n", code); 475*9a2590e5Sderaadt 476*9a2590e5Sderaadt if (emit_commas) 477*9a2590e5Sderaadt comma = ','; 478*9a2590e5Sderaadt else 479*9a2590e5Sderaadt comma = ' '; 480*9a2590e5Sderaadt 481*9a2590e5Sderaadt /* Figure out the size of the data. */ 482*9a2590e5Sderaadt for (i = 0; dhcp_options [code].format [i]; i++) { 483*9a2590e5Sderaadt if (!numhunk) { 484*9a2590e5Sderaadt warn ("%s: Excess information in format string: %s", 485*9a2590e5Sderaadt dhcp_options [code].name, 486*9a2590e5Sderaadt &(dhcp_options [code].format [i])); 487*9a2590e5Sderaadt break; 488*9a2590e5Sderaadt } 489*9a2590e5Sderaadt numelem++; 490*9a2590e5Sderaadt fmtbuf [i] = dhcp_options [code].format [i]; 491*9a2590e5Sderaadt switch (dhcp_options [code].format [i]) { 492*9a2590e5Sderaadt case 'A': 493*9a2590e5Sderaadt --numelem; 494*9a2590e5Sderaadt fmtbuf [i] = 0; 495*9a2590e5Sderaadt numhunk = 0; 496*9a2590e5Sderaadt break; 497*9a2590e5Sderaadt case 'X': 498*9a2590e5Sderaadt for (k = 0; k < len; k++) { 499*9a2590e5Sderaadt if (!isascii (data [k]) || 500*9a2590e5Sderaadt !isprint (data [k])) 501*9a2590e5Sderaadt break; 502*9a2590e5Sderaadt } 503*9a2590e5Sderaadt if (k == len) { 504*9a2590e5Sderaadt fmtbuf [i] = 't'; 505*9a2590e5Sderaadt numhunk = -2; 506*9a2590e5Sderaadt } else { 507*9a2590e5Sderaadt fmtbuf [i] = 'x'; 508*9a2590e5Sderaadt hunksize++; 509*9a2590e5Sderaadt comma = ':'; 510*9a2590e5Sderaadt numhunk = 0; 511*9a2590e5Sderaadt } 512*9a2590e5Sderaadt fmtbuf [i + 1] = 0; 513*9a2590e5Sderaadt break; 514*9a2590e5Sderaadt case 't': 515*9a2590e5Sderaadt fmtbuf [i] = 't'; 516*9a2590e5Sderaadt fmtbuf [i + 1] = 0; 517*9a2590e5Sderaadt numhunk = -2; 518*9a2590e5Sderaadt break; 519*9a2590e5Sderaadt case 'I': 520*9a2590e5Sderaadt case 'l': 521*9a2590e5Sderaadt case 'L': 522*9a2590e5Sderaadt hunksize += 4; 523*9a2590e5Sderaadt break; 524*9a2590e5Sderaadt case 's': 525*9a2590e5Sderaadt case 'S': 526*9a2590e5Sderaadt hunksize += 2; 527*9a2590e5Sderaadt break; 528*9a2590e5Sderaadt case 'b': 529*9a2590e5Sderaadt case 'B': 530*9a2590e5Sderaadt case 'f': 531*9a2590e5Sderaadt hunksize++; 532*9a2590e5Sderaadt break; 533*9a2590e5Sderaadt case 'e': 534*9a2590e5Sderaadt break; 535*9a2590e5Sderaadt default: 536*9a2590e5Sderaadt warn ("%s: garbage in format string: %s", 537*9a2590e5Sderaadt dhcp_options [code].name, 538*9a2590e5Sderaadt &(dhcp_options [code].format [i])); 539*9a2590e5Sderaadt break; 540*9a2590e5Sderaadt } 541*9a2590e5Sderaadt } 542*9a2590e5Sderaadt 543*9a2590e5Sderaadt /* Check for too few bytes... */ 544*9a2590e5Sderaadt if (hunksize > len) { 545*9a2590e5Sderaadt warn ("%s: expecting at least %d bytes; got %d", 546*9a2590e5Sderaadt dhcp_options [code].name, 547*9a2590e5Sderaadt hunksize, len); 548*9a2590e5Sderaadt return "<error>"; 549*9a2590e5Sderaadt } 550*9a2590e5Sderaadt /* Check for too many bytes... */ 551*9a2590e5Sderaadt if (numhunk == -1 && hunksize < len) 552*9a2590e5Sderaadt warn ("%s: %d extra bytes", 553*9a2590e5Sderaadt dhcp_options [code].name, 554*9a2590e5Sderaadt len - hunksize); 555*9a2590e5Sderaadt 556*9a2590e5Sderaadt /* If this is an array, compute its size. */ 557*9a2590e5Sderaadt if (!numhunk) 558*9a2590e5Sderaadt numhunk = len / hunksize; 559*9a2590e5Sderaadt /* See if we got an exact number of hunks. */ 560*9a2590e5Sderaadt if (numhunk > 0 && numhunk * hunksize < len) 561*9a2590e5Sderaadt warn ("%s: %d extra bytes at end of array", 562*9a2590e5Sderaadt dhcp_options [code].name, 563*9a2590e5Sderaadt len - numhunk * hunksize); 564*9a2590e5Sderaadt 565*9a2590e5Sderaadt /* A one-hunk array prints the same as a single hunk. */ 566*9a2590e5Sderaadt if (numhunk < 0) 567*9a2590e5Sderaadt numhunk = 1; 568*9a2590e5Sderaadt 569*9a2590e5Sderaadt /* Cycle through the array (or hunk) printing the data. */ 570*9a2590e5Sderaadt for (i = 0; i < numhunk; i++) { 571*9a2590e5Sderaadt for (j = 0; j < numelem; j++) { 572*9a2590e5Sderaadt int opcount; 573*9a2590e5Sderaadt switch (fmtbuf [j]) { 574*9a2590e5Sderaadt case 't': 575*9a2590e5Sderaadt if (emit_quotes) { 576*9a2590e5Sderaadt *op++ = '"'; 577*9a2590e5Sderaadt opleft--; 578*9a2590e5Sderaadt } 579*9a2590e5Sderaadt for (; dp < data + len; dp++) { 580*9a2590e5Sderaadt if (!isascii (*dp) || 581*9a2590e5Sderaadt !isprint (*dp)) { 582*9a2590e5Sderaadt if (dp + 1 != data + len || 583*9a2590e5Sderaadt *dp != 0) { 584*9a2590e5Sderaadt snprintf(op, opleft, 585*9a2590e5Sderaadt "\\%03o", *dp); 586*9a2590e5Sderaadt op += 4; 587*9a2590e5Sderaadt opleft -= 4; 588*9a2590e5Sderaadt } 589*9a2590e5Sderaadt } else if (*dp == '"' || 590*9a2590e5Sderaadt *dp == '\'' || 591*9a2590e5Sderaadt *dp == '$' || 592*9a2590e5Sderaadt *dp == '`' || 593*9a2590e5Sderaadt *dp == '\\') { 594*9a2590e5Sderaadt *op++ = '\\'; 595*9a2590e5Sderaadt *op++ = *dp; 596*9a2590e5Sderaadt opleft -= 2; 597*9a2590e5Sderaadt } else { 598*9a2590e5Sderaadt *op++ = *dp; 599*9a2590e5Sderaadt opleft--; 600*9a2590e5Sderaadt } 601*9a2590e5Sderaadt } 602*9a2590e5Sderaadt if (emit_quotes) { 603*9a2590e5Sderaadt *op++ = '"'; 604*9a2590e5Sderaadt opleft--; 605*9a2590e5Sderaadt } 606*9a2590e5Sderaadt 607*9a2590e5Sderaadt *op = 0; 608*9a2590e5Sderaadt break; 609*9a2590e5Sderaadt case 'I': 610*9a2590e5Sderaadt foo.s_addr = htonl(getULong (dp)); 611*9a2590e5Sderaadt opcount = strlcpy(op, inet_ntoa (foo), 612*9a2590e5Sderaadt opleft); 613*9a2590e5Sderaadt if (opcount >= opleft) 614*9a2590e5Sderaadt goto toobig; 615*9a2590e5Sderaadt opleft -= opcount; 616*9a2590e5Sderaadt dp += 4; 617*9a2590e5Sderaadt break; 618*9a2590e5Sderaadt case 'l': 619*9a2590e5Sderaadt opcount = snprintf(op, opleft,"%ld", 620*9a2590e5Sderaadt (long)getLong (dp)); 621*9a2590e5Sderaadt if (opcount >= opleft) 622*9a2590e5Sderaadt goto toobig; 623*9a2590e5Sderaadt opleft -= opcount; 624*9a2590e5Sderaadt dp += 4; 625*9a2590e5Sderaadt break; 626*9a2590e5Sderaadt case 'L': 627*9a2590e5Sderaadt opcount = snprintf(op, opleft, "%ld", 628*9a2590e5Sderaadt (unsigned long)getULong (dp)); 629*9a2590e5Sderaadt if (opcount >= opleft) 630*9a2590e5Sderaadt goto toobig; 631*9a2590e5Sderaadt opleft -= opcount; 632*9a2590e5Sderaadt dp += 4; 633*9a2590e5Sderaadt break; 634*9a2590e5Sderaadt case 's': 635*9a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 636*9a2590e5Sderaadt getShort (dp)); 637*9a2590e5Sderaadt if (opcount >= opleft) 638*9a2590e5Sderaadt goto toobig; 639*9a2590e5Sderaadt opleft -= opcount; 640*9a2590e5Sderaadt dp += 2; 641*9a2590e5Sderaadt break; 642*9a2590e5Sderaadt case 'S': 643*9a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 644*9a2590e5Sderaadt getUShort (dp)); 645*9a2590e5Sderaadt if (opcount >= opleft) 646*9a2590e5Sderaadt goto toobig; 647*9a2590e5Sderaadt opleft -= opcount; 648*9a2590e5Sderaadt dp += 2; 649*9a2590e5Sderaadt break; 650*9a2590e5Sderaadt case 'b': 651*9a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 652*9a2590e5Sderaadt *(char *)dp++); 653*9a2590e5Sderaadt if (opcount >= opleft) 654*9a2590e5Sderaadt goto toobig; 655*9a2590e5Sderaadt opleft -= opcount; 656*9a2590e5Sderaadt break; 657*9a2590e5Sderaadt case 'B': 658*9a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", *dp++); 659*9a2590e5Sderaadt if (opcount >= opleft) 660*9a2590e5Sderaadt goto toobig; 661*9a2590e5Sderaadt opleft -= opcount; 662*9a2590e5Sderaadt break; 663*9a2590e5Sderaadt case 'x': 664*9a2590e5Sderaadt opcount = snprintf(op, opleft, "%x", *dp++); 665*9a2590e5Sderaadt if (opcount >= opleft) 666*9a2590e5Sderaadt goto toobig; 667*9a2590e5Sderaadt opleft -= opcount; 668*9a2590e5Sderaadt break; 669*9a2590e5Sderaadt case 'f': 670*9a2590e5Sderaadt opcount = strlcpy(op, 671*9a2590e5Sderaadt *dp++ ? "true" : "false", opleft); 672*9a2590e5Sderaadt if (opcount >= opleft) 673*9a2590e5Sderaadt goto toobig; 674*9a2590e5Sderaadt opleft -= opcount; 675*9a2590e5Sderaadt break; 676*9a2590e5Sderaadt default: 677*9a2590e5Sderaadt warn ("Unexpected format code %c", fmtbuf [j]); 678*9a2590e5Sderaadt } 679*9a2590e5Sderaadt op += strlen (op); 680*9a2590e5Sderaadt opleft -= strlen(op); 681*9a2590e5Sderaadt if (opleft < 1) 682*9a2590e5Sderaadt goto toobig; 683*9a2590e5Sderaadt if (j + 1 < numelem && comma != ':') { 684*9a2590e5Sderaadt *op++ = ' '; 685*9a2590e5Sderaadt opleft--; 686*9a2590e5Sderaadt } 687*9a2590e5Sderaadt } 688*9a2590e5Sderaadt if (i + 1 < numhunk) { 689*9a2590e5Sderaadt *op++ = comma; 690*9a2590e5Sderaadt opleft--; 691*9a2590e5Sderaadt } 692*9a2590e5Sderaadt if (opleft < 1) 693*9a2590e5Sderaadt goto toobig; 694*9a2590e5Sderaadt 695*9a2590e5Sderaadt } 696*9a2590e5Sderaadt return optbuf; 697*9a2590e5Sderaadt toobig: 698*9a2590e5Sderaadt warn ("dhcp option too large"); 699*9a2590e5Sderaadt return "<error>"; 700*9a2590e5Sderaadt } 701*9a2590e5Sderaadt 702*9a2590e5Sderaadt void do_packet (interface, packet, len, from_port, from, hfrom) 703*9a2590e5Sderaadt struct interface_info *interface; 704*9a2590e5Sderaadt struct dhcp_packet *packet; 705*9a2590e5Sderaadt int len; 706*9a2590e5Sderaadt unsigned int from_port; 707*9a2590e5Sderaadt struct iaddr from; 708*9a2590e5Sderaadt struct hardware *hfrom; 709*9a2590e5Sderaadt { 710*9a2590e5Sderaadt struct packet tp; 711*9a2590e5Sderaadt int i; 712*9a2590e5Sderaadt 713*9a2590e5Sderaadt if (packet -> hlen > sizeof packet -> chaddr) { 714*9a2590e5Sderaadt note ("Discarding packet with invalid hlen."); 715*9a2590e5Sderaadt return; 716*9a2590e5Sderaadt } 717*9a2590e5Sderaadt 718*9a2590e5Sderaadt memset (&tp, 0, sizeof tp); 719*9a2590e5Sderaadt tp.raw = packet; 720*9a2590e5Sderaadt tp.packet_length = len; 721*9a2590e5Sderaadt tp.client_port = from_port; 722*9a2590e5Sderaadt tp.client_addr = from; 723*9a2590e5Sderaadt tp.interface = interface; 724*9a2590e5Sderaadt tp.haddr = hfrom; 725*9a2590e5Sderaadt 726*9a2590e5Sderaadt parse_options (&tp); 727*9a2590e5Sderaadt if (tp.options_valid && 728*9a2590e5Sderaadt tp.options [DHO_DHCP_MESSAGE_TYPE].data) 729*9a2590e5Sderaadt tp.packet_type = 730*9a2590e5Sderaadt tp.options [DHO_DHCP_MESSAGE_TYPE].data [0]; 731*9a2590e5Sderaadt if (tp.packet_type) 732*9a2590e5Sderaadt dhcp (&tp); 733*9a2590e5Sderaadt else 734*9a2590e5Sderaadt bootp (&tp); 735*9a2590e5Sderaadt 736*9a2590e5Sderaadt /* Free the data associated with the options. */ 737*9a2590e5Sderaadt for (i = 0; i < 256; i++) { 738*9a2590e5Sderaadt if (tp.options [i].len && tp.options [i].data) 739*9a2590e5Sderaadt dfree (tp.options [i].data, "do_packet"); 740*9a2590e5Sderaadt } 741*9a2590e5Sderaadt } 742*9a2590e5Sderaadt 743