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