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