📄 options.c
字号:
/* options.c DHCP options parsing and reassembly. *//* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * <info@isc.org> * http://www.isc.org/ * * This software has been written for Internet Systems Consortium * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. * To learn more about Internet Systems Consortium, see * ``http://www.isc.org/''. To learn more about Vixie Enterprises, * see ``http://www.vix.com''. To learn more about Nominum, Inc., see * ``http://www.nominum.com''. */#ifndef lintstatic char copyright[] ="$Id: options.c,v 1.85.2.13 2004/06/10 17:59:19 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";#endif /* not lint */#define DHCP_OPTION_DATA#include "dhcpd.h"#include <omapip/omapip_p.h>struct option *vendor_cfg_option;static void do_option_set PROTO ((pair *, struct option_cache *, enum statement_op));/* Parse all available options out of the specified packet. */int parse_options (packet) struct packet *packet;{ int i; struct option_cache *op = (struct option_cache *)0; /* Allocate a new option state. */ if (!option_state_allocate (&packet -> options, MDL)) { packet -> options_valid = 0; return 0; } /* If we don't see the magic cookie, there's nothing to parse. */ if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) { packet -> options_valid = 0; return 1; } /* Go through the options field, up to the end of the packet or the End field. */ if (!parse_option_buffer (packet -> options, &packet -> raw -> options [4], (packet -> packet_length - DHCP_FIXED_NON_UDP - 4), &dhcp_universe)) return 0; /* If we parsed a DHCP Option Overload option, parse more options out of the buffer(s) containing them. */ if (packet -> options_valid && (op = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_OPTION_OVERLOAD))) { if (op -> data.data [0] & 1) { if (!parse_option_buffer (packet -> options, (unsigned char *)packet -> raw -> file, sizeof packet -> raw -> file, &dhcp_universe)) return 0; } if (op -> data.data [0] & 2) { if (!parse_option_buffer (packet -> options, (unsigned char *)packet -> raw -> sname, sizeof packet -> raw -> sname, &dhcp_universe)) return 0; } } packet -> options_valid = 1; return 1;}/* Parse options out of the specified buffer, storing addresses of option values in packet -> options and setting packet -> options_valid if no errors are encountered. */int parse_option_buffer (options, buffer, length, universe) struct option_state *options; const unsigned char *buffer; unsigned length; struct universe *universe;{ unsigned char *t; const unsigned char *end = buffer + length; unsigned len, offset; int code; struct option_cache *op = (struct option_cache *)0; struct buffer *bp = (struct buffer *)0; if (!buffer_allocate (&bp, length, MDL)) { log_error ("no memory for option buffer."); return 0; } memcpy (bp -> data, buffer, length); for (offset = 0; buffer [offset] != DHO_END && offset < length; ) { code = buffer [offset]; /* Pad options don't have a length - just skip them. */ if (code == DHO_PAD) { ++offset; continue; } /* Don't look for length if the buffer isn't that big. */ if (offset + 2 > length) { len = 65536; goto bogus; } /* All other fields (except end, see above) have a one-byte length. */ len = buffer [offset + 1]; /* If the length is outrageous, the options are bad. */ if (offset + len + 2 > length) { bogus: log_error ("parse_option_buffer: option %s (%d) %s.", dhcp_options [code].name, len, "larger than buffer"); buffer_dereference (&bp, MDL); return 0; } /* If the option contains an encapsulation, parse it. If the parse fails, or the option isn't an encapsulation (by far the most common case), or the option isn't entirely an encapsulation, keep the raw data as well. */ if (universe -> options [code] && !((universe -> options [code] -> format [0] == 'e' || universe -> options [code] -> format [0] == 'E') && (parse_encapsulated_suboptions (options, universe -> options [code], buffer + offset + 2, len, universe, (const char *)0)))) { op = lookup_option (universe, options, code); if (op) { struct data_string new; memset (&new, 0, sizeof new); if (!buffer_allocate (&new.buffer, op -> data.len + len, MDL)) { log_error ("parse_option_buffer: No memory."); return 0; } memcpy (new.buffer -> data, op -> data.data, op -> data.len); memcpy (&new.buffer -> data [op -> data.len], &bp -> data [offset + 2], len); new.len = op -> data.len + len; new.data = new.buffer -> data; data_string_forget (&op -> data, MDL); data_string_copy (&op -> data, &new, MDL); data_string_forget (&new, MDL); } else { save_option_buffer (universe, options, bp, &bp -> data [offset + 2], len, universe -> options [code], 1); } } offset += len + 2; } buffer_dereference (&bp, MDL); return 1;}/* If an option in an option buffer turns out to be an encapsulation, figure out what to do. If we don't know how to de-encapsulate it, or it's not well-formed, return zero; otherwise, return 1, indicating that we succeeded in de-encapsulating it. */struct universe *find_option_universe (struct option *eopt, const char *uname){ int i; char *s, *t; struct universe *universe = (struct universe *)0; /* Look for the E option in the option format. */ s = strchr (eopt -> format, 'E'); if (!s) { log_error ("internal encapsulation format error 1."); return 0; } /* Look for the universe name in the option format. */ t = strchr (++s, '.'); /* If there was no trailing '.', or there's something after the trailing '.', the option is bogus and we can't use it. */ if (!t || t [1]) { log_error ("internal encapsulation format error 2."); return 0; } if (t == s && uname) { for (i = 0; i < universe_count; i++) { if (!strcmp (universes [i] -> name, uname)) { universe = universes [i]; break; } } } else if (t != s) { for (i = 0; i < universe_count; i++) { if (strlen (universes [i] -> name) == t - s && !memcmp (universes [i] -> name, s, (unsigned)(t - s))) { universe = universes [i]; break; } } } return universe;}/* If an option in an option buffer turns out to be an encapsulation, figure out what to do. If we don't know how to de-encapsulate it, or it's not well-formed, return zero; otherwise, return 1, indicating that we succeeded in de-encapsulating it. */int parse_encapsulated_suboptions (struct option_state *options, struct option *eopt, const unsigned char *buffer, unsigned len, struct universe *eu, const char *uname){ int i; struct universe *universe = find_option_universe (eopt, uname); /* If we didn't find the universe, we can't do anything with it right now (e.g., we can't decode vendor options until we've decoded the packet and executed the scopes that it matches). */ if (!universe) return 0; /* If we don't have a decoding function for it, we can't decode it. */ if (!universe -> decode) return 0; i = (*universe -> decode) (options, buffer, len, universe); /* If there is stuff before the suboptions, we have to keep it. */ if (eopt -> format [0] != 'E') return 0; /* Otherwise, return the status of the decode function. */ return i;}int fqdn_universe_decode (struct option_state *options, const unsigned char *buffer, unsigned length, struct universe *u){ char *name; struct buffer *bp = (struct buffer *)0; /* FQDN options have to be at least four bytes long. */ if (length < 3) return 0; /* Save the contents of the option in a buffer. */ if (!buffer_allocate (&bp, length + 4, MDL)) { log_error ("no memory for option buffer."); return 0; } memcpy (&bp -> data [3], buffer + 1, length - 1); if (buffer [0] & 4) /* encoded */ bp -> data [0] = 1; else bp -> data [0] = 0; if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [0], 1, &fqdn_options [FQDN_ENCODED], 0)) { bad: buffer_dereference (&bp, MDL); return 0; } if (buffer [0] & 1) /* server-update */ bp -> data [2] = 1; else bp -> data [2] = 0; if (buffer [0] & 2) /* no-client-update */ bp -> data [1] = 1; else bp -> data [1] = 0; /* XXX Ideally we should store the name in DNS format, so if the XXX label isn't in DNS format, we convert it to DNS format, XXX rather than converting labels specified in DNS format to XXX the plain ASCII representation. But that's hard, so XXX not now. */ /* Not encoded using DNS format? */ if (!bp -> data [0]) { unsigned i; /* Some broken clients NUL-terminate this option. */ if (buffer [length - 1] == 0) { --length; bp -> data [1] = 1; } /* Determine the length of the hostname component of the name. If the name contains no '.' character, it represents a non-qualified label. */ for (i = 3; i < length && buffer [i] != '.'; i++); i -= 3; /* Note: If the client sends a FQDN, the first '.' will be used as a NUL terminator for the hostname. */ if (i) if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data[5], i, &fqdn_options [FQDN_HOSTNAME], 0)) goto bad; /* Note: If the client sends a single label, the FQDN_DOMAINNAME option won't be set. */ if (length > 4 + i && !save_option_buffer (&fqdn_universe, options, bp, &bp -> data[6 + i], length - 4 - i, &fqdn_options [FQDN_DOMAINNAME], 1)) goto bad; /* Also save the whole name. */ if (length > 3) if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [5], length - 3, &fqdn_options [FQDN_FQDN], 1)) goto bad; } else { unsigned len; unsigned total_len = 0; unsigned first_len = 0; int terminated = 0; unsigned char *s; s = &bp -> data[5]; while (s < &bp -> data[0] + length + 2) { len = *s; if (len > 63) { log_info ("fancy bits in fqdn option"); return 0; } if (len == 0) { terminated = 1; break; } if (s + len > &bp -> data [0] + length + 3) { log_info ("fqdn tag longer than buffer"); return 0; } if (first_len == 0) { first_len = len; } *s = '.'; s += len + 1; total_len += len + 1; } /* We wind up with a length that's one too many because we shouldn't increment for the last label, but there's no way to tell we're at the last label until we exit the loop. :'*/ if (total_len > 0) total_len--; if (!terminated) { first_len = total_len; } if (first_len > 0 && !save_option_buffer (&fqdn_universe, options, bp, &bp -> data[6], first_len, &fqdn_options [FQDN_HOSTNAME], 0)) goto bad; if (total_len > 0 && first_len != total_len) { if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data[6 + first_len], total_len - first_len, &fqdn_options [FQDN_DOMAINNAME], 1)) goto bad; } if (total_len > 0) if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [6], total_len, &fqdn_options [FQDN_FQDN], 1)) goto bad; } if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [1], 1, &fqdn_options [FQDN_NO_CLIENT_UPDATE], 0)) goto bad; if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [2], 1, &fqdn_options [FQDN_SERVER_UPDATE], 0)) goto bad; if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [3], 1, &fqdn_options [FQDN_RCODE1], 0)) goto bad; if (!save_option_buffer (&fqdn_universe, options, bp, &bp -> data [4], 1, &fqdn_options [FQDN_RCODE2], 0)) goto bad; buffer_dereference (&bp, MDL); return 1;}/* cons options into a big buffer, and then split them out into the three seperate buffers if needed. This allows us to cons up a set of vendor options using the same routine. */int cons_options (inpacket, outpacket, lease, client_state, mms, in_options, cfg_options, scope, overload, terminate, bootpp, prl, vuname) struct packet *inpacket;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -