761643141f916142a32bf6d3630916d2be4e3a97
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootSupport.c
1 /** @file
2 Support functions implementation for UEFI HTTP boot driver.
3
4 Copyright (c) 2015, 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 /**
47 This function is to convert UINTN to ASCII string with the required formatting.
48
49 @param[in] Number Numeric value to be converted.
50 @param[in] Buffer The pointer to the buffer for ASCII string.
51 @param[in] Length The length of the required format.
52
53 **/
54 VOID
55 HttpBootUintnToAscDecWithFormat (
56 IN UINTN Number,
57 IN UINT8 *Buffer,
58 IN INTN Length
59 )
60 {
61 UINTN Remainder;
62
63 while (Length > 0) {
64 Length--;
65 Remainder = Number % 10;
66 Number /= 10;
67 Buffer[Length] = (UINT8) ('0' + Remainder);
68 }
69 }
70
71 /**
72 This function is to display the IPv4 address.
73
74 @param[in] Ip The pointer to the IPv4 address.
75
76 **/
77 VOID
78 HttpBootShowIp4Addr (
79 IN EFI_IPv4_ADDRESS *Ip
80 )
81 {
82 UINTN Index;
83
84 for (Index = 0; Index < 4; Index++) {
85 AsciiPrint ("%d", Ip->Addr[Index]);
86 if (Index < 3) {
87 AsciiPrint (".");
88 }
89 }
90 }
91
92 /**
93 Create a HTTP_IO_HEADER to hold the HTTP header items.
94
95 @param[in] MaxHeaderCount The maximun number of HTTP header in this holder.
96
97 @return A pointer of the HTTP header holder or NULL if failed.
98
99 **/
100 HTTP_IO_HEADER *
101 HttpBootCreateHeader (
102 UINTN MaxHeaderCount
103 )
104 {
105 HTTP_IO_HEADER *HttpIoHeader;
106
107 if (MaxHeaderCount == 0) {
108 return NULL;
109 }
110
111 HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER));
112 if (HttpIoHeader == NULL) {
113 return NULL;
114 }
115
116 HttpIoHeader->MaxHeaderCount = MaxHeaderCount;
117 HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1);
118
119 return HttpIoHeader;
120 }
121
122 /**
123 Destroy the HTTP_IO_HEADER and release the resouces.
124
125 @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed.
126
127 **/
128 VOID
129 HttpBootFreeHeader (
130 IN HTTP_IO_HEADER *HttpIoHeader
131 )
132 {
133 UINTN Index;
134
135 if (HttpIoHeader != NULL) {
136 if (HttpIoHeader->HeaderCount != 0) {
137 for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) {
138 FreePool (HttpIoHeader->Headers[Index].FieldName);
139 FreePool (HttpIoHeader->Headers[Index].FieldValue);
140 }
141 }
142 FreePool (HttpIoHeader);
143 }
144 }
145
146 /**
147 Find a specified header field according to the field name.
148
149 @param[in] HeaderCount Number of HTTP header structures in Headers list.
150 @param[in] Headers Array containing list of HTTP headers.
151 @param[in] FieldName Null terminated string which describes a field name.
152
153 @return Pointer to the found header or NULL.
154
155 **/
156 EFI_HTTP_HEADER *
157 HttpBootFindHeader (
158 IN UINTN HeaderCount,
159 IN EFI_HTTP_HEADER *Headers,
160 IN CHAR8 *FieldName
161 )
162 {
163 UINTN Index;
164
165 if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {
166 return NULL;
167 }
168
169 for (Index = 0; Index < HeaderCount; Index++){
170 //
171 // Field names are case-insensitive (RFC 2616).
172 //
173 if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
174 return &Headers[Index];
175 }
176 }
177 return NULL;
178 }
179
180 /**
181 Set or update a HTTP header with the field name and corresponding value.
182
183 @param[in] HttpIoHeader Point to the HTTP header holder.
184 @param[in] FieldName Null terminated string which describes a field name.
185 @param[in] FieldValue Null terminated string which describes the corresponding field value.
186
187 @retval EFI_SUCCESS The HTTP header has been set or updated.
188 @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
189 @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation.
190 @retval Other Unexpected error happened.
191
192 **/
193 EFI_STATUS
194 HttpBootSetHeader (
195 IN HTTP_IO_HEADER *HttpIoHeader,
196 IN CHAR8 *FieldName,
197 IN CHAR8 *FieldValue
198 )
199 {
200 EFI_HTTP_HEADER *Header;
201 UINTN StrSize;
202 CHAR8 *NewFieldValue;
203
204 if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) {
205 return EFI_INVALID_PARAMETER;
206 }
207
208 Header = HttpBootFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName);
209 if (Header == NULL) {
210 //
211 // Add a new header.
212 //
213 if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) {
214 return EFI_OUT_OF_RESOURCES;
215 }
216 Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount];
217
218 StrSize = AsciiStrSize (FieldName);
219 Header->FieldName = AllocatePool (StrSize);
220 if (Header->FieldName == NULL) {
221 return EFI_OUT_OF_RESOURCES;
222 }
223 CopyMem (Header->FieldName, FieldName, StrSize);
224 Header->FieldName[StrSize -1] = '\0';
225
226 StrSize = AsciiStrSize (FieldValue);
227 Header->FieldValue = AllocatePool (StrSize);
228 if (Header->FieldValue == NULL) {
229 FreePool (Header->FieldName);
230 return EFI_OUT_OF_RESOURCES;
231 }
232 CopyMem (Header->FieldValue, FieldValue, StrSize);
233 Header->FieldValue[StrSize -1] = '\0';
234
235 HttpIoHeader->HeaderCount++;
236 } else {
237 //
238 // Update an existing one.
239 //
240 StrSize = AsciiStrSize (FieldValue);
241 NewFieldValue = AllocatePool (StrSize);
242 if (NewFieldValue == NULL) {
243 return EFI_OUT_OF_RESOURCES;
244 }
245 CopyMem (NewFieldValue, FieldValue, StrSize);
246 NewFieldValue[StrSize -1] = '\0';
247
248 if (Header->FieldValue != NULL) {
249 FreePool (Header->FieldValue);
250 }
251 Header->FieldValue = NewFieldValue;
252 }
253
254 return EFI_SUCCESS;
255 }
256
257 /**
258 Notify the callback function when an event is triggered.
259
260 @param[in] Event The triggered event.
261 @param[in] Context The opaque parameter to the function.
262
263 **/
264 VOID
265 EFIAPI
266 HttpIoCommonNotify (
267 IN EFI_EVENT Event,
268 IN VOID *Context
269 )
270 {
271 *((BOOLEAN *) Context) = TRUE;
272 }
273
274 /**
275 Create a HTTP_IO to access the HTTP service. It will create and configure
276 a HTTP child handle.
277
278 @param[in] Image The handle of the driver image.
279 @param[in] Controller The handle of the controller.
280 @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
281 @param[in] ConfigData The HTTP_IO configuration data.
282 @param[out] HttpIo The HTTP_IO.
283
284 @retval EFI_SUCCESS The HTTP_IO is created and configured.
285 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
286 @retval EFI_UNSUPPORTED One or more of the control options are not
287 supported in the implementation.
288 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
289 @retval Others Failed to create the HTTP_IO or configure it.
290
291 **/
292 EFI_STATUS
293 HttpIoCreateIo (
294 IN EFI_HANDLE Image,
295 IN EFI_HANDLE Controller,
296 IN UINT8 IpVersion,
297 IN HTTP_IO_CONFIG_DATA *ConfigData,
298 OUT HTTP_IO *HttpIo
299 )
300 {
301 EFI_STATUS Status;
302 EFI_HTTP_CONFIG_DATA HttpConfigData;
303 EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;
304 EFI_HTTP_PROTOCOL *Http;
305 EFI_EVENT Event;
306
307 if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) {
308 return EFI_INVALID_PARAMETER;
309 }
310
311 if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
312 return EFI_UNSUPPORTED;
313 }
314
315 ZeroMem (HttpIo, sizeof (HTTP_IO));
316
317 //
318 // Create the HTTP child instance and get the HTTP protocol.
319 //
320 Status = NetLibCreateServiceChild (
321 Controller,
322 Image,
323 &gEfiHttpServiceBindingProtocolGuid,
324 &HttpIo->Handle
325 );
326 if (EFI_ERROR (Status)) {
327 return Status;
328 }
329
330 Status = gBS->OpenProtocol (
331 HttpIo->Handle,
332 &gEfiHttpProtocolGuid,
333 (VOID **) &Http,
334 Image,
335 Controller,
336 EFI_OPEN_PROTOCOL_BY_DRIVER
337 );
338 if (EFI_ERROR (Status) || (Http == NULL)) {
339 goto ON_ERROR;
340 }
341
342 //
343 // Init the configuration data and configure the HTTP child.
344 //
345 HttpIo->Image = Image;
346 HttpIo->Controller = Controller;
347 HttpIo->IpVersion = IpVersion;
348 HttpIo->Http = Http;
349
350 ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
351 HttpConfigData.HttpVersion = HttpVersion11;
352 HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;
353 if (HttpIo->IpVersion == IP_VERSION_4) {
354 HttpConfigData.LocalAddressIsIPv6 = FALSE;
355
356 Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
357 Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;
358 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
359 IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
360 HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
361 } else {
362 ASSERT (FALSE);
363 }
364
365 Status = Http->Configure (Http, &HttpConfigData);
366 if (EFI_ERROR (Status)) {
367 goto ON_ERROR;
368 }
369
370 //
371 // Create events for variuos asynchronous operations.
372 //
373 Status = gBS->CreateEvent (
374 EVT_NOTIFY_SIGNAL,
375 TPL_NOTIFY,
376 HttpIoCommonNotify,
377 &HttpIo->IsTxDone,
378 &Event
379 );
380 if (EFI_ERROR (Status)) {
381 goto ON_ERROR;
382 }
383 HttpIo->ReqToken.Event = Event;
384 HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
385
386 Status = gBS->CreateEvent (
387 EVT_NOTIFY_SIGNAL,
388 TPL_NOTIFY,
389 HttpIoCommonNotify,
390 &HttpIo->IsRxDone,
391 &Event
392 );
393 if (EFI_ERROR (Status)) {
394 goto ON_ERROR;
395 }
396 HttpIo->RspToken.Event = Event;
397 HttpIo->RspToken.Message = &HttpIo->RspMessage;
398
399 return EFI_SUCCESS;
400
401 ON_ERROR:
402 HttpIoDestroyIo (HttpIo);
403
404 return Status;
405 }
406
407 /**
408 Destroy the HTTP_IO and release the resouces.
409
410 @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
411
412 **/
413 VOID
414 HttpIoDestroyIo (
415 IN HTTP_IO *HttpIo
416 )
417 {
418 EFI_HTTP_PROTOCOL *Http;
419 EFI_EVENT Event;
420
421 if (HttpIo == NULL) {
422 return;
423 }
424
425 Event = HttpIo->ReqToken.Event;
426 if (Event != NULL) {
427 gBS->CloseEvent (Event);
428 }
429
430 Event = HttpIo->RspToken.Event;
431 if (Event != NULL) {
432 gBS->CloseEvent (Event);
433 }
434
435 Http = HttpIo->Http;
436 if (Http != NULL) {
437 Http->Configure (Http, NULL);
438 gBS->CloseProtocol (
439 HttpIo->Handle,
440 &gEfiHttpProtocolGuid,
441 HttpIo->Image,
442 HttpIo->Controller
443 );
444 }
445
446 NetLibDestroyServiceChild (
447 HttpIo->Controller,
448 HttpIo->Image,
449 &gEfiHttpServiceBindingProtocolGuid,
450 HttpIo->Handle
451 );
452 }
453
454 /**
455 Synchronously send a HTTP REQUEST message to the server.
456
457 @param[in] HttpIo The HttpIo wrapping the HTTP service.
458 @param[in] Request A pointer to storage such data as URL and HTTP method.
459 @param[in] HeaderCount Number of HTTP header structures in Headers list.
460 @param[in] Headers Array containing list of HTTP headers.
461 @param[in] BodyLength Length in bytes of the HTTP body.
462 @param[in] Body Body associated with the HTTP request.
463
464 @retval EFI_SUCCESS The HTTP request is trasmitted.
465 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
466 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
467 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
468 @retval Others Other errors as indicated.
469
470 **/
471 EFI_STATUS
472 HttpIoSendRequest (
473 IN HTTP_IO *HttpIo,
474 IN EFI_HTTP_REQUEST_DATA *Request,
475 IN UINTN HeaderCount,
476 IN EFI_HTTP_HEADER *Headers,
477 IN UINTN BodyLength,
478 IN VOID *Body
479 )
480 {
481 EFI_STATUS Status;
482 EFI_HTTP_PROTOCOL *Http;
483
484 if (HttpIo == NULL || HttpIo->Http == NULL) {
485 return EFI_INVALID_PARAMETER;
486 }
487
488 HttpIo->ReqToken.Status = EFI_NOT_READY;
489 HttpIo->ReqToken.Message->Data.Request = Request;
490 HttpIo->ReqToken.Message->HeaderCount = HeaderCount;
491 HttpIo->ReqToken.Message->Headers = Headers;
492 HttpIo->ReqToken.Message->BodyLength = BodyLength;
493 HttpIo->ReqToken.Message->Body = Body;
494
495 //
496 // Queue the request token to HTTP instances.
497 //
498 Http = HttpIo->Http;
499 HttpIo->IsTxDone = FALSE;
500 Status = Http->Request (
501 Http,
502 &HttpIo->ReqToken
503 );
504 if (EFI_ERROR (Status)) {
505 return Status;
506 }
507
508 //
509 // Poll the network until transmit finish.
510 //
511 while (!HttpIo->IsTxDone) {
512 Http->Poll (Http);
513 }
514
515 return HttpIo->ReqToken.Status;
516 }
517
518 /**
519 Synchronously receive a HTTP RESPONSE message from the server.
520
521 @param[in] HttpIo The HttpIo wrapping the HTTP service.
522 @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
523 FALSE to continue receive the previous response message.
524 @param[out] ResponseData Point to a wrapper of the received response data.
525
526 @retval EFI_SUCCESS The HTTP resopnse is received.
527 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
528 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
529 @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
530 @retval Others Other errors as indicated.
531
532 **/
533 EFI_STATUS
534 HttpIoRecvResponse (
535 IN HTTP_IO *HttpIo,
536 IN BOOLEAN RecvMsgHeader,
537 OUT HTTP_IO_RESOPNSE_DATA *ResponseData
538 )
539 {
540 EFI_STATUS Status;
541 EFI_HTTP_PROTOCOL *Http;
542
543 if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
544 return EFI_INVALID_PARAMETER;
545 }
546
547 //
548 // Queue the response token to HTTP instances.
549 //
550 HttpIo->RspToken.Status = EFI_NOT_READY;
551 if (RecvMsgHeader) {
552 HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
553 } else {
554 HttpIo->RspToken.Message->Data.Response = NULL;
555 }
556 HttpIo->RspToken.Message->HeaderCount = 0;
557 HttpIo->RspToken.Message->Headers = NULL;
558 HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;
559 HttpIo->RspToken.Message->Body = ResponseData->Body;
560
561 Http = HttpIo->Http;
562 HttpIo->IsRxDone = FALSE;
563 Status = Http->Response (
564 Http,
565 &HttpIo->RspToken
566 );
567
568 if (EFI_ERROR (Status)) {
569 return Status;
570 }
571
572 //
573 // Poll the network until transmit finish.
574 //
575 while (!HttpIo->IsRxDone) {
576 Http->Poll (Http);
577 }
578
579 //
580 // Store the received data into the wrapper.
581 //
582 Status = HttpIo->ReqToken.Status;
583 if (!EFI_ERROR (Status)) {
584 ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
585 ResponseData->Headers = HttpIo->RspToken.Message->Headers;
586 ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;
587 }
588
589 return Status;
590 }