]>
Commit | Line | Data |
---|---|---|
d7837182 TL |
1 | /* options.c |
2 | ||
3 | DHCP options parsing and reassembly. */ | |
4 | ||
5 | /* | |
49733f31 TL |
6 | * Copyright (c) 1995-2000 Internet Software Consortium. |
7 | * All rights reserved. | |
d7837182 | 8 | * |
49733f31 TL |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
d7837182 | 12 | * |
49733f31 TL |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. Neither the name of The Internet Software Consortium nor the names | |
19 | * of its contributors may be used to endorse or promote products derived | |
20 | * from this software without specific prior written permission. | |
d7837182 | 21 | * |
49733f31 TL |
22 | * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND |
23 | * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
24 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
25 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
26 | * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR | |
27 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
29 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
30 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
31 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
33 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | * | |
36 | * This software has been written for the Internet Software Consortium | |
37 | * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. | |
38 | * To learn more about the Internet Software Consortium, see | |
39 | * ``http://www.isc.org/''. To learn more about Vixie Enterprises, | |
40 | * see ``http://www.vix.com''. To learn more about Nominum, Inc., see | |
41 | * ``http://www.nominum.com''. | |
d7837182 TL |
42 | */ |
43 | ||
44 | #ifndef lint | |
45 | static char copyright[] = | |
20916cae | 46 | "$Id: options.c,v 1.59 2000/05/16 23:02:27 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium. All rights reserved.\n"; |
d7837182 TL |
47 | #endif /* not lint */ |
48 | ||
49 | #define DHCP_OPTION_DATA | |
50 | #include "dhcpd.h" | |
4bd8800e | 51 | #include <omapip/omapip_p.h> |
d7837182 | 52 | |
a370b55d | 53 | static void do_option_set PROTO ((pair *, |
b4807938 TL |
54 | struct option_cache *, |
55 | enum statement_op)); | |
56 | ||
d7837182 TL |
57 | /* Parse all available options out of the specified packet. */ |
58 | ||
a370b55d | 59 | int parse_options (packet) |
d7837182 TL |
60 | struct packet *packet; |
61 | { | |
a370b55d TL |
62 | int i; |
63 | struct option_cache *op = (struct option_cache *)0; | |
64 | ||
c9605ddb | 65 | /* Allocate a new option state. */ |
dbf6124a | 66 | if (!option_state_allocate (&packet -> options, MDL)) { |
c9605ddb TL |
67 | packet -> options_valid = 0; |
68 | return 0; | |
69 | } | |
d7837182 TL |
70 | |
71 | /* If we don't see the magic cookie, there's nothing to parse. */ | |
72 | if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) { | |
73 | packet -> options_valid = 0; | |
a370b55d | 74 | return 1; |
d7837182 TL |
75 | } |
76 | ||
77 | /* Go through the options field, up to the end of the packet | |
78 | or the End field. */ | |
a370b55d TL |
79 | if (!parse_option_buffer (packet, &packet -> raw -> options [4], |
80 | (packet -> packet_length - | |
81 | DHCP_FIXED_NON_UDP - 4))) | |
82 | return 0; | |
83 | ||
d7837182 TL |
84 | /* If we parsed a DHCP Option Overload option, parse more |
85 | options out of the buffer(s) containing them. */ | |
a370b55d | 86 | if (packet -> options_valid && |
c9605ddb | 87 | (op = lookup_option (&dhcp_universe, packet -> options, |
a370b55d TL |
88 | DHO_DHCP_OPTION_OVERLOAD))) { |
89 | if (op -> data.data [0] & 1) { | |
90 | if (!parse_option_buffer | |
91 | (packet, (unsigned char *)packet -> raw -> file, | |
92 | sizeof packet -> raw -> file)) | |
93 | return 0; | |
94 | } | |
95 | if (op -> data.data [0] & 2) { | |
96 | if (!parse_option_buffer | |
97 | (packet, | |
98 | (unsigned char *)packet -> raw -> sname, | |
99 | sizeof packet -> raw -> sname)) | |
100 | return 0; | |
101 | } | |
d7837182 | 102 | } |
a370b55d | 103 | return 1; |
d7837182 TL |
104 | } |
105 | ||
106 | /* Parse options out of the specified buffer, storing addresses of option | |
107 | values in packet -> options and setting packet -> options_valid if no | |
108 | errors are encountered. */ | |
109 | ||
a370b55d | 110 | int parse_option_buffer (packet, buffer, length) |
d7837182 TL |
111 | struct packet *packet; |
112 | unsigned char *buffer; | |
b1b7b521 | 113 | unsigned length; |
d7837182 | 114 | { |
a370b55d | 115 | unsigned char *t; |
d7837182 | 116 | unsigned char *end = buffer + length; |
a370b55d | 117 | int len, offset; |
d7837182 | 118 | int code; |
a370b55d TL |
119 | struct option_cache *op = (struct option_cache *)0; |
120 | struct buffer *bp = (struct buffer *)0; | |
d7837182 | 121 | |
dbf6124a | 122 | if (!buffer_allocate (&bp, length, MDL)) { |
c9605ddb | 123 | log_error ("no memory for option buffer."); |
a370b55d TL |
124 | return 0; |
125 | } | |
126 | memcpy (bp -> data, buffer, length); | |
127 | ||
128 | for (offset = 0; buffer [offset] != DHO_END && offset < length; ) { | |
129 | code = buffer [offset]; | |
d7837182 TL |
130 | /* Pad options don't have a length - just skip them. */ |
131 | if (code == DHO_PAD) { | |
a370b55d | 132 | ++offset; |
d7837182 TL |
133 | continue; |
134 | } | |
a370b55d | 135 | |
d7837182 TL |
136 | /* All other fields (except end, see above) have a |
137 | one-byte length. */ | |
a370b55d | 138 | len = buffer [offset + 1]; |
a3e52198 | 139 | |
d7837182 | 140 | /* If the length is outrageous, the options are bad. */ |
a370b55d | 141 | if (offset + len + 2 > length) { |
c9605ddb TL |
142 | log_error ("Client option %s (%d) larger than buffer.", |
143 | dhcp_options [code].name, len); | |
dbf6124a | 144 | buffer_dereference (&bp, MDL); |
a370b55d | 145 | return 0; |
d7837182 | 146 | } |
81477e91 TL |
147 | |
148 | /* If this is a Relay Agent Information option, we must | |
149 | handle it specially. */ | |
150 | if (code == DHO_DHCP_AGENT_OPTIONS) { | |
a370b55d TL |
151 | if (!parse_agent_information_option |
152 | (packet, len, buffer + offset + 2)) { | |
c9605ddb | 153 | log_error ("bad agent information option."); |
dbf6124a | 154 | buffer_dereference (&bp, MDL); |
a370b55d | 155 | return 0; |
81477e91 | 156 | } |
d7837182 | 157 | } else { |
dbf6124a | 158 | if (!option_cache_allocate (&op, MDL)) { |
c9605ddb TL |
159 | log_error ("No memory for option %s.", |
160 | dhcp_options [code].name); | |
dbf6124a | 161 | buffer_dereference (&bp, MDL); |
a370b55d TL |
162 | return 0; |
163 | } | |
164 | ||
165 | /* Reference buffer copy to option cache. */ | |
166 | op -> data.buffer = (struct buffer *)0; | |
dbf6124a | 167 | buffer_reference (&op -> data.buffer, bp, MDL); |
a370b55d TL |
168 | |
169 | /* Point option cache into buffer. */ | |
170 | op -> data.data = &bp -> data [offset + 2]; | |
171 | op -> data.len = len; | |
172 | ||
173 | /* NUL terminate (we can get away with this | |
174 | because we allocated one more than the | |
175 | buffer size, and because the byte following | |
176 | the end of an option is always the code of | |
177 | the next option, which we're getting out of | |
178 | the *original* buffer. */ | |
179 | bp -> data [offset + 2 + len] = 0; | |
180 | op -> data.terminated = 1; | |
181 | ||
182 | op -> option = &dhcp_options [code]; | |
183 | /* Now store the option. */ | |
c9605ddb | 184 | save_option (&dhcp_universe, packet -> options, op); |
a370b55d TL |
185 | |
186 | /* And let go of our reference. */ | |
dbf6124a | 187 | option_cache_dereference (&op, MDL); |
d7837182 | 188 | } |
a370b55d | 189 | offset += len + 2; |
d7837182 TL |
190 | } |
191 | packet -> options_valid = 1; | |
dbf6124a | 192 | buffer_dereference (&bp, MDL); |
a370b55d | 193 | return 1; |
d7837182 TL |
194 | } |
195 | ||
a3e52198 TL |
196 | /* cons options into a big buffer, and then split them out into the |
197 | three seperate buffers if needed. This allows us to cons up a set | |
198 | of vendor options using the same routine. */ | |
199 | ||
4038ec52 | 200 | int cons_options (inpacket, outpacket, lease, mms, in_options, cfg_options, |
dbf6124a | 201 | scope, overload, terminate, bootpp, prl) |
a3e52198 | 202 | struct packet *inpacket; |
a4cb16ca | 203 | struct dhcp_packet *outpacket; |
da38df14 | 204 | struct lease *lease; |
81477e91 | 205 | int mms; |
4038ec52 TL |
206 | struct option_state *in_options; |
207 | struct option_state *cfg_options; | |
dbf6124a | 208 | struct binding_scope *scope; |
a3e52198 | 209 | int overload; /* Overload flags that may be set. */ |
c34fcd38 | 210 | int terminate; |
eb0a9b2a | 211 | int bootpp; |
ce0ec46d | 212 | struct data_string *prl; |
a3e52198 | 213 | { |
a370b55d | 214 | #define PRIORITY_COUNT 300 |
e703795d | 215 | unsigned priority_list [PRIORITY_COUNT]; |
a3e52198 TL |
216 | int priority_len; |
217 | unsigned char buffer [4096]; /* Really big buffer... */ | |
b1b7b521 TL |
218 | unsigned main_buffer_size; |
219 | unsigned mainbufix, bufix, agentix; | |
220 | unsigned option_size; | |
221 | unsigned length; | |
a370b55d TL |
222 | int i; |
223 | struct option_cache *op; | |
224 | struct data_string ds; | |
c9605ddb | 225 | pair pp, *hash; |
a370b55d TL |
226 | |
227 | memset (&ds, 0, sizeof ds); | |
a3e52198 | 228 | |
81477e91 TL |
229 | /* If there's a Maximum Message Size option in the incoming packet |
230 | and no alternate maximum message size has been specified, take the | |
231 | one in the packet. */ | |
232 | ||
a370b55d | 233 | if (!mms && inpacket && |
c9605ddb | 234 | (op = lookup_option (&dhcp_universe, inpacket -> options, |
a370b55d | 235 | DHO_DHCP_MAX_MESSAGE_SIZE))) { |
dbf6124a TL |
236 | evaluate_option_cache (&ds, inpacket, lease, in_options, |
237 | cfg_options, scope, op, MDL); | |
a370b55d TL |
238 | if (ds.len >= sizeof (u_int16_t)) |
239 | mms = getUShort (ds.data); | |
dbf6124a | 240 | data_string_forget (&ds, MDL); |
81477e91 TL |
241 | } |
242 | ||
a3e52198 | 243 | /* If the client has provided a maximum DHCP message size, |
eb0a9b2a TL |
244 | use that; otherwise, if it's BOOTP, only 64 bytes; otherwise |
245 | use up to the minimum IP MTU size (576 bytes). */ | |
246 | /* XXX if a BOOTP client specifies a max message size, we will | |
247 | honor it. */ | |
81477e91 TL |
248 | |
249 | if (mms) { | |
250 | main_buffer_size = mms - DHCP_FIXED_LEN; | |
251 | ||
a8b53b42 TL |
252 | /* Enforce a minimum packet size... */ |
253 | if (main_buffer_size < (576 - DHCP_FIXED_LEN)) | |
254 | main_buffer_size = 576 - DHCP_FIXED_LEN; | |
0a98d498 TL |
255 | } else if (bootpp) { |
256 | if (inpacket) { | |
257 | main_buffer_size = | |
258 | inpacket -> packet_length - DHCP_FIXED_LEN; | |
259 | if (main_buffer_size < 64) | |
260 | main_buffer_size = 64; | |
261 | } else | |
262 | main_buffer_size = 64; | |
263 | } else | |
a3e52198 TL |
264 | main_buffer_size = 576 - DHCP_FIXED_LEN; |
265 | ||
81477e91 TL |
266 | /* Set a hard limit at the size of the output buffer. */ |
267 | if (main_buffer_size > sizeof buffer) | |
268 | main_buffer_size = sizeof buffer; | |
269 | ||
a3e52198 TL |
270 | /* Preload the option priority list with mandatory options. */ |
271 | priority_len = 0; | |
272 | priority_list [priority_len++] = DHO_DHCP_MESSAGE_TYPE; | |
273 | priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER; | |
274 | priority_list [priority_len++] = DHO_DHCP_LEASE_TIME; | |
275 | priority_list [priority_len++] = DHO_DHCP_MESSAGE; | |
a370b55d | 276 | priority_list [priority_len++] = DHO_DHCP_REQUESTED_ADDRESS; |
a3e52198 | 277 | |
ce0ec46d TL |
278 | if (prl && prl -> len > 0) { |
279 | data_string_truncate (prl, (PRIORITY_COUNT - priority_len)); | |
a3e52198 | 280 | |
ce0ec46d TL |
281 | for (i = 0; i < prl -> len; i++) |
282 | priority_list [priority_len++] = prl -> data [i]; | |
a3e52198 | 283 | } else { |
a370b55d TL |
284 | /* First, hardcode some more options that ought to be |
285 | sent first... */ | |
286 | priority_list [priority_len++] = DHO_SUBNET_MASK; | |
287 | priority_list [priority_len++] = DHO_ROUTERS; | |
288 | priority_list [priority_len++] = DHO_DOMAIN_NAME_SERVERS; | |
289 | priority_list [priority_len++] = DHO_HOST_NAME; | |
290 | ||
33c85638 TL |
291 | /* Append a list of the standard DHCP options from the |
292 | standard DHCP option space. Actually, if a site | |
293 | option space hasn't been specified, we wind up | |
294 | treating the dhcp option space as the site option | |
295 | space, and the first for loop is skipped, because | |
296 | it's slightly more general to do it this way, | |
297 | taking the 1Q99 DHCP futures work into account. */ | |
4038ec52 | 298 | if (cfg_options -> site_code_min) { |
33c85638 | 299 | for (i = 0; i < OPTION_HASH_SIZE; i++) { |
4038ec52 | 300 | hash = cfg_options -> universes [dhcp_universe.index]; |
c9605ddb | 301 | for (pp = hash [i]; pp; pp = pp -> cdr) { |
a370b55d | 302 | op = (struct option_cache *)(pp -> car); |
33c85638 | 303 | if (op -> option -> code < |
4038ec52 | 304 | cfg_options -> site_code_min && |
33c85638 | 305 | priority_len < PRIORITY_COUNT) |
a370b55d TL |
306 | priority_list [priority_len++] = |
307 | op -> option -> code; | |
308 | } | |
33c85638 | 309 | } |
a370b55d | 310 | } |
33c85638 TL |
311 | |
312 | /* Now cycle through the site option space, or if there | |
313 | is no site option space, we'll be cycling through the | |
314 | dhcp option space. */ | |
315 | for (i = 0; i < OPTION_HASH_SIZE; i++) { | |
4038ec52 TL |
316 | hash = (cfg_options -> universes |
317 | [cfg_options -> site_universe]); | |
33c85638 TL |
318 | for (pp = hash [i]; pp; pp = pp -> cdr) { |
319 | op = (struct option_cache *)(pp -> car); | |
320 | if (op -> option -> code >= | |
4038ec52 | 321 | cfg_options -> site_code_min && |
33c85638 TL |
322 | priority_len < PRIORITY_COUNT) |
323 | priority_list [priority_len++] = | |
324 | op -> option -> code; | |
325 | } | |
326 | } | |
a3e52198 TL |
327 | } |
328 | ||
a3e52198 | 329 | /* Copy the options into the big buffer... */ |
1a74d33b TL |
330 | option_size = store_options (buffer, |
331 | (main_buffer_size - 7 + | |
332 | ((overload & 1) ? DHCP_FILE_LEN : 0) + | |
333 | ((overload & 2) ? DHCP_SNAME_LEN : 0)), | |
4038ec52 | 334 | inpacket, |
da38df14 | 335 | lease, |
dbf6124a | 336 | in_options, cfg_options, scope, |
b4807938 | 337 | priority_list, priority_len, |
1a74d33b TL |
338 | main_buffer_size, |
339 | (main_buffer_size + | |
c34fcd38 TL |
340 | ((overload & 1) ? DHCP_FILE_LEN : 0)), |
341 | terminate); | |
a3e52198 | 342 | |
1a74d33b | 343 | /* Put the cookie up front... */ |
a4cb16ca | 344 | memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4); |
1a74d33b TL |
345 | mainbufix = 4; |
346 | ||
347 | /* If we're going to have to overload, store the overload | |
348 | option at the beginning. If we can, though, just store the | |
349 | whole thing in the packet's option buffer and leave it at | |
350 | that. */ | |
351 | if (option_size <= main_buffer_size - mainbufix) { | |
a4cb16ca | 352 | memcpy (&outpacket -> options [mainbufix], |
1a74d33b TL |
353 | buffer, option_size); |
354 | mainbufix += option_size; | |
81477e91 TL |
355 | if (mainbufix < main_buffer_size) { |
356 | agentix = mainbufix; | |
a370b55d | 357 | outpacket -> options [mainbufix++] = DHO_END; |
81477e91 TL |
358 | } else |
359 | agentix = mainbufix; | |
a4cb16ca | 360 | length = DHCP_FIXED_NON_UDP + mainbufix; |
1a74d33b | 361 | } else { |
a370b55d | 362 | outpacket -> options [mainbufix++] = DHO_DHCP_OPTION_OVERLOAD; |
a4cb16ca | 363 | outpacket -> options [mainbufix++] = 1; |
1a74d33b | 364 | if (option_size > main_buffer_size - mainbufix + DHCP_FILE_LEN) |
a4cb16ca | 365 | outpacket -> options [mainbufix++] = 3; |
1a74d33b | 366 | else |
a4cb16ca | 367 | outpacket -> options [mainbufix++] = 1; |
1a74d33b | 368 | |
a4cb16ca | 369 | memcpy (&outpacket -> options [mainbufix], |
1a74d33b | 370 | buffer, main_buffer_size - mainbufix); |
81477e91 TL |
371 | length = DHCP_FIXED_NON_UDP + main_buffer_size; |
372 | agentix = main_buffer_size; | |
373 | ||
1a74d33b | 374 | bufix = main_buffer_size - mainbufix; |
1a74d33b TL |
375 | if (overload & 1) { |
376 | if (option_size - bufix <= DHCP_FILE_LEN) { | |
a4cb16ca | 377 | memcpy (outpacket -> file, |
1a74d33b TL |
378 | &buffer [bufix], option_size - bufix); |
379 | mainbufix = option_size - bufix; | |
380 | if (mainbufix < DHCP_FILE_LEN) | |
a4cb16ca | 381 | outpacket -> file [mainbufix++] |
1a74d33b TL |
382 | = DHO_END; |
383 | while (mainbufix < DHCP_FILE_LEN) | |
a4cb16ca | 384 | outpacket -> file [mainbufix++] |
1a74d33b TL |
385 | = DHO_PAD; |
386 | } else { | |
a4cb16ca | 387 | memcpy (outpacket -> file, |
1a74d33b TL |
388 | &buffer [bufix], DHCP_FILE_LEN); |
389 | bufix += DHCP_FILE_LEN; | |
390 | } | |
391 | } | |
392 | if ((overload & 2) && option_size < bufix) { | |
a4cb16ca | 393 | memcpy (outpacket -> sname, |
1a74d33b TL |
394 | &buffer [bufix], option_size - bufix); |
395 | ||
396 | mainbufix = option_size - bufix; | |
397 | if (mainbufix < DHCP_SNAME_LEN) | |
a4cb16ca | 398 | outpacket -> file [mainbufix++] |
1a74d33b TL |
399 | = DHO_END; |
400 | while (mainbufix < DHCP_SNAME_LEN) | |
a4cb16ca | 401 | outpacket -> file [mainbufix++] |
1a74d33b TL |
402 | = DHO_PAD; |
403 | } | |
404 | } | |
81477e91 | 405 | |
d9c0544c TL |
406 | length = cons_agent_information_options (cfg_options, |
407 | outpacket, agentix, length); | |
81477e91 | 408 | |
a4cb16ca | 409 | return length; |
a3e52198 TL |
410 | } |
411 | ||
412 | /* Store all the requested options into the requested buffer. */ | |
413 | ||
4038ec52 | 414 | int store_options (buffer, buflen, packet, lease, |
dbf6124a | 415 | in_options, cfg_options, scope, priority_list, |
4038ec52 | 416 | priority_len, first_cutoff, second_cutoff, terminate) |
a3e52198 | 417 | unsigned char *buffer; |
b1b7b521 | 418 | unsigned buflen; |
4038ec52 | 419 | struct packet *packet; |
da38df14 | 420 | struct lease *lease; |
4038ec52 TL |
421 | struct option_state *in_options; |
422 | struct option_state *cfg_options; | |
dbf6124a | 423 | struct binding_scope *scope; |
b1b7b521 | 424 | unsigned *priority_list; |
a3e52198 | 425 | int priority_len; |
b1b7b521 | 426 | unsigned first_cutoff, second_cutoff; |
c34fcd38 | 427 | int terminate; |
a3e52198 TL |
428 | { |
429 | int bufix = 0; | |
a3e52198 TL |
430 | int i; |
431 | int ix; | |
c34fcd38 | 432 | int tto; |
b4807938 | 433 | struct data_string od; |
a370b55d | 434 | struct option_cache *oc; |
a3e52198 | 435 | |
a370b55d TL |
436 | memset (&od, 0, sizeof od); |
437 | ||
438 | /* Eliminate duplicate options in the parameter request list. | |
439 | There's got to be some clever knuthian way to do this: | |
440 | Eliminate all but the first occurance of a value in an array | |
441 | of values without otherwise disturbing the order of the array. */ | |
442 | for (i = 0; i < priority_len - 1; i++) { | |
443 | tto = 0; | |
444 | for (ix = i + 1; ix < priority_len + tto; ix++) { | |
445 | if (tto) | |
446 | priority_list [ix - tto] = | |
447 | priority_list [ix]; | |
448 | if (priority_list [i] == priority_list [ix]) { | |
449 | tto++; | |
450 | priority_len--; | |
451 | } | |
452 | } | |
453 | } | |
a3e52198 TL |
454 | |
455 | /* Copy out the options in the order that they appear in the | |
456 | priority list... */ | |
457 | for (i = 0; i < priority_len; i++) { | |
458 | /* Code for next option to try to store. */ | |
b1b7b521 | 459 | unsigned code = priority_list [i]; |
1a74d33b | 460 | int optstart; |
a3e52198 TL |
461 | |
462 | /* Number of bytes left to store (some may already | |
463 | have been stored by a previous pass). */ | |
464 | int length; | |
465 | ||
33c85638 TL |
466 | /* Look up the option in the site option space if the code |
467 | is above the cutoff, otherwise in the DHCP option space. */ | |
4038ec52 | 468 | if (code >= cfg_options -> site_code_min) |
33c85638 | 469 | oc = lookup_option |
4038ec52 TL |
470 | (universes [cfg_options -> site_universe], |
471 | cfg_options, code); | |
33c85638 | 472 | else |
4038ec52 | 473 | oc = lookup_option (&dhcp_universe, cfg_options, code); |
33c85638 | 474 | |
a3e52198 | 475 | /* If no data is available for this option, skip it. */ |
33c85638 | 476 | if (!oc) { |
a3e52198 TL |
477 | continue; |
478 | } | |
479 | ||
a3e52198 | 480 | /* Find the value of the option... */ |
dbf6124a TL |
481 | evaluate_option_cache (&od, packet, lease, in_options, |
482 | cfg_options, scope, oc, MDL); | |
a370b55d | 483 | if (!od.len) { |
6f76b6ac | 484 | continue; |
a370b55d | 485 | } |
6f76b6ac | 486 | |
a3e52198 | 487 | /* We should now have a constant length for the option. */ |
b4807938 | 488 | length = od.len; |
a3e52198 | 489 | |
c34fcd38 TL |
490 | /* Do we add a NUL? */ |
491 | if (terminate && dhcp_options [code].format [0] == 't') { | |
492 | length++; | |
493 | tto = 1; | |
494 | } else { | |
495 | tto = 0; | |
496 | } | |
497 | ||
1a74d33b | 498 | /* Try to store the option. */ |
a3e52198 TL |
499 | |
500 | /* If the option's length is more than 255, we must store it | |
1a74d33b TL |
501 | in multiple hunks. Store 255-byte hunks first. However, |
502 | in any case, if the option data will cross a buffer | |
503 | boundary, split it across that boundary. */ | |
a3e52198 TL |
504 | |
505 | ix = 0; | |
506 | ||
1a74d33b | 507 | optstart = bufix; |
a3e52198 TL |
508 | while (length) { |
509 | unsigned char incr = length > 255 ? 255 : length; | |
1a74d33b TL |
510 | |
511 | /* If this hunk of the buffer will cross a | |
512 | boundary, only go up to the boundary in this | |
513 | pass. */ | |
514 | if (bufix < first_cutoff && | |
515 | bufix + incr > first_cutoff) | |
516 | incr = first_cutoff - bufix; | |
517 | else if (bufix < second_cutoff && | |
518 | bufix + incr > second_cutoff) | |
519 | incr = second_cutoff - bufix; | |
520 | ||
521 | /* If this option is going to overflow the buffer, | |
522 | skip it. */ | |
523 | if (bufix + 2 + incr > buflen) { | |
524 | bufix = optstart; | |
525 | break; | |
526 | } | |
527 | ||
528 | /* Everything looks good - copy it in! */ | |
a3e52198 TL |
529 | buffer [bufix] = code; |
530 | buffer [bufix + 1] = incr; | |
c34fcd38 TL |
531 | if (tto && incr == length) { |
532 | memcpy (buffer + bufix + 2, | |
b1b7b521 | 533 | od.data + ix, (unsigned)(incr - 1)); |
c34fcd38 TL |
534 | buffer [bufix + 2 + incr - 1] = 0; |
535 | } else { | |
536 | memcpy (buffer + bufix + 2, | |
b1b7b521 | 537 | od.data + ix, (unsigned)incr); |
c34fcd38 | 538 | } |
a3e52198 TL |
539 | length -= incr; |
540 | ix += incr; | |
541 | bufix += 2 + incr; | |
542 | } | |
dbf6124a | 543 | data_string_forget (&od, MDL); |
a3e52198 TL |
544 | } |
545 | return bufix; | |
d7837182 TL |
546 | } |
547 | ||
548 | /* Format the specified option so that a human can easily read it. */ | |
549 | ||
b1b7b521 | 550 | const char *pretty_print_option (code, data, len, emit_commas, emit_quotes) |
a9a9b7f1 | 551 | unsigned int code; |
b1b7b521 TL |
552 | const unsigned char *data; |
553 | unsigned len; | |
171c47a6 | 554 | int emit_commas; |
a9a9b7f1 | 555 | int emit_quotes; |
d7837182 TL |
556 | { |
557 | static char optbuf [32768]; /* XXX */ | |
558 | int hunksize = 0; | |
559 | int numhunk = -1; | |
560 | int numelem = 0; | |
561 | char fmtbuf [32]; | |
c6e7518c | 562 | int i, j, k; |
d7837182 | 563 | char *op = optbuf; |
b1b7b521 | 564 | const unsigned char *dp = data; |
d7837182 | 565 | struct in_addr foo; |
17f4dab7 | 566 | char comma; |
d7837182 | 567 | |
a9a9b7f1 TL |
568 | /* Code should be between 0 and 255. */ |
569 | if (code > 255) | |
8ae2d595 | 570 | log_fatal ("pretty_print_option: bad code %d\n", code); |
a9a9b7f1 | 571 | |
17f4dab7 TL |
572 | if (emit_commas) |
573 | comma = ','; | |
574 | else | |
575 | comma = ' '; | |
576 | ||
d7837182 TL |
577 | /* Figure out the size of the data. */ |
578 | for (i = 0; dhcp_options [code].format [i]; i++) { | |
579 | if (!numhunk) { | |
c9605ddb | 580 | log_error ("%s: Extra codes in format string: %s\n", |
c6e7518c TL |
581 | dhcp_options [code].name, |
582 | &(dhcp_options [code].format [i])); | |
d7837182 TL |
583 | break; |
584 | } | |
585 | numelem++; | |
586 | fmtbuf [i] = dhcp_options [code].format [i]; | |
587 | switch (dhcp_options [code].format [i]) { | |
588 | case 'A': | |
589 | --numelem; | |
590 | fmtbuf [i] = 0; | |
591 | numhunk = 0; | |
592 | break; | |
17f4dab7 | 593 | case 'X': |
c6e7518c TL |
594 | for (k = 0; k < len; k++) { |
595 | if (!isascii (data [k]) || | |
596 | !isprint (data [k])) | |
597 | break; | |
598 | } | |
dbf6124a TL |
599 | /* If we found no bogus characters, or the bogus |
600 | character we found is a trailing NUL, it's | |
601 | okay to print this option as text. */ | |
602 | if (k == len || (k + 1 == len && data [k] == 0)) { | |
c6e7518c TL |
603 | fmtbuf [i] = 't'; |
604 | numhunk = -2; | |
605 | } else { | |
606 | fmtbuf [i] = 'x'; | |
607 | hunksize++; | |
608 | comma = ':'; | |
609 | numhunk = 0; | |
610 | } | |
17f4dab7 | 611 | fmtbuf [i + 1] = 0; |
17f4dab7 | 612 | break; |
d7837182 TL |
613 | case 't': |
614 | fmtbuf [i] = 't'; | |
615 | fmtbuf [i + 1] = 0; | |
616 | numhunk = -2; | |
617 | break; | |
618 | case 'I': | |
619 | case 'l': | |
620 | case 'L': | |
621 | hunksize += 4; | |
622 | break; | |
623 | case 's': | |
624 | case 'S': | |
625 | hunksize += 2; | |
626 | break; | |
627 | case 'b': | |
628 | case 'B': | |
629 | case 'f': | |
630 | hunksize++; | |
631 | break; | |
632 | case 'e': | |
633 | break; | |
634 | default: | |
8ae2d595 | 635 | log_error ("%s: garbage in format string: %s\n", |
d7837182 TL |
636 | dhcp_options [code].name, |
637 | &(dhcp_options [code].format [i])); | |
638 | break; | |
639 | } | |
640 | } | |
641 | ||
642 | /* Check for too few bytes... */ | |
643 | if (hunksize > len) { | |
8ae2d595 | 644 | log_error ("%s: expecting at least %d bytes; got %d", |
d7837182 TL |
645 | dhcp_options [code].name, |
646 | hunksize, len); | |
647 | return "<error>"; | |
648 | } | |
649 | /* Check for too many bytes... */ | |
650 | if (numhunk == -1 && hunksize < len) | |
8ae2d595 | 651 | log_error ("%s: %d extra bytes", |
d7837182 TL |
652 | dhcp_options [code].name, |
653 | len - hunksize); | |
654 | ||
655 | /* If this is an array, compute its size. */ | |
656 | if (!numhunk) | |
657 | numhunk = len / hunksize; | |
658 | /* See if we got an exact number of hunks. */ | |
659 | if (numhunk > 0 && numhunk * hunksize < len) | |
8ae2d595 | 660 | log_error ("%s: %d extra bytes at end of array\n", |
d7837182 TL |
661 | dhcp_options [code].name, |
662 | len - numhunk * hunksize); | |
663 | ||
664 | /* A one-hunk array prints the same as a single hunk. */ | |
665 | if (numhunk < 0) | |
666 | numhunk = 1; | |
667 | ||
d7837182 TL |
668 | /* Cycle through the array (or hunk) printing the data. */ |
669 | for (i = 0; i < numhunk; i++) { | |
670 | for (j = 0; j < numelem; j++) { | |
671 | switch (fmtbuf [j]) { | |
672 | case 't': | |
a9a9b7f1 TL |
673 | if (emit_quotes) |
674 | *op++ = '"'; | |
b1b7b521 TL |
675 | strcpy (op, (const char *)dp); |
676 | op += strlen ((const char *)dp); | |
a9a9b7f1 TL |
677 | if (emit_quotes) |
678 | *op++ = '"'; | |
171c47a6 | 679 | *op = 0; |
d7837182 TL |
680 | break; |
681 | case 'I': | |
682 | foo.s_addr = htonl (getULong (dp)); | |
683 | strcpy (op, inet_ntoa (foo)); | |
684 | dp += 4; | |
685 | break; | |
686 | case 'l': | |
67bb0d24 | 687 | sprintf (op, "%ld", (long)getLong (dp)); |
d7837182 TL |
688 | dp += 4; |
689 | break; | |
690 | case 'L': | |
67bb0d24 TL |
691 | sprintf (op, "%ld", |
692 | (unsigned long)getULong (dp)); | |
d7837182 TL |
693 | dp += 4; |
694 | break; | |
695 | case 's': | |
2c9c0291 | 696 | sprintf (op, "%d", (int)getShort (dp)); |
d7837182 TL |
697 | dp += 2; |
698 | break; | |
699 | case 'S': | |
2c9c0291 | 700 | sprintf (op, "%d", (unsigned)getUShort (dp)); |
d7837182 TL |
701 | dp += 2; |
702 | break; | |
703 | case 'b': | |
b1b7b521 | 704 | sprintf (op, "%d", *(const char *)dp++); |
d7837182 TL |
705 | break; |
706 | case 'B': | |
707 | sprintf (op, "%d", *dp++); | |
708 | break; | |
17f4dab7 TL |
709 | case 'x': |
710 | sprintf (op, "%x", *dp++); | |
711 | break; | |
d7837182 TL |
712 | case 'f': |
713 | strcpy (op, *dp++ ? "true" : "false"); | |
714 | break; | |
715 | default: | |
c9605ddb TL |
716 | log_error ("Unexpected format code %c", |
717 | fmtbuf [j]); | |
d7837182 TL |
718 | } |
719 | op += strlen (op); | |
17f4dab7 | 720 | if (j + 1 < numelem && comma != ':') |
29539f1a TL |
721 | *op++ = ' '; |
722 | } | |
723 | if (i + 1 < numhunk) { | |
17f4dab7 | 724 | *op++ = comma; |
d7837182 | 725 | } |
29539f1a | 726 | |
d7837182 | 727 | } |
d7837182 TL |
728 | return optbuf; |
729 | } | |
17f4dab7 | 730 | |
4038ec52 | 731 | int hashed_option_get (result, universe, packet, lease, |
dbf6124a | 732 | in_options, cfg_options, options, scope, code) |
a370b55d | 733 | struct data_string *result; |
c9605ddb | 734 | struct universe *universe; |
da38df14 TL |
735 | struct packet *packet; |
736 | struct lease *lease; | |
4038ec52 TL |
737 | struct option_state *in_options; |
738 | struct option_state *cfg_options; | |
a370b55d | 739 | struct option_state *options; |
dbf6124a | 740 | struct binding_scope *scope; |
b1b7b521 | 741 | unsigned code; |
f4c34053 | 742 | { |
a370b55d | 743 | struct option_cache *oc; |
f4c34053 | 744 | |
c9605ddb TL |
745 | if (!universe -> lookup_func) |
746 | return 0; | |
2c9c0291 | 747 | oc = ((*universe -> lookup_func) (universe, options, code)); |
c9605ddb | 748 | if (!oc) |
a370b55d | 749 | return 0; |
dbf6124a TL |
750 | if (!evaluate_option_cache (result, packet, lease, in_options, |
751 | cfg_options, scope, oc, MDL)) | |
a370b55d TL |
752 | return 0; |
753 | return 1; | |
f4c34053 TL |
754 | } |
755 | ||
4038ec52 | 756 | int agent_option_get (result, universe, packet, lease, |
dbf6124a | 757 | in_options, cfg_options, options, scope, code) |
a370b55d | 758 | struct data_string *result; |
c9605ddb | 759 | struct universe *universe; |
da38df14 TL |
760 | struct packet *packet; |
761 | struct lease *lease; | |
4038ec52 TL |
762 | struct option_state *in_options; |
763 | struct option_state *cfg_options; | |
a370b55d | 764 | struct option_state *options; |
dbf6124a | 765 | struct binding_scope *scope; |
b1b7b521 | 766 | unsigned code; |
f4c34053 TL |
767 | { |
768 | struct agent_options *ao; | |
769 | struct option_tag *t; | |
f4c34053 | 770 | |
c9605ddb TL |
771 | /* Make sure there's agent option state. */ |
772 | if (universe -> index >= options -> universe_count || | |
773 | !(options -> universes [universe -> index])) | |
774 | return 0; | |
775 | ao = (struct agent_options *)options -> universes [universe -> index]; | |
776 | ||
f4c34053 | 777 | /* Find the last set of agent options and consider it definitive. */ |
c9605ddb | 778 | for (; ao -> next; ao = ao -> next) |
a370b55d | 779 | ; |
f4c34053 | 780 | if (ao) { |
a370b55d | 781 | for (t = ao -> first; t; t = t -> next) { |
f4c34053 | 782 | if (t -> data [0] == code) { |
a370b55d | 783 | result -> len = t -> data [1]; |
dbf6124a TL |
784 | if (!(buffer_allocate (&result -> buffer, |
785 | result -> len + 1, | |
786 | MDL))) { | |
a370b55d TL |
787 | result -> len = 0; |
788 | buffer_dereference | |
dbf6124a | 789 | (&result -> buffer, MDL); |
a370b55d TL |
790 | return 0; |
791 | } | |
792 | result -> data = &result -> buffer -> data [0]; | |
b1b7b521 | 793 | memcpy (result -> buffer -> data, |
a370b55d | 794 | &t -> data [2], result -> len); |
b1b7b521 | 795 | result -> buffer -> data [result -> len] = 0; |
a370b55d TL |
796 | result -> terminated = 1; |
797 | return 1; | |
f4c34053 | 798 | } |
a370b55d | 799 | } |
f4c34053 | 800 | } |
a370b55d | 801 | return 0; |
f4c34053 TL |
802 | } |
803 | ||
c9605ddb TL |
804 | void hashed_option_set (universe, options, option, op) |
805 | struct universe *universe; | |
b4807938 TL |
806 | struct option_state *options; |
807 | struct option_cache *option; | |
808 | enum statement_op op; | |
b4807938 | 809 | { |
a370b55d | 810 | struct option_cache *oc, *noc; |
b4807938 TL |
811 | |
812 | switch (op) { | |
813 | case if_statement: | |
814 | case add_statement: | |
815 | case eval_statement: | |
816 | case break_statement: | |
817 | default: | |
8ae2d595 | 818 | log_error ("bogus statement type in do_option_set."); |
b4807938 TL |
819 | break; |
820 | ||
821 | case default_option_statement: | |
c9605ddb TL |
822 | oc = lookup_option (universe, options, |
823 | option -> option -> code); | |
a370b55d | 824 | if (oc) |
b4807938 | 825 | break; |
c9605ddb | 826 | save_option (universe, options, option); |
b4807938 TL |
827 | break; |
828 | ||
829 | case supersede_option_statement: | |
a370b55d | 830 | /* Install the option, replacing any existing version. */ |
c9605ddb | 831 | save_option (universe, options, option); |
b4807938 TL |
832 | break; |
833 | ||
834 | case append_option_statement: | |
835 | case prepend_option_statement: | |
c9605ddb TL |
836 | oc = lookup_option (universe, options, |
837 | option -> option -> code); | |
a370b55d | 838 | if (!oc) { |
c9605ddb | 839 | save_option (universe, options, option); |
b4807938 TL |
840 | break; |
841 | } | |
a370b55d TL |
842 | /* If it's not an expression, make it into one. */ |
843 | if (!oc -> expression && oc -> data.len) { | |
dbf6124a | 844 | if (!expression_allocate (&oc -> expression, MDL)) { |
8ae2d595 | 845 | log_error ("Can't allocate const expression."); |
a370b55d TL |
846 | break; |
847 | } | |
848 | oc -> expression -> op = expr_const_data; | |
849 | data_string_copy | |
850 | (&oc -> expression -> data.const_data, | |
dbf6124a TL |
851 | &oc -> data, MDL); |
852 | data_string_forget (&oc -> data, MDL); | |
a370b55d TL |
853 | } |
854 | noc = (struct option_cache *)0; | |
dbf6124a | 855 | if (!option_cache_allocate (&noc, MDL)) |
a370b55d TL |
856 | break; |
857 | if (op == append_option_statement) { | |
858 | if (!make_concat (&noc -> expression, | |
859 | oc -> expression, | |
860 | option -> expression)) { | |
dbf6124a | 861 | option_cache_dereference (&noc, MDL); |
a370b55d TL |
862 | break; |
863 | } | |
864 | } else { | |
865 | if (!make_concat (&noc -> expression, | |
866 | option -> expression, | |
867 | oc -> expression)) { | |
dbf6124a | 868 | option_cache_dereference (&noc, MDL); |
b4807938 TL |
869 | break; |
870 | } | |
b4807938 | 871 | } |
a370b55d | 872 | noc -> option = oc -> option; |
c9605ddb | 873 | save_option (universe, options, noc); |
dbf6124a | 874 | option_cache_dereference (&noc, MDL); |
a370b55d TL |
875 | break; |
876 | } | |
877 | } | |
878 | ||
c9605ddb TL |
879 | struct option_cache *lookup_option (universe, options, code) |
880 | struct universe *universe; | |
881 | struct option_state *options; | |
b1b7b521 | 882 | unsigned code; |
c9605ddb TL |
883 | { |
884 | if (universe -> lookup_func) | |
885 | return (*universe -> lookup_func) (universe, options, code); | |
886 | else | |
887 | log_error ("can't look up options in %s space.", | |
888 | universe -> name); | |
889 | return (struct option_cache *)0; | |
890 | } | |
891 | ||
892 | struct option_cache *lookup_hashed_option (universe, options, code) | |
893 | struct universe *universe; | |
894 | struct option_state *options; | |
b1b7b521 | 895 | unsigned code; |
a370b55d TL |
896 | { |
897 | int hashix; | |
898 | pair bptr; | |
c9605ddb TL |
899 | pair *hash; |
900 | ||
901 | /* Make sure there's a hash table. */ | |
902 | if (universe -> index >= options -> universe_count || | |
903 | !(options -> universes [universe -> index])) | |
904 | return (struct option_cache *)0; | |
905 | ||
906 | hash = options -> universes [universe -> index]; | |
a370b55d | 907 | |
59ab1324 | 908 | hashix = compute_option_hash (code); |
a370b55d TL |
909 | for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { |
910 | if (((struct option_cache *)(bptr -> car)) -> option -> code == | |
911 | code) | |
912 | return (struct option_cache *)(bptr -> car); | |
913 | } | |
914 | return (struct option_cache *)0; | |
915 | } | |
916 | ||
c9605ddb TL |
917 | void save_option (universe, options, oc) |
918 | struct universe *universe; | |
919 | struct option_state *options; | |
920 | struct option_cache *oc; | |
921 | { | |
922 | if (universe -> save_func) | |
923 | (*universe -> save_func) (universe, options, oc); | |
924 | else | |
925 | log_error ("can't store options in %s space.", | |
926 | universe -> name); | |
927 | } | |
928 | ||
929 | void save_hashed_option (universe, options, oc) | |
930 | struct universe *universe; | |
931 | struct option_state *options; | |
a370b55d TL |
932 | struct option_cache *oc; |
933 | { | |
934 | int hashix; | |
935 | pair bptr; | |
c9605ddb | 936 | pair *hash = options -> universes [universe -> index]; |
a370b55d | 937 | |
c9605ddb | 938 | /* Compute the hash. */ |
59ab1324 | 939 | hashix = compute_option_hash (oc -> option -> code); |
a370b55d | 940 | |
c9605ddb TL |
941 | /* If there's no hash table, make one. */ |
942 | if (!hash) { | |
dbf6124a | 943 | hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, MDL); |
c9605ddb TL |
944 | if (!hash) { |
945 | log_error ("no memory to store %s.%s", | |
946 | universe -> name, oc -> option -> name); | |
947 | return; | |
948 | } | |
949 | memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash); | |
950 | options -> universes [universe -> index] = (VOIDPTR)hash; | |
a370b55d | 951 | } else { |
c9605ddb TL |
952 | /* Try to find an existing option matching the new one. */ |
953 | for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { | |
954 | if (((struct option_cache *) | |
955 | (bptr -> car)) -> option -> code == | |
956 | oc -> option -> code) | |
957 | break; | |
958 | } | |
959 | ||
960 | /* If we find one, dereference it and put the new one | |
961 | in its place. */ | |
962 | if (bptr) { | |
963 | option_cache_dereference | |
dbf6124a | 964 | ((struct option_cache **)&bptr -> car, MDL); |
c9605ddb TL |
965 | option_cache_reference |
966 | ((struct option_cache **)&bptr -> car, | |
dbf6124a | 967 | oc, MDL); |
a370b55d TL |
968 | return; |
969 | } | |
a370b55d | 970 | } |
c9605ddb TL |
971 | |
972 | /* Otherwise, just put the new one at the head of the list. */ | |
dbf6124a | 973 | bptr = new_pair (MDL); |
c9605ddb TL |
974 | if (!bptr) { |
975 | log_error ("No memory for option_cache reference."); | |
976 | return; | |
977 | } | |
978 | bptr -> cdr = hash [hashix]; | |
979 | bptr -> car = 0; | |
dbf6124a | 980 | option_cache_reference ((struct option_cache **)&bptr -> car, oc, MDL); |
c9605ddb | 981 | hash [hashix] = bptr; |
a370b55d TL |
982 | } |
983 | ||
c9605ddb TL |
984 | void delete_option (universe, options, code) |
985 | struct universe *universe; | |
986 | struct option_state *options; | |
987 | int code; | |
988 | { | |
989 | if (universe -> delete_func) | |
990 | (*universe -> delete_func) (universe, options, code); | |
991 | else | |
992 | log_error ("can't delete options from %s space.", | |
993 | universe -> name); | |
994 | } | |
995 | ||
996 | void delete_hashed_option (universe, options, code) | |
997 | struct universe *universe; | |
998 | struct option_state *options; | |
a370b55d TL |
999 | int code; |
1000 | { | |
1001 | int hashix; | |
1002 | pair bptr, prev = (pair)0; | |
c9605ddb TL |
1003 | pair *hash = options -> universes [universe -> index]; |
1004 | ||
1005 | /* There may not be any options in this space. */ | |
1006 | if (!hash) | |
1007 | return; | |
a370b55d TL |
1008 | |
1009 | /* Try to find an existing option matching the new one. */ | |
59ab1324 | 1010 | hashix = compute_option_hash (code); |
a370b55d TL |
1011 | for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { |
1012 | if (((struct option_cache *)(bptr -> car)) -> option -> code | |
1013 | == code) | |
1014 | break; | |
1015 | prev = bptr; | |
1016 | } | |
1017 | /* If we found one, wipe it out... */ | |
1018 | if (bptr) { | |
1019 | if (prev) | |
1020 | prev -> cdr = bptr -> cdr; | |
b4807938 | 1021 | else |
a370b55d TL |
1022 | hash [hashix] = bptr -> cdr; |
1023 | option_cache_dereference | |
dbf6124a TL |
1024 | ((struct option_cache **)(&bptr -> car), MDL); |
1025 | free_pair (bptr, MDL); | |
a370b55d TL |
1026 | } |
1027 | } | |
b4807938 | 1028 | |
a370b55d TL |
1029 | extern struct option_cache *free_option_caches; /* XXX */ |
1030 | ||
dbf6124a | 1031 | int option_cache_dereference (ptr, file, line) |
a370b55d | 1032 | struct option_cache **ptr; |
dbf6124a TL |
1033 | const char *file; |
1034 | int line; | |
a370b55d TL |
1035 | { |
1036 | if (!ptr || !*ptr) { | |
dbf6124a TL |
1037 | log_error ("Null pointer in option_cache_dereference: %s(%d)", |
1038 | file, line); | |
8e0a40b8 | 1039 | #if defined (POINTER_DEBUG) |
a370b55d | 1040 | abort (); |
8e0a40b8 TL |
1041 | #else |
1042 | return 0; | |
1043 | #endif | |
a370b55d TL |
1044 | } |
1045 | ||
1046 | (*ptr) -> refcnt--; | |
37e365b4 | 1047 | rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt); |
a370b55d TL |
1048 | if (!(*ptr) -> refcnt) { |
1049 | if ((*ptr) -> data.buffer) | |
4bd8800e | 1050 | data_string_forget (&(*ptr) -> data, file, line); |
a370b55d | 1051 | if ((*ptr) -> expression) |
4bd8800e TL |
1052 | expression_dereference (&(*ptr) -> expression, |
1053 | file, line); | |
a370b55d TL |
1054 | /* Put it back on the free list... */ |
1055 | (*ptr) -> expression = (struct expression *)free_option_caches; | |
1056 | free_option_caches = *ptr; | |
dbf6124a TL |
1057 | dmalloc_reuse (free_option_caches, (char *)0, 0, 0); |
1058 | } | |
1059 | if ((*ptr) -> refcnt < 0) { | |
1060 | log_error ("%s(%d): negative refcnt!", file, line); | |
1061 | #if defined (DEBUG_RC_HISTORY) | |
1062 | dump_rc_history (); | |
1063 | #endif | |
1064 | #if defined (POINTER_DEBUG) | |
1065 | abort (); | |
1066 | #else | |
a77040e7 | 1067 | *ptr = (struct option_cache *)0; |
dbf6124a TL |
1068 | return 0; |
1069 | #endif | |
b4807938 | 1070 | } |
a370b55d TL |
1071 | *ptr = (struct option_cache *)0; |
1072 | return 1; | |
1073 | ||
b4807938 | 1074 | } |
c9605ddb | 1075 | |
59ab1324 | 1076 | int hashed_option_state_dereference (universe, state, file, line) |
c9605ddb TL |
1077 | struct universe *universe; |
1078 | struct option_state *state; | |
59ab1324 TL |
1079 | const char *file; |
1080 | int line; | |
c9605ddb TL |
1081 | { |
1082 | pair *heads; | |
1083 | pair cp, next; | |
1084 | int i; | |
1085 | ||
1086 | /* Get the pointer to the array of hash table bucket heads. */ | |
1087 | heads = (pair *)(state -> universes [universe -> index]); | |
1088 | if (!heads) | |
1089 | return 0; | |
1090 | ||
1091 | /* For each non-null head, loop through all the buckets dereferencing | |
1092 | the attached option cache structures and freeing the buckets. */ | |
1093 | for (i = 0; i < OPTION_HASH_SIZE; i++) { | |
1094 | for (cp = heads [i]; cp; cp = next) { | |
1095 | next = cp -> cdr; | |
1096 | option_cache_dereference | |
59ab1324 TL |
1097 | ((struct option_cache **)&cp -> car, file, line); |
1098 | free_pair (cp, file, line); | |
c9605ddb TL |
1099 | } |
1100 | } | |
1101 | ||
59ab1324 | 1102 | dfree (heads, file, line); |
c9605ddb TL |
1103 | state -> universes [universe -> index] = (void *)0; |
1104 | return 1; | |
1105 | } | |
1106 | ||
59ab1324 | 1107 | int agent_option_state_dereference (universe, state, file, line) |
c9605ddb TL |
1108 | struct universe *universe; |
1109 | struct option_state *state; | |
59ab1324 TL |
1110 | const char *file; |
1111 | int line; | |
c9605ddb TL |
1112 | { |
1113 | struct agent_options *a, *na; | |
1114 | struct option_tag *ot, *not; | |
1115 | ||
1116 | if (universe -> index >= state -> universe_count || | |
1117 | !state -> universes [universe -> index]) | |
1118 | return 0; | |
1119 | ||
1120 | /* We can also release the agent options, if any... */ | |
1121 | for (a = (struct agent_options *)(state -> universes | |
1122 | [universe -> index]); a; a = na) { | |
1123 | na = a -> next; | |
1124 | for (ot = a -> first; ot; ot = not) { | |
1125 | not = ot -> next; | |
59ab1324 | 1126 | dfree (ot, file, line); |
c9605ddb TL |
1127 | } |
1128 | } | |
1129 | ||
59ab1324 | 1130 | dfree (state -> universes [universe -> index], file, line); |
c9605ddb TL |
1131 | state -> universes [universe -> index] = (void *)0; |
1132 | return 1; | |
1133 | } | |
1134 | ||
dbf6124a TL |
1135 | int store_option (result, universe, packet, lease, |
1136 | in_options, cfg_options, scope, oc) | |
c9605ddb TL |
1137 | struct data_string *result; |
1138 | struct universe *universe; | |
4038ec52 | 1139 | struct packet *packet; |
da38df14 | 1140 | struct lease *lease; |
4038ec52 TL |
1141 | struct option_state *in_options; |
1142 | struct option_state *cfg_options; | |
dbf6124a | 1143 | struct binding_scope *scope; |
c9605ddb TL |
1144 | struct option_cache *oc; |
1145 | { | |
1146 | struct data_string d1, d2; | |
1147 | ||
1148 | memset (&d1, 0, sizeof d1); | |
1149 | memset (&d2, 0, sizeof d2); | |
1150 | ||
dbf6124a TL |
1151 | if (evaluate_option_cache (&d2, packet, lease, in_options, |
1152 | cfg_options, scope, oc, MDL)) { | |
c9605ddb TL |
1153 | if (!buffer_allocate (&d1.buffer, |
1154 | (result -> len + | |
1155 | universe -> length_size + | |
dbf6124a TL |
1156 | universe -> tag_size + d2.len), MDL)) { |
1157 | data_string_forget (result, MDL); | |
1158 | data_string_forget (&d2, MDL); | |
c9605ddb TL |
1159 | return 0; |
1160 | } | |
1161 | d1.data = &d1.buffer -> data [0]; | |
1162 | if (result -> len) | |
b1b7b521 TL |
1163 | memcpy (d1.buffer -> data, |
1164 | result -> data, result -> len); | |
c9605ddb | 1165 | d1.len = result -> len; |
b1b7b521 | 1166 | (*universe -> store_tag) (&d1.buffer -> data [d1.len], |
c9605ddb TL |
1167 | oc -> option -> code); |
1168 | d1.len += universe -> tag_size; | |
b1b7b521 TL |
1169 | (*universe -> store_length) (&d1.buffer -> data [d1.len], |
1170 | d2.len); | |
c9605ddb | 1171 | d1.len += universe -> length_size; |
b1b7b521 | 1172 | memcpy (&d1.buffer -> data [d1.len], d2.data, d2.len); |
c9605ddb | 1173 | d1.len += d2.len; |
dbf6124a TL |
1174 | data_string_forget (&d2, MDL); |
1175 | data_string_forget (result, MDL); | |
1176 | data_string_copy (result, &d1, MDL); | |
1177 | data_string_forget (&d1, MDL); | |
c9605ddb TL |
1178 | return 1; |
1179 | } | |
1180 | return 0; | |
1181 | } | |
1182 | ||
4038ec52 | 1183 | int option_space_encapsulate (result, packet, lease, |
dbf6124a | 1184 | in_options, cfg_options, scope, name) |
c9605ddb | 1185 | struct data_string *result; |
4038ec52 | 1186 | struct packet *packet; |
da38df14 | 1187 | struct lease *lease; |
4038ec52 TL |
1188 | struct option_state *in_options; |
1189 | struct option_state *cfg_options; | |
dbf6124a | 1190 | struct binding_scope *scope; |
c9605ddb TL |
1191 | struct data_string *name; |
1192 | { | |
1193 | struct universe *u; | |
1194 | ||
20916cae TL |
1195 | u = (struct universe *)0; |
1196 | universe_hash_lookup (&u, universe_hash, | |
1197 | name -> data, name -> len, MDL); | |
c9605ddb TL |
1198 | if (!u) { |
1199 | log_error ("unknown option space %s.", name -> data); | |
1200 | return 0; | |
1201 | } | |
1202 | ||
1203 | if (u -> encapsulate) | |
4038ec52 | 1204 | return (*u -> encapsulate) (result, packet, lease, |
dbf6124a | 1205 | in_options, cfg_options, scope, u); |
c9605ddb TL |
1206 | log_error ("encapsulation requested for %s with no support.", |
1207 | name -> data); | |
1208 | return 0; | |
1209 | } | |
1210 | ||
4038ec52 | 1211 | int hashed_option_space_encapsulate (result, packet, lease, |
dbf6124a | 1212 | in_options, cfg_options, scope, universe) |
c9605ddb | 1213 | struct data_string *result; |
4038ec52 | 1214 | struct packet *packet; |
da38df14 | 1215 | struct lease *lease; |
4038ec52 TL |
1216 | struct option_state *in_options; |
1217 | struct option_state *cfg_options; | |
dbf6124a | 1218 | struct binding_scope *scope; |
c9605ddb TL |
1219 | struct universe *universe; |
1220 | { | |
1221 | pair p, *hash; | |
1222 | int status; | |
1223 | int i; | |
1224 | ||
4038ec52 | 1225 | if (universe -> index >= cfg_options -> universe_count) |
c9605ddb TL |
1226 | return 0; |
1227 | ||
4038ec52 | 1228 | hash = cfg_options -> universes [universe -> index]; |
c9605ddb TL |
1229 | if (!hash) |
1230 | return 0; | |
1231 | ||
1232 | status = 0; | |
1233 | for (i = 0; i < OPTION_HASH_SIZE; i++) { | |
1234 | for (p = hash [i]; p; p = p -> cdr) { | |
4038ec52 | 1235 | if (store_option (result, universe, packet, lease, |
dbf6124a | 1236 | in_options, cfg_options, scope, |
c9605ddb TL |
1237 | (struct option_cache *)p -> car)) |
1238 | status = 1; | |
1239 | } | |
1240 | } | |
1241 | ||
1242 | return status; | |
1243 | } | |
4038ec52 | 1244 | |
6dd369e0 | 1245 | int nwip_option_space_encapsulate (result, packet, lease, |
dbf6124a | 1246 | in_options, cfg_options, scope, universe) |
6dd369e0 TL |
1247 | struct data_string *result; |
1248 | struct packet *packet; | |
1249 | struct lease *lease; | |
1250 | struct option_state *in_options; | |
1251 | struct option_state *cfg_options; | |
dbf6124a | 1252 | struct binding_scope *scope; |
6dd369e0 TL |
1253 | struct universe *universe; |
1254 | { | |
1255 | pair p, *hash; | |
1256 | int status; | |
1257 | int i; | |
1258 | static struct option_cache *no_nwip; | |
1259 | struct data_string ds; | |
1260 | ||
1261 | if (universe -> index >= cfg_options -> universe_count) | |
1262 | return 0; | |
1263 | ||
1264 | hash = cfg_options -> universes [universe -> index]; | |
1265 | status = 0; | |
1266 | for (i = 0; hash && i < OPTION_HASH_SIZE; i++) { | |
1267 | for (p = hash [i]; p; p = p -> cdr) { | |
1268 | if (store_option (result, universe, packet, lease, | |
dbf6124a | 1269 | in_options, cfg_options, scope, |
6dd369e0 TL |
1270 | (struct option_cache *)p -> car)) |
1271 | status = 1; | |
1272 | } | |
1273 | } | |
1274 | ||
1275 | /* If there's no data, the nwip suboption is supposed to contain | |
1276 | a suboption saying there's no data. */ | |
1277 | if (!status) { | |
1278 | if (!no_nwip) { | |
1279 | static unsigned char nni [] = { 1, 0 }; | |
1280 | memset (&ds, 0, sizeof ds); | |
1281 | ds.data = nni; | |
1282 | ds.len = 2; | |
dbf6124a TL |
1283 | if (option_cache_allocate (&no_nwip, MDL)) |
1284 | data_string_copy (&no_nwip -> data, &ds, MDL); | |
1285 | no_nwip -> option = nwip_universe.options [1]; | |
6dd369e0 TL |
1286 | } |
1287 | if (no_nwip) { | |
1288 | if (store_option (result, universe, packet, lease, | |
1289 | in_options, cfg_options, | |
dbf6124a | 1290 | scope, no_nwip)) |
6dd369e0 TL |
1291 | status = 1; |
1292 | } | |
1293 | } else { | |
1294 | /* If we have nwip options, the first one has to be the | |
1295 | nwip-exists-in-option-area option. */ | |
dbf6124a TL |
1296 | if (!buffer_allocate (&ds.buffer, result -> len + 2, MDL)) { |
1297 | data_string_forget (result, MDL); | |
6dd369e0 TL |
1298 | return 0; |
1299 | } | |
1300 | ds.data = &ds.buffer -> data [0]; | |
1301 | ds.buffer -> data [0] = 2; | |
1302 | ds.buffer -> data [1] = 0; | |
1303 | memcpy (&ds.buffer -> data [2], result -> data, result -> len); | |
dbf6124a TL |
1304 | data_string_forget (result, MDL); |
1305 | data_string_copy (result, &ds, MDL); | |
1306 | data_string_forget (&ds, MDL); | |
6dd369e0 TL |
1307 | } |
1308 | ||
1309 | return status; | |
1310 | } | |
1311 | ||
4038ec52 TL |
1312 | void do_packet (interface, packet, len, from_port, from, hfrom) |
1313 | struct interface_info *interface; | |
1314 | struct dhcp_packet *packet; | |
b1b7b521 | 1315 | unsigned len; |
4038ec52 TL |
1316 | unsigned int from_port; |
1317 | struct iaddr from; | |
1318 | struct hardware *hfrom; | |
1319 | { | |
1320 | int i; | |
1321 | struct option_cache *op; | |
1322 | struct packet *decoded_packet; | |
dbf6124a TL |
1323 | #if defined (DEBUG_MEMORY_LEAKAGE) |
1324 | unsigned long previous_outstanding = dmalloc_outstanding; | |
1325 | #endif | |
4038ec52 TL |
1326 | |
1327 | decoded_packet = (struct packet *)0; | |
dbf6124a | 1328 | if (!packet_allocate (&decoded_packet, MDL)) { |
4038ec52 TL |
1329 | log_error ("do_packet: no memory for incoming packet!"); |
1330 | return; | |
1331 | } | |
1332 | decoded_packet -> raw = packet; | |
1333 | decoded_packet -> packet_length = len; | |
1334 | decoded_packet -> client_port = from_port; | |
1335 | decoded_packet -> client_addr = from; | |
20916cae | 1336 | interface_reference (&decoded_packet -> interface, interface, MDL); |
4038ec52 TL |
1337 | decoded_packet -> haddr = hfrom; |
1338 | ||
1339 | if (packet -> hlen > sizeof packet -> chaddr) { | |
dbf6124a | 1340 | packet_dereference (&decoded_packet, MDL); |
4038ec52 TL |
1341 | log_info ("Discarding packet with bogus hlen."); |
1342 | return; | |
1343 | } | |
4038ec52 | 1344 | |
bb404b74 TL |
1345 | /* If there's an option buffer, try to parse it. */ |
1346 | if (decoded_packet -> packet_length >= DHCP_FIXED_NON_UDP + 4) { | |
1347 | if (!parse_options (decoded_packet)) { | |
1348 | if (decoded_packet -> options) | |
1349 | option_state_dereference | |
dbf6124a TL |
1350 | (&decoded_packet -> options, MDL); |
1351 | packet_dereference (&decoded_packet, MDL); | |
bb404b74 TL |
1352 | return; |
1353 | } | |
1354 | ||
1355 | if (decoded_packet -> options_valid && | |
1356 | (op = lookup_option (&dhcp_universe, | |
1357 | decoded_packet -> options, | |
1358 | DHO_DHCP_MESSAGE_TYPE))) { | |
1359 | struct data_string dp; | |
1360 | memset (&dp, 0, sizeof dp); | |
1361 | evaluate_option_cache (&dp, decoded_packet, | |
1362 | (struct lease *)0, | |
1363 | decoded_packet -> options, | |
dbf6124a TL |
1364 | (struct option_state *)0, |
1365 | (struct binding_scope *)0, | |
1366 | op, MDL); | |
bb404b74 TL |
1367 | if (dp.len > 0) |
1368 | decoded_packet -> packet_type = dp.data [0]; | |
1369 | else | |
1370 | decoded_packet -> packet_type = 0; | |
dbf6124a | 1371 | data_string_forget (&dp, MDL); |
bb404b74 | 1372 | } |
4038ec52 TL |
1373 | } |
1374 | ||
1375 | if (decoded_packet -> packet_type) | |
1376 | dhcp (decoded_packet); | |
1377 | else | |
1378 | bootp (decoded_packet); | |
1379 | ||
2545e6e7 TL |
1380 | /* If the caller kept the packet, they'll have upped the refcnt. */ |
1381 | packet_dereference (&decoded_packet, MDL); | |
1382 | ||
dbf6124a TL |
1383 | #if defined (DEBUG_MEMORY_LEAKAGE) |
1384 | log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term", | |
1385 | dmalloc_generation, | |
1386 | dmalloc_outstanding - previous_outstanding, | |
1387 | dmalloc_outstanding, dmalloc_longterm); | |
1388 | #endif | |
1389 | #if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) | |
1390 | dmalloc_dump_outstanding (); | |
1391 | #endif | |
a77040e7 | 1392 | #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) |
59ab1324 TL |
1393 | dump_rc_history (); |
1394 | #endif | |
4038ec52 TL |
1395 | } |
1396 |