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