1 /** @file
2   Function to validate, parse, process the DHCP options.
3 
4 Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "Dhcp4Impl.h"
16 
17 ///
18 /// A list of the format of DHCP Options sorted by option tag
19 /// to validate a dhcp message. Refere the comments of the
20 /// DHCP_OPTION_FORMAT structure.
21 ///
22 DHCP_OPTION_FORMAT DhcpOptionFormats[] = {
23   {DHCP_TAG_NETMASK,        DHCP_OPTION_IP,     1, 1  , TRUE},
24   {DHCP_TAG_TIME_OFFSET,    DHCP_OPTION_INT32,  1, 1  , FALSE},
25   {DHCP_TAG_ROUTER,         DHCP_OPTION_IP,     1, -1 , TRUE},
26   {DHCP_TAG_TIME_SERVER,    DHCP_OPTION_IP,     1, -1 , FALSE},
27   {DHCP_TAG_NAME_SERVER,    DHCP_OPTION_IP,     1, -1 , FALSE},
28   {DHCP_TAG_DNS_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
29   {DHCP_TAG_LOG_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
30   {DHCP_TAG_COOKIE_SERVER,  DHCP_OPTION_IP,     1, -1 , FALSE},
31   {DHCP_TAG_LPR_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
32   {DHCP_TAG_IMPRESS_SERVER, DHCP_OPTION_IP,     1, -1 , FALSE},
33   {DHCP_TAG_RL_SERVER,      DHCP_OPTION_IP,     1, -1 , FALSE},
34   {DHCP_TAG_HOSTNAME,       DHCP_OPTION_INT8,   1, -1 , FALSE},
35   {DHCP_TAG_BOOTFILE_LEN,   DHCP_OPTION_INT16,  1, 1  , FALSE},
36   {DHCP_TAG_DUMP,           DHCP_OPTION_INT8,   1, -1 , FALSE},
37   {DHCP_TAG_DOMAINNAME,     DHCP_OPTION_INT8,   1, -1 , FALSE},
38   {DHCP_TAG_SWAP_SERVER,    DHCP_OPTION_IP,     1, 1  , FALSE},
39   {DHCP_TAG_ROOTPATH,       DHCP_OPTION_INT8,   1, -1 , FALSE},
40   {DHCP_TAG_EXTEND_PATH,    DHCP_OPTION_INT8,   1, -1 , FALSE},
41 
42   {DHCP_TAG_IPFORWARD,      DHCP_OPTION_SWITCH, 1, 1  , FALSE},
43   {DHCP_TAG_NONLOCAL_SRR,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},
44   {DHCP_TAG_POLICY_SRR,     DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
45   {DHCP_TAG_EMTU,           DHCP_OPTION_INT16,  1, 1  , FALSE},
46   {DHCP_TAG_TTL,            DHCP_OPTION_INT8,   1, 1  , FALSE},
47   {DHCP_TAG_PATHMTU_AGE,    DHCP_OPTION_INT32,  1, 1  , FALSE},
48   {DHCP_TAG_PATHMTU_PLATEAU,DHCP_OPTION_INT16,  1, -1 , FALSE},
49 
50   {DHCP_TAG_IFMTU,          DHCP_OPTION_INT16,  1, 1  , FALSE},
51   {DHCP_TAG_SUBNET_LOCAL,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},
52   {DHCP_TAG_BROADCAST,      DHCP_OPTION_IP,     1, 1  , FALSE},
53   {DHCP_TAG_DISCOVER_MASK,  DHCP_OPTION_SWITCH, 1, 1  , FALSE},
54   {DHCP_TAG_SUPPLY_MASK,    DHCP_OPTION_SWITCH, 1, 1  , FALSE},
55   {DHCP_TAG_DISCOVER_ROUTE, DHCP_OPTION_SWITCH, 1, 1  , FALSE},
56   {DHCP_TAG_ROUTER_SOLICIT, DHCP_OPTION_IP,     1, 1  , FALSE},
57   {DHCP_TAG_STATIC_ROUTE,   DHCP_OPTION_IPPAIR, 1, -1 , FALSE},
58 
59   {DHCP_TAG_TRAILER,        DHCP_OPTION_SWITCH, 1, 1  , FALSE},
60   {DHCP_TAG_ARPAGE,         DHCP_OPTION_INT32,  1, 1  , FALSE},
61   {DHCP_TAG_ETHER_ENCAP,    DHCP_OPTION_SWITCH, 1, 1  , FALSE},
62 
63   {DHCP_TAG_TCP_TTL,        DHCP_OPTION_INT8,   1, 1  , FALSE},
64   {DHCP_TAG_KEEP_INTERVAL,  DHCP_OPTION_INT32,  1, 1  , FALSE},
65   {DHCP_TAG_KEEP_GARBAGE,   DHCP_OPTION_SWITCH, 1, 1  , FALSE},
66 
67   {DHCP_TAG_NIS_DOMAIN,     DHCP_OPTION_INT8,   1, -1 , FALSE},
68   {DHCP_TAG_NIS_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
69   {DHCP_TAG_NTP_SERVER,     DHCP_OPTION_IP,     1, -1 , FALSE},
70   {DHCP_TAG_VENDOR,         DHCP_OPTION_INT8,   1, -1 , FALSE},
71   {DHCP_TAG_NBNS,           DHCP_OPTION_IP,     1, -1 , FALSE},
72   {DHCP_TAG_NBDD,           DHCP_OPTION_IP,     1, -1 , FALSE},
73   {DHCP_TAG_NBTYPE,         DHCP_OPTION_INT8,   1, 1  , FALSE},
74   {DHCP_TAG_NBSCOPE,        DHCP_OPTION_INT8,   1, -1 , FALSE},
75   {DHCP_TAG_XFONT,          DHCP_OPTION_IP,     1, -1 , FALSE},
76   {DHCP_TAG_XDM,            DHCP_OPTION_IP,     1, -1 , FALSE},
77 
78   {DHCP_TAG_REQUEST_IP,     DHCP_OPTION_IP,     1, 1  , FALSE},
79   {DHCP_TAG_LEASE,          DHCP_OPTION_INT32,  1, 1  , TRUE},
80   {DHCP_TAG_OVERLOAD,       DHCP_OPTION_INT8,   1, 1  , TRUE},
81   {DHCP_TAG_TYPE,           DHCP_OPTION_INT8,   1, 1  , TRUE},
82   {DHCP_TAG_SERVER_ID,      DHCP_OPTION_IP,     1, 1  , TRUE},
83   {DHCP_TAG_PARA_LIST,      DHCP_OPTION_INT8,   1, -1 , FALSE},
84   {DHCP_TAG_MESSAGE,        DHCP_OPTION_INT8,   1, -1 , FALSE},
85   {DHCP_TAG_MAXMSG,         DHCP_OPTION_INT16,  1, 1  , FALSE},
86   {DHCP_TAG_T1,             DHCP_OPTION_INT32,  1, 1  , TRUE},
87   {DHCP_TAG_T2,             DHCP_OPTION_INT32,  1, 1  , TRUE},
88   {DHCP_TAG_VENDOR_CLASS,   DHCP_OPTION_INT8,   1, -1 , FALSE},
89   {DHCP_TAG_CLIENT_ID,      DHCP_OPTION_INT8,   2, -1 , FALSE},
90 
91   {DHCP_TAG_NISPLUS,        DHCP_OPTION_INT8,   1, -1 , FALSE},
92   {DHCP_TAG_NISPLUS_SERVER, DHCP_OPTION_IP,     1, -1 , FALSE},
93 
94   {DHCP_TAG_TFTP,           DHCP_OPTION_INT8,   1, -1 , FALSE},
95   {DHCP_TAG_BOOTFILE,       DHCP_OPTION_INT8,   1, -1 , FALSE},
96 
97   {DHCP_TAG_MOBILEIP,       DHCP_OPTION_IP,     0, -1 , FALSE},
98   {DHCP_TAG_SMTP,           DHCP_OPTION_IP,     1, -1 , FALSE},
99   {DHCP_TAG_POP3,           DHCP_OPTION_IP,     1, -1 , FALSE},
100   {DHCP_TAG_NNTP,           DHCP_OPTION_IP,     1, -1 , FALSE},
101   {DHCP_TAG_WWW,            DHCP_OPTION_IP,     1, -1 , FALSE},
102   {DHCP_TAG_FINGER,         DHCP_OPTION_IP,     1, -1 , FALSE},
103   {DHCP_TAG_IRC,            DHCP_OPTION_IP,     1, -1 , FALSE},
104   {DHCP_TAG_STTALK,         DHCP_OPTION_IP,     1, -1 , FALSE},
105   {DHCP_TAG_STDA,           DHCP_OPTION_IP,     1, -1 , FALSE},
106 
107   {DHCP_TAG_CLASSLESS_ROUTE,DHCP_OPTION_INT8,   5, -1 , FALSE},
108 };
109 
110 
111 /**
112   Binary search the DhcpOptionFormats array to find the format
113   information about a specific option.
114 
115   @param[in]  Tag                    The option's tag.
116 
117   @return The point to the option's format, NULL if not found.
118 
119 **/
120 DHCP_OPTION_FORMAT *
DhcpFindOptionFormat(IN UINT8 Tag)121 DhcpFindOptionFormat (
122   IN UINT8                  Tag
123   )
124 {
125   INTN                      Left;
126   INTN                      Right;
127   INTN                      Middle;
128 
129   Left  = 0;
130   Right = sizeof (DhcpOptionFormats) / sizeof (DHCP_OPTION_FORMAT) - 1;
131 
132   while (Right >= Left) {
133     Middle = (Left + Right) / 2;
134 
135     if (Tag == DhcpOptionFormats[Middle].Tag) {
136       return &DhcpOptionFormats[Middle];
137     }
138 
139     if (Tag < DhcpOptionFormats[Middle].Tag) {
140       Right = Middle - 1;
141     } else {
142       Left  = Middle + 1;
143     }
144   }
145 
146   return NULL;
147 }
148 
149 
150 /**
151   Validate whether a single DHCP option is valid according to its format.
152 
153   @param[in]  Format                 The option's format
154   @param[in]  OptValue               The value of the option
155   @param[in]  Len                    The length of the option value
156 
157   @retval TRUE     The option is valid.
158   @retval FALSE    Otherwise.
159 
160 **/
161 BOOLEAN
DhcpOptionIsValid(IN DHCP_OPTION_FORMAT * Format,IN UINT8 * OptValue,IN INTN Len)162 DhcpOptionIsValid (
163   IN DHCP_OPTION_FORMAT     *Format,
164   IN UINT8                  *OptValue,
165   IN INTN                   Len
166   )
167 {
168   INTN                      Unit;
169   INTN                      Occur;
170   INTN                      Index;
171 
172   Unit = 0;
173 
174   switch (Format->Type) {
175   case DHCP_OPTION_SWITCH:
176   case DHCP_OPTION_INT8:
177     Unit = 1;
178     break;
179 
180   case DHCP_OPTION_INT16:
181     Unit = 2;
182     break;
183 
184   case DHCP_OPTION_INT32:
185   case DHCP_OPTION_IP:
186     Unit = 4;
187     break;
188 
189   case DHCP_OPTION_IPPAIR:
190     Unit = 8;
191     break;
192   }
193 
194   ASSERT (Unit != 0);
195 
196   //
197   // Validate that the option appears in the full units.
198   //
199   if ((Len % Unit) != 0) {
200     return FALSE;
201   }
202 
203   //
204   // Validate the occurance of the option unit is with in [MinOccur, MaxOccur]
205   //
206   Occur = Len / Unit;
207 
208   if (((Format->MinOccur != -1) && (Occur < Format->MinOccur)) ||
209       ((Format->MaxOccur != -1) && (Occur > Format->MaxOccur))
210       ) {
211     return FALSE;
212   }
213 
214   //
215   // If the option is of type switch, only 0/1 are valid values.
216   //
217   if (Format->Type == DHCP_OPTION_SWITCH) {
218     for (Index = 0; Index < Occur; Index++) {
219       if ((OptValue[Index] != 0) && (OptValue[Index] != 1)) {
220         return FALSE;
221       }
222     }
223   }
224 
225   return TRUE;
226 }
227 
228 
229 /**
230   Extract the client interested options, all the parameters are
231   converted to host byte order.
232 
233   @param[in]  Tag                    The DHCP option tag
234   @param[in]  Len                    The length of the option
235   @param[in]  Data                   The value of the DHCP option
236   @param[out] Para                   The variable to save the interested parameter
237 
238   @retval EFI_SUCCESS            The DHCP option is successfully extracted.
239   @retval EFI_INVALID_PARAMETER  The DHCP option is mal-formated
240 
241 **/
242 EFI_STATUS
DhcpGetParameter(IN UINT8 Tag,IN INTN Len,IN UINT8 * Data,OUT DHCP_PARAMETER * Para)243 DhcpGetParameter (
244   IN  UINT8                  Tag,
245   IN  INTN                   Len,
246   IN  UINT8                  *Data,
247   OUT DHCP_PARAMETER         *Para
248   )
249 {
250   switch (Tag) {
251   case DHCP_TAG_NETMASK:
252     Para->NetMask = NetGetUint32 (Data);
253     break;
254 
255   case DHCP_TAG_ROUTER:
256     //
257     // Return the first router to consumer which is the preferred one
258     //
259     Para->Router = NetGetUint32 (Data);
260     break;
261 
262   case DHCP_TAG_LEASE:
263     Para->Lease = NetGetUint32 (Data);
264     break;
265 
266   case DHCP_TAG_OVERLOAD:
267     Para->Overload = *Data;
268 
269     if ((Para->Overload < 1) || (Para->Overload > 3)) {
270       return EFI_INVALID_PARAMETER;
271     }
272     break;
273 
274   case DHCP_TAG_TYPE:
275     Para->DhcpType = *Data;
276 
277     if ((Para->DhcpType < 1) || (Para->DhcpType > 9)) {
278       return EFI_INVALID_PARAMETER;
279     }
280     break;
281 
282   case DHCP_TAG_SERVER_ID:
283     Para->ServerId = NetGetUint32 (Data);
284     break;
285 
286   case DHCP_TAG_T1:
287     Para->T1 = NetGetUint32 (Data);
288     break;
289 
290   case DHCP_TAG_T2:
291     Para->T2 = NetGetUint32 (Data);
292     break;
293   }
294 
295   return EFI_SUCCESS;
296 }
297 
298 
299 /**
300   Inspect all the options in a single buffer. DHCP options may be contained
301   in several buffers, such as the BOOTP options filed, boot file or server
302   name. Each option buffer is required to end with DHCP_TAG_EOP.
303 
304   @param[in]  Buffer                 The buffer which contains DHCP options
305   @param[in]  BufLen                 The length of the buffer
306   @param[in]  Check                  The callback function for each option found
307   @param[in]  Context                The opaque parameter for the Check
308   @param[out] Overload               Variable to save the value of DHCP_TAG_OVERLOAD
309                                      option.
310 
311   @retval EFI_SUCCESS            All the options are valid
312   @retval EFI_INVALID_PARAMETER  The options are mal-formated.
313 
314 **/
315 EFI_STATUS
DhcpIterateBufferOptions(IN UINT8 * Buffer,IN INTN BufLen,IN DHCP_CHECK_OPTION Check OPTIONAL,IN VOID * Context,OUT UINT8 * Overload OPTIONAL)316 DhcpIterateBufferOptions (
317   IN  UINT8                 *Buffer,
318   IN  INTN                  BufLen,
319   IN  DHCP_CHECK_OPTION     Check             OPTIONAL,
320   IN  VOID                  *Context,
321   OUT UINT8                 *Overload         OPTIONAL
322   )
323 {
324   INTN                      Cur;
325   UINT8                     Tag;
326   UINT8                     Len;
327 
328   Cur = 0;
329 
330   while (Cur < BufLen) {
331     Tag = Buffer[Cur];
332 
333     if (Tag == DHCP_TAG_PAD) {
334       Cur++;
335       continue;
336     } else if (Tag == DHCP_TAG_EOP) {
337       return EFI_SUCCESS;
338     }
339 
340     Cur++;
341 
342     if (Cur == BufLen) {
343       return EFI_INVALID_PARAMETER;
344     }
345 
346     Len = Buffer[Cur++];
347 
348     if (Cur + Len > BufLen) {
349       return EFI_INVALID_PARAMETER;
350     }
351 
352     if ((Tag == DHCP_TAG_OVERLOAD) && (Overload != NULL)) {
353       if (Len != 1) {
354         return EFI_INVALID_PARAMETER;
355       }
356 
357       *Overload = Buffer[Cur];
358     }
359 
360     if ((Check != NULL) && EFI_ERROR (Check (Tag, Len, Buffer + Cur, Context))) {
361       return EFI_INVALID_PARAMETER;
362     }
363 
364     Cur += Len;
365   }
366 
367   //
368   // Each option buffer is expected to end with an EOP
369   //
370   return EFI_INVALID_PARAMETER;
371 }
372 
373 
374 /**
375   Iterate through a DHCP message to visit each option. First inspect
376   all the options in the OPTION field. Then if overloaded, inspect
377   the options in FILENAME and SERVERNAME fields. One option may be
378   encoded in several places. See RFC 3396 Encoding Long Options in DHCP
379 
380   @param[in]  Packet                 The DHCP packet to check the options for
381   @param[in]  Check                  The callback function to be called for each option
382                                      found
383   @param[in]  Context                The opaque parameter for Check
384 
385   @retval EFI_SUCCESS            The DHCP packet's options are well formated
386   @retval EFI_INVALID_PARAMETER  The DHCP packet's options are not well formated
387 
388 **/
389 EFI_STATUS
DhcpIterateOptions(IN EFI_DHCP4_PACKET * Packet,IN DHCP_CHECK_OPTION Check OPTIONAL,IN VOID * Context)390 DhcpIterateOptions (
391   IN  EFI_DHCP4_PACKET      *Packet,
392   IN  DHCP_CHECK_OPTION     Check         OPTIONAL,
393   IN  VOID                  *Context
394   )
395 {
396   EFI_STATUS                Status;
397   UINT8                     Overload;
398 
399   Overload = 0;
400 
401   Status   = DhcpIterateBufferOptions (
402                Packet->Dhcp4.Option,
403                Packet->Length - sizeof (EFI_DHCP4_HEADER) - sizeof (UINT32),
404                Check,
405                Context,
406                &Overload
407                );
408 
409   if (EFI_ERROR (Status)) {
410     return Status;
411   }
412 
413   if ((Overload == DHCP_OVERLOAD_FILENAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
414     Status = DhcpIterateBufferOptions (
415                (UINT8 *) Packet->Dhcp4.Header.BootFileName,
416                128,
417                Check,
418                Context,
419                NULL
420                );
421 
422     if (EFI_ERROR (Status)) {
423       return Status;
424     }
425   }
426 
427   if ((Overload == DHCP_OVERLOAD_SVRNAME) || (Overload == DHCP_OVERLOAD_BOTH)) {
428     Status = DhcpIterateBufferOptions (
429                (UINT8 *) Packet->Dhcp4.Header.ServerName,
430                64,
431                Check,
432                Context,
433                NULL
434                );
435 
436     if (EFI_ERROR (Status)) {
437       return Status;
438     }
439   }
440 
441   return EFI_SUCCESS;
442 }
443 
444 
445 /**
446   Call back function to DhcpIterateOptions to compute each option's
447   length. It just adds the data length of all the occurances of this
448   Tag. Context is an array of 256 DHCP_OPTION_COUNT.
449 
450   @param[in]  Tag                    The current option to check
451   @param[in]  Len                    The length of the option data
452   @param[in]  Data                   The option data
453   @param[in]  Context                The context, which is a array of 256
454                                      DHCP_OPTION_COUNT.
455 
456   @retval EFI_SUCCESS            It always returns EFI_SUCCESS.
457 
458 **/
459 EFI_STATUS
DhcpGetOptionLen(IN UINT8 Tag,IN UINT8 Len,IN UINT8 * Data,IN VOID * Context)460 DhcpGetOptionLen (
461   IN UINT8                  Tag,
462   IN UINT8                  Len,
463   IN UINT8                  *Data,
464   IN VOID                   *Context
465   )
466 {
467   DHCP_OPTION_COUNT         *OpCount;
468 
469   OpCount             = (DHCP_OPTION_COUNT *) Context;
470   OpCount[Tag].Offset = (UINT16) (OpCount[Tag].Offset + Len);
471 
472   return EFI_SUCCESS;
473 }
474 
475 
476 /**
477   Call back function to DhcpIterateOptions to consolidate each option's
478   data. There are maybe several occurrence of the same option.
479 
480   @param[in]  Tag                    The option to consolidate its data
481   @param[in]  Len                    The length of option data
482   @param[in]  Data                   The data of the option's current occurance
483   @param[in]  Context                The context, which is DHCP_OPTION_CONTEXT. This
484                                      array is  just a wrap to pass THREE parameters.
485 
486   @retval EFI_SUCCESS            It always returns EFI_SUCCESS
487 
488 **/
489 EFI_STATUS
DhcpFillOption(IN UINT8 Tag,IN UINT8 Len,IN UINT8 * Data,IN VOID * Context)490 DhcpFillOption (
491   IN UINT8                  Tag,
492   IN UINT8                  Len,
493   IN UINT8                  *Data,
494   IN VOID                   *Context
495   )
496 {
497   DHCP_OPTION_CONTEXT       *OptContext;
498   DHCP_OPTION_COUNT         *OptCount;
499   DHCP_OPTION               *Options;
500   UINT8                     *Buf;
501   UINT8                     Index;
502 
503   OptContext  = (DHCP_OPTION_CONTEXT *) Context;
504 
505   OptCount    = OptContext->OpCount;
506   Index       = OptCount[Tag].Index;
507   Options     = OptContext->Options;
508   Buf         = OptContext->Buf;
509 
510   if (Options[Index].Data == NULL) {
511     Options[Index].Tag  = Tag;
512     Options[Index].Data = Buf + OptCount[Tag].Offset;
513   }
514 
515   CopyMem (Buf + OptCount[Tag].Offset, Data, Len);
516 
517   OptCount[Tag].Offset  = (UINT16) (OptCount[Tag].Offset + Len);
518   Options[Index].Len    = (UINT16) (Options[Index].Len + Len);
519   return EFI_SUCCESS;
520 }
521 
522 
523 /**
524   Parse the options of a DHCP packet. It supports RFC 3396: Encoding
525   Long Options in DHCP. That is, it will combine all the option value
526   of all the occurances of each option.
527   A little bit of implemenation:
528   It adopts the "Key indexed counting" algorithm. First, it allocates
529   an array of 256 DHCP_OPTION_COUNTs because DHCP option tag is encoded
530   as a UINT8. It then iterates the DHCP packet to get data length of
531   each option by calling DhcpIterOptions with DhcpGetOptionLen. Now, it
532   knows the number of present options and their length. It allocates a
533   array of DHCP_OPTION and a continuous buffer after the array to put
534   all the options' data. Each option's data is pointed to by the Data
535   field in DHCP_OPTION structure. At last, it call DhcpIterateOptions
536   with DhcpFillOption to fill each option's data to its position in the
537   buffer.
538 
539   @param[in]  Packet                 The DHCP packet to parse the options
540   @param[out] Count                  The number of valid dhcp options present in the
541                                      packet
542   @param[out] OptionPoint            The array that contains the DHCP options. Caller
543                                      should free it.
544 
545   @retval EFI_NOT_FOUND          Cannot find any option.
546   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to parse the packet.
547   @retval EFI_INVALID_PARAMETER  The options are mal-formated
548   @retval EFI_SUCCESS            The options are parsed into OptionPoint
549 
550 **/
551 EFI_STATUS
DhcpParseOption(IN EFI_DHCP4_PACKET * Packet,OUT INTN * Count,OUT DHCP_OPTION ** OptionPoint)552 DhcpParseOption (
553   IN  EFI_DHCP4_PACKET      *Packet,
554   OUT INTN                  *Count,
555   OUT DHCP_OPTION           **OptionPoint
556   )
557 {
558   DHCP_OPTION_CONTEXT       Context;
559   DHCP_OPTION               *Options;
560   DHCP_OPTION_COUNT         *OptCount;
561   EFI_STATUS                Status;
562   UINT16                    TotalLen;
563   INTN                      OptNum;
564   INTN                      Index;
565 
566   ASSERT ((Count != NULL) && (OptionPoint != NULL));
567 
568   //
569   // First compute how many options and how long each option is
570   // with the "Key indexed counting" algorithms.
571   //
572   OptCount = AllocateZeroPool (DHCP_MAX_OPTIONS * sizeof (DHCP_OPTION_COUNT));
573 
574   if (OptCount == NULL) {
575     return EFI_OUT_OF_RESOURCES;
576   }
577 
578   Status = DhcpIterateOptions (Packet, DhcpGetOptionLen, OptCount);
579 
580   if (EFI_ERROR (Status)) {
581     goto ON_EXIT;
582   }
583 
584   //
585   // Before the loop, Offset is the length of the option. After loop,
586   // OptCount[Index].Offset specifies the offset into the continuous
587   // option value buffer to put the data.
588   //
589   TotalLen  = 0;
590   OptNum    = 0;
591 
592   for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
593     if (OptCount[Index].Offset != 0) {
594       OptCount[Index].Index   = (UINT8) OptNum;
595 
596       TotalLen                = (UINT16) (TotalLen + OptCount[Index].Offset);
597       OptCount[Index].Offset  = (UINT16) (TotalLen - OptCount[Index].Offset);
598 
599       OptNum++;
600     }
601   }
602 
603   *Count        = OptNum;
604   *OptionPoint  = NULL;
605 
606   if (OptNum == 0) {
607     goto ON_EXIT;
608   }
609 
610   //
611   // Allocate a buffer to hold the DHCP options, and after that, a
612   // continuous buffer to put all the options' data.
613   //
614   Options = AllocateZeroPool ((UINTN) (OptNum * sizeof (DHCP_OPTION)) + TotalLen);
615 
616   if (Options == NULL) {
617     Status = EFI_OUT_OF_RESOURCES;
618     goto ON_EXIT;
619   }
620 
621   Context.OpCount = OptCount;
622   Context.Options = Options;
623   Context.Buf     = (UINT8 *) (Options + OptNum);
624 
625   Status          = DhcpIterateOptions (Packet, DhcpFillOption, &Context);
626 
627   if (EFI_ERROR (Status)) {
628     FreePool (Options);
629     goto ON_EXIT;
630   }
631 
632   *OptionPoint = Options;
633 
634 ON_EXIT:
635   FreePool (OptCount);
636   return Status;
637 }
638 
639 
640 /**
641   Validate the packet's options. If necessary, allocate
642   and fill in the interested parameters.
643 
644   @param[in]  Packet                 The packet to validate the options
645   @param[out] Para                   The variable to save the DHCP parameters.
646 
647   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory to validate the packet.
648   @retval EFI_INVALID_PARAMETER  The options are mal-formated
649   @retval EFI_SUCCESS            The options are parsed into OptionPoint
650 
651 **/
652 EFI_STATUS
DhcpValidateOptions(IN EFI_DHCP4_PACKET * Packet,OUT DHCP_PARAMETER ** Para OPTIONAL)653 DhcpValidateOptions (
654   IN  EFI_DHCP4_PACKET      *Packet,
655   OUT DHCP_PARAMETER        **Para       OPTIONAL
656   )
657 {
658   DHCP_PARAMETER            Parameter;
659   DHCP_OPTION_FORMAT        *Format;
660   DHCP_OPTION               *AllOption;
661   DHCP_OPTION               *Option;
662   EFI_STATUS                Status;
663   BOOLEAN                   Updated;
664   INTN                      Count;
665   INTN                      Index;
666 
667   if (Para != NULL) {
668     *Para = NULL;
669   }
670 
671   AllOption = NULL;
672 
673   Status = DhcpParseOption (Packet, &Count, &AllOption);
674   if (EFI_ERROR (Status) || (Count == 0)) {
675     return Status;
676   }
677   ASSERT (AllOption != NULL);
678 
679   Updated = FALSE;
680   ZeroMem (&Parameter, sizeof (Parameter));
681 
682   for (Index = 0; Index < Count; Index++) {
683     Option = &AllOption[Index];
684 
685     //
686     // Find the format of the option then validate it.
687     //
688     Format = DhcpFindOptionFormat (Option->Tag);
689 
690     if (Format == NULL) {
691       continue;
692     }
693 
694     if (!DhcpOptionIsValid (Format, Option->Data, Option->Len)) {
695       Status = EFI_INVALID_PARAMETER;
696       goto ON_EXIT;
697     }
698 
699     //
700     // Get the client interested parameters
701     //
702     if (Format->Alert && (Para != NULL)) {
703       Updated = TRUE;
704       Status  = DhcpGetParameter (Option->Tag, Option->Len, Option->Data, &Parameter);
705 
706       if (EFI_ERROR (Status)) {
707         goto ON_EXIT;
708       }
709     }
710   }
711 
712   if (Updated && (Para != NULL)) {
713     *Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), &Parameter);
714     if (*Para == NULL) {
715       Status = EFI_OUT_OF_RESOURCES;
716       goto ON_EXIT;
717     }
718   }
719 
720 ON_EXIT:
721   FreePool (AllOption);
722   return Status;
723 }
724 
725 
726 
727 /**
728   Append an option to the memory, if the option is longer than
729   255 bytes, splits it into several options.
730 
731   @param[out] Buf                    The buffer to append the option to
732   @param[in]  Tag                    The option's tag
733   @param[in]  DataLen                The length of the option's data
734   @param[in]  Data                   The option's data
735 
736   @return The position to append the next option
737 
738 **/
739 UINT8 *
DhcpAppendOption(OUT UINT8 * Buf,IN UINT8 Tag,IN UINT16 DataLen,IN UINT8 * Data)740 DhcpAppendOption (
741   OUT UINT8                  *Buf,
742   IN  UINT8                  Tag,
743   IN  UINT16                 DataLen,
744   IN  UINT8                  *Data
745   )
746 {
747   INTN                      Index;
748   INTN                      Len;
749 
750   ASSERT (DataLen != 0);
751 
752   for (Index = 0; Index < (DataLen + 254) / 255; Index++) {
753     Len      = MIN (255, DataLen - Index * 255);
754 
755     *(Buf++) = Tag;
756     *(Buf++) = (UINT8) Len;
757     CopyMem (Buf, Data + Index * 255, (UINTN) Len);
758 
759     Buf     += Len;
760   }
761 
762   return Buf;
763 }
764 
765 
766 /**
767   Build a new DHCP packet from a seed packet. Options may be deleted or
768   appended. The caller should free the NewPacket when finished using it.
769 
770   @param[in]  SeedPacket             The seed packet to start with
771   @param[in]  DeleteCount            The number of options to delete
772   @param[in]  DeleteList             The options to delete from the packet
773   @param[in]  AppendCount            The number of options to append
774   @param[in]  AppendList             The options to append to the packet
775   @param[out] NewPacket              The new packet, allocated and built by this
776                                      function.
777 
778   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory
779   @retval EFI_INVALID_PARAMETER  The options in SeekPacket are mal-formated
780   @retval EFI_SUCCESS            The packet is build.
781 
782 **/
783 EFI_STATUS
DhcpBuild(IN EFI_DHCP4_PACKET * SeedPacket,IN UINT32 DeleteCount,IN UINT8 * DeleteList OPTIONAL,IN UINT32 AppendCount,IN EFI_DHCP4_PACKET_OPTION * AppendList[]OPTIONAL,OUT EFI_DHCP4_PACKET ** NewPacket)784 DhcpBuild (
785   IN  EFI_DHCP4_PACKET        *SeedPacket,
786   IN  UINT32                  DeleteCount,
787   IN  UINT8                   *DeleteList     OPTIONAL,
788   IN  UINT32                  AppendCount,
789   IN  EFI_DHCP4_PACKET_OPTION *AppendList[]   OPTIONAL,
790   OUT EFI_DHCP4_PACKET        **NewPacket
791   )
792 {
793   DHCP_OPTION               *Mark;
794   DHCP_OPTION               *SeedOptions;
795   EFI_DHCP4_PACKET          *Packet;
796   EFI_STATUS                Status;
797   INTN                      Count;
798   UINT32                    Index;
799   UINT32                    Len;
800   UINT8                     *Buf;
801 
802   //
803   // Use an array of DHCP_OPTION to mark the existance
804   // and position of each valid options.
805   //
806   Mark = AllocatePool (sizeof (DHCP_OPTION) * DHCP_MAX_OPTIONS);
807 
808   if (Mark == NULL) {
809     return EFI_OUT_OF_RESOURCES;
810   }
811 
812   for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
813     Mark[Index].Tag = (UINT8) Index;
814     Mark[Index].Len = 0;
815   }
816 
817   //
818   // Get list of the options from the seed packet, then put
819   // them to the mark array according to their tags.
820   //
821   SeedOptions = NULL;
822   Status      = DhcpParseOption (SeedPacket, &Count, &SeedOptions);
823 
824   if (EFI_ERROR (Status)) {
825     goto ON_ERROR;
826   }
827 
828   if (SeedOptions != NULL) {
829     for (Index = 0; Index < (UINT32) Count; Index++) {
830       Mark[SeedOptions[Index].Tag] = SeedOptions[Index];
831     }
832   }
833 
834   //
835   // Mark the option's length is zero if it is in the DeleteList.
836   //
837   for (Index = 0; Index < DeleteCount; Index++) {
838     Mark[DeleteList[Index]].Len = 0;
839   }
840 
841   //
842   // Add or replace the option if it is in the append list.
843   //
844   for (Index = 0; Index < AppendCount; Index++) {
845     Mark[AppendList[Index]->OpCode].Len  = AppendList[Index]->Length;
846     Mark[AppendList[Index]->OpCode].Data = AppendList[Index]->Data;
847   }
848 
849   //
850   // compute the new packet length. No need to add 1 byte for
851   // EOP option since EFI_DHCP4_PACKET includes one extra byte
852   // for option. It is necessary to split the option if it is
853   // longer than 255 bytes.
854   //
855   Len = sizeof (EFI_DHCP4_PACKET);
856 
857   for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
858     if (Mark[Index].Len != 0) {
859       Len += ((Mark[Index].Len + 254) / 255) * 2 + Mark[Index].Len;
860     }
861   }
862 
863   Status  = EFI_OUT_OF_RESOURCES;
864   Packet  = (EFI_DHCP4_PACKET *) AllocatePool (Len);
865 
866   if (Packet == NULL) {
867     goto ON_ERROR;
868   }
869 
870   Packet->Size         = Len;
871   Packet->Length       = 0;
872   CopyMem (&Packet->Dhcp4.Header, &SeedPacket->Dhcp4.Header, sizeof (Packet->Dhcp4.Header));
873   Packet->Dhcp4.Magik  = DHCP_OPTION_MAGIC;
874   Buf                  = Packet->Dhcp4.Option;
875 
876   for (Index = 0; Index < DHCP_MAX_OPTIONS; Index++) {
877     if (Mark[Index].Len != 0) {
878       Buf = DhcpAppendOption (Buf, Mark[Index].Tag, Mark[Index].Len, Mark[Index].Data);
879     }
880   }
881 
882   *(Buf++)        = DHCP_TAG_EOP;
883   Packet->Length  = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)
884                       + (UINT32) (Buf - Packet->Dhcp4.Option);
885 
886   *NewPacket      = Packet;
887   Status          = EFI_SUCCESS;
888 
889 ON_ERROR:
890   if (SeedOptions != NULL) {
891     FreePool (SeedOptions);
892   }
893 
894   FreePool (Mark);
895   return Status;
896 }
897