]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/HttpBootDxe/HttpBootSupport.c
NetworkPkg: Add URI configuration form to HTTP boot driver.
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootSupport.c
CommitLineData
d933e70a
JW
1/** @file\r
2 Support functions implementation for UEFI HTTP boot driver.\r
3\r
072289f4 4Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>\r
d933e70a
JW
5This program and the accompanying materials are licensed and made available under \r
6the terms and conditions of the BSD License that accompanies this distribution. \r
7The 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
13**/\r
14\r
15#include "HttpBootDxe.h"\r
16\r
17\r
18/**\r
19 Get the Nic handle using any child handle in the IPv4 stack.\r
20\r
21 @param[in] ControllerHandle Pointer to child handle over IPv4.\r
22\r
23 @return NicHandle The pointer to the Nic handle.\r
24 @return NULL Can't find the Nic handle.\r
25\r
26**/\r
27EFI_HANDLE\r
28HttpBootGetNicByIp4Children (\r
29 IN EFI_HANDLE ControllerHandle\r
30 )\r
31{\r
32 EFI_HANDLE NicHandle;\r
33\r
34 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);\r
35 if (NicHandle == NULL) {\r
36 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);\r
37 if (NicHandle == NULL) {\r
38 return NULL;\r
39 }\r
40 }\r
41\r
42 return NicHandle;\r
43}\r
44\r
b659408b
ZL
45/**\r
46 Get the Nic handle using any child handle in the IPv6 stack.\r
47\r
48 @param[in] ControllerHandle Pointer to child handle over IPv6.\r
49\r
50 @return NicHandle The pointer to the Nic handle.\r
51 @return NULL Can't find the Nic handle.\r
52\r
53**/\r
54EFI_HANDLE\r
55HttpBootGetNicByIp6Children (\r
56 IN EFI_HANDLE ControllerHandle\r
57 )\r
58{\r
59 EFI_HANDLE NicHandle;\r
60 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);\r
61 if (NicHandle == NULL) {\r
62 NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);\r
63 if (NicHandle == NULL) {\r
64 return NULL;\r
65 }\r
66 }\r
67\r
68 return NicHandle;\r
69}\r
d933e70a
JW
70\r
71/**\r
72 This function is to convert UINTN to ASCII string with the required formatting.\r
73\r
74 @param[in] Number Numeric value to be converted.\r
75 @param[in] Buffer The pointer to the buffer for ASCII string.\r
76 @param[in] Length The length of the required format.\r
77\r
78**/\r
79VOID\r
80HttpBootUintnToAscDecWithFormat (\r
81 IN UINTN Number,\r
82 IN UINT8 *Buffer,\r
83 IN INTN Length\r
84 )\r
85{\r
86 UINTN Remainder;\r
87\r
88 while (Length > 0) {\r
89 Length--;\r
90 Remainder = Number % 10;\r
91 Number /= 10;\r
92 Buffer[Length] = (UINT8) ('0' + Remainder);\r
93 }\r
94}\r
95\r
96/**\r
97 This function is to display the IPv4 address.\r
98\r
99 @param[in] Ip The pointer to the IPv4 address.\r
100\r
101**/\r
102VOID\r
103HttpBootShowIp4Addr (\r
104 IN EFI_IPv4_ADDRESS *Ip\r
105 )\r
106{\r
107 UINTN Index;\r
108\r
109 for (Index = 0; Index < 4; Index++) {\r
110 AsciiPrint ("%d", Ip->Addr[Index]);\r
111 if (Index < 3) {\r
112 AsciiPrint (".");\r
113 }\r
114 }\r
115}\r
116\r
b659408b
ZL
117/**\r
118 This function is to display the IPv6 address.\r
119\r
120 @param[in] Ip The pointer to the IPv6 address.\r
121\r
122**/\r
123VOID\r
124HttpBootShowIp6Addr (\r
125 IN EFI_IPv6_ADDRESS *Ip\r
126 )\r
127{\r
128 UINTN Index;\r
129\r
130 for (Index = 0; Index < 16; Index++) {\r
131\r
132 if (Ip->Addr[Index] != 0) {\r
133 AsciiPrint ("%x", Ip->Addr[Index]);\r
134 }\r
135 Index++;\r
136 if (Index > 15) {\r
137 return;\r
138 }\r
139 if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {\r
140 AsciiPrint ("0");\r
141 }\r
142 AsciiPrint ("%x", Ip->Addr[Index]);\r
143 if (Index < 15) {\r
144 AsciiPrint (":");\r
145 }\r
146 }\r
147}\r
148\r
62cae351
ZL
149/**\r
150 This function is to display the HTTP error status.\r
151\r
152 @param[in] StatusCode The status code value in HTTP message.\r
153\r
154**/\r
155VOID\r
156HttpBootPrintErrorMessage (\r
157 EFI_HTTP_STATUS_CODE StatusCode\r
158 )\r
159{\r
160 AsciiPrint ("\n");\r
161\r
162 switch (StatusCode) {\r
163 case HTTP_STATUS_300_MULTIPLE_CHIOCES:\r
164 AsciiPrint ("\n Redirection: 300 Multiple Choices");\r
165 break; \r
166 \r
167 case HTTP_STATUS_301_MOVED_PERMANENTLY:\r
168 AsciiPrint ("\n Redirection: 301 Moved Permanently");\r
169 break; \r
170 \r
171 case HTTP_STATUS_302_FOUND:\r
172 AsciiPrint ("\n Redirection: 302 Found");\r
173 break; \r
174 \r
175 case HTTP_STATUS_303_SEE_OTHER:\r
176 AsciiPrint ("\n Redirection: 303 See Other");\r
177 break; \r
178\r
179 case HTTP_STATUS_304_NOT_MODIFIED:\r
180 AsciiPrint ("\n Redirection: 304 Not Modified");\r
181 break; \r
182\r
183 case HTTP_STATUS_305_USE_PROXY:\r
184 AsciiPrint ("\n Redirection: 305 Use Proxy");\r
185 break; \r
186\r
187 case HTTP_STATUS_307_TEMPORARY_REDIRECT:\r
188 AsciiPrint ("\n Redirection: 307 Temporary Redirect");\r
189 break; \r
190\r
191 case HTTP_STATUS_400_BAD_REQUEST:\r
192 AsciiPrint ("\n Client Error: 400 Bad Request");\r
193 break;\r
194 \r
195 case HTTP_STATUS_401_UNAUTHORIZED:\r
196 AsciiPrint ("\n Client Error: 401 Unauthorized");\r
197 break;\r
198 \r
199 case HTTP_STATUS_402_PAYMENT_REQUIRED:\r
200 AsciiPrint ("\n Client Error: 402 Payment Required");\r
201 break;\r
202\r
203 case HTTP_STATUS_403_FORBIDDEN:\r
204 AsciiPrint ("\n Client Error: 403 Forbidden");\r
205 break;\r
206\r
207 case HTTP_STATUS_404_NOT_FOUND:\r
208 AsciiPrint ("\n Client Error: 404 Not Found");\r
209 break;\r
210\r
211 case HTTP_STATUS_405_METHOD_NOT_ALLOWED:\r
212 AsciiPrint ("\n Client Error: 405 Method Not Allowed");\r
213 break;\r
214\r
215 case HTTP_STATUS_406_NOT_ACCEPTABLE:\r
216 AsciiPrint ("\n Client Error: 406 Not Acceptable");\r
217 break;\r
218\r
219 case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED:\r
220 AsciiPrint ("\n Client Error: 407 Proxy Authentication Required");\r
221 break;\r
222\r
223 case HTTP_STATUS_408_REQUEST_TIME_OUT:\r
224 AsciiPrint ("\n Client Error: 408 Request Timeout");\r
225 break;\r
226\r
227 case HTTP_STATUS_409_CONFLICT:\r
228 AsciiPrint ("\n Client Error: 409 Conflict");\r
229 break;\r
230\r
231 case HTTP_STATUS_410_GONE:\r
232 AsciiPrint ("\n Client Error: 410 Gone");\r
233 break;\r
234\r
235 case HTTP_STATUS_411_LENGTH_REQUIRED:\r
236 AsciiPrint ("\n Client Error: 411 Length Required");\r
237 break;\r
238\r
239 case HTTP_STATUS_412_PRECONDITION_FAILED:\r
240 AsciiPrint ("\n Client Error: 412 Precondition Failed");\r
241 break;\r
242\r
243 case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE:\r
244 AsciiPrint ("\n Client Error: 413 Request Entity Too Large");\r
245 break;\r
246\r
247 case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE:\r
248 AsciiPrint ("\n Client Error: 414 Request URI Too Long");\r
249 break;\r
250\r
251 case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE:\r
252 AsciiPrint ("\n Client Error: 415 Unsupported Media Type");\r
253 break;\r
254\r
255 case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED:\r
256 AsciiPrint ("\n Client Error: 416 Requested Range Not Satisfiable");\r
257 break;\r
258\r
259 case HTTP_STATUS_417_EXPECTATION_FAILED:\r
260 AsciiPrint ("\n Client Error: 417 Expectation Failed");\r
261 break;\r
262\r
263 case HTTP_STATUS_500_INTERNAL_SERVER_ERROR:\r
264 AsciiPrint ("\n Server Error: 500 Internal Server Error");\r
265 break;\r
266\r
267 case HTTP_STATUS_501_NOT_IMPLEMENTED:\r
268 AsciiPrint ("\n Server Error: 501 Not Implemented");\r
269 break;\r
270\r
271 case HTTP_STATUS_502_BAD_GATEWAY:\r
272 AsciiPrint ("\n Server Error: 502 Bad Gateway");\r
273 break;\r
274\r
275 case HTTP_STATUS_503_SERVICE_UNAVAILABLE:\r
276 AsciiPrint ("\n Server Error: 503 Service Unavailable");\r
277 break;\r
278\r
279 case HTTP_STATUS_504_GATEWAY_TIME_OUT:\r
280 AsciiPrint ("\n Server Error: 504 Gateway Timeout");\r
281 break;\r
282\r
283 case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED:\r
284 AsciiPrint ("\n Server Error: 505 HTTP Version Not Supported");\r
285 break;\r
286\r
287 default: ;\r
288 \r
289 }\r
290}\r
291\r
b659408b
ZL
292/**\r
293 Notify the callback function when an event is triggered.\r
294\r
295 @param[in] Event The triggered event.\r
296 @param[in] Context The opaque parameter to the function.\r
297\r
298**/\r
299VOID\r
300EFIAPI\r
301HttpBootCommonNotify (\r
302 IN EFI_EVENT Event,\r
303 IN VOID *Context\r
304 )\r
305{\r
306 *((BOOLEAN *) Context) = TRUE;\r
307}\r
308\r
309/**\r
310 Retrieve the host address using the EFI_DNS6_PROTOCOL.\r
311\r
312 @param[in] Private The pointer to the driver's private data.\r
313 @param[in] HostName Pointer to buffer containing hostname.\r
314 @param[out] IpAddress On output, pointer to buffer containing IPv6 address.\r
315\r
316 @retval EFI_SUCCESS Operation succeeded.\r
317 @retval EFI_DEVICE_ERROR An unexpected network error occurred.\r
318 @retval Others Other errors as indicated. \r
319**/\r
320EFI_STATUS\r
321HttpBootDns (\r
322 IN HTTP_BOOT_PRIVATE_DATA *Private,\r
323 IN CHAR16 *HostName,\r
324 OUT EFI_IPv6_ADDRESS *IpAddress \r
325 )\r
326{\r
327 EFI_STATUS Status;\r
328 EFI_DNS6_PROTOCOL *Dns6;\r
329 EFI_DNS6_CONFIG_DATA Dns6ConfigData;\r
330 EFI_DNS6_COMPLETION_TOKEN Token;\r
331 EFI_HANDLE Dns6Handle;\r
332 EFI_IP6_CONFIG_PROTOCOL *Ip6Config;\r
333 EFI_IPv6_ADDRESS *DnsServerList;\r
334 UINTN DnsServerListCount;\r
335 UINTN DataSize;\r
336 BOOLEAN IsDone; \r
337 \r
338 DnsServerList = NULL;\r
339 DnsServerListCount = 0;\r
340 Dns6 = NULL;\r
341 Dns6Handle = NULL;\r
342 ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));\r
343 \r
344 //\r
345 // Get DNS server list from EFI IPv6 Configuration protocol.\r
346 //\r
347 Status = gBS->HandleProtocol (Private->Controller, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config);\r
348 if (!EFI_ERROR (Status)) {\r
349 //\r
350 // Get the required size.\r
351 //\r
352 DataSize = 0;\r
353 Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL);\r
354 if (Status == EFI_BUFFER_TOO_SMALL) {\r
355 DnsServerList = AllocatePool (DataSize);\r
356 if (DnsServerList == NULL) {\r
357 return EFI_OUT_OF_RESOURCES;\r
358 } \r
359\r
360 Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList);\r
361 if (EFI_ERROR (Status)) {\r
362 FreePool (DnsServerList);\r
363 DnsServerList = NULL;\r
364 } else {\r
365 DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS);\r
366 }\r
367 }\r
368 }\r
369 //\r
370 // Create a DNSv6 child instance and get the protocol.\r
371 //\r
372 Status = NetLibCreateServiceChild (\r
373 Private->Controller,\r
374 Private->Image,\r
375 &gEfiDns6ServiceBindingProtocolGuid,\r
376 &Dns6Handle\r
377 );\r
378 if (EFI_ERROR (Status)) {\r
379 goto Exit;\r
380 } \r
381 \r
382 Status = gBS->OpenProtocol (\r
383 Dns6Handle,\r
384 &gEfiDns6ProtocolGuid,\r
385 (VOID **) &Dns6,\r
386 Private->Image,\r
387 Private->Controller,\r
388 EFI_OPEN_PROTOCOL_BY_DRIVER\r
389 );\r
390 if (EFI_ERROR (Status)) {\r
391 goto Exit;\r
392 }\r
393\r
394 //\r
395 // Configure DNS6 instance for the DNS server address and protocol.\r
396 //\r
397 ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));\r
398 Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount;\r
399 Dns6ConfigData.DnsServerList = DnsServerList;\r
400 Dns6ConfigData.EnableDnsCache = TRUE;\r
401 Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP;\r
402 IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp,&Private->StationIp.v6);\r
403 Status = Dns6->Configure (\r
404 Dns6,\r
405 &Dns6ConfigData\r
406 );\r
407 if (EFI_ERROR (Status)) {\r
408 goto Exit;\r
409 }\r
410 \r
411 Token.Status = EFI_NOT_READY;\r
412 IsDone = FALSE;\r
413 //\r
414 // Create event to set the IsDone flag when name resolution is finished.\r
415 //\r
416 Status = gBS->CreateEvent (\r
417 EVT_NOTIFY_SIGNAL,\r
418 TPL_NOTIFY,\r
419 HttpBootCommonNotify,\r
420 &IsDone,\r
421 &Token.Event\r
422 );\r
423 if (EFI_ERROR (Status)) {\r
424 goto Exit;\r
425 }\r
426\r
427 //\r
428 // Start asynchronous name resolution.\r
429 //\r
430 Status = Dns6->HostNameToIp (Dns6, HostName, &Token);\r
431 if (EFI_ERROR (Status)) {\r
432 goto Exit;\r
433 }\r
434\r
435 while (!IsDone) {\r
436 Dns6->Poll (Dns6);\r
437 }\r
438 \r
439 //\r
440 // Name resolution is done, check result.\r
441 //\r
442 Status = Token.Status; \r
443 if (!EFI_ERROR (Status)) {\r
444 if (Token.RspData.H2AData == NULL) {\r
445 Status = EFI_DEVICE_ERROR;\r
446 goto Exit;\r
447 }\r
448 if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {\r
449 Status = EFI_DEVICE_ERROR;\r
450 goto Exit;\r
451 }\r
452 //\r
453 // We just return the first IPv6 address from DNS protocol.\r
454 //\r
455 IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);\r
456 Status = EFI_SUCCESS;\r
457 }\r
458Exit:\r
459\r
460 if (Token.Event != NULL) {\r
461 gBS->CloseEvent (Token.Event);\r
462 }\r
463 if (Token.RspData.H2AData != NULL) {\r
464 if (Token.RspData.H2AData->IpList != NULL) {\r
465 FreePool (Token.RspData.H2AData->IpList);\r
466 }\r
467 FreePool (Token.RspData.H2AData);\r
468 }\r
469\r
470 if (Dns6 != NULL) {\r
471 Dns6->Configure (Dns6, NULL);\r
472 \r
473 gBS->CloseProtocol (\r
474 Dns6Handle,\r
475 &gEfiDns6ProtocolGuid,\r
476 Private->Image,\r
477 Private->Controller\r
478 );\r
479 }\r
480\r
481 if (Dns6Handle != NULL) {\r
482 NetLibDestroyServiceChild (\r
483 Private->Controller,\r
484 Private->Image,\r
485 &gEfiDns6ServiceBindingProtocolGuid,\r
486 Dns6Handle\r
487 );\r
488 }\r
489\r
490 if (DnsServerList != NULL) {\r
491 FreePool (DnsServerList);\r
492 }\r
493 \r
494 return Status; \r
495}\r
d933e70a
JW
496/**\r
497 Create a HTTP_IO_HEADER to hold the HTTP header items.\r
498\r
499 @param[in] MaxHeaderCount The maximun number of HTTP header in this holder.\r
500\r
501 @return A pointer of the HTTP header holder or NULL if failed.\r
502 \r
503**/\r
504HTTP_IO_HEADER *\r
505HttpBootCreateHeader (\r
506 UINTN MaxHeaderCount\r
b659408b 507 )\r
d933e70a
JW
508{\r
509 HTTP_IO_HEADER *HttpIoHeader;\r
510\r
511 if (MaxHeaderCount == 0) {\r
512 return NULL;\r
513 }\r
514\r
515 HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER));\r
516 if (HttpIoHeader == NULL) {\r
517 return NULL;\r
518 }\r
519\r
520 HttpIoHeader->MaxHeaderCount = MaxHeaderCount;\r
521 HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1);\r
522\r
523 return HttpIoHeader;\r
524}\r
525\r
526/**\r
527 Destroy the HTTP_IO_HEADER and release the resouces. \r
528\r
529 @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed.\r
530\r
531**/\r
532VOID\r
533HttpBootFreeHeader (\r
534 IN HTTP_IO_HEADER *HttpIoHeader\r
535 )\r
536{\r
537 UINTN Index;\r
538 \r
539 if (HttpIoHeader != NULL) {\r
540 if (HttpIoHeader->HeaderCount != 0) {\r
541 for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) {\r
542 FreePool (HttpIoHeader->Headers[Index].FieldName);\r
543 FreePool (HttpIoHeader->Headers[Index].FieldValue);\r
544 }\r
545 }\r
546 FreePool (HttpIoHeader);\r
547 }\r
548}\r
549\r
550/**\r
551 Find a specified header field according to the field name.\r
552\r
553 @param[in] HeaderCount Number of HTTP header structures in Headers list. \r
554 @param[in] Headers Array containing list of HTTP headers.\r
555 @param[in] FieldName Null terminated string which describes a field name. \r
556\r
557 @return Pointer to the found header or NULL.\r
558\r
559**/\r
560EFI_HTTP_HEADER *\r
561HttpBootFindHeader (\r
562 IN UINTN HeaderCount,\r
563 IN EFI_HTTP_HEADER *Headers,\r
564 IN CHAR8 *FieldName\r
565 )\r
566{\r
567 UINTN Index;\r
568 \r
569 if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {\r
570 return NULL;\r
571 }\r
572\r
573 for (Index = 0; Index < HeaderCount; Index++){\r
574 //\r
575 // Field names are case-insensitive (RFC 2616).\r
576 //\r
577 if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {\r
578 return &Headers[Index];\r
579 }\r
580 }\r
581 return NULL;\r
582}\r
583\r
584/**\r
585 Set or update a HTTP header with the field name and corresponding value.\r
586\r
587 @param[in] HttpIoHeader Point to the HTTP header holder.\r
588 @param[in] FieldName Null terminated string which describes a field name. \r
589 @param[in] FieldValue Null terminated string which describes the corresponding field value.\r
590\r
591 @retval EFI_SUCCESS The HTTP header has been set or updated.\r
592 @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
593 @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation.\r
594 @retval Other Unexpected error happened.\r
595 \r
596**/\r
597EFI_STATUS\r
598HttpBootSetHeader (\r
599 IN HTTP_IO_HEADER *HttpIoHeader,\r
600 IN CHAR8 *FieldName,\r
601 IN CHAR8 *FieldValue\r
602 )\r
603{\r
604 EFI_HTTP_HEADER *Header;\r
605 UINTN StrSize;\r
606 CHAR8 *NewFieldValue;\r
607 \r
608 if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) {\r
609 return EFI_INVALID_PARAMETER;\r
610 }\r
611\r
612 Header = HttpBootFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName);\r
613 if (Header == NULL) {\r
614 //\r
615 // Add a new header.\r
616 //\r
617 if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) {\r
618 return EFI_OUT_OF_RESOURCES;\r
619 }\r
620 Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount];\r
621\r
622 StrSize = AsciiStrSize (FieldName);\r
623 Header->FieldName = AllocatePool (StrSize);\r
624 if (Header->FieldName == NULL) {\r
625 return EFI_OUT_OF_RESOURCES;\r
626 }\r
627 CopyMem (Header->FieldName, FieldName, StrSize);\r
628 Header->FieldName[StrSize -1] = '\0';\r
629\r
630 StrSize = AsciiStrSize (FieldValue);\r
631 Header->FieldValue = AllocatePool (StrSize);\r
632 if (Header->FieldValue == NULL) {\r
633 FreePool (Header->FieldName);\r
634 return EFI_OUT_OF_RESOURCES;\r
635 }\r
636 CopyMem (Header->FieldValue, FieldValue, StrSize);\r
637 Header->FieldValue[StrSize -1] = '\0';\r
638\r
639 HttpIoHeader->HeaderCount++;\r
640 } else {\r
641 //\r
642 // Update an existing one.\r
643 //\r
644 StrSize = AsciiStrSize (FieldValue);\r
645 NewFieldValue = AllocatePool (StrSize);\r
646 if (NewFieldValue == NULL) {\r
647 return EFI_OUT_OF_RESOURCES;\r
648 }\r
649 CopyMem (NewFieldValue, FieldValue, StrSize);\r
650 NewFieldValue[StrSize -1] = '\0';\r
651 \r
652 if (Header->FieldValue != NULL) {\r
653 FreePool (Header->FieldValue);\r
654 }\r
655 Header->FieldValue = NewFieldValue;\r
656 }\r
657\r
658 return EFI_SUCCESS;\r
659}\r
660\r
d933e70a
JW
661/**\r
662 Create a HTTP_IO to access the HTTP service. It will create and configure\r
663 a HTTP child handle.\r
664\r
665 @param[in] Image The handle of the driver image.\r
666 @param[in] Controller The handle of the controller.\r
667 @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.\r
668 @param[in] ConfigData The HTTP_IO configuration data.\r
669 @param[out] HttpIo The HTTP_IO.\r
670 \r
671 @retval EFI_SUCCESS The HTTP_IO is created and configured.\r
672 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
673 @retval EFI_UNSUPPORTED One or more of the control options are not\r
674 supported in the implementation.\r
675 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
676 @retval Others Failed to create the HTTP_IO or configure it.\r
677\r
678**/\r
679EFI_STATUS\r
680HttpIoCreateIo (\r
681 IN EFI_HANDLE Image,\r
682 IN EFI_HANDLE Controller,\r
683 IN UINT8 IpVersion,\r
684 IN HTTP_IO_CONFIG_DATA *ConfigData,\r
685 OUT HTTP_IO *HttpIo\r
686 )\r
687{\r
688 EFI_STATUS Status;\r
689 EFI_HTTP_CONFIG_DATA HttpConfigData;\r
690 EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;\r
b659408b 691 EFI_HTTPv6_ACCESS_POINT Http6AccessPoint;\r
d933e70a
JW
692 EFI_HTTP_PROTOCOL *Http;\r
693 EFI_EVENT Event;\r
694 \r
695 if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) {\r
696 return EFI_INVALID_PARAMETER;\r
697 }\r
698\r
699 if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {\r
700 return EFI_UNSUPPORTED;\r
701 }\r
702\r
703 ZeroMem (HttpIo, sizeof (HTTP_IO));\r
704 \r
705 //\r
706 // Create the HTTP child instance and get the HTTP protocol.\r
707 // \r
708 Status = NetLibCreateServiceChild (\r
709 Controller,\r
710 Image,\r
711 &gEfiHttpServiceBindingProtocolGuid,\r
712 &HttpIo->Handle\r
713 );\r
714 if (EFI_ERROR (Status)) {\r
715 return Status;\r
716 }\r
717\r
718 Status = gBS->OpenProtocol (\r
719 HttpIo->Handle,\r
720 &gEfiHttpProtocolGuid,\r
721 (VOID **) &Http,\r
722 Image,\r
723 Controller,\r
724 EFI_OPEN_PROTOCOL_BY_DRIVER\r
725 );\r
726 if (EFI_ERROR (Status) || (Http == NULL)) {\r
727 goto ON_ERROR;\r
728 }\r
729\r
730 //\r
731 // Init the configuration data and configure the HTTP child.\r
732 //\r
733 HttpIo->Image = Image;\r
734 HttpIo->Controller = Controller;\r
735 HttpIo->IpVersion = IpVersion;\r
736 HttpIo->Http = Http;\r
737\r
738 ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));\r
739 HttpConfigData.HttpVersion = HttpVersion11;\r
740 HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;\r
741 if (HttpIo->IpVersion == IP_VERSION_4) {\r
742 HttpConfigData.LocalAddressIsIPv6 = FALSE;\r
743 \r
744 Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;\r
745 Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;\r
746 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);\r
747 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);\r
748 HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint; \r
749 } else {\r
b659408b
ZL
750 HttpConfigData.LocalAddressIsIPv6 = TRUE;\r
751 Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;\r
752 IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);\r
753 HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;\r
d933e70a
JW
754 }\r
755 \r
756 Status = Http->Configure (Http, &HttpConfigData);\r
757 if (EFI_ERROR (Status)) {\r
758 goto ON_ERROR;\r
759 }\r
760\r
761 //\r
762 // Create events for variuos asynchronous operations.\r
763 //\r
764 Status = gBS->CreateEvent (\r
765 EVT_NOTIFY_SIGNAL,\r
766 TPL_NOTIFY,\r
b659408b 767 HttpBootCommonNotify,\r
d933e70a
JW
768 &HttpIo->IsTxDone,\r
769 &Event\r
770 );\r
771 if (EFI_ERROR (Status)) {\r
772 goto ON_ERROR;\r
773 }\r
774 HttpIo->ReqToken.Event = Event;\r
775 HttpIo->ReqToken.Message = &HttpIo->ReqMessage;\r
776\r
777 Status = gBS->CreateEvent (\r
778 EVT_NOTIFY_SIGNAL,\r
779 TPL_NOTIFY,\r
b659408b 780 HttpBootCommonNotify,\r
d933e70a
JW
781 &HttpIo->IsRxDone,\r
782 &Event\r
783 );\r
784 if (EFI_ERROR (Status)) {\r
785 goto ON_ERROR;\r
786 }\r
787 HttpIo->RspToken.Event = Event;\r
788 HttpIo->RspToken.Message = &HttpIo->RspMessage;\r
789\r
790 return EFI_SUCCESS;\r
791 \r
792ON_ERROR:\r
793 HttpIoDestroyIo (HttpIo);\r
794\r
795 return Status;\r
796}\r
797\r
798/**\r
799 Destroy the HTTP_IO and release the resouces. \r
800\r
801 @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.\r
802\r
803**/\r
804VOID\r
805HttpIoDestroyIo (\r
806 IN HTTP_IO *HttpIo\r
807 )\r
808{\r
809 EFI_HTTP_PROTOCOL *Http;\r
810 EFI_EVENT Event;\r
811\r
812 if (HttpIo == NULL) {\r
813 return;\r
814 }\r
815\r
816 Event = HttpIo->ReqToken.Event;\r
817 if (Event != NULL) {\r
818 gBS->CloseEvent (Event);\r
819 }\r
820\r
821 Event = HttpIo->RspToken.Event;\r
822 if (Event != NULL) {\r
823 gBS->CloseEvent (Event);\r
824 }\r
825 \r
826 Http = HttpIo->Http;\r
827 if (Http != NULL) {\r
828 Http->Configure (Http, NULL);\r
829 gBS->CloseProtocol (\r
830 HttpIo->Handle,\r
831 &gEfiHttpProtocolGuid,\r
832 HttpIo->Image,\r
833 HttpIo->Controller\r
834 );\r
835 }\r
836\r
837 NetLibDestroyServiceChild (\r
838 HttpIo->Controller,\r
839 HttpIo->Image,\r
840 &gEfiHttpServiceBindingProtocolGuid,\r
841 HttpIo->Handle\r
842 );\r
843}\r
844\r
845/**\r
846 Synchronously send a HTTP REQUEST message to the server.\r
847 \r
848 @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
849 @param[in] Request A pointer to storage such data as URL and HTTP method.\r
850 @param[in] HeaderCount Number of HTTP header structures in Headers list. \r
851 @param[in] Headers Array containing list of HTTP headers.\r
852 @param[in] BodyLength Length in bytes of the HTTP body.\r
853 @param[in] Body Body associated with the HTTP request. \r
854 \r
855 @retval EFI_SUCCESS The HTTP request is trasmitted.\r
856 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
857 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
858 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
859 @retval Others Other errors as indicated.\r
860\r
861**/\r
862EFI_STATUS\r
863HttpIoSendRequest (\r
864 IN HTTP_IO *HttpIo,\r
865 IN EFI_HTTP_REQUEST_DATA *Request,\r
866 IN UINTN HeaderCount,\r
867 IN EFI_HTTP_HEADER *Headers,\r
868 IN UINTN BodyLength,\r
869 IN VOID *Body\r
870 )\r
871{\r
872 EFI_STATUS Status;\r
873 EFI_HTTP_PROTOCOL *Http;\r
874\r
875 if (HttpIo == NULL || HttpIo->Http == NULL) {\r
876 return EFI_INVALID_PARAMETER;\r
877 }\r
878\r
879 HttpIo->ReqToken.Status = EFI_NOT_READY;\r
880 HttpIo->ReqToken.Message->Data.Request = Request;\r
881 HttpIo->ReqToken.Message->HeaderCount = HeaderCount;\r
882 HttpIo->ReqToken.Message->Headers = Headers;\r
883 HttpIo->ReqToken.Message->BodyLength = BodyLength;\r
884 HttpIo->ReqToken.Message->Body = Body;\r
885\r
886 //\r
887 // Queue the request token to HTTP instances.\r
888 //\r
889 Http = HttpIo->Http;\r
890 HttpIo->IsTxDone = FALSE;\r
891 Status = Http->Request (\r
892 Http,\r
893 &HttpIo->ReqToken\r
894 );\r
895 if (EFI_ERROR (Status)) {\r
896 return Status;\r
897 }\r
898\r
899 //\r
900 // Poll the network until transmit finish.\r
901 //\r
902 while (!HttpIo->IsTxDone) {\r
903 Http->Poll (Http);\r
904 }\r
905\r
906 return HttpIo->ReqToken.Status;\r
907}\r
908\r
909/**\r
910 Synchronously receive a HTTP RESPONSE message from the server.\r
911 \r
912 @param[in] HttpIo The HttpIo wrapping the HTTP service.\r
913 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).\r
914 FALSE to continue receive the previous response message.\r
915 @param[out] ResponseData Point to a wrapper of the received response data.\r
916 \r
ef422fc5 917 @retval EFI_SUCCESS The HTTP response is received.\r
d933e70a
JW
918 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
919 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
920 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.\r
921 @retval Others Other errors as indicated.\r
922\r
923**/\r
924EFI_STATUS\r
925HttpIoRecvResponse (\r
926 IN HTTP_IO *HttpIo,\r
927 IN BOOLEAN RecvMsgHeader,\r
ef422fc5 928 OUT HTTP_IO_RESPONSE_DATA *ResponseData\r
d933e70a
JW
929 )\r
930{\r
931 EFI_STATUS Status;\r
932 EFI_HTTP_PROTOCOL *Http;\r
933\r
934 if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {\r
935 return EFI_INVALID_PARAMETER;\r
936 }\r
937\r
938 //\r
939 // Queue the response token to HTTP instances.\r
940 //\r
941 HttpIo->RspToken.Status = EFI_NOT_READY;\r
942 if (RecvMsgHeader) {\r
943 HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;\r
944 } else {\r
945 HttpIo->RspToken.Message->Data.Response = NULL;\r
946 }\r
947 HttpIo->RspToken.Message->HeaderCount = 0;\r
948 HttpIo->RspToken.Message->Headers = NULL;\r
949 HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;\r
950 HttpIo->RspToken.Message->Body = ResponseData->Body;\r
951\r
952 Http = HttpIo->Http;\r
953 HttpIo->IsRxDone = FALSE;\r
954 Status = Http->Response (\r
955 Http,\r
956 &HttpIo->RspToken\r
957 );\r
958 \r
959 if (EFI_ERROR (Status)) {\r
960 return Status;\r
961 }\r
962\r
963 //\r
62cae351 964 // Poll the network until receive finish.\r
d933e70a
JW
965 //\r
966 while (!HttpIo->IsRxDone) {\r
967 Http->Poll (Http);\r
968 }\r
969\r
970 //\r
971 // Store the received data into the wrapper.\r
972 //\r
072289f4
ZL
973 ResponseData->Status = HttpIo->RspToken.Status;\r
974 ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;\r
975 ResponseData->Headers = HttpIo->RspToken.Message->Headers;\r
976 ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;\r
d933e70a
JW
977\r
978 return Status;\r
979}\r
fa848a40
FS
980\r
981/**\r
982 Get the URI address string from the input device path.\r
983\r
984 Caller need to free the buffer in the UriAddress pointer.\r
985 \r
986 @param[in] FilePath Pointer to the device path which contains a URI device path node.\r
987 @param[in] UriAddress The URI address string extract from the device path.\r
988 \r
989 @retval EFI_SUCCESS The URI string is returned.\r
990 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
991\r
992**/\r
993EFI_STATUS\r
994HttpBootParseFilePath (\r
995 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
996 OUT CHAR8 **UriAddress\r
997 )\r
998{\r
999 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
1000 URI_DEVICE_PATH *UriDevicePath;\r
1001 CHAR8 *Uri;\r
1002 UINTN UriStrLength;\r
1003\r
1004 if (FilePath == NULL) {\r
1005 return EFI_INVALID_PARAMETER;\r
1006 }\r
1007\r
1008 *UriAddress = NULL;\r
1009\r
1010 //\r
1011 // Extract the URI address from the FilePath\r
1012 //\r
1013 TempDevicePath = FilePath;\r
1014 while (!IsDevicePathEnd (TempDevicePath)) {\r
1015 if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&\r
1016 (DevicePathSubType (TempDevicePath) == MSG_URI_DP)) {\r
1017 UriDevicePath = (URI_DEVICE_PATH*) TempDevicePath;\r
1018 //\r
1019 // UEFI Spec doesn't require the URI to be a NULL-terminated string\r
1020 // So we allocate a new buffer and always append a '\0' to it.\r
1021 //\r
1022 UriStrLength = DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL);\r
1023 if (UriStrLength == 0) {\r
1024 //\r
1025 // return a NULL UriAddress if it's a empty URI device path node.\r
1026 //\r
1027 break;\r
1028 }\r
1029 Uri = AllocatePool (UriStrLength + 1);\r
1030 if (Uri == NULL) {\r
1031 return EFI_OUT_OF_RESOURCES;\r
1032 }\r
1033 CopyMem (Uri, UriDevicePath->Uri, DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL));\r
1034 Uri[DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)] = '\0';\r
1035\r
1036 *UriAddress = Uri;\r
1037 }\r
1038 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
1039 }\r
1040\r
1041 return EFI_SUCCESS;\r
1042}\r