]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.c
MdeModulePkg: Fix potential memory leaks in DxeHttpLib.
[mirror_edk2.git] / MdeModulePkg / Library / DxeHttpLib / DxeHttpLib.c
1 /** @file
2 This library is used to share code between UEFI network stack modules.
3 It provides the helper routines to parse the HTTP message byte stream.
4
5 Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
6 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at<BR>
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include "DxeHttpLib.h"
18
19
20
21 /**
22 Decode a percent-encoded URI component to the ASCII character.
23
24 Decode the input component in Buffer according to RFC 3986. The caller is responsible to make
25 sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))
26 in bytes.
27
28 @param[in] Buffer The pointer to a percent-encoded URI component.
29 @param[in] BufferLength Length of Buffer in bytes.
30 @param[out] ResultBuffer Point to the buffer to store the decode result.
31 @param[out] ResultLength Length of decoded string in ResultBuffer in bytes.
32
33 @retval EFI_SUCCESS Successfully decoded the URI.
34 @retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string.
35
36 **/
37 EFI_STATUS
38 EFIAPI
39 UriPercentDecode (
40 IN CHAR8 *Buffer,
41 IN UINT32 BufferLength,
42 OUT CHAR8 *ResultBuffer,
43 OUT UINT32 *ResultLength
44 )
45 {
46 UINTN Index;
47 UINTN Offset;
48 CHAR8 HexStr[3];
49
50 if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) {
51 return EFI_INVALID_PARAMETER;
52 }
53
54 Index = 0;
55 Offset = 0;
56 HexStr[2] = '\0';
57 while (Index < BufferLength) {
58 if (Buffer[Index] == '%') {
59 if (!NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) {
60 return EFI_INVALID_PARAMETER;
61 }
62 HexStr[0] = Buffer[Index+1];
63 HexStr[1] = Buffer[Index+2];
64 ResultBuffer[Offset] = (CHAR8) AsciiStrHexToUintn (HexStr);
65 Index += 3;
66 } else {
67 ResultBuffer[Offset] = Buffer[Index];
68 Index++;
69 }
70 Offset++;
71 }
72
73 *ResultLength = (UINT32) Offset;
74
75 return EFI_SUCCESS;
76 }
77
78 /**
79 This function return the updated state according to the input state and next character of
80 the authority.
81
82 @param[in] Char Next character.
83 @param[in] State Current value of the parser state machine.
84 @param[in] IsRightBracket TRUE if there is an sign ']' in the authority component and
85 indicates the next part is ':' before Port.
86
87 @return Updated state value.
88 **/
89 HTTP_URL_PARSE_STATE
90 NetHttpParseAuthorityChar (
91 IN CHAR8 Char,
92 IN HTTP_URL_PARSE_STATE State,
93 IN BOOLEAN *IsRightBracket
94 )
95 {
96
97 //
98 // RFC 3986:
99 // The authority component is preceded by a double slash ("//") and is
100 // terminated by the next slash ("/"), question mark ("?"), or number
101 // sign ("#") character, or by the end of the URI.
102 //
103 if (Char == ' ' || Char == '\r' || Char == '\n') {
104 return UrlParserStateMax;
105 }
106
107 //
108 // authority = [ userinfo "@" ] host [ ":" port ]
109 //
110 switch (State) {
111 case UrlParserUserInfo:
112 if (Char == '@') {
113 return UrlParserHostStart;
114 }
115 break;
116
117 case UrlParserHost:
118 case UrlParserHostStart:
119 if (Char == '[') {
120 return UrlParserHostIpv6;
121 }
122
123 if (Char == ':') {
124 return UrlParserPortStart;
125 }
126
127 return UrlParserHost;
128
129 case UrlParserHostIpv6:
130 if (Char == ']') {
131 *IsRightBracket = TRUE;
132 }
133
134 if (Char == ':' && *IsRightBracket) {
135 return UrlParserPortStart;
136 }
137 return UrlParserHostIpv6;
138
139 case UrlParserPort:
140 case UrlParserPortStart:
141 return UrlParserPort;
142
143 default:
144 break;
145 }
146
147 return State;
148 }
149
150 /**
151 This function parse the authority component of the input URL and update the parser.
152
153 @param[in] Url The pointer to a HTTP URL string.
154 @param[in] FoundAt TRUE if there is an at sign ('@') in the authority component.
155 @param[in, out] UrlParser Pointer to the buffer of the parse result.
156
157 @retval EFI_SUCCESS Successfully parse the authority.
158 @retval Other Error happened.
159
160 **/
161 EFI_STATUS
162 NetHttpParseAuthority (
163 IN CHAR8 *Url,
164 IN BOOLEAN FoundAt,
165 IN OUT HTTP_URL_PARSER *UrlParser
166 )
167 {
168 CHAR8 *Char;
169 CHAR8 *Authority;
170 UINT32 Length;
171 HTTP_URL_PARSE_STATE State;
172 UINT32 Field;
173 UINT32 OldField;
174 BOOLEAN IsrightBracket;
175
176 ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0);
177
178 //
179 // authority = [ userinfo "@" ] host [ ":" port ]
180 //
181 if (FoundAt) {
182 State = UrlParserUserInfo;
183 } else {
184 State = UrlParserHost;
185 }
186
187 IsrightBracket = FALSE;
188 Field = HTTP_URI_FIELD_MAX;
189 OldField = Field;
190 Authority = Url + UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Offset;
191 Length = UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Length;
192 for (Char = Authority; Char < Authority + Length; Char++) {
193 State = NetHttpParseAuthorityChar (*Char, State, &IsrightBracket);
194 switch (State) {
195 case UrlParserStateMax:
196 return EFI_INVALID_PARAMETER;
197
198 case UrlParserHostStart:
199 case UrlParserPortStart:
200 continue;
201
202 case UrlParserUserInfo:
203 Field = HTTP_URI_FIELD_USERINFO;
204 break;
205
206 case UrlParserHost:
207 Field = HTTP_URI_FIELD_HOST;
208 break;
209
210 case UrlParserHostIpv6:
211 Field = HTTP_URI_FIELD_HOST;
212 break;
213
214 case UrlParserPort:
215 Field = HTTP_URI_FIELD_PORT;
216 break;
217
218 default:
219 ASSERT (FALSE);
220 }
221
222 //
223 // Field not changed, count the length.
224 //
225 ASSERT (Field < HTTP_URI_FIELD_MAX);
226 if (Field == OldField) {
227 UrlParser->FieldData[Field].Length++;
228 continue;
229 }
230
231 //
232 // New field start
233 //
234 UrlParser->FieldBitMap |= BIT (Field);
235 UrlParser->FieldData[Field].Offset = (UINT32) (Char - Url);
236 UrlParser->FieldData[Field].Length = 1;
237 OldField = Field;
238 }
239
240 return EFI_SUCCESS;
241 }
242
243 /**
244 This function return the updated state according to the input state and next character of a URL.
245
246 @param[in] Char Next character.
247 @param[in] State Current value of the parser state machine.
248
249 @return Updated state value.
250
251 **/
252 HTTP_URL_PARSE_STATE
253 NetHttpParseUrlChar (
254 IN CHAR8 Char,
255 IN HTTP_URL_PARSE_STATE State
256 )
257 {
258 if (Char == ' ' || Char == '\r' || Char == '\n') {
259 return UrlParserStateMax;
260 }
261
262 //
263 // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
264 //
265 // Request-URI = "*" | absolute-URI | path-absolute | authority
266 //
267 // absolute-URI = scheme ":" hier-part [ "?" query ]
268 // path-absolute = "/" [ segment-nz *( "/" segment ) ]
269 // authority = [ userinfo "@" ] host [ ":" port ]
270 //
271 switch (State) {
272 case UrlParserUrlStart:
273 if (Char == '*' || Char == '/') {
274 return UrlParserPath;
275 }
276 return UrlParserScheme;
277
278 case UrlParserScheme:
279 if (Char == ':') {
280 return UrlParserSchemeColon;
281 }
282 break;
283
284 case UrlParserSchemeColon:
285 if (Char == '/') {
286 return UrlParserSchemeColonSlash;
287 }
288 break;
289
290 case UrlParserSchemeColonSlash:
291 if (Char == '/') {
292 return UrlParserSchemeColonSlashSlash;
293 }
294 break;
295
296 case UrlParserAtInAuthority:
297 if (Char == '@') {
298 return UrlParserStateMax;
299 }
300
301 case UrlParserAuthority:
302 case UrlParserSchemeColonSlashSlash:
303 if (Char == '@') {
304 return UrlParserAtInAuthority;
305 }
306 if (Char == '/') {
307 return UrlParserPath;
308 }
309 if (Char == '?') {
310 return UrlParserQueryStart;
311 }
312 if (Char == '#') {
313 return UrlParserFragmentStart;
314 }
315 return UrlParserAuthority;
316
317 case UrlParserPath:
318 if (Char == '?') {
319 return UrlParserQueryStart;
320 }
321 if (Char == '#') {
322 return UrlParserFragmentStart;
323 }
324 break;
325
326 case UrlParserQuery:
327 case UrlParserQueryStart:
328 if (Char == '#') {
329 return UrlParserFragmentStart;
330 }
331 return UrlParserQuery;
332
333 case UrlParserFragmentStart:
334 return UrlParserFragment;
335
336 default:
337 break;
338 }
339
340 return State;
341 }
342 /**
343 Create a URL parser for the input URL string.
344
345 This function will parse and dereference the input HTTP URL into it components. The original
346 content of the URL won't be modified and the result will be returned in UrlParser, which can
347 be used in other functions like NetHttpUrlGetHostName().
348
349 @param[in] Url The pointer to a HTTP URL string.
350 @param[in] Length Length of Url in bytes.
351 @param[in] IsConnectMethod Whether the Url is used in HTTP CONNECT method or not.
352 @param[out] UrlParser Pointer to the returned buffer to store the parse result.
353
354 @retval EFI_SUCCESS Successfully dereferenced the HTTP URL.
355 @retval EFI_INVALID_PARAMETER UrlParser is NULL or Url is not a valid HTTP URL.
356 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
357
358 **/
359 EFI_STATUS
360 EFIAPI
361 HttpParseUrl (
362 IN CHAR8 *Url,
363 IN UINT32 Length,
364 IN BOOLEAN IsConnectMethod,
365 OUT VOID **UrlParser
366 )
367 {
368 HTTP_URL_PARSE_STATE State;
369 CHAR8 *Char;
370 UINT32 Field;
371 UINT32 OldField;
372 BOOLEAN FoundAt;
373 EFI_STATUS Status;
374 HTTP_URL_PARSER *Parser;
375
376 if (Url == NULL || Length == 0 || UrlParser == NULL) {
377 return EFI_INVALID_PARAMETER;
378 }
379
380 Parser = AllocateZeroPool (sizeof (HTTP_URL_PARSER));
381 if (Parser == NULL) {
382 return EFI_OUT_OF_RESOURCES;
383 }
384
385 if (IsConnectMethod) {
386 //
387 // According to RFC 2616, the authority form is only used by the CONNECT method.
388 //
389 State = UrlParserAuthority;
390 } else {
391 State = UrlParserUrlStart;
392 }
393
394 Field = HTTP_URI_FIELD_MAX;
395 OldField = Field;
396 FoundAt = FALSE;
397 for (Char = Url; Char < Url + Length; Char++) {
398 //
399 // Update state machine according to next char.
400 //
401 State = NetHttpParseUrlChar (*Char, State);
402
403 switch (State) {
404 case UrlParserStateMax:
405 return EFI_INVALID_PARAMETER;
406
407 case UrlParserSchemeColon:
408 case UrlParserSchemeColonSlash:
409 case UrlParserSchemeColonSlashSlash:
410 case UrlParserQueryStart:
411 case UrlParserFragmentStart:
412 //
413 // Skip all the delimiting char: "://" "?" "@"
414 //
415 continue;
416
417 case UrlParserScheme:
418 Field = HTTP_URI_FIELD_SCHEME;
419 break;
420
421 case UrlParserAtInAuthority:
422 FoundAt = TRUE;
423 case UrlParserAuthority:
424 Field = HTTP_URI_FIELD_AUTHORITY;
425 break;
426
427 case UrlParserPath:
428 Field = HTTP_URI_FIELD_PATH;
429 break;
430
431 case UrlParserQuery:
432 Field = HTTP_URI_FIELD_QUERY;
433 break;
434
435 case UrlParserFragment:
436 Field = HTTP_URI_FIELD_FRAGMENT;
437 break;
438
439 default:
440 ASSERT (FALSE);
441 }
442
443 //
444 // Field not changed, count the length.
445 //
446 ASSERT (Field < HTTP_URI_FIELD_MAX);
447 if (Field == OldField) {
448 Parser->FieldData[Field].Length++;
449 continue;
450 }
451
452 //
453 // New field start
454 //
455 Parser->FieldBitMap |= BIT (Field);
456 Parser->FieldData[Field].Offset = (UINT32) (Char - Url);
457 Parser->FieldData[Field].Length = 1;
458 OldField = Field;
459 }
460
461 //
462 // If has authority component, continue to parse the username, host and port.
463 //
464 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0) {
465 Status = NetHttpParseAuthority (Url, FoundAt, Parser);
466 if (EFI_ERROR (Status)) {
467 return Status;
468 }
469 }
470
471 *UrlParser = Parser;
472 return EFI_SUCCESS;
473 }
474
475 /**
476 Get the Hostname from a HTTP URL.
477
478 This function will return the HostName according to the Url and previous parse result ,and
479 it is the caller's responsibility to free the buffer returned in *HostName.
480
481 @param[in] Url The pointer to a HTTP URL string.
482 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
483 @param[out] HostName Pointer to a buffer to store the HostName.
484
485 @retval EFI_SUCCESS Successfully get the required component.
486 @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.
487 @retval EFI_NOT_FOUND No hostName component in the URL.
488 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
489
490 **/
491 EFI_STATUS
492 EFIAPI
493 HttpUrlGetHostName (
494 IN CHAR8 *Url,
495 IN VOID *UrlParser,
496 OUT CHAR8 **HostName
497 )
498 {
499 CHAR8 *Name;
500 EFI_STATUS Status;
501 UINT32 ResultLength;
502 HTTP_URL_PARSER *Parser;
503
504 if (Url == NULL || UrlParser == NULL || HostName == NULL) {
505 return EFI_INVALID_PARAMETER;
506 }
507
508 Parser = (HTTP_URL_PARSER*) UrlParser;
509
510 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
511 return EFI_NOT_FOUND;
512 }
513
514 Name = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
515 if (Name == NULL) {
516 return EFI_OUT_OF_RESOURCES;
517 }
518
519 Status = UriPercentDecode (
520 Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
521 Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
522 Name,
523 &ResultLength
524 );
525 if (EFI_ERROR (Status)) {
526 FreePool (Name);
527 return Status;
528 }
529
530 Name[ResultLength] = '\0';
531 *HostName = Name;
532 return EFI_SUCCESS;
533 }
534
535
536 /**
537 Get the IPv4 address from a HTTP URL.
538
539 This function will return the IPv4 address according to the Url and previous parse result.
540
541 @param[in] Url The pointer to a HTTP URL string.
542 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
543 @param[out] Ip4Address Pointer to a buffer to store the IP address.
544
545 @retval EFI_SUCCESS Successfully get the required component.
546 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip4Address is NULL or UrlParser is invalid.
547 @retval EFI_NOT_FOUND No IPv4 address component in the URL.
548 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
549
550 **/
551 EFI_STATUS
552 EFIAPI
553 HttpUrlGetIp4 (
554 IN CHAR8 *Url,
555 IN VOID *UrlParser,
556 OUT EFI_IPv4_ADDRESS *Ip4Address
557 )
558 {
559 CHAR8 *Ip4String;
560 EFI_STATUS Status;
561 UINT32 ResultLength;
562 HTTP_URL_PARSER *Parser;
563
564 if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) {
565 return EFI_INVALID_PARAMETER;
566 }
567
568 Parser = (HTTP_URL_PARSER*) UrlParser;
569
570 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
571 return EFI_INVALID_PARAMETER;
572 }
573
574 Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
575 if (Ip4String == NULL) {
576 return EFI_OUT_OF_RESOURCES;
577 }
578
579 Status = UriPercentDecode (
580 Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
581 Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
582 Ip4String,
583 &ResultLength
584 );
585 if (EFI_ERROR (Status)) {
586 FreePool (Ip4String);
587 return Status;
588 }
589
590 Ip4String[ResultLength] = '\0';
591 Status = NetLibAsciiStrToIp4 (Ip4String, Ip4Address);
592 FreePool (Ip4String);
593
594 return Status;
595 }
596
597 /**
598 Get the IPv6 address from a HTTP URL.
599
600 This function will return the IPv6 address according to the Url and previous parse result.
601
602 @param[in] Url The pointer to a HTTP URL string.
603 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
604 @param[out] Ip6Address Pointer to a buffer to store the IP address.
605
606 @retval EFI_SUCCESS Successfully get the required component.
607 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip6Address is NULL or UrlParser is invalid.
608 @retval EFI_NOT_FOUND No IPv6 address component in the URL.
609 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
610
611 **/
612 EFI_STATUS
613 EFIAPI
614 HttpUrlGetIp6 (
615 IN CHAR8 *Url,
616 IN VOID *UrlParser,
617 OUT EFI_IPv6_ADDRESS *Ip6Address
618 )
619 {
620 CHAR8 *Ip6String;
621 CHAR8 *Ptr;
622 UINT32 Length;
623 EFI_STATUS Status;
624 UINT32 ResultLength;
625 HTTP_URL_PARSER *Parser;
626
627 if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) {
628 return EFI_INVALID_PARAMETER;
629 }
630
631 Parser = (HTTP_URL_PARSER*) UrlParser;
632
633 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
634 return EFI_INVALID_PARAMETER;
635 }
636
637 //
638 // IP-literal = "[" ( IPv6address / IPvFuture ) "]"
639 //
640 Length = Parser->FieldData[HTTP_URI_FIELD_HOST].Length;
641 if (Length < 2) {
642 return EFI_INVALID_PARAMETER;
643 }
644
645 Ptr = Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset;
646 if ((Ptr[0] != '[') || (Ptr[Length - 1] != ']')) {
647 return EFI_INVALID_PARAMETER;
648 }
649
650 Ip6String = AllocatePool (Length);
651 if (Ip6String == NULL) {
652 return EFI_OUT_OF_RESOURCES;
653 }
654
655 Status = UriPercentDecode (
656 Ptr + 1,
657 Length - 2,
658 Ip6String,
659 &ResultLength
660 );
661 if (EFI_ERROR (Status)) {
662 FreePool (Ip6String);
663 return Status;
664 }
665
666 Ip6String[ResultLength] = '\0';
667 Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);
668 FreePool (Ip6String);
669
670 return Status;
671 }
672
673 /**
674 Get the port number from a HTTP URL.
675
676 This function will return the port number according to the Url and previous parse result.
677
678 @param[in] Url The pointer to a HTTP URL string.
679 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
680 @param[out] Port Pointer to a buffer to store the port number.
681
682 @retval EFI_SUCCESS Successfully get the required component.
683 @retval EFI_INVALID_PARAMETER Uri is NULL or Port is NULL or UrlParser is invalid.
684 @retval EFI_NOT_FOUND No port number in the URL.
685 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
686
687 **/
688 EFI_STATUS
689 EFIAPI
690 HttpUrlGetPort (
691 IN CHAR8 *Url,
692 IN VOID *UrlParser,
693 OUT UINT16 *Port
694 )
695 {
696 CHAR8 *PortString;
697 EFI_STATUS Status;
698 UINTN Index;
699 UINTN Data;
700 UINT32 ResultLength;
701 HTTP_URL_PARSER *Parser;
702
703 if (Url == NULL || UrlParser == NULL || Port == NULL) {
704 return EFI_INVALID_PARAMETER;
705 }
706
707 *Port = 0;
708 Index = 0;
709
710 Parser = (HTTP_URL_PARSER*) UrlParser;
711
712 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {
713 return EFI_INVALID_PARAMETER;
714 }
715
716 PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1);
717 if (PortString == NULL) {
718 return EFI_OUT_OF_RESOURCES;
719 }
720
721 Status = UriPercentDecode (
722 Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset,
723 Parser->FieldData[HTTP_URI_FIELD_PORT].Length,
724 PortString,
725 &ResultLength
726 );
727 if (EFI_ERROR (Status)) {
728 goto ON_EXIT;
729 }
730
731 PortString[ResultLength] = '\0';
732
733 while (Index < ResultLength) {
734 if (!NET_IS_DIGIT (PortString[Index])) {
735 Status = EFI_INVALID_PARAMETER;
736 goto ON_EXIT;
737 }
738 Index ++;
739 }
740
741 Status = AsciiStrDecimalToUintnS (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset, (CHAR8 **) NULL, &Data);
742
743 if (Data > HTTP_URI_PORT_MAX_NUM) {
744 Status = EFI_INVALID_PARAMETER;
745 goto ON_EXIT;
746 }
747
748 *Port = (UINT16) Data;
749
750 ON_EXIT:
751 FreePool (PortString);
752 return Status;
753 }
754
755 /**
756 Get the Path from a HTTP URL.
757
758 This function will return the Path according to the Url and previous parse result,and
759 it is the caller's responsibility to free the buffer returned in *Path.
760
761 @param[in] Url The pointer to a HTTP URL string.
762 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
763 @param[out] Path Pointer to a buffer to store the Path.
764
765 @retval EFI_SUCCESS Successfully get the required component.
766 @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.
767 @retval EFI_NOT_FOUND No hostName component in the URL.
768 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
769
770 **/
771 EFI_STATUS
772 EFIAPI
773 HttpUrlGetPath (
774 IN CHAR8 *Url,
775 IN VOID *UrlParser,
776 OUT CHAR8 **Path
777 )
778 {
779 CHAR8 *PathStr;
780 EFI_STATUS Status;
781 UINT32 ResultLength;
782 HTTP_URL_PARSER *Parser;
783
784 if (Url == NULL || UrlParser == NULL || Path == NULL) {
785 return EFI_INVALID_PARAMETER;
786 }
787
788 Parser = (HTTP_URL_PARSER*) UrlParser;
789
790 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PATH)) == 0) {
791 return EFI_NOT_FOUND;
792 }
793
794 PathStr = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PATH].Length + 1);
795 if (PathStr == NULL) {
796 return EFI_OUT_OF_RESOURCES;
797 }
798
799 Status = UriPercentDecode (
800 Url + Parser->FieldData[HTTP_URI_FIELD_PATH].Offset,
801 Parser->FieldData[HTTP_URI_FIELD_PATH].Length,
802 PathStr,
803 &ResultLength
804 );
805 if (EFI_ERROR (Status)) {
806 FreePool (PathStr);
807 return Status;
808 }
809
810 PathStr[ResultLength] = '\0';
811 *Path = PathStr;
812 return EFI_SUCCESS;
813 }
814
815 /**
816 Release the resource of the URL parser.
817
818 @param[in] UrlParser Pointer to the parser.
819
820 **/
821 VOID
822 EFIAPI
823 HttpUrlFreeParser (
824 IN VOID *UrlParser
825 )
826 {
827 FreePool (UrlParser);
828 }
829
830 /**
831 Find a specified header field according to the field name.
832
833 @param[in] HeaderCount Number of HTTP header structures in Headers list.
834 @param[in] Headers Array containing list of HTTP headers.
835 @param[in] FieldName Null terminated string which describes a field name.
836
837 @return Pointer to the found header or NULL.
838
839 **/
840 EFI_HTTP_HEADER *
841 EFIAPI
842 HttpFindHeader (
843 IN UINTN HeaderCount,
844 IN EFI_HTTP_HEADER *Headers,
845 IN CHAR8 *FieldName
846 )
847 {
848 UINTN Index;
849
850 if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {
851 return NULL;
852 }
853
854 for (Index = 0; Index < HeaderCount; Index++){
855 //
856 // Field names are case-insensitive (RFC 2616).
857 //
858 if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
859 return &Headers[Index];
860 }
861 }
862 return NULL;
863 }
864
865 typedef enum {
866 BodyParserBodyStart,
867 BodyParserBodyIdentity,
868 BodyParserChunkSizeStart,
869 BodyParserChunkSize,
870 BodyParserChunkSizeEndCR,
871 BodyParserChunkExtStart,
872 BodyParserChunkDataStart,
873 BodyParserChunkDataEnd,
874 BodyParserChunkDataEndCR,
875 BodyParserTrailer,
876 BodyParserLastCRLF,
877 BodyParserLastCRLFEnd,
878 BodyParserComplete,
879 BodyParserStateMax
880 } HTTP_BODY_PARSE_STATE;
881
882 typedef struct {
883 BOOLEAN IgnoreBody; // "MUST NOT" include a message-body
884 BOOLEAN IsChunked; // "chunked" transfer-coding.
885 BOOLEAN ContentLengthIsValid;
886 UINTN ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE
887
888 HTTP_BODY_PARSER_CALLBACK Callback;
889 VOID *Context;
890 UINTN ParsedBodyLength;
891 HTTP_BODY_PARSE_STATE State;
892 UINTN CurrentChunkSize;
893 UINTN CurrentChunkParsedSize;
894 } HTTP_BODY_PARSER;
895
896 /**
897
898 Convert an Ascii char to its uppercase.
899
900 @param[in] Char Ascii character.
901
902 @return Uppercase value of the input Char.
903
904 **/
905 CHAR8
906 HttpIoCharToUpper (
907 IN CHAR8 Char
908 )
909 {
910 if (Char >= 'a' && Char <= 'z') {
911 return Char - ('a' - 'A');
912 }
913
914 return Char;
915 }
916
917 /**
918 Convert an hexadecimal char to a value of type UINTN.
919
920 @param[in] Char Ascii character.
921
922 @return Value translated from Char.
923
924 **/
925 UINTN
926 HttpIoHexCharToUintn (
927 IN CHAR8 Char
928 )
929 {
930 if (Char >= '0' && Char <= '9') {
931 return Char - '0';
932 }
933
934 return (10 + HttpIoCharToUpper (Char) - 'A');
935 }
936
937 /**
938 Get the value of the content length if there is a "Content-Length" header.
939
940 @param[in] HeaderCount Number of HTTP header structures in Headers.
941 @param[in] Headers Array containing list of HTTP headers.
942 @param[out] ContentLength Pointer to save the value of the content length.
943
944 @retval EFI_SUCCESS Successfully get the content length.
945 @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
946
947 **/
948 EFI_STATUS
949 HttpIoParseContentLengthHeader (
950 IN UINTN HeaderCount,
951 IN EFI_HTTP_HEADER *Headers,
952 OUT UINTN *ContentLength
953 )
954 {
955 EFI_HTTP_HEADER *Header;
956
957 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);
958 if (Header == NULL) {
959 return EFI_NOT_FOUND;
960 }
961
962 return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength);
963 }
964
965 /**
966
967 Check whether the HTTP message is using the "chunked" transfer-coding.
968
969 @param[in] HeaderCount Number of HTTP header structures in Headers.
970 @param[in] Headers Array containing list of HTTP headers.
971
972 @return The message is "chunked" transfer-coding (TRUE) or not (FALSE).
973
974 **/
975 BOOLEAN
976 HttpIoIsChunked (
977 IN UINTN HeaderCount,
978 IN EFI_HTTP_HEADER *Headers
979 )
980 {
981 EFI_HTTP_HEADER *Header;
982
983
984 Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);
985 if (Header == NULL) {
986 return FALSE;
987 }
988
989 if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {
990 return TRUE;
991 }
992
993 return FALSE;
994 }
995
996 /**
997 Check whether the HTTP message should have a message-body.
998
999 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
1000 @param[in] StatusCode Response status code returned by the remote host.
1001
1002 @return The message should have a message-body (FALSE) or not (TRUE).
1003
1004 **/
1005 BOOLEAN
1006 HttpIoNoMessageBody (
1007 IN EFI_HTTP_METHOD Method,
1008 IN EFI_HTTP_STATUS_CODE StatusCode
1009 )
1010 {
1011 //
1012 // RFC 2616:
1013 // All responses to the HEAD request method
1014 // MUST NOT include a message-body, even though the presence of entity-
1015 // header fields might lead one to believe they do. All 1xx
1016 // (informational), 204 (no content), and 304 (not modified) responses
1017 // MUST NOT include a message-body. All other responses do include a
1018 // message-body, although it MAY be of zero length.
1019 //
1020 if (Method == HttpMethodHead) {
1021 return TRUE;
1022 }
1023
1024 if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||
1025 (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||
1026 (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||
1027 (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))
1028 {
1029 return TRUE;
1030 }
1031
1032 return FALSE;
1033 }
1034
1035 /**
1036 Initialize a HTTP message-body parser.
1037
1038 This function will create and initialize a HTTP message parser according to caller provided HTTP message
1039 header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().
1040
1041 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
1042 @param[in] StatusCode Response status code returned by the remote host.
1043 @param[in] HeaderCount Number of HTTP header structures in Headers.
1044 @param[in] Headers Array containing list of HTTP headers.
1045 @param[in] Callback Callback function that is invoked when parsing the HTTP message-body,
1046 set to NULL to ignore all events.
1047 @param[in] Context Pointer to the context that will be passed to Callback.
1048 @param[out] MsgParser Pointer to the returned buffer to store the message parser.
1049
1050 @retval EFI_SUCCESS Successfully initialized the parser.
1051 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
1052 @retval EFI_INVALID_PARAMETER MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.
1053 @retval Others Failed to initialize the parser.
1054
1055 **/
1056 EFI_STATUS
1057 EFIAPI
1058 HttpInitMsgParser (
1059 IN EFI_HTTP_METHOD Method,
1060 IN EFI_HTTP_STATUS_CODE StatusCode,
1061 IN UINTN HeaderCount,
1062 IN EFI_HTTP_HEADER *Headers,
1063 IN HTTP_BODY_PARSER_CALLBACK Callback,
1064 IN VOID *Context,
1065 OUT VOID **MsgParser
1066 )
1067 {
1068 EFI_STATUS Status;
1069 HTTP_BODY_PARSER *Parser;
1070
1071 if (HeaderCount != 0 && Headers == NULL) {
1072 return EFI_INVALID_PARAMETER;
1073 }
1074
1075 if (MsgParser == NULL) {
1076 return EFI_INVALID_PARAMETER;
1077 }
1078
1079 Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));
1080 if (Parser == NULL) {
1081 return EFI_OUT_OF_RESOURCES;
1082 }
1083
1084 Parser->State = BodyParserBodyStart;
1085
1086 //
1087 // Determine the message length according to RFC 2616.
1088 // 1. Check whether the message "MUST NOT" have a message-body.
1089 //
1090 Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);
1091 //
1092 // 2. Check whether the message using "chunked" transfer-coding.
1093 //
1094 Parser->IsChunked = HttpIoIsChunked (HeaderCount, Headers);
1095 //
1096 // 3. Check whether the message has a Content-Length header field.
1097 //
1098 Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);
1099 if (!EFI_ERROR (Status)) {
1100 Parser->ContentLengthIsValid = TRUE;
1101 }
1102 //
1103 // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".
1104 // 5. By server closing the connection
1105 //
1106
1107 //
1108 // Set state to skip body parser if the message shouldn't have a message body.
1109 //
1110 if (Parser->IgnoreBody) {
1111 Parser->State = BodyParserComplete;
1112 } else {
1113 Parser->Callback = Callback;
1114 Parser->Context = Context;
1115 }
1116
1117 *MsgParser = Parser;
1118 return EFI_SUCCESS;
1119 }
1120
1121 /**
1122 Parse message body.
1123
1124 Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.
1125
1126 @param[in, out] MsgParser Pointer to the message parser.
1127 @param[in] BodyLength Length in bytes of the Body.
1128 @param[in] Body Pointer to the buffer of the message-body to be parsed.
1129
1130 @retval EFI_SUCCESS Successfully parse the message-body.
1131 @retval EFI_INVALID_PARAMETER MsgParser is NULL or Body is NULL or BodyLength is 0.
1132 @retval Others Operation aborted.
1133
1134 **/
1135 EFI_STATUS
1136 EFIAPI
1137 HttpParseMessageBody (
1138 IN OUT VOID *MsgParser,
1139 IN UINTN BodyLength,
1140 IN CHAR8 *Body
1141 )
1142 {
1143 CHAR8 *Char;
1144 UINTN RemainderLengthInThis;
1145 UINTN LengthForCallback;
1146 EFI_STATUS Status;
1147 HTTP_BODY_PARSER *Parser;
1148
1149 if (BodyLength == 0 || Body == NULL) {
1150 return EFI_INVALID_PARAMETER;
1151 }
1152
1153 if (MsgParser == NULL) {
1154 return EFI_INVALID_PARAMETER;
1155 }
1156
1157 Parser = (HTTP_BODY_PARSER*) MsgParser;
1158
1159 if (Parser->IgnoreBody) {
1160 Parser->State = BodyParserComplete;
1161 if (Parser->Callback != NULL) {
1162 Status = Parser->Callback (
1163 BodyParseEventOnComplete,
1164 Body,
1165 0,
1166 Parser->Context
1167 );
1168 if (EFI_ERROR (Status)) {
1169 return Status;
1170 }
1171 }
1172 return EFI_SUCCESS;
1173 }
1174
1175 if (Parser->State == BodyParserBodyStart) {
1176 Parser->ParsedBodyLength = 0;
1177 if (Parser->IsChunked) {
1178 Parser->State = BodyParserChunkSizeStart;
1179 } else {
1180 Parser->State = BodyParserBodyIdentity;
1181 }
1182 }
1183
1184 //
1185 // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.
1186 //
1187 for (Char = Body; Char < Body + BodyLength; ) {
1188
1189 switch (Parser->State) {
1190 case BodyParserStateMax:
1191 return EFI_ABORTED;
1192
1193 case BodyParserBodyIdentity:
1194 //
1195 // Identity transfer-coding, just notify user to save the body data.
1196 //
1197 if (Parser->Callback != NULL) {
1198 Status = Parser->Callback (
1199 BodyParseEventOnData,
1200 Char,
1201 MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength),
1202 Parser->Context
1203 );
1204 if (EFI_ERROR (Status)) {
1205 return Status;
1206 }
1207 }
1208 Char += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
1209 Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
1210 if (Parser->ParsedBodyLength == Parser->ContentLength) {
1211 Parser->State = BodyParserComplete;
1212 if (Parser->Callback != NULL) {
1213 Status = Parser->Callback (
1214 BodyParseEventOnComplete,
1215 Char,
1216 0,
1217 Parser->Context
1218 );
1219 if (EFI_ERROR (Status)) {
1220 return Status;
1221 }
1222 }
1223 }
1224 break;
1225
1226 case BodyParserChunkSizeStart:
1227 //
1228 // First byte of chunk-size, the chunk-size might be truncated.
1229 //
1230 Parser->CurrentChunkSize = 0;
1231 Parser->State = BodyParserChunkSize;
1232 case BodyParserChunkSize:
1233 if (!NET_IS_HEX_CHAR (*Char)) {
1234 if (*Char == ';') {
1235 Parser->State = BodyParserChunkExtStart;
1236 Char++;
1237 } else if (*Char == '\r') {
1238 Parser->State = BodyParserChunkSizeEndCR;
1239 Char++;
1240 } else {
1241 Parser->State = BodyParserStateMax;
1242 }
1243 break;
1244 }
1245
1246 if (Parser->CurrentChunkSize > (((~((UINTN) 0)) - 16) / 16)) {
1247 return EFI_INVALID_PARAMETER;
1248 }
1249 Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);
1250 Char++;
1251 break;
1252
1253 case BodyParserChunkExtStart:
1254 //
1255 // Ignore all the chunk extensions.
1256 //
1257 if (*Char == '\r') {
1258 Parser->State = BodyParserChunkSizeEndCR;
1259 }
1260 Char++;
1261 break;
1262
1263 case BodyParserChunkSizeEndCR:
1264 if (*Char != '\n') {
1265 Parser->State = BodyParserStateMax;
1266 break;
1267 }
1268 Char++;
1269 if (Parser->CurrentChunkSize == 0) {
1270 //
1271 // The last chunk has been parsed and now assumed the state
1272 // of HttpBodyParse is ParserLastCRLF. So it need to decide
1273 // whether the rest message is trailer or last CRLF in the next round.
1274 //
1275 Parser->ContentLengthIsValid = TRUE;
1276 Parser->State = BodyParserLastCRLF;
1277 break;
1278 }
1279 Parser->State = BodyParserChunkDataStart;
1280 Parser->CurrentChunkParsedSize = 0;
1281 break;
1282
1283 case BodyParserLastCRLF:
1284 //
1285 // Judge the byte is belong to the Last CRLF or trailer, and then
1286 // configure the state of HttpBodyParse to corresponding state.
1287 //
1288 if (*Char == '\r') {
1289 Char++;
1290 Parser->State = BodyParserLastCRLFEnd;
1291 break;
1292 } else {
1293 Parser->State = BodyParserTrailer;
1294 break;
1295 }
1296
1297 case BodyParserLastCRLFEnd:
1298 if (*Char == '\n') {
1299 Parser->State = BodyParserComplete;
1300 Char++;
1301 if (Parser->Callback != NULL) {
1302 Status = Parser->Callback (
1303 BodyParseEventOnComplete,
1304 Char,
1305 0,
1306 Parser->Context
1307 );
1308 if (EFI_ERROR (Status)) {
1309 return Status;
1310 }
1311 }
1312 break;
1313 } else {
1314 Parser->State = BodyParserStateMax;
1315 break;
1316 }
1317
1318 case BodyParserTrailer:
1319 if (*Char == '\r') {
1320 Parser->State = BodyParserChunkSizeEndCR;
1321 }
1322 Char++;
1323 break;
1324
1325 case BodyParserChunkDataStart:
1326 //
1327 // First byte of chunk-data, the chunk data also might be truncated.
1328 //
1329 RemainderLengthInThis = BodyLength - (Char - Body);
1330 LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);
1331 if (Parser->Callback != NULL) {
1332 Status = Parser->Callback (
1333 BodyParseEventOnData,
1334 Char,
1335 LengthForCallback,
1336 Parser->Context
1337 );
1338 if (EFI_ERROR (Status)) {
1339 return Status;
1340 }
1341 }
1342 Char += LengthForCallback;
1343 Parser->ContentLength += LengthForCallback;
1344 Parser->CurrentChunkParsedSize += LengthForCallback;
1345 if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {
1346 Parser->State = BodyParserChunkDataEnd;
1347 }
1348 break;
1349
1350 case BodyParserChunkDataEnd:
1351 if (*Char == '\r') {
1352 Parser->State = BodyParserChunkDataEndCR;
1353 } else {
1354 Parser->State = BodyParserStateMax;
1355 }
1356 Char++;
1357 break;
1358
1359 case BodyParserChunkDataEndCR:
1360 if (*Char != '\n') {
1361 Parser->State = BodyParserStateMax;
1362 break;
1363 }
1364 Char++;
1365 Parser->State = BodyParserChunkSizeStart;
1366 break;
1367
1368 default:
1369 break;
1370 }
1371
1372 }
1373
1374 if (Parser->State == BodyParserStateMax) {
1375 return EFI_ABORTED;
1376 }
1377
1378 return EFI_SUCCESS;
1379 }
1380
1381 /**
1382 Check whether the message-body is complete or not.
1383
1384 @param[in] MsgParser Pointer to the message parser.
1385
1386 @retval TRUE Message-body is complete.
1387 @retval FALSE Message-body is not complete.
1388
1389 **/
1390 BOOLEAN
1391 EFIAPI
1392 HttpIsMessageComplete (
1393 IN VOID *MsgParser
1394 )
1395 {
1396 HTTP_BODY_PARSER *Parser;
1397
1398 Parser = (HTTP_BODY_PARSER*) MsgParser;
1399
1400 if (Parser->State == BodyParserComplete) {
1401 return TRUE;
1402 }
1403 return FALSE;
1404 }
1405
1406 /**
1407 Get the content length of the entity.
1408
1409 Note that in trunk transfer, the entity length is not valid until the whole message body is received.
1410
1411 @param[in] MsgParser Pointer to the message parser.
1412 @param[out] ContentLength Pointer to store the length of the entity.
1413
1414 @retval EFI_SUCCESS Successfully to get the entity length.
1415 @retval EFI_NOT_READY Entity length is not valid yet.
1416 @retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL.
1417
1418 **/
1419 EFI_STATUS
1420 EFIAPI
1421 HttpGetEntityLength (
1422 IN VOID *MsgParser,
1423 OUT UINTN *ContentLength
1424 )
1425 {
1426 HTTP_BODY_PARSER *Parser;
1427
1428 if (MsgParser == NULL || ContentLength == NULL) {
1429 return EFI_INVALID_PARAMETER;
1430 }
1431
1432 Parser = (HTTP_BODY_PARSER*) MsgParser;
1433
1434 if (!Parser->ContentLengthIsValid) {
1435 return EFI_NOT_READY;
1436 }
1437
1438 *ContentLength = Parser->ContentLength;
1439 return EFI_SUCCESS;
1440 }
1441
1442 /**
1443 Release the resource of the message parser.
1444
1445 @param[in] MsgParser Pointer to the message parser.
1446
1447 **/
1448 VOID
1449 EFIAPI
1450 HttpFreeMsgParser (
1451 IN VOID *MsgParser
1452 )
1453 {
1454 FreePool (MsgParser);
1455 }
1456
1457
1458 /**
1459 Get the next string, which is distinguished by specified separator.
1460
1461 @param[in] String Pointer to the string.
1462 @param[in] Separator Specified separator used to distinguish where is the beginning
1463 of next string.
1464
1465 @return Pointer to the next string.
1466 @return NULL if not find or String is NULL.
1467
1468 **/
1469 CHAR8 *
1470 EFIAPI
1471 AsciiStrGetNextToken (
1472 IN CONST CHAR8 *String,
1473 IN CHAR8 Separator
1474 )
1475 {
1476 CONST CHAR8 *Token;
1477
1478 Token = String;
1479 while (TRUE) {
1480 if (*Token == 0) {
1481 return NULL;
1482 }
1483 if (*Token == Separator) {
1484 return (CHAR8 *)(Token + 1);
1485 }
1486 Token++;
1487 }
1488 }
1489
1490 /**
1491 Set FieldName and FieldValue into specified HttpHeader.
1492
1493 @param[in,out] HttpHeader Specified HttpHeader.
1494 @param[in] FieldName FieldName of this HttpHeader, a NULL terminated ASCII string.
1495 @param[in] FieldValue FieldValue of this HttpHeader, a NULL terminated ASCII string.
1496
1497
1498 @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully.
1499 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1500
1501 **/
1502 EFI_STATUS
1503 EFIAPI
1504 HttpSetFieldNameAndValue (
1505 IN OUT EFI_HTTP_HEADER *HttpHeader,
1506 IN CONST CHAR8 *FieldName,
1507 IN CONST CHAR8 *FieldValue
1508 )
1509 {
1510 UINTN FieldNameSize;
1511 UINTN FieldValueSize;
1512
1513 if (HttpHeader->FieldName != NULL) {
1514 FreePool (HttpHeader->FieldName);
1515 }
1516 if (HttpHeader->FieldValue != NULL) {
1517 FreePool (HttpHeader->FieldValue);
1518 }
1519
1520 FieldNameSize = AsciiStrSize (FieldName);
1521 HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);
1522 if (HttpHeader->FieldName == NULL) {
1523 return EFI_OUT_OF_RESOURCES;
1524 }
1525 CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);
1526 HttpHeader->FieldName[FieldNameSize - 1] = 0;
1527
1528 FieldValueSize = AsciiStrSize (FieldValue);
1529 HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);
1530 if (HttpHeader->FieldValue == NULL) {
1531 return EFI_OUT_OF_RESOURCES;
1532 }
1533 CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);
1534 HttpHeader->FieldValue[FieldValueSize - 1] = 0;
1535
1536 return EFI_SUCCESS;
1537 }
1538
1539 /**
1540 Get one key/value header pair from the raw string.
1541
1542 @param[in] String Pointer to the raw string.
1543 @param[out] FieldName Points directly to field name within 'HttpHeader'.
1544 @param[out] FieldValue Points directly to field value within 'HttpHeader'.
1545
1546 @return Pointer to the next raw string.
1547 @return NULL if no key/value header pair from this raw string.
1548
1549 **/
1550 CHAR8 *
1551 EFIAPI
1552 HttpGetFieldNameAndValue (
1553 IN CHAR8 *String,
1554 OUT CHAR8 **FieldName,
1555 OUT CHAR8 **FieldValue
1556 )
1557 {
1558 CHAR8 *FieldNameStr;
1559 CHAR8 *FieldValueStr;
1560 CHAR8 *StrPtr;
1561
1562 if (String == NULL || FieldName == NULL || FieldValue == NULL) {
1563 return NULL;
1564 }
1565
1566 *FieldName = NULL;
1567 *FieldValue = NULL;
1568 FieldNameStr = NULL;
1569 FieldValueStr = NULL;
1570 StrPtr = NULL;
1571
1572 //
1573 // Each header field consists of a name followed by a colon (":") and the field value.
1574 //
1575 FieldNameStr = String;
1576 FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');
1577 if (FieldValueStr == NULL) {
1578 return NULL;
1579 }
1580
1581 //
1582 // Replace ':' with 0
1583 //
1584 *(FieldValueStr - 1) = 0;
1585
1586 //
1587 // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.
1588 //
1589 while (TRUE) {
1590 if (*FieldValueStr == ' ' || *FieldValueStr == '\t') {
1591 FieldValueStr ++;
1592 } else if (*FieldValueStr == '\r' && *(FieldValueStr + 1) == '\n' &&
1593 (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t')) {
1594 FieldValueStr = FieldValueStr + 3;
1595 } else {
1596 break;
1597 }
1598 }
1599
1600 //
1601 // Header fields can be extended over multiple lines by preceding each extra
1602 // line with at least one SP or HT.
1603 //
1604 StrPtr = FieldValueStr;
1605 do {
1606 StrPtr = AsciiStrGetNextToken (StrPtr, '\r');
1607 if (StrPtr == NULL || *StrPtr != '\n') {
1608 return NULL;
1609 }
1610
1611 StrPtr++;
1612 } while (*StrPtr == ' ' || *StrPtr == '\t');
1613
1614 //
1615 // Replace '\r' with 0
1616 //
1617 *(StrPtr - 2) = 0;
1618
1619 //
1620 // Get FieldName and FieldValue.
1621 //
1622 *FieldName = FieldNameStr;
1623 *FieldValue = FieldValueStr;
1624
1625 return StrPtr;
1626 }
1627
1628 /**
1629 Free existing HeaderFields.
1630
1631 @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free.
1632 @param[in] FieldCount The number of header pairs in HeaderFields.
1633
1634 **/
1635 VOID
1636 EFIAPI
1637 HttpFreeHeaderFields (
1638 IN EFI_HTTP_HEADER *HeaderFields,
1639 IN UINTN FieldCount
1640 )
1641 {
1642 UINTN Index;
1643
1644 if (HeaderFields != NULL) {
1645 for (Index = 0; Index < FieldCount; Index++) {
1646 if (HeaderFields[Index].FieldName != NULL) {
1647 FreePool (HeaderFields[Index].FieldName);
1648 }
1649 if (HeaderFields[Index].FieldValue != NULL) {
1650 FreePool (HeaderFields[Index].FieldValue);
1651 }
1652 }
1653
1654 FreePool (HeaderFields);
1655 }
1656 }
1657
1658 /**
1659 Generate HTTP request message.
1660
1661 This function will allocate memory for the whole HTTP message and generate a
1662 well formatted HTTP Request message in it, include the Request-Line, header
1663 fields and also the message body. It is the caller's responsibility to free
1664 the buffer returned in *RequestMsg.
1665
1666 @param[in] Message Pointer to the EFI_HTTP_MESSAGE structure which
1667 contains the required information to generate
1668 the HTTP request message.
1669 @param[in] Url The URL of a remote host.
1670 @param[out] RequestMsg Pointer to the created HTTP request message.
1671 NULL if any error occured.
1672 @param[out] RequestMsgSize Size of the RequestMsg (in bytes).
1673
1674 @return EFI_SUCCESS If HTTP request string was created successfully
1675 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
1676 @retval EFI_INVALID_PARAMETER The input arguments are invalid
1677
1678 **/
1679 EFI_STATUS
1680 EFIAPI
1681 HttpGenRequestMessage (
1682 IN CONST EFI_HTTP_MESSAGE *Message,
1683 IN CONST CHAR8 *Url,
1684 OUT CHAR8 **RequestMsg,
1685 OUT UINTN *RequestMsgSize
1686 )
1687 {
1688 EFI_STATUS Status;
1689 UINTN StrLength;
1690 CHAR8 *RequestPtr;
1691 UINTN HttpHdrSize;
1692 UINTN MsgSize;
1693 BOOLEAN Success;
1694 VOID *HttpHdr;
1695 EFI_HTTP_HEADER **AppendList;
1696 UINTN Index;
1697 EFI_HTTP_UTILITIES_PROTOCOL *HttpUtilitiesProtocol;
1698
1699
1700 ASSERT (Message != NULL);
1701
1702 *RequestMsg = NULL;
1703 Status = EFI_SUCCESS;
1704 HttpHdrSize = 0;
1705 MsgSize = 0;
1706 Success = FALSE;
1707 HttpHdr = NULL;
1708 AppendList = NULL;
1709 HttpUtilitiesProtocol = NULL;
1710
1711 //
1712 // 1. If we have a Request, we cannot have a NULL Url
1713 // 2. If we have a Request, HeaderCount can not be non-zero
1714 // 3. If we do not have a Request, HeaderCount should be zero
1715 // 4. If we do not have Request and Headers, we need at least a message-body
1716 //
1717 if ((Message->Data.Request != NULL && Url == NULL) ||
1718 (Message->Data.Request != NULL && Message->HeaderCount == 0) ||
1719 (Message->Data.Request == NULL && Message->HeaderCount != 0) ||
1720 (Message->Data.Request == NULL && Message->HeaderCount == 0 && Message->BodyLength == 0)) {
1721 return EFI_INVALID_PARAMETER;
1722 }
1723
1724 if (Message->HeaderCount != 0) {
1725 //
1726 // Locate the HTTP_UTILITIES protocol.
1727 //
1728 Status = gBS->LocateProtocol (
1729 &gEfiHttpUtilitiesProtocolGuid,
1730 NULL,
1731 (VOID **)&HttpUtilitiesProtocol
1732 );
1733
1734 if (EFI_ERROR (Status)) {
1735 DEBUG ((DEBUG_ERROR,"Failed to locate Http Utilities protocol. Status = %r.\n", Status));
1736 return Status;
1737 }
1738
1739 //
1740 // Build AppendList to send into HttpUtilitiesBuild
1741 //
1742 AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount));
1743 if (AppendList == NULL) {
1744 return EFI_OUT_OF_RESOURCES;
1745 }
1746
1747 for(Index = 0; Index < Message->HeaderCount; Index++){
1748 AppendList[Index] = &Message->Headers[Index];
1749 }
1750
1751 //
1752 // Build raw HTTP Headers
1753 //
1754 Status = HttpUtilitiesProtocol->Build (
1755 HttpUtilitiesProtocol,
1756 0,
1757 NULL,
1758 0,
1759 NULL,
1760 Message->HeaderCount,
1761 AppendList,
1762 &HttpHdrSize,
1763 &HttpHdr
1764 );
1765
1766 if (AppendList != NULL) {
1767 FreePool (AppendList);
1768 }
1769
1770 if (EFI_ERROR (Status) || HttpHdr == NULL){
1771 return Status;
1772 }
1773 }
1774
1775 //
1776 // If we have headers to be sent, account for it.
1777 //
1778 if (Message->HeaderCount != 0) {
1779 MsgSize = HttpHdrSize;
1780 }
1781
1782 //
1783 // If we have a request line, account for the fields.
1784 //
1785 if (Message->Data.Request != NULL) {
1786 MsgSize += HTTP_METHOD_MAXIMUM_LEN + AsciiStrLen (HTTP_VERSION_CRLF_STR) + AsciiStrLen (Url);
1787 }
1788
1789
1790 //
1791 // If we have a message body to be sent, account for it.
1792 //
1793 MsgSize += Message->BodyLength;
1794
1795 //
1796 // memory for the string that needs to be sent to TCP
1797 //
1798 *RequestMsg = AllocateZeroPool (MsgSize);
1799 if (*RequestMsg == NULL) {
1800 Status = EFI_OUT_OF_RESOURCES;
1801 goto Exit;
1802 }
1803
1804 RequestPtr = *RequestMsg;
1805 //
1806 // Construct header request
1807 //
1808 if (Message->Data.Request != NULL) {
1809 switch (Message->Data.Request->Method) {
1810 case HttpMethodGet:
1811 StrLength = sizeof (HTTP_METHOD_GET) - 1;
1812 CopyMem (RequestPtr, HTTP_METHOD_GET, StrLength);
1813 RequestPtr += StrLength;
1814 break;
1815 case HttpMethodPut:
1816 StrLength = sizeof (HTTP_METHOD_PUT) - 1;
1817 CopyMem (RequestPtr, HTTP_METHOD_PUT, StrLength);
1818 RequestPtr += StrLength;
1819 break;
1820 case HttpMethodPatch:
1821 StrLength = sizeof (HTTP_METHOD_PATCH) - 1;
1822 CopyMem (RequestPtr, HTTP_METHOD_PATCH, StrLength);
1823 RequestPtr += StrLength;
1824 break;
1825 case HttpMethodPost:
1826 StrLength = sizeof (HTTP_METHOD_POST) - 1;
1827 CopyMem (RequestPtr, HTTP_METHOD_POST, StrLength);
1828 RequestPtr += StrLength;
1829 break;
1830 case HttpMethodHead:
1831 StrLength = sizeof (HTTP_METHOD_HEAD) - 1;
1832 CopyMem (RequestPtr, HTTP_METHOD_HEAD, StrLength);
1833 RequestPtr += StrLength;
1834 break;
1835 case HttpMethodDelete:
1836 StrLength = sizeof (HTTP_METHOD_DELETE) - 1;
1837 CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength);
1838 RequestPtr += StrLength;
1839 break;
1840 default:
1841 ASSERT (FALSE);
1842 Status = EFI_INVALID_PARAMETER;
1843 goto Exit;
1844 }
1845
1846 StrLength = AsciiStrLen(EMPTY_SPACE);
1847 CopyMem (RequestPtr, EMPTY_SPACE, StrLength);
1848 RequestPtr += StrLength;
1849
1850 StrLength = AsciiStrLen (Url);
1851 CopyMem (RequestPtr, Url, StrLength);
1852 RequestPtr += StrLength;
1853
1854 StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1;
1855 CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength);
1856 RequestPtr += StrLength;
1857
1858 if (HttpHdr != NULL) {
1859 //
1860 // Construct header
1861 //
1862 CopyMem (RequestPtr, HttpHdr, HttpHdrSize);
1863 RequestPtr += HttpHdrSize;
1864 }
1865 }
1866
1867 //
1868 // Construct body
1869 //
1870 if (Message->Body != NULL) {
1871 CopyMem (RequestPtr, Message->Body, Message->BodyLength);
1872 RequestPtr += Message->BodyLength;
1873 }
1874
1875 //
1876 // Done
1877 //
1878 (*RequestMsgSize) = (UINTN)(RequestPtr) - (UINTN)(*RequestMsg);
1879 Success = TRUE;
1880
1881 Exit:
1882
1883 if (!Success) {
1884 if (*RequestMsg != NULL) {
1885 FreePool (*RequestMsg);
1886 }
1887 *RequestMsg = NULL;
1888 return Status;
1889 }
1890
1891 if (HttpHdr != NULL) {
1892 FreePool (HttpHdr);
1893 }
1894
1895 return EFI_SUCCESS;
1896 }
1897
1898 /**
1899 Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined
1900 in UEFI 2.5 specification.
1901
1902 @param[in] StatusCode The status code value in HTTP message.
1903
1904 @return Value defined in EFI_HTTP_STATUS_CODE .
1905
1906 **/
1907 EFI_HTTP_STATUS_CODE
1908 EFIAPI
1909 HttpMappingToStatusCode (
1910 IN UINTN StatusCode
1911 )
1912 {
1913 switch (StatusCode) {
1914 case 100:
1915 return HTTP_STATUS_100_CONTINUE;
1916 case 101:
1917 return HTTP_STATUS_101_SWITCHING_PROTOCOLS;
1918 case 200:
1919 return HTTP_STATUS_200_OK;
1920 case 201:
1921 return HTTP_STATUS_201_CREATED;
1922 case 202:
1923 return HTTP_STATUS_202_ACCEPTED;
1924 case 203:
1925 return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;
1926 case 204:
1927 return HTTP_STATUS_204_NO_CONTENT;
1928 case 205:
1929 return HTTP_STATUS_205_RESET_CONTENT;
1930 case 206:
1931 return HTTP_STATUS_206_PARTIAL_CONTENT;
1932 case 300:
1933 return HTTP_STATUS_300_MULTIPLE_CHIOCES;
1934 case 301:
1935 return HTTP_STATUS_301_MOVED_PERMANENTLY;
1936 case 302:
1937 return HTTP_STATUS_302_FOUND;
1938 case 303:
1939 return HTTP_STATUS_303_SEE_OTHER;
1940 case 304:
1941 return HTTP_STATUS_304_NOT_MODIFIED;
1942 case 305:
1943 return HTTP_STATUS_305_USE_PROXY;
1944 case 307:
1945 return HTTP_STATUS_307_TEMPORARY_REDIRECT;
1946 case 400:
1947 return HTTP_STATUS_400_BAD_REQUEST;
1948 case 401:
1949 return HTTP_STATUS_401_UNAUTHORIZED;
1950 case 402:
1951 return HTTP_STATUS_402_PAYMENT_REQUIRED;
1952 case 403:
1953 return HTTP_STATUS_403_FORBIDDEN;
1954 case 404:
1955 return HTTP_STATUS_404_NOT_FOUND;
1956 case 405:
1957 return HTTP_STATUS_405_METHOD_NOT_ALLOWED;
1958 case 406:
1959 return HTTP_STATUS_406_NOT_ACCEPTABLE;
1960 case 407:
1961 return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;
1962 case 408:
1963 return HTTP_STATUS_408_REQUEST_TIME_OUT;
1964 case 409:
1965 return HTTP_STATUS_409_CONFLICT;
1966 case 410:
1967 return HTTP_STATUS_410_GONE;
1968 case 411:
1969 return HTTP_STATUS_411_LENGTH_REQUIRED;
1970 case 412:
1971 return HTTP_STATUS_412_PRECONDITION_FAILED;
1972 case 413:
1973 return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;
1974 case 414:
1975 return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;
1976 case 415:
1977 return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;
1978 case 416:
1979 return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;
1980 case 417:
1981 return HTTP_STATUS_417_EXPECTATION_FAILED;
1982 case 500:
1983 return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;
1984 case 501:
1985 return HTTP_STATUS_501_NOT_IMPLEMENTED;
1986 case 502:
1987 return HTTP_STATUS_502_BAD_GATEWAY;
1988 case 503:
1989 return HTTP_STATUS_503_SERVICE_UNAVAILABLE;
1990 case 504:
1991 return HTTP_STATUS_504_GATEWAY_TIME_OUT;
1992 case 505:
1993 return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;
1994
1995 default:
1996 return HTTP_STATUS_UNSUPPORTED_STATUS;
1997 }
1998 }
1999
2000 /**
2001 Check whether header field called FieldName is in DeleteList.
2002
2003 @param[in] DeleteList Pointer to array of key/value header pairs.
2004 @param[in] DeleteCount The number of header pairs.
2005 @param[in] FieldName Pointer to header field's name.
2006
2007 @return TRUE if FieldName is not in DeleteList, that means this header field is valid.
2008 @return FALSE if FieldName is in DeleteList, that means this header field is invalid.
2009
2010 **/
2011 BOOLEAN
2012 EFIAPI
2013 HttpIsValidHttpHeader (
2014 IN CHAR8 *DeleteList[],
2015 IN UINTN DeleteCount,
2016 IN CHAR8 *FieldName
2017 )
2018 {
2019 UINTN Index;
2020
2021 for (Index = 0; Index < DeleteCount; Index++) {
2022 if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {
2023 return FALSE;
2024 }
2025 }
2026
2027 return TRUE;
2028 }
2029