1*99c003b1Skrw /* $OpenBSD: options.c,v 1.41 2012/06/26 14:46:42 krw Exp $ */ 29a2590e5Sderaadt 3e7eb2effShenning /* DHCP options parsing and reassembly. */ 49a2590e5Sderaadt 59a2590e5Sderaadt /* 69a2590e5Sderaadt * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. 79a2590e5Sderaadt * All rights reserved. 89a2590e5Sderaadt * 99a2590e5Sderaadt * Redistribution and use in source and binary forms, with or without 109a2590e5Sderaadt * modification, are permitted provided that the following conditions 119a2590e5Sderaadt * are met: 129a2590e5Sderaadt * 139a2590e5Sderaadt * 1. Redistributions of source code must retain the above copyright 149a2590e5Sderaadt * notice, this list of conditions and the following disclaimer. 159a2590e5Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 169a2590e5Sderaadt * notice, this list of conditions and the following disclaimer in the 179a2590e5Sderaadt * documentation and/or other materials provided with the distribution. 189a2590e5Sderaadt * 3. Neither the name of The Internet Software Consortium nor the names 199a2590e5Sderaadt * of its contributors may be used to endorse or promote products derived 209a2590e5Sderaadt * from this software without specific prior written permission. 219a2590e5Sderaadt * 229a2590e5Sderaadt * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 239a2590e5Sderaadt * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 249a2590e5Sderaadt * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 259a2590e5Sderaadt * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 269a2590e5Sderaadt * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 279a2590e5Sderaadt * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 289a2590e5Sderaadt * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 299a2590e5Sderaadt * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 309a2590e5Sderaadt * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 319a2590e5Sderaadt * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 329a2590e5Sderaadt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 339a2590e5Sderaadt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 349a2590e5Sderaadt * SUCH DAMAGE. 359a2590e5Sderaadt * 369a2590e5Sderaadt * This software has been written for the Internet Software Consortium 379a2590e5Sderaadt * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 389a2590e5Sderaadt * Enterprises. To learn more about the Internet Software Consortium, 399a2590e5Sderaadt * see ``http://www.vix.com/isc''. To learn more about Vixie 409a2590e5Sderaadt * Enterprises, see ``http://www.vix.com''. 419a2590e5Sderaadt */ 429a2590e5Sderaadt 43c714dadcShenning #include <ctype.h> 44c714dadcShenning 459a2590e5Sderaadt #include "dhcpd.h" 469a2590e5Sderaadt 4702e02bd5Skrw int parse_option_buffer(struct option_data *, unsigned char *, int); 489a2590e5Sderaadt 49c714dadcShenning /* 50c714dadcShenning * Parse options out of the specified buffer, storing addresses of 514f062ee3Skrw * option values in options and setting client->options_valid if 52c714dadcShenning * no errors are encountered. 53c714dadcShenning */ 5402e02bd5Skrw int 554f062ee3Skrw parse_option_buffer(struct option_data *options, unsigned char *buffer, 564f062ee3Skrw int length) 579a2590e5Sderaadt { 58285f06efSderaadt unsigned char *s, *t, *end = buffer + length; 59285f06efSderaadt int len, code; 609a2590e5Sderaadt 619a2590e5Sderaadt for (s = buffer; *s != DHO_END && s < end; ) { 629a2590e5Sderaadt code = s[0]; 639a2590e5Sderaadt 649a2590e5Sderaadt /* Pad options don't have a length - just skip them. */ 659a2590e5Sderaadt if (code == DHO_PAD) { 66f1e89499Shenning s++; 679a2590e5Sderaadt continue; 689a2590e5Sderaadt } 699a2590e5Sderaadt 70c714dadcShenning /* 71*99c003b1Skrw * All options other than DHO_PAD and DHO_END have a one-byte 72*99c003b1Skrw * length field. It could be 0! Make sure that the length byte 73*99c003b1Skrw * is present, and all the data is available. 74c714dadcShenning */ 75*99c003b1Skrw if (s + 1 < end) { 769a2590e5Sderaadt len = s[1]; 77*99c003b1Skrw if (s + 1 + len < end) { 78*99c003b1Skrw ; /* option data is all there. */ 79*99c003b1Skrw } else { 80b6fc88b9Skrw warning("option %s (%d) larger than buffer.", 81b6fc88b9Skrw dhcp_options[code].name, len); 82c955dd46Smickey warning("rejecting bogus offer."); 8302e02bd5Skrw return (0); 849a2590e5Sderaadt } 85*99c003b1Skrw } else { 86*99c003b1Skrw warning("option %s has no length field.", 87*99c003b1Skrw dhcp_options[code].name); 88*99c003b1Skrw warning("rejecting bogus offer."); 89*99c003b1Skrw return (0); 90*99c003b1Skrw } 91df453039Skrw 92df453039Skrw /* 93df453039Skrw * Strip trailing NULs from ascii ('t') options. They 94df453039Skrw * will be treated as DHO_PAD options. i.e. ignored. RFC 2132 95df453039Skrw * says "Options containing NVT ASCII data SHOULD NOT include 96df453039Skrw * a trailing NULL; however, the receiver of such options 97df453039Skrw * MUST be prepared to delete trailing nulls if they exist." 98df453039Skrw */ 99df453039Skrw if (dhcp_options[code].format[0] == 't') { 100*99c003b1Skrw while (len > 0 && s[len + 1] == '\0') 101*99c003b1Skrw len--; 102df453039Skrw } 103df453039Skrw 104c714dadcShenning /* 105c714dadcShenning * If we haven't seen this option before, just make 106c714dadcShenning * space for it and copy it there. 107c714dadcShenning */ 1084f062ee3Skrw if (!options[code].data) { 1098e916ab9Shenning if (!(t = calloc(1, len + 1))) 1109a2590e5Sderaadt error("Can't allocate storage for option %s.", 1119a2590e5Sderaadt dhcp_options[code].name); 112c714dadcShenning /* 113c714dadcShenning * Copy and NUL-terminate the option (in case 114cff08477Sstevesk * it's an ASCII string). 115c714dadcShenning */ 1169a2590e5Sderaadt memcpy(t, &s[2], len); 1179a2590e5Sderaadt t[len] = 0; 1184f062ee3Skrw options[code].len = len; 1194f062ee3Skrw options[code].data = t; 1209a2590e5Sderaadt } else { 121c714dadcShenning /* 122c714dadcShenning * If it's a repeat, concatenate it to whatever 123c714dadcShenning * we last saw. This is really only required 124c714dadcShenning * for clients, but what the heck... 125c714dadcShenning */ 1264f062ee3Skrw t = calloc(1, len + options[code].len + 1); 1279a2590e5Sderaadt if (!t) 1289a2590e5Sderaadt error("Can't expand storage for option %s.", 1299a2590e5Sderaadt dhcp_options[code].name); 1304f062ee3Skrw memcpy(t, options[code].data, options[code].len); 1314f062ee3Skrw memcpy(t + options[code].len, &s[2], len); 1324f062ee3Skrw options[code].len += len; 1334f062ee3Skrw t[options[code].len] = 0; 1344f062ee3Skrw free(options[code].data); 1354f062ee3Skrw options[code].data = t; 1369a2590e5Sderaadt } 1379a2590e5Sderaadt s += len + 2; 1389a2590e5Sderaadt } 13902e02bd5Skrw 14002e02bd5Skrw return (1); 1419a2590e5Sderaadt } 1429a2590e5Sderaadt 143c714dadcShenning /* 14496978980Skrw * Copy as many options as fit in buflen bytes of buf. Return the 14596978980Skrw * offset of the start of the last option copied. A caller can check 14696978980Skrw * to see if it's DHO_END to decide if all the options were copied. 147c714dadcShenning */ 148c714dadcShenning int 149d6a67f0fSkrw cons_options(struct option_data *options) 1509a2590e5Sderaadt { 151d6a67f0fSkrw unsigned char *buf = client->packet.options; 152d6a67f0fSkrw int buflen = 576 - DHCP_FIXED_LEN; 15396978980Skrw int ix, incr, length, bufix, code, lastopt = -1; 1549a2590e5Sderaadt 15596978980Skrw bzero(buf, buflen); 1569a2590e5Sderaadt 15796978980Skrw memcpy(buf, DHCP_OPTIONS_COOKIE, 4); 158d6a67f0fSkrw if (options[DHO_DHCP_MESSAGE_TYPE].data) { 159d6a67f0fSkrw memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3); 160d6a67f0fSkrw buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0]; 161d6a67f0fSkrw bufix = 7; 162d6a67f0fSkrw } else 16396978980Skrw bufix = 4; 1649a2590e5Sderaadt 16596978980Skrw for (code = DHO_SUBNET_MASK; code < DHO_END; code++) { 166d6a67f0fSkrw if (!options[code].data || code == DHO_DHCP_MESSAGE_TYPE) 1679a2590e5Sderaadt continue; 1689a2590e5Sderaadt 169d7d9bbf5Skrw length = options[code].len; 17096978980Skrw if (bufix + length + 2*((length+254)/255) >= buflen) 17196978980Skrw return (lastopt); 1729a2590e5Sderaadt 17396978980Skrw lastopt = bufix; 1749a2590e5Sderaadt ix = 0; 1759a2590e5Sderaadt 1769a2590e5Sderaadt while (length) { 17796978980Skrw incr = length > 255 ? 255 : length; 1789a2590e5Sderaadt 17996978980Skrw buf[bufix++] = code; 18096978980Skrw buf[bufix++] = incr; 18196978980Skrw memcpy(buf + bufix, options[code].data + ix, incr); 1829a2590e5Sderaadt 1839a2590e5Sderaadt length -= incr; 1849a2590e5Sderaadt ix += incr; 1856fc9f4f6Skrw bufix += incr; 1869a2590e5Sderaadt } 1879a2590e5Sderaadt } 18896978980Skrw 18996978980Skrw if (bufix < buflen) { 19096978980Skrw buf[bufix] = DHO_END; 19196978980Skrw lastopt = bufix; 19296978980Skrw } 19396978980Skrw 19496978980Skrw return (lastopt); 1959a2590e5Sderaadt } 1969a2590e5Sderaadt 197c714dadcShenning /* 198c714dadcShenning * Format the specified option so that a human can easily read it. 199c714dadcShenning */ 200c714dadcShenning char * 201c714dadcShenning pretty_print_option(unsigned int code, unsigned char *data, int len, 202c714dadcShenning int emit_commas, int emit_quotes) 2039a2590e5Sderaadt { 2049a2590e5Sderaadt static char optbuf[32768]; /* XXX */ 205285f06efSderaadt int hunksize = 0, numhunk = -1, numelem = 0; 206285f06efSderaadt char fmtbuf[32], *op = optbuf; 207285f06efSderaadt int i, j, k, opleft = sizeof(optbuf); 2089a2590e5Sderaadt unsigned char *dp = data; 2099a2590e5Sderaadt struct in_addr foo; 2109a2590e5Sderaadt char comma; 2119a2590e5Sderaadt 2129a2590e5Sderaadt /* Code should be between 0 and 255. */ 2139a2590e5Sderaadt if (code > 255) 2145eb2300aShenning error("pretty_print_option: bad code %d", code); 2159a2590e5Sderaadt 2169a2590e5Sderaadt if (emit_commas) 2179a2590e5Sderaadt comma = ','; 2189a2590e5Sderaadt else 2199a2590e5Sderaadt comma = ' '; 2209a2590e5Sderaadt 2219a2590e5Sderaadt /* Figure out the size of the data. */ 2229a2590e5Sderaadt for (i = 0; dhcp_options[code].format[i]; i++) { 2239a2590e5Sderaadt if (!numhunk) { 224c955dd46Smickey warning("%s: Excess information in format string: %s", 2259a2590e5Sderaadt dhcp_options[code].name, 2269a2590e5Sderaadt &(dhcp_options[code].format[i])); 2279a2590e5Sderaadt break; 2289a2590e5Sderaadt } 2299a2590e5Sderaadt numelem++; 2309a2590e5Sderaadt fmtbuf[i] = dhcp_options[code].format[i]; 2319a2590e5Sderaadt switch (dhcp_options[code].format[i]) { 2329a2590e5Sderaadt case 'A': 2339a2590e5Sderaadt --numelem; 2349a2590e5Sderaadt fmtbuf[i] = 0; 2359a2590e5Sderaadt numhunk = 0; 23629432cd9Sphessler if (hunksize == 0) { 23729432cd9Sphessler warning("%s: no size indicator before A" 23829432cd9Sphessler " in format string: %s", 23929432cd9Sphessler dhcp_options[code].name, 24029432cd9Sphessler dhcp_options[code].format); 24129432cd9Sphessler return ("<fmt error>"); 24229432cd9Sphessler } 2439a2590e5Sderaadt break; 2449a2590e5Sderaadt case 'X': 245c714dadcShenning for (k = 0; k < len; k++) 2469a2590e5Sderaadt if (!isascii(data[k]) || 2479a2590e5Sderaadt !isprint(data[k])) 2489a2590e5Sderaadt break; 249b54c879eShenning if (k == len) { 2509a2590e5Sderaadt fmtbuf[i] = 't'; 2519a2590e5Sderaadt numhunk = -2; 2529a2590e5Sderaadt } else { 2539a2590e5Sderaadt fmtbuf[i] = 'x'; 2549a2590e5Sderaadt hunksize++; 2559a2590e5Sderaadt comma = ':'; 2569a2590e5Sderaadt numhunk = 0; 2579a2590e5Sderaadt } 2589a2590e5Sderaadt fmtbuf[i + 1] = 0; 2599a2590e5Sderaadt break; 2609a2590e5Sderaadt case 't': 2619a2590e5Sderaadt fmtbuf[i] = 't'; 2629a2590e5Sderaadt fmtbuf[i + 1] = 0; 2639a2590e5Sderaadt numhunk = -2; 2649a2590e5Sderaadt break; 2659a2590e5Sderaadt case 'I': 2669a2590e5Sderaadt case 'l': 2679a2590e5Sderaadt case 'L': 2689a2590e5Sderaadt hunksize += 4; 2699a2590e5Sderaadt break; 2709a2590e5Sderaadt case 's': 2719a2590e5Sderaadt case 'S': 2729a2590e5Sderaadt hunksize += 2; 2739a2590e5Sderaadt break; 2749a2590e5Sderaadt case 'b': 2759a2590e5Sderaadt case 'B': 2769a2590e5Sderaadt case 'f': 2779a2590e5Sderaadt hunksize++; 2789a2590e5Sderaadt break; 2799a2590e5Sderaadt case 'e': 2809a2590e5Sderaadt break; 2819a2590e5Sderaadt default: 282c955dd46Smickey warning("%s: garbage in format string: %s", 2839a2590e5Sderaadt dhcp_options[code].name, 2849a2590e5Sderaadt &(dhcp_options[code].format[i])); 2859a2590e5Sderaadt break; 2869a2590e5Sderaadt } 2879a2590e5Sderaadt } 2889a2590e5Sderaadt 2899a2590e5Sderaadt /* Check for too few bytes... */ 2909a2590e5Sderaadt if (hunksize > len) { 291c955dd46Smickey warning("%s: expecting at least %d bytes; got %d", 292c714dadcShenning dhcp_options[code].name, hunksize, len); 293c714dadcShenning return ("<error>"); 2949a2590e5Sderaadt } 2959a2590e5Sderaadt /* Check for too many bytes... */ 2969a2590e5Sderaadt if (numhunk == -1 && hunksize < len) 297c955dd46Smickey warning("%s: %d extra bytes", 298c714dadcShenning dhcp_options[code].name, len - hunksize); 2999a2590e5Sderaadt 3009a2590e5Sderaadt /* If this is an array, compute its size. */ 3019a2590e5Sderaadt if (!numhunk) 3029a2590e5Sderaadt numhunk = len / hunksize; 3039a2590e5Sderaadt /* See if we got an exact number of hunks. */ 3049a2590e5Sderaadt if (numhunk > 0 && numhunk * hunksize < len) 305c955dd46Smickey warning("%s: %d extra bytes at end of array", 306c714dadcShenning dhcp_options[code].name, len - numhunk * hunksize); 3079a2590e5Sderaadt 3089a2590e5Sderaadt /* A one-hunk array prints the same as a single hunk. */ 3099a2590e5Sderaadt if (numhunk < 0) 3109a2590e5Sderaadt numhunk = 1; 3119a2590e5Sderaadt 3129a2590e5Sderaadt /* Cycle through the array (or hunk) printing the data. */ 3139a2590e5Sderaadt for (i = 0; i < numhunk; i++) { 3149a2590e5Sderaadt for (j = 0; j < numelem; j++) { 3159a2590e5Sderaadt int opcount; 316975511d9Spvalchev size_t oplen; 3179a2590e5Sderaadt switch (fmtbuf[j]) { 3189a2590e5Sderaadt case 't': 3199a2590e5Sderaadt if (emit_quotes) { 3209a2590e5Sderaadt *op++ = '"'; 3219a2590e5Sderaadt opleft--; 3229a2590e5Sderaadt } 3239a2590e5Sderaadt for (; dp < data + len; dp++) { 3249a2590e5Sderaadt if (!isascii(*dp) || 3259a2590e5Sderaadt !isprint(*dp)) { 3269a2590e5Sderaadt if (dp + 1 != data + len || 3279a2590e5Sderaadt *dp != 0) { 328d0538928Spvalchev size_t oplen; 3299a2590e5Sderaadt snprintf(op, opleft, 3309a2590e5Sderaadt "\\%03o", *dp); 331d0538928Spvalchev oplen = strlen(op); 332d0538928Spvalchev op += oplen; 333d0538928Spvalchev opleft -= oplen; 3349a2590e5Sderaadt } 3359a2590e5Sderaadt } else if (*dp == '"' || 3369a2590e5Sderaadt *dp == '\'' || 3379a2590e5Sderaadt *dp == '$' || 3389a2590e5Sderaadt *dp == '`' || 3399a2590e5Sderaadt *dp == '\\') { 3409a2590e5Sderaadt *op++ = '\\'; 3419a2590e5Sderaadt *op++ = *dp; 3429a2590e5Sderaadt opleft -= 2; 3439a2590e5Sderaadt } else { 3449a2590e5Sderaadt *op++ = *dp; 3459a2590e5Sderaadt opleft--; 3469a2590e5Sderaadt } 3479a2590e5Sderaadt } 3489a2590e5Sderaadt if (emit_quotes) { 3499a2590e5Sderaadt *op++ = '"'; 3509a2590e5Sderaadt opleft--; 3519a2590e5Sderaadt } 3529a2590e5Sderaadt 3539a2590e5Sderaadt *op = 0; 3549a2590e5Sderaadt break; 3559a2590e5Sderaadt case 'I': 3569a2590e5Sderaadt foo.s_addr = htonl(getULong(dp)); 357c714dadcShenning opcount = strlcpy(op, inet_ntoa(foo), opleft); 3589a2590e5Sderaadt if (opcount >= opleft) 3599a2590e5Sderaadt goto toobig; 3609a2590e5Sderaadt opleft -= opcount; 3619a2590e5Sderaadt dp += 4; 3629a2590e5Sderaadt break; 3639a2590e5Sderaadt case 'l': 3649a2590e5Sderaadt opcount = snprintf(op, opleft, "%ld", 3659a2590e5Sderaadt (long)getLong(dp)); 3666ed3db51Sderaadt if (opcount >= opleft || opcount == -1) 3679a2590e5Sderaadt goto toobig; 3689a2590e5Sderaadt opleft -= opcount; 3699a2590e5Sderaadt dp += 4; 3709a2590e5Sderaadt break; 3719a2590e5Sderaadt case 'L': 3729a2590e5Sderaadt opcount = snprintf(op, opleft, "%ld", 3739a2590e5Sderaadt (unsigned long)getULong(dp)); 3746ed3db51Sderaadt if (opcount >= opleft || opcount == -1) 3759a2590e5Sderaadt goto toobig; 3769a2590e5Sderaadt opleft -= opcount; 3779a2590e5Sderaadt dp += 4; 3789a2590e5Sderaadt break; 3799a2590e5Sderaadt case 's': 3809a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 3819a2590e5Sderaadt getShort(dp)); 3826ed3db51Sderaadt if (opcount >= opleft || opcount == -1) 3839a2590e5Sderaadt goto toobig; 3849a2590e5Sderaadt opleft -= opcount; 3859a2590e5Sderaadt dp += 2; 3869a2590e5Sderaadt break; 3879a2590e5Sderaadt case 'S': 3889a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 3899a2590e5Sderaadt getUShort(dp)); 3906ed3db51Sderaadt if (opcount >= opleft || opcount == -1) 3919a2590e5Sderaadt goto toobig; 3929a2590e5Sderaadt opleft -= opcount; 3939a2590e5Sderaadt dp += 2; 3949a2590e5Sderaadt break; 3959a2590e5Sderaadt case 'b': 3969a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", 3979a2590e5Sderaadt *(char *)dp++); 3986ed3db51Sderaadt if (opcount >= opleft || opcount == -1) 3999a2590e5Sderaadt goto toobig; 4009a2590e5Sderaadt opleft -= opcount; 4019a2590e5Sderaadt break; 4029a2590e5Sderaadt case 'B': 4039a2590e5Sderaadt opcount = snprintf(op, opleft, "%d", *dp++); 4046ed3db51Sderaadt if (opcount >= opleft || opcount == -1) 4059a2590e5Sderaadt goto toobig; 4069a2590e5Sderaadt opleft -= opcount; 4079a2590e5Sderaadt break; 4089a2590e5Sderaadt case 'x': 4099a2590e5Sderaadt opcount = snprintf(op, opleft, "%x", *dp++); 4106ed3db51Sderaadt if (opcount >= opleft || opcount == -1) 4119a2590e5Sderaadt goto toobig; 4129a2590e5Sderaadt opleft -= opcount; 4139a2590e5Sderaadt break; 4149a2590e5Sderaadt case 'f': 4159a2590e5Sderaadt opcount = strlcpy(op, 4169a2590e5Sderaadt *dp++ ? "true" : "false", opleft); 4179a2590e5Sderaadt if (opcount >= opleft) 4189a2590e5Sderaadt goto toobig; 4199a2590e5Sderaadt opleft -= opcount; 4209a2590e5Sderaadt break; 4219a2590e5Sderaadt default: 422c955dd46Smickey warning("Unexpected format code %c", fmtbuf[j]); 4239a2590e5Sderaadt } 424975511d9Spvalchev oplen = strlen(op); 425975511d9Spvalchev op += oplen; 426975511d9Spvalchev opleft -= oplen; 4279a2590e5Sderaadt if (opleft < 1) 4289a2590e5Sderaadt goto toobig; 4299a2590e5Sderaadt if (j + 1 < numelem && comma != ':') { 4309a2590e5Sderaadt *op++ = ' '; 4319a2590e5Sderaadt opleft--; 4329a2590e5Sderaadt } 4339a2590e5Sderaadt } 4349a2590e5Sderaadt if (i + 1 < numhunk) { 4359a2590e5Sderaadt *op++ = comma; 4369a2590e5Sderaadt opleft--; 4379a2590e5Sderaadt } 4389a2590e5Sderaadt if (opleft < 1) 4399a2590e5Sderaadt goto toobig; 4409a2590e5Sderaadt 4419a2590e5Sderaadt } 442c714dadcShenning return (optbuf); 4439a2590e5Sderaadt toobig: 444c955dd46Smickey warning("dhcp option too large"); 445c714dadcShenning return ("<error>"); 4469a2590e5Sderaadt } 4479a2590e5Sderaadt 448c714dadcShenning void 449d5dec8a0Skrw do_packet(int len, unsigned int from_port, struct iaddr from, 450d5dec8a0Skrw struct hardware *hfrom) 4519a2590e5Sderaadt { 45202e02bd5Skrw struct dhcp_packet *packet = &client->packet; 4534f062ee3Skrw struct option_data options[256]; 45402e02bd5Skrw struct iaddrlist *ap; 45502e02bd5Skrw void (*handler)(struct iaddr, struct option_data *); 45602e02bd5Skrw char *type; 45702e02bd5Skrw int i, options_valid = 1; 4589a2590e5Sderaadt 45902e02bd5Skrw if (packet->hlen > sizeof(packet->chaddr)) { 4609a2590e5Sderaadt note("Discarding packet with invalid hlen."); 4619a2590e5Sderaadt return; 4629a2590e5Sderaadt } 4639a2590e5Sderaadt 46402e02bd5Skrw /* 46502e02bd5Skrw * Silently drop the packet if the client hardware address in the 46602e02bd5Skrw * packet is not the hardware address of the interface being managed. 46702e02bd5Skrw */ 46802e02bd5Skrw if ((ifi->hw_address.hlen != packet->hlen) || 46902e02bd5Skrw (memcmp(ifi->hw_address.haddr, packet->chaddr, packet->hlen))) 47002e02bd5Skrw return; 4719a2590e5Sderaadt 47202e02bd5Skrw memset(options, 0, sizeof(options)); 47302e02bd5Skrw 47402e02bd5Skrw if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) { 47502e02bd5Skrw /* Parse the BOOTP/DHCP options field. */ 47602e02bd5Skrw options_valid = parse_option_buffer(options, 47702e02bd5Skrw &packet->options[4], sizeof(packet->options) - 4); 47802e02bd5Skrw 47902e02bd5Skrw /* Only DHCP packets have overload areas for options. */ 48002e02bd5Skrw if (options_valid && 48102e02bd5Skrw options[DHO_DHCP_MESSAGE_TYPE].data && 48202e02bd5Skrw options[DHO_DHCP_OPTION_OVERLOAD].data) { 48302e02bd5Skrw if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) 48402e02bd5Skrw options_valid = parse_option_buffer(options, 48502e02bd5Skrw (unsigned char *)packet->file, 48602e02bd5Skrw sizeof(packet->file)); 48702e02bd5Skrw if (options_valid && 48802e02bd5Skrw options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) 48902e02bd5Skrw options_valid = parse_option_buffer(options, 49002e02bd5Skrw (unsigned char *)packet->sname, 49102e02bd5Skrw sizeof(packet->sname)); 49202e02bd5Skrw } 49302e02bd5Skrw } 49402e02bd5Skrw 49502e02bd5Skrw type = ""; 49602e02bd5Skrw handler = NULL; 49702e02bd5Skrw 4984f062ee3Skrw if (options[DHO_DHCP_MESSAGE_TYPE].data) { 49902e02bd5Skrw /* Always try a DHCP packet, even if a bad option was seen. */ 50002e02bd5Skrw switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) { 50102e02bd5Skrw case DHCPOFFER: 50202e02bd5Skrw handler = dhcpoffer; 50302e02bd5Skrw type = "DHCPOFFER"; 50402e02bd5Skrw break; 50502e02bd5Skrw case DHCPNAK: 50602e02bd5Skrw handler = dhcpnak; 50702e02bd5Skrw type = "DHCPNACK"; 50802e02bd5Skrw break; 50902e02bd5Skrw case DHCPACK: 51002e02bd5Skrw handler = dhcpack; 51102e02bd5Skrw type = "DHCPACK"; 51202e02bd5Skrw break; 51302e02bd5Skrw default: 51402e02bd5Skrw break; 51502e02bd5Skrw } 51602e02bd5Skrw } else if (options_valid && packet->op == BOOTREPLY) { 51702e02bd5Skrw handler = dhcpoffer; 51802e02bd5Skrw type = "BOOTREPLY"; 51902e02bd5Skrw } 5209a2590e5Sderaadt 521ce824bacSkrw if (handler && client->xid == client->packet.xid) { 522ce824bacSkrw if (hfrom->hlen == 6) 523ce824bacSkrw note("%s from %s (%s)", type, piaddr(from), 524ce824bacSkrw ether_ntoa((struct ether_addr *)hfrom->haddr)); 525ce824bacSkrw else 526ce824bacSkrw note("%s from %s", type, piaddr(from)); 527ce824bacSkrw } else 528ce824bacSkrw handler = NULL; 529ce824bacSkrw 53002e02bd5Skrw for (ap = config->reject_list; ap && handler; ap = ap->next) 53102e02bd5Skrw if (addr_eq(from, ap->addr)) { 53202e02bd5Skrw note("%s from %s rejected.", type, piaddr(from)); 53302e02bd5Skrw handler = NULL; 53402e02bd5Skrw } 53502e02bd5Skrw 53602e02bd5Skrw if (handler) 53702e02bd5Skrw (*handler)(from, options); 53802e02bd5Skrw 539c714dadcShenning for (i = 0; i < 256; i++) 5404f062ee3Skrw if (options[i].len && options[i].data) 5414f062ee3Skrw free(options[i].data); 5429a2590e5Sderaadt } 543