]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/isc-dhcp/common/options.c
This commit was generated by cvs2svn to compensate for changes in r56083,
[FreeBSD/FreeBSD.git] / contrib / isc-dhcp / common / options.c
1 /* options.c
2
3    DHCP options parsing and reassembly. */
4
5 /*
6  * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
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.
21  *
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 <mellon@fugue.com> in cooperation with Vixie
38  * Enterprises.  To learn more about the Internet Software Consortium,
39  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40  * Enterprises, see ``http://www.vix.com''.
41  */
42
43 #ifndef lint
44 static char copyright[] =
45 "$Id: options.c,v 1.26.2.10 1999/05/06 21:54:34 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.  All rights reserved.\n";
46 #endif /* not lint */
47
48 #define DHCP_OPTION_DATA
49 #include "dhcpd.h"
50 #include <ctype.h>
51
52 /* Parse all available options out of the specified packet. */
53
54 void parse_options (packet)
55         struct packet *packet;
56 {
57         /* Initially, zero all option pointers. */
58         memset (packet -> options, 0, sizeof (packet -> options));
59
60         /* If we don't see the magic cookie, there's nothing to parse. */
61         if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
62                 packet -> options_valid = 0;
63                 return;
64         }
65
66         /* Go through the options field, up to the end of the packet
67            or the End field. */
68         parse_option_buffer (packet, &packet -> raw -> options [4],
69                              packet -> packet_length - DHCP_FIXED_NON_UDP - 4);
70         /* If we parsed a DHCP Option Overload option, parse more
71            options out of the buffer(s) containing them. */
72         if (packet -> options_valid
73             && packet -> options [DHO_DHCP_OPTION_OVERLOAD].data) {
74                 if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)
75                         parse_option_buffer (packet,
76                                              (unsigned char *)
77                                              packet -> raw -> file,
78                                              sizeof packet -> raw -> file);
79                 if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)
80                         parse_option_buffer (packet,
81                                              (unsigned char *)
82                                              packet -> raw -> sname,
83                                              sizeof packet -> raw -> sname);
84         }
85 }
86
87 /* Parse options out of the specified buffer, storing addresses of option
88    values in packet -> options and setting packet -> options_valid if no
89    errors are encountered. */
90
91 void parse_option_buffer (packet, buffer, length)
92         struct packet *packet;
93         unsigned char *buffer;
94         int length;
95 {
96         unsigned char *s, *t;
97         unsigned char *end = buffer + length;
98         int len;
99         int code;
100
101         for (s = buffer; *s != DHO_END && s < end; ) {
102                 code = s [0];
103                 /* Pad options don't have a length - just skip them. */
104                 if (code == DHO_PAD) {
105                         ++s;
106                         continue;
107                 }
108                 /* All other fields (except end, see above) have a
109                    one-byte length. */
110                 len = s [1];
111
112                 /* If the length is outrageous, the options are bad. */
113                 if (s + len + 2 > end) {
114                         warn ("Option %s length %d overflows input buffer.",
115                               dhcp_options [code].name,
116                               len);
117                         packet -> options_valid = 0;
118                         return;
119                 }
120                 /* If we haven't seen this option before, just make
121                    space for it and copy it there. */
122                 if (!packet -> options [code].data) {
123                         if (!(t = ((unsigned char *)
124                                    dmalloc (len + 1, "parse_option_buffer"))))
125                                 error ("Can't allocate storage for option %s.",
126                                        dhcp_options [code].name);
127                         /* Copy and NUL-terminate the option (in case it's an
128                            ASCII string. */
129                         memcpy (t, &s [2], len);
130                         t [len] = 0;
131                         packet -> options [code].len = len;
132                         packet -> options [code].data = t;
133                 } else {
134                         /* If it's a repeat, concatenate it to whatever
135                            we last saw.   This is really only required
136                            for clients, but what the heck... */
137                         t = ((unsigned char *)
138                              dmalloc (len + packet -> options [code].len + 1,
139                                       "parse_option_buffer"));
140                         if (!t)
141                                 error ("Can't expand storage for option %s.",
142                                        dhcp_options [code].name);
143                         memcpy (t, packet -> options [code].data,
144                                 packet -> options [code].len);
145                         memcpy (t + packet -> options [code].len,
146                                 &s [2], len);
147                         packet -> options [code].len += len;
148                         t [packet -> options [code].len] = 0;
149                         dfree (packet -> options [code].data,
150                                "parse_option_buffer");
151                         packet -> options [code].data = t;
152                 }
153                 s += len + 2;
154         }
155         packet -> options_valid = 1;
156 }
157
158 /* cons options into a big buffer, and then split them out into the
159    three seperate buffers if needed.  This allows us to cons up a set
160    of vendor options using the same routine. */
161
162 int cons_options (inpacket, outpacket, mms,
163                   options, overload, terminate, bootpp, prl, prl_len)
164         struct packet *inpacket;
165         struct dhcp_packet *outpacket;
166         int mms;
167         struct tree_cache **options;
168         int overload;   /* Overload flags that may be set. */
169         int terminate;
170         int bootpp;
171         u_int8_t *prl;
172         int prl_len;
173 {
174         unsigned char priority_list [300];
175         int priority_len;
176         unsigned char buffer [4096];    /* Really big buffer... */
177         int main_buffer_size;
178         int mainbufix, bufix;
179         int option_size;
180         int length;
181
182         /* If the client has provided a maximum DHCP message size,
183            use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
184            use up to the minimum IP MTU size (576 bytes). */
185         /* XXX if a BOOTP client specifies a max message size, we will
186            honor it. */
187         if (!mms &&
188             inpacket &&
189             inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data &&
190             (inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].len >=
191              sizeof (u_int16_t)))
192                 mms = getUShort (inpacket -> options
193                                  [DHO_DHCP_MAX_MESSAGE_SIZE].data);
194
195         /* If the client has provided a maximum DHCP message size,
196            use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
197            use up to the minimum IP MTU size (576 bytes). */
198         /* XXX if a BOOTP client specifies a max message size, we will
199            honor it. */
200         if (mms)
201                 main_buffer_size = mms - DHCP_FIXED_LEN;
202         else if (bootpp)
203                 main_buffer_size = 64;
204         else
205                 main_buffer_size = 576 - DHCP_FIXED_LEN;
206
207         if (main_buffer_size > sizeof buffer)
208                 main_buffer_size = sizeof buffer;
209
210         /* Preload the option priority list with mandatory options. */
211         priority_len = 0;
212         priority_list [priority_len++] = DHO_DHCP_MESSAGE_TYPE;
213         priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
214         priority_list [priority_len++] = DHO_DHCP_LEASE_TIME;
215         priority_list [priority_len++] = DHO_DHCP_MESSAGE;
216
217         /* If the client has provided a list of options that it wishes
218            returned, use it to prioritize.  Otherwise, prioritize
219            based on the default priority list. */
220
221         if (inpacket &&
222             inpacket -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
223                 int prlen = (inpacket ->
224                              options [DHO_DHCP_PARAMETER_REQUEST_LIST].len);
225                 if (prlen + priority_len > sizeof priority_list)
226                         prlen = (sizeof priority_list) - priority_len;
227
228                 memcpy (&priority_list [priority_len],
229                         (inpacket -> options
230                          [DHO_DHCP_PARAMETER_REQUEST_LIST].data), prlen);
231                 priority_len += prlen;
232                 prl = priority_list;
233         } else if (prl) {
234                 if (prl_len + priority_len > sizeof priority_list)
235                         prl_len = (sizeof priority_list) - priority_len;
236
237                 memcpy (&priority_list [priority_len], prl, prl_len);
238                 priority_len += prl_len;
239                 prl = priority_list;
240         } else {
241                 memcpy (&priority_list [priority_len],
242                         dhcp_option_default_priority_list,
243                         sizeof_dhcp_option_default_priority_list);
244                 priority_len += sizeof_dhcp_option_default_priority_list;
245         }
246
247         /* Copy the options into the big buffer... */
248         option_size = store_options (buffer,
249                                      (main_buffer_size - 7 +
250                                       ((overload & 1) ? DHCP_FILE_LEN : 0) +
251                                       ((overload & 2) ? DHCP_SNAME_LEN : 0)),
252                                      options, priority_list, priority_len,
253                                      main_buffer_size,
254                                      (main_buffer_size +
255                                       ((overload & 1) ? DHCP_FILE_LEN : 0)),
256                                      terminate);
257
258         /* Put the cookie up front... */
259         memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4);
260         mainbufix = 4;
261
262         /* If we're going to have to overload, store the overload
263            option at the beginning.  If we can, though, just store the
264            whole thing in the packet's option buffer and leave it at
265            that. */
266         if (option_size <= main_buffer_size - mainbufix) {
267                 memcpy (&outpacket -> options [mainbufix],
268                         buffer, option_size);
269                 mainbufix += option_size;
270                 if (mainbufix < main_buffer_size)
271                         outpacket -> options [mainbufix++]
272                                 = DHO_END;
273                 length = DHCP_FIXED_NON_UDP + mainbufix;
274         } else {
275                 outpacket -> options [mainbufix++] =
276                         DHO_DHCP_OPTION_OVERLOAD;
277                 outpacket -> options [mainbufix++] = 1;
278                 if (option_size > main_buffer_size - mainbufix + DHCP_FILE_LEN)
279                         outpacket -> options [mainbufix++] = 3;
280                 else
281                         outpacket -> options [mainbufix++] = 1;
282
283                 memcpy (&outpacket -> options [mainbufix],
284                         buffer, main_buffer_size - mainbufix);
285                 bufix = main_buffer_size - mainbufix;
286                 length = DHCP_FIXED_NON_UDP + mainbufix;
287                 if (overload & 1) {
288                         if (option_size - bufix <= DHCP_FILE_LEN) {
289                                 memcpy (outpacket -> file,
290                                         &buffer [bufix], option_size - bufix);
291                                 mainbufix = option_size - bufix;
292                                 if (mainbufix < DHCP_FILE_LEN)
293                                         outpacket -> file [mainbufix++]
294                                                 = DHO_END;
295                                 while (mainbufix < DHCP_FILE_LEN)
296                                         outpacket -> file [mainbufix++]
297                                                 = DHO_PAD;
298                         } else {
299                                 memcpy (outpacket -> file,
300                                         &buffer [bufix], DHCP_FILE_LEN);
301                                 bufix += DHCP_FILE_LEN;
302                         }
303                 }
304                 if ((overload & 2) && option_size < bufix) {
305                         memcpy (outpacket -> sname,
306                                 &buffer [bufix], option_size - bufix);
307
308                         mainbufix = option_size - bufix;
309                         if (mainbufix < DHCP_SNAME_LEN)
310                                 outpacket -> file [mainbufix++]
311                                         = DHO_END;
312                         while (mainbufix < DHCP_SNAME_LEN)
313                                 outpacket -> file [mainbufix++]
314                                         = DHO_PAD;
315                 }
316         }
317         return length;
318 }
319
320 /* Store all the requested options into the requested buffer. */
321
322 int store_options (buffer, buflen, options, priority_list, priority_len,
323                    first_cutoff, second_cutoff, terminate)
324         unsigned char *buffer;
325         int buflen;
326         struct tree_cache **options;
327         unsigned char *priority_list;
328         int priority_len;
329         int first_cutoff, second_cutoff;
330         int terminate;
331 {
332         int bufix = 0;
333         int option_stored [256];
334         int i;
335         int ix;
336         int tto;
337
338         /* Zero out the stored-lengths array. */
339         memset (option_stored, 0, sizeof option_stored);
340
341         /* Copy out the options in the order that they appear in the
342            priority list... */
343         for (i = 0; i < priority_len; i++) {
344                 /* Code for next option to try to store. */
345                 int code = priority_list [i];
346                 int optstart;
347
348                 /* Number of bytes left to store (some may already
349                    have been stored by a previous pass). */
350                 int length;
351
352                 /* If no data is available for this option, skip it. */
353                 if (!options [code]) {
354                         continue;
355                 }
356
357                 /* The client could ask for things that are mandatory,
358                    in which case we should avoid storing them twice... */
359                 if (option_stored [code])
360                         continue;
361                 option_stored [code] = 1;
362
363                 /* Find the value of the option... */
364                 if (!tree_evaluate (options [code])) {
365                         continue;
366                 }
367
368                 /* We should now have a constant length for the option. */
369                 length = options [code] -> len;
370
371                 /* Do we add a NUL? */
372                 if (terminate && dhcp_options [code].format [0] == 't') {
373                         length++;
374                         tto = 1;
375                 } else {
376                         tto = 0;
377                 }
378
379                 /* Try to store the option. */
380
381                 /* If the option's length is more than 255, we must store it
382                    in multiple hunks.   Store 255-byte hunks first.  However,
383                    in any case, if the option data will cross a buffer
384                    boundary, split it across that boundary. */
385
386                 ix = 0;
387
388                 optstart = bufix;
389                 while (length) {
390                         unsigned char incr = length > 255 ? 255 : length;
391
392                         /* If this hunk of the buffer will cross a
393                            boundary, only go up to the boundary in this
394                            pass. */
395                         if (bufix < first_cutoff &&
396                             bufix + incr > first_cutoff)
397                                 incr = first_cutoff - bufix;
398                         else if (bufix < second_cutoff &&
399                                  bufix + incr > second_cutoff)
400                                 incr = second_cutoff - bufix;
401
402                         /* If this option is going to overflow the buffer,
403                            skip it. */
404                         if (bufix + 2 + incr > buflen) {
405                                 bufix = optstart;
406                                 break;
407                         }
408
409                         /* Everything looks good - copy it in! */
410                         buffer [bufix] = code;
411                         buffer [bufix + 1] = incr;
412                         if (tto && incr == length) {
413                                 memcpy (buffer + bufix + 2,
414                                         options [code] -> value + ix,
415                                         incr - 1);
416                                 buffer [bufix + 2 + incr - 1] = 0;
417                         } else {
418                                 memcpy (buffer + bufix + 2,
419                                         options [code] -> value + ix, incr);
420                         }
421                         length -= incr;
422                         ix += incr;
423                         bufix += 2 + incr;
424                 }
425         }
426         return bufix;
427 }
428
429 /* Format the specified option so that a human can easily read it. */
430
431 char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
432         unsigned int code;
433         unsigned char *data;
434         int len;
435         int emit_commas;
436         int emit_quotes;
437 {
438         static char optbuf [32768]; /* XXX */
439         int hunksize = 0;
440         int numhunk = -1;
441         int numelem = 0;
442         char fmtbuf [32];
443         int i, j, k;
444         char *op = optbuf;
445         unsigned char *dp = data;
446         struct in_addr foo;
447         char comma;
448
449         /* Code should be between 0 and 255. */
450         if (code > 255)
451                 error ("pretty_print_option: bad code %d\n", code);
452
453         if (emit_commas)
454                 comma = ',';
455         else
456                 comma = ' ';
457         
458         /* Figure out the size of the data. */
459         for (i = 0; dhcp_options [code].format [i]; i++) {
460                 if (!numhunk) {
461                         warn ("%s: Excess information in format string: %s\n",
462                               dhcp_options [code].name,
463                               &(dhcp_options [code].format [i]));
464                         break;
465                 }
466                 numelem++;
467                 fmtbuf [i] = dhcp_options [code].format [i];
468                 switch (dhcp_options [code].format [i]) {
469                       case 'A':
470                         --numelem;
471                         fmtbuf [i] = 0;
472                         numhunk = 0;
473                         break;
474                       case 'X':
475                         for (k = 0; k < len; k++) {
476                                 if (!isascii (data [k]) ||
477                                     !isprint (data [k]))
478                                         break;
479                         }
480                         if (k == len) {
481                                 fmtbuf [i] = 't';
482                                 numhunk = -2;
483                         } else {
484                                 fmtbuf [i] = 'x';
485                                 hunksize++;
486                                 comma = ':';
487                                 numhunk = 0;
488                         }
489                         fmtbuf [i + 1] = 0;
490                         break;
491                       case 't':
492                         fmtbuf [i] = 't';
493                         fmtbuf [i + 1] = 0;
494                         numhunk = -2;
495                         break;
496                       case 'I':
497                       case 'l':
498                       case 'L':
499                         hunksize += 4;
500                         break;
501                       case 's':
502                       case 'S':
503                         hunksize += 2;
504                         break;
505                       case 'b':
506                       case 'B':
507                       case 'f':
508                         hunksize++;
509                         break;
510                       case 'e':
511                         break;
512                       default:
513                         warn ("%s: garbage in format string: %s\n",
514                               dhcp_options [code].name,
515                               &(dhcp_options [code].format [i]));
516                         break;
517                 } 
518         }
519
520         /* Check for too few bytes... */
521         if (hunksize > len) {
522                 warn ("%s: expecting at least %d bytes; got %d",
523                       dhcp_options [code].name,
524                       hunksize, len);
525                 return "<error>";
526         }
527         /* Check for too many bytes... */
528         if (numhunk == -1 && hunksize < len)
529                 warn ("%s: %d extra bytes",
530                       dhcp_options [code].name,
531                       len - hunksize);
532
533         /* If this is an array, compute its size. */
534         if (!numhunk)
535                 numhunk = len / hunksize;
536         /* See if we got an exact number of hunks. */
537         if (numhunk > 0 && numhunk * hunksize < len)
538                 warn ("%s: %d extra bytes at end of array\n",
539                       dhcp_options [code].name,
540                       len - numhunk * hunksize);
541
542         /* A one-hunk array prints the same as a single hunk. */
543         if (numhunk < 0)
544                 numhunk = 1;
545
546         /* Cycle through the array (or hunk) printing the data. */
547         for (i = 0; i < numhunk; i++) {
548                 for (j = 0; j < numelem; j++) {
549                         switch (fmtbuf [j]) {
550                               case 't':
551                                 if (emit_quotes)
552                                         *op++ = '"';
553                                 strcpy (op, (char *)dp);
554                                 op += strlen ((char *)dp);
555                                 if (emit_quotes)
556                                         *op++ = '"';
557                                 *op = 0;
558                                 break;
559                               case 'I':
560                                 foo.s_addr = htonl (getULong (dp));
561                                 strcpy (op, inet_ntoa (foo));
562                                 dp += 4;
563                                 break;
564                               case 'l':
565                                 sprintf (op, "%ld", (long)getLong (dp));
566                                 dp += 4;
567                                 break;
568                               case 'L':
569                                 sprintf (op, "%ld",
570                                          (unsigned long)getULong (dp));
571                                 dp += 4;
572                                 break;
573                               case 's':
574                                 sprintf (op, "%d", getShort (dp));
575                                 dp += 2;
576                                 break;
577                               case 'S':
578                                 sprintf (op, "%d", getUShort (dp));
579                                 dp += 2;
580                                 break;
581                               case 'b':
582                                 sprintf (op, "%d", *(char *)dp++);
583                                 break;
584                               case 'B':
585                                 sprintf (op, "%d", *dp++);
586                                 break;
587                               case 'x':
588                                 sprintf (op, "%x", *dp++);
589                                 break;
590                               case 'f':
591                                 strcpy (op, *dp++ ? "true" : "false");
592                                 break;
593                               default:
594                                 warn ("Unexpected format code %c", fmtbuf [j]);
595                         }
596                         op += strlen (op);
597                         if (j + 1 < numelem && comma != ':')
598                                 *op++ = ' ';
599                 }
600                 if (i + 1 < numhunk) {
601                         *op++ = comma;
602                 }
603                 
604         }
605         return optbuf;
606 }
607
608 void do_packet (interface, packet, len, from_port, from, hfrom)
609         struct interface_info *interface;
610         struct dhcp_packet *packet;
611         int len;
612         unsigned int from_port;
613         struct iaddr from;
614         struct hardware *hfrom;
615 {
616         struct packet tp;
617         int i;
618
619         if (packet -> hlen > sizeof packet -> chaddr) {
620                 note ("Discarding packet with invalid hlen.");
621                 return;
622         }
623
624         memset (&tp, 0, sizeof tp);
625         tp.raw = packet;
626         tp.packet_length = len;
627         tp.client_port = from_port;
628         tp.client_addr = from;
629         tp.interface = interface;
630         tp.haddr = hfrom;
631         
632         parse_options (&tp);
633         if (tp.options_valid &&
634             tp.options [DHO_DHCP_MESSAGE_TYPE].data)
635                 tp.packet_type =
636                         tp.options [DHO_DHCP_MESSAGE_TYPE].data [0];
637         if (tp.packet_type)
638                 dhcp (&tp);
639         else
640                 bootp (&tp);
641
642         /* Free the data associated with the options. */
643         for (i = 0; i < 256; i++) {
644                 if (tp.options [i].len && tp.options [i].data)
645                         dfree (tp.options [i].data, "do_packet");
646         }
647 }
648