]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.c
MdeModulePkg: Add HttpLib.
[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, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at<BR>
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include <Uefi.h>
17 #include <Library/NetLib.h>
18 #include <Library/HttpLib.h>
19 #include <Library/BaseLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23
24 #define BIT(x) (1 << x)
25
26 #define NET_IS_HEX_CHAR(Ch) \
27 ((('0' <= (Ch)) && ((Ch) <= '9')) || \
28 (('A' <= (Ch)) && ((Ch) <= 'F')) || \
29 (('a' <= (Ch)) && ((Ch) <= 'f')))
30
31 //
32 // Field index of the HTTP URL parse result.
33 //
34 #define HTTP_URI_FIELD_SCHEME 0
35 #define HTTP_URI_FIELD_AUTHORITY 1
36 #define HTTP_URI_FIELD_PATH 2
37 #define HTTP_URI_FIELD_QUERY 3
38 #define HTTP_URI_FIELD_FRAGMENT 4
39 #define HTTP_URI_FIELD_USERINFO 5
40 #define HTTP_URI_FIELD_HOST 6
41 #define HTTP_URI_FIELD_PORT 7
42 #define HTTP_URI_FIELD_MAX 8
43
44 //
45 // Structure to store the parse result of a HTTP URL.
46 //
47 typedef struct {
48 UINT32 Offset;
49 UINT32 Length;
50 } HTTP_URL_FILED_DATA;
51
52 typedef struct {
53 UINT16 FieldBitMap;
54 HTTP_URL_FILED_DATA FieldData[HTTP_URI_FIELD_MAX];
55 } HTTP_URL_PARSER;
56
57 typedef enum {
58 UrlParserUrlStart,
59 UrlParserScheme,
60 UrlParserSchemeColon, // ":"
61 UrlParserSchemeColonSlash, // ":/"
62 UrlParserSchemeColonSlashSlash, // "://"
63 UrlParserAuthority,
64 UrlParserAtInAuthority,
65 UrlParserPath,
66 UrlParserQueryStart, // "?"
67 UrlParserQuery,
68 UrlParserFragmentStart, // "#"
69 UrlParserFragment,
70 UrlParserUserInfo,
71 UrlParserHostStart, // "@"
72 UrlParserHost,
73 UrlParserPortStart, // ":"
74 UrlParserPort,
75 UrlParserStateMax
76 } HTTP_URL_PARSE_STATE;
77
78 /**
79 Decode a percent-encoded URI component to the ASCII character.
80
81 Decode the input component in Buffer according to RFC 3986. The caller is responsible to make
82 sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer))
83 in bytes.
84
85 @param[in] Buffer The pointer to a percent-encoded URI component.
86 @param[in] BufferLength Length of Buffer in bytes.
87 @param[out] ResultBuffer Point to the buffer to store the decode result.
88 @param[out] ResultLength Length of decoded string in ResultBuffer in bytes.
89
90 @retval EFI_SUCCESS Successfully decoded the URI.
91 @retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string.
92
93 **/
94 EFI_STATUS
95 EFIAPI
96 UriPercentDecode (
97 IN CHAR8 *Buffer,
98 IN UINT32 BufferLength,
99 OUT CHAR8 *ResultBuffer,
100 OUT UINT32 *ResultLength
101 )
102 {
103 UINTN Index;
104 UINTN Offset;
105 CHAR8 HexStr[3];
106
107 if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) {
108 return EFI_INVALID_PARAMETER;
109 }
110
111 Index = 0;
112 Offset = 0;
113 HexStr[2] = '\0';
114 while (Index < BufferLength) {
115 if (Buffer[Index] == '%') {
116 if (!NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) {
117 return EFI_INVALID_PARAMETER;
118 }
119 HexStr[0] = Buffer[Index+1];
120 HexStr[1] = Buffer[Index+2];
121 ResultBuffer[Offset] = (CHAR8) AsciiStrHexToUintn (HexStr);
122 Index += 3;
123 } else {
124 ResultBuffer[Offset] = Buffer[Index];
125 Index++;
126 }
127 Offset++;
128 }
129
130 *ResultLength = (UINT32) Offset;
131
132 return EFI_SUCCESS;
133 }
134
135 /**
136 This function return the updated state accroding to the input state and next character of
137 the authority.
138
139 @param[in] Char Next character.
140 @param[in] State Current value of the parser state machine.
141
142 @return Updated state value.
143 **/
144 HTTP_URL_PARSE_STATE
145 NetHttpParseAuthorityChar (
146 IN CHAR8 Char,
147 IN HTTP_URL_PARSE_STATE State
148 )
149 {
150
151 //
152 // RFC 3986:
153 // The authority component is preceded by a double slash ("//") and is
154 // terminated by the next slash ("/"), question mark ("?"), or number
155 // sign ("#") character, or by the end of the URI.
156 //
157 if (Char == ' ' || Char == '\r' || Char == '\n') {
158 return UrlParserStateMax;
159 }
160
161 //
162 // authority = [ userinfo "@" ] host [ ":" port ]
163 //
164 switch (State) {
165 case UrlParserUserInfo:
166 if (Char == '@') {
167 return UrlParserHostStart;
168 }
169 break;
170
171 case UrlParserHost:
172 case UrlParserHostStart:
173 if (Char == ':') {
174 return UrlParserPortStart;
175 }
176 return UrlParserHost;
177
178 case UrlParserPort:
179 case UrlParserPortStart:
180 return UrlParserPort;
181
182 default:
183 break;
184 }
185
186 return State;
187 }
188
189 /**
190 This function parse the authority component of the input URL and update the parser.
191
192 @param[in] Url The pointer to a HTTP URL string.
193 @param[in] FoundAt TRUE if there is an at sign ('@') in the authority component.
194 @param[in, out] UrlParser Pointer to the buffer of the parse result.
195
196 @retval EFI_SUCCESS Successfully parse the authority.
197 @retval Other Error happened.
198
199 **/
200 EFI_STATUS
201 NetHttpParseAuthority (
202 IN CHAR8 *Url,
203 IN BOOLEAN FoundAt,
204 IN OUT HTTP_URL_PARSER *UrlParser
205 )
206 {
207 CHAR8 *Char;
208 CHAR8 *Authority;
209 UINT32 Length;
210 HTTP_URL_PARSE_STATE State;
211 UINT32 Field;
212 UINT32 OldField;
213
214 ASSERT ((UrlParser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0);
215
216 //
217 // authority = [ userinfo "@" ] host [ ":" port ]
218 //
219 if (FoundAt) {
220 State = UrlParserUserInfo;
221 } else {
222 State = UrlParserHost;
223 }
224
225 Field = HTTP_URI_FIELD_MAX;
226 OldField = Field;
227 Authority = Url + UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Offset;
228 Length = UrlParser->FieldData[HTTP_URI_FIELD_AUTHORITY].Length;
229 for (Char = Authority; Char < Authority + Length; Char++) {
230 State = NetHttpParseAuthorityChar (*Char, State);
231 switch (State) {
232 case UrlParserStateMax:
233 return EFI_INVALID_PARAMETER;
234
235 case UrlParserHostStart:
236 case UrlParserPortStart:
237 continue;
238
239 case UrlParserUserInfo:
240 Field = HTTP_URI_FIELD_USERINFO;
241 break;
242
243 case UrlParserHost:
244 Field = HTTP_URI_FIELD_HOST;
245 break;
246
247 case UrlParserPort:
248 Field = HTTP_URI_FIELD_PORT;
249 break;
250
251 default:
252 ASSERT (FALSE);
253 }
254
255 //
256 // Field not changed, count the length.
257 //
258 ASSERT (Field < HTTP_URI_FIELD_MAX);
259 if (Field == OldField) {
260 UrlParser->FieldData[Field].Length++;
261 continue;
262 }
263
264 //
265 // New field start
266 //
267 UrlParser->FieldBitMap |= BIT (Field);
268 UrlParser->FieldData[Field].Offset = (UINT32) (Char - Url);
269 UrlParser->FieldData[Field].Length = 1;
270 OldField = Field;
271 }
272
273 return EFI_SUCCESS;
274 }
275
276 /**
277 This function return the updated state accroding to the input state and next character of a URL.
278
279 @param[in] Char Next character.
280 @param[in] State Current value of the parser state machine.
281
282 @return Updated state value.
283
284 **/
285 HTTP_URL_PARSE_STATE
286 NetHttpParseUrlChar (
287 IN CHAR8 Char,
288 IN HTTP_URL_PARSE_STATE State
289 )
290 {
291 if (Char == ' ' || Char == '\r' || Char == '\n') {
292 return UrlParserStateMax;
293 }
294
295 //
296 // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
297 //
298 // Request-URI = "*" | absolute-URI | path-absolute | authority
299 //
300 // absolute-URI = scheme ":" hier-part [ "?" query ]
301 // path-absolute = "/" [ segment-nz *( "/" segment ) ]
302 // authority = [ userinfo "@" ] host [ ":" port ]
303 //
304 switch (State) {
305 case UrlParserUrlStart:
306 if (Char == '*' || Char == '/') {
307 return UrlParserPath;
308 }
309 return UrlParserScheme;
310
311 case UrlParserScheme:
312 if (Char == ':') {
313 return UrlParserSchemeColon;
314 }
315 break;
316
317 case UrlParserSchemeColon:
318 if (Char == '/') {
319 return UrlParserSchemeColonSlash;
320 }
321 break;
322
323 case UrlParserSchemeColonSlash:
324 if (Char == '/') {
325 return UrlParserSchemeColonSlashSlash;
326 }
327 break;
328
329 case UrlParserAtInAuthority:
330 if (Char == '@') {
331 return UrlParserStateMax;
332 }
333
334 case UrlParserAuthority:
335 case UrlParserSchemeColonSlashSlash:
336 if (Char == '@') {
337 return UrlParserAtInAuthority;
338 }
339 if (Char == '/') {
340 return UrlParserPath;
341 }
342 if (Char == '?') {
343 return UrlParserQueryStart;
344 }
345 if (Char == '#') {
346 return UrlParserFragmentStart;
347 }
348 return UrlParserAuthority;
349
350 case UrlParserPath:
351 if (Char == '?') {
352 return UrlParserQueryStart;
353 }
354 if (Char == '#') {
355 return UrlParserFragmentStart;
356 }
357 break;
358
359 case UrlParserQuery:
360 case UrlParserQueryStart:
361 if (Char == '#') {
362 return UrlParserFragmentStart;
363 }
364 return UrlParserQuery;
365
366 case UrlParserFragmentStart:
367 return UrlParserFragment;
368
369 default:
370 break;
371 }
372
373 return State;
374 }
375 /**
376 Create a URL parser for the input URL string.
377
378 This function will parse and dereference the input HTTP URL into it components. The original
379 content of the URL won't be modified and the result will be returned in UrlParser, which can
380 be used in other functions like NetHttpUrlGetHostName().
381
382 @param[in] Url The pointer to a HTTP URL string.
383 @param[in] Length Length of Url in bytes.
384 @param[in] IsConnectMethod Whether the Url is used in HTTP CONNECT method or not.
385 @param[out] UrlParser Pointer to the returned buffer to store the parse result.
386
387 @retval EFI_SUCCESS Successfully dereferenced the HTTP URL.
388 @retval EFI_INVALID_PARAMETER UrlParser is NULL or Url is not a valid HTTP URL.
389 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
390
391 **/
392 EFI_STATUS
393 EFIAPI
394 HttpParseUrl (
395 IN CHAR8 *Url,
396 IN UINT32 Length,
397 IN BOOLEAN IsConnectMethod,
398 OUT VOID **UrlParser
399 )
400 {
401 HTTP_URL_PARSE_STATE State;
402 CHAR8 *Char;
403 UINT32 Field;
404 UINT32 OldField;
405 BOOLEAN FoundAt;
406 EFI_STATUS Status;
407 HTTP_URL_PARSER *Parser;
408
409 if (Url == NULL || Length == 0 || UrlParser == NULL) {
410 return EFI_INVALID_PARAMETER;
411 }
412
413 Parser = AllocateZeroPool (sizeof (HTTP_URL_PARSER));
414 if (Parser == NULL) {
415 return EFI_OUT_OF_RESOURCES;
416 }
417
418 if (IsConnectMethod) {
419 //
420 // According to RFC 2616, the authority form is only used by the CONNECT method.
421 //
422 State = UrlParserAuthority;
423 } else {
424 State = UrlParserUrlStart;
425 }
426
427 Field = HTTP_URI_FIELD_MAX;
428 OldField = Field;
429 FoundAt = FALSE;
430 for (Char = Url; Char < Url + Length; Char++) {
431 //
432 // Update state machine accoring to next char.
433 //
434 State = NetHttpParseUrlChar (*Char, State);
435
436 switch (State) {
437 case UrlParserStateMax:
438 return EFI_INVALID_PARAMETER;
439
440 case UrlParserSchemeColon:
441 case UrlParserSchemeColonSlash:
442 case UrlParserSchemeColonSlashSlash:
443 case UrlParserQueryStart:
444 case UrlParserFragmentStart:
445 //
446 // Skip all the delimiting char: "://" "?" "@"
447 //
448 continue;
449
450 case UrlParserScheme:
451 Field = HTTP_URI_FIELD_SCHEME;
452 break;
453
454 case UrlParserAtInAuthority:
455 FoundAt = TRUE;
456 case UrlParserAuthority:
457 Field = HTTP_URI_FIELD_AUTHORITY;
458 break;
459
460 case UrlParserPath:
461 Field = HTTP_URI_FIELD_PATH;
462 break;
463
464 case UrlParserQuery:
465 Field = HTTP_URI_FIELD_QUERY;
466 break;
467
468 case UrlParserFragment:
469 Field = HTTP_URI_FIELD_FRAGMENT;
470 break;
471
472 default:
473 ASSERT (FALSE);
474 }
475
476 //
477 // Field not changed, count the length.
478 //
479 ASSERT (Field < HTTP_URI_FIELD_MAX);
480 if (Field == OldField) {
481 Parser->FieldData[Field].Length++;
482 continue;
483 }
484
485 //
486 // New field start
487 //
488 Parser->FieldBitMap |= BIT (Field);
489 Parser->FieldData[Field].Offset = (UINT32) (Char - Url);
490 Parser->FieldData[Field].Length = 1;
491 OldField = Field;
492 }
493
494 //
495 // If has authority component, continue to parse the username, host and port.
496 //
497 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_AUTHORITY)) != 0) {
498 Status = NetHttpParseAuthority (Url, FoundAt, Parser);
499 if (EFI_ERROR (Status)) {
500 return Status;
501 }
502 }
503
504 *UrlParser = Parser;
505 return EFI_SUCCESS;
506 }
507
508 /**
509 Get the Hostname from a HTTP URL.
510
511 This function will return the HostName according to the Url and previous parse result ,and
512 it is the caller's responsibility to free the buffer returned in *HostName.
513
514 @param[in] Url The pointer to a HTTP URL string.
515 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
516 @param[out] HostName Pointer to a buffer to store the HostName.
517
518 @retval EFI_SUCCESS Successfully get the required component.
519 @retval EFI_INVALID_PARAMETER Uri is NULL or HostName is NULL or UrlParser is invalid.
520 @retval EFI_NOT_FOUND No hostName component in the URL.
521 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
522
523 **/
524 EFI_STATUS
525 EFIAPI
526 HttpUrlGetHostName (
527 IN CHAR8 *Url,
528 IN VOID *UrlParser,
529 OUT CHAR8 **HostName
530 )
531 {
532 CHAR8 *Name;
533 EFI_STATUS Status;
534 UINT32 ResultLength;
535 HTTP_URL_PARSER *Parser;
536
537 if (Url == NULL || UrlParser == NULL || HostName == NULL) {
538 return EFI_INVALID_PARAMETER;
539 }
540
541 Parser = (HTTP_URL_PARSER*) UrlParser;
542
543 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
544 return EFI_NOT_FOUND;
545 }
546
547 Name = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
548 if (Name == NULL) {
549 return EFI_OUT_OF_RESOURCES;
550 }
551
552 Status = UriPercentDecode (
553 Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
554 Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
555 Name,
556 &ResultLength
557 );
558 if (EFI_ERROR (Status)) {
559 return Status;
560 }
561
562 Name[ResultLength] = '\0';
563 *HostName = Name;
564 return EFI_SUCCESS;
565 }
566
567
568 /**
569 Get the IPv4 address from a HTTP URL.
570
571 This function will return the IPv4 address according to the Url and previous parse result.
572
573 @param[in] Url The pointer to a HTTP URL string.
574 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
575 @param[out] Ip4Address Pointer to a buffer to store the IP address.
576
577 @retval EFI_SUCCESS Successfully get the required component.
578 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip4Address is NULL or UrlParser is invalid.
579 @retval EFI_NOT_FOUND No IPv4 address component in the URL.
580 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
581
582 **/
583 EFI_STATUS
584 EFIAPI
585 HttpUrlGetIp4 (
586 IN CHAR8 *Url,
587 IN VOID *UrlParser,
588 OUT EFI_IPv4_ADDRESS *Ip4Address
589 )
590 {
591 CHAR8 *Ip4String;
592 EFI_STATUS Status;
593 UINT32 ResultLength;
594 HTTP_URL_PARSER *Parser;
595
596 if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) {
597 return EFI_INVALID_PARAMETER;
598 }
599
600 Parser = (HTTP_URL_PARSER*) UrlParser;
601
602 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
603 return EFI_INVALID_PARAMETER;
604 }
605
606 Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
607 if (Ip4String == NULL) {
608 return EFI_OUT_OF_RESOURCES;
609 }
610
611 Status = UriPercentDecode (
612 Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
613 Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
614 Ip4String,
615 &ResultLength
616 );
617 if (EFI_ERROR (Status)) {
618 return Status;
619 }
620
621 Ip4String[ResultLength] = '\0';
622 Status = NetLibAsciiStrToIp4 (Ip4String, Ip4Address);
623 FreePool (Ip4String);
624
625 return Status;
626 }
627
628 /**
629 Get the IPv6 address from a HTTP URL.
630
631 This function will return the IPv6 address according to the Url and previous parse result.
632
633 @param[in] Url The pointer to a HTTP URL string.
634 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
635 @param[out] Ip6Address Pointer to a buffer to store the IP address.
636
637 @retval EFI_SUCCESS Successfully get the required component.
638 @retval EFI_INVALID_PARAMETER Uri is NULL or Ip6Address is NULL or UrlParser is invalid.
639 @retval EFI_NOT_FOUND No IPv6 address component in the URL.
640 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
641
642 **/
643 EFI_STATUS
644 EFIAPI
645 HttpUrlGetIp6 (
646 IN CHAR8 *Url,
647 IN VOID *UrlParser,
648 OUT EFI_IPv6_ADDRESS *Ip6Address
649 )
650 {
651 CHAR8 *Ip6String;
652 CHAR8 *Ptr;
653 UINT32 Length;
654 EFI_STATUS Status;
655 UINT32 ResultLength;
656 HTTP_URL_PARSER *Parser;
657
658 if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) {
659 return EFI_INVALID_PARAMETER;
660 }
661
662 Parser = (HTTP_URL_PARSER*) UrlParser;
663
664 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
665 return EFI_INVALID_PARAMETER;
666 }
667
668 //
669 // IP-literal = "[" ( IPv6address / IPvFuture ) "]"
670 //
671 Length = Parser->FieldData[HTTP_URI_FIELD_HOST].Length;
672 if (Length < 2) {
673 return EFI_INVALID_PARAMETER;
674 }
675
676 Ptr = Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset;
677 if ((Ptr[0] != '[') || (Ptr[Length - 1] != ']')) {
678 return EFI_INVALID_PARAMETER;
679 }
680
681 Ip6String = AllocatePool (Length);
682 if (Ip6String == NULL) {
683 return EFI_OUT_OF_RESOURCES;
684 }
685
686 Status = UriPercentDecode (
687 Ptr + 1,
688 Length - 2,
689 Ip6String,
690 &ResultLength
691 );
692 if (EFI_ERROR (Status)) {
693 return Status;
694 }
695
696 Ip6String[ResultLength] = '\0';
697 Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);
698 FreePool (Ip6String);
699
700 return Status;
701 }
702
703 /**
704 Get the port number from a HTTP URL.
705
706 This function will return the port number according to the Url and previous parse result.
707
708 @param[in] Url The pointer to a HTTP URL string.
709 @param[in] UrlParser URL Parse result returned by NetHttpParseUrl().
710 @param[out] Port Pointer to a buffer to store the port number.
711
712 @retval EFI_SUCCESS Successfully get the required component.
713 @retval EFI_INVALID_PARAMETER Uri is NULL or Port is NULL or UrlParser is invalid.
714 @retval EFI_NOT_FOUND No port number in the URL.
715 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
716
717 **/
718 EFI_STATUS
719 EFIAPI
720 HttpUrlGetPort (
721 IN CHAR8 *Url,
722 IN VOID *UrlParser,
723 OUT UINT16 *Port
724 )
725 {
726 CHAR8 *PortString;
727 EFI_STATUS Status;
728 UINT32 ResultLength;
729 HTTP_URL_PARSER *Parser;
730
731 if (Url == NULL || UrlParser == NULL || Port == NULL) {
732 return EFI_INVALID_PARAMETER;
733 }
734
735 Parser = (HTTP_URL_PARSER*) UrlParser;
736
737 if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {
738 return EFI_INVALID_PARAMETER;
739 }
740
741 PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1);
742 if (PortString == NULL) {
743 return EFI_OUT_OF_RESOURCES;
744 }
745
746 Status = UriPercentDecode (
747 Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset,
748 Parser->FieldData[HTTP_URI_FIELD_PORT].Length,
749 PortString,
750 &ResultLength
751 );
752 if (EFI_ERROR (Status)) {
753 return Status;
754 }
755
756 PortString[ResultLength] = '\0';
757 *Port = (UINT16) AsciiStrDecimalToUintn (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset);
758
759 return EFI_SUCCESS;
760 }
761
762 /**
763 Release the resource of the URL parser.
764
765 @param[in] UrlParser Pointer to the parser.
766
767 **/
768 VOID
769 EFIAPI
770 HttpUrlFreeParser (
771 IN VOID *UrlParser
772 )
773 {
774 FreePool (UrlParser);
775 }
776
777 /**
778 Find a specified header field according to the field name.
779
780 @param[in] HeaderCount Number of HTTP header structures in Headers list.
781 @param[in] Headers Array containing list of HTTP headers.
782 @param[in] FieldName Null terminated string which describes a field name.
783
784 @return Pointer to the found header or NULL.
785
786 **/
787 EFI_HTTP_HEADER *
788 HttpIoFindHeader (
789 IN UINTN HeaderCount,
790 IN EFI_HTTP_HEADER *Headers,
791 IN CHAR8 *FieldName
792 )
793 {
794 UINTN Index;
795
796 if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {
797 return NULL;
798 }
799
800 for (Index = 0; Index < HeaderCount; Index++){
801 //
802 // Field names are case-insensitive (RFC 2616).
803 //
804 if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
805 return &Headers[Index];
806 }
807 }
808 return NULL;
809 }
810
811 typedef enum {
812 BodyParserBodyStart,
813 BodyParserBodyIdentity,
814 BodyParserChunkSizeStart,
815 BodyParserChunkSize,
816 BodyParserChunkSizeEndCR,
817 BodyParserChunkExtStart,
818 BodyParserChunkDataStart,
819 BodyParserChunkDataEnd,
820 BodyParserChunkDataEndCR,
821 BodyParserComplete,
822 BodyParserStateMax
823 } HTTP_BODY_PARSE_STATE;
824
825 typedef struct {
826 BOOLEAN IgnoreBody; // "MUST NOT" include a message-body
827 BOOLEAN IsChunked; // "chunked" transfer-coding.
828 BOOLEAN ContentLengthIsValid;
829 UINTN ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE
830
831 HTTP_BODY_PARSER_CALLBACK Callback;
832 VOID *Context;
833 UINTN ParsedBodyLength;
834 HTTP_BODY_PARSE_STATE State;
835 UINTN CurrentChunkSize;
836 UINTN CurrentChunkParsedSize;
837 } HTTP_BODY_PARSER;
838
839 /**
840
841 Convert an Ascii char to its uppercase.
842
843 @param[in] Char Ascii character.
844
845 @return Uppercase value of the input Char.
846
847 **/
848 CHAR8
849 HttpIoCharToUpper (
850 IN CHAR8 Char
851 )
852 {
853 if (Char >= 'a' && Char <= 'z') {
854 return Char - ('a' - 'A');
855 }
856
857 return Char;
858 }
859
860 /**
861 Convert an hexadecimal char to a value of type UINTN.
862
863 @param[in] Char Ascii character.
864
865 @return Value translated from Char.
866
867 **/
868 UINTN
869 HttpIoHexCharToUintn (
870 IN CHAR8 Char
871 )
872 {
873 if (Char >= '0' && Char <= '9') {
874 return Char - '0';
875 }
876
877 return (10 + HttpIoCharToUpper (Char) - 'A');
878 }
879
880 /**
881 Get the value of the content length if there is a "Content-Length" header.
882
883 @param[in] HeaderCount Number of HTTP header structures in Headers.
884 @param[in] Headers Array containing list of HTTP headers.
885 @param[out] ContentLength Pointer to save the value of the content length.
886
887 @retval EFI_SUCCESS Successfully get the content length.
888 @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
889
890 **/
891 EFI_STATUS
892 HttpIoParseContentLengthHeader (
893 IN UINTN HeaderCount,
894 IN EFI_HTTP_HEADER *Headers,
895 OUT UINTN *ContentLength
896 )
897 {
898 EFI_HTTP_HEADER *Header;
899
900 Header = HttpIoFindHeader (HeaderCount, Headers, "Content-Length");
901 if (Header == NULL) {
902 return EFI_NOT_FOUND;
903 }
904
905 *ContentLength = AsciiStrDecimalToUintn (Header->FieldValue);
906 return EFI_SUCCESS;
907 }
908
909 /**
910
911 Check whether the HTTP message is using the "chunked" transfer-coding.
912
913 @param[in] HeaderCount Number of HTTP header structures in Headers.
914 @param[in] Headers Array containing list of HTTP headers.
915
916 @return The message is "chunked" transfer-coding (TRUE) or not (FALSE).
917
918 **/
919 BOOLEAN
920 HttpIoIsChunked (
921 IN UINTN HeaderCount,
922 IN EFI_HTTP_HEADER *Headers
923 )
924 {
925 EFI_HTTP_HEADER *Header;
926
927
928 Header = HttpIoFindHeader (HeaderCount, Headers, "Transfer-Encoding");
929 if (Header == NULL) {
930 return FALSE;
931 }
932
933 if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {
934 return TRUE;
935 }
936
937 return FALSE;
938 }
939
940 /**
941 Check whether the HTTP message should have a message-body.
942
943 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
944 @param[in] StatusCode Response status code returned by the remote host.
945
946 @return The message should have a message-body (FALSE) or not (TRUE).
947
948 **/
949 BOOLEAN
950 HttpIoNoMessageBody (
951 IN EFI_HTTP_METHOD Method,
952 IN EFI_HTTP_STATUS_CODE StatusCode
953 )
954 {
955 //
956 // RFC 2616:
957 // All responses to the HEAD request method
958 // MUST NOT include a message-body, even though the presence of entity-
959 // header fields might lead one to believe they do. All 1xx
960 // (informational), 204 (no content), and 304 (not modified) responses
961 // MUST NOT include a message-body. All other responses do include a
962 // message-body, although it MAY be of zero length.
963 //
964 if (Method == HttpMethodHead) {
965 return TRUE;
966 }
967
968 if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||
969 (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||
970 (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||
971 (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))
972 {
973 return TRUE;
974 }
975
976 return FALSE;
977 }
978
979 /**
980 Initialize a HTTP message-body parser.
981
982 This function will create and initialize a HTTP message parser according to caller provided HTTP message
983 header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().
984
985 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
986 @param[in] StatusCode Response status code returned by the remote host.
987 @param[in] HeaderCount Number of HTTP header structures in Headers.
988 @param[in] Headers Array containing list of HTTP headers.
989 @param[in] Callback Callback function that is invoked when parsing the HTTP message-body,
990 set to NULL to ignore all events.
991 @param[in] Context Pointer to the context that will be passed to Callback.
992 @param[out] MsgParser Pointer to the returned buffer to store the message parser.
993
994 @retval EFI_SUCCESS Successfully initialized the parser.
995 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
996 @retval EFI_INVALID_PARAMETER MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.
997 @retval Others Failed to initialize the parser.
998
999 **/
1000 EFI_STATUS
1001 EFIAPI
1002 HttpInitMsgParser (
1003 IN EFI_HTTP_METHOD Method,
1004 IN EFI_HTTP_STATUS_CODE StatusCode,
1005 IN UINTN HeaderCount,
1006 IN EFI_HTTP_HEADER *Headers,
1007 IN HTTP_BODY_PARSER_CALLBACK Callback,
1008 IN VOID *Context,
1009 OUT VOID **MsgParser
1010 )
1011 {
1012 EFI_STATUS Status;
1013 HTTP_BODY_PARSER *Parser;
1014
1015 if (HeaderCount != 0 && Headers == NULL) {
1016 return EFI_INVALID_PARAMETER;
1017 }
1018
1019 if (MsgParser == NULL) {
1020 return EFI_INVALID_PARAMETER;
1021 }
1022
1023 Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));
1024 if (Parser == NULL) {
1025 return EFI_OUT_OF_RESOURCES;
1026 }
1027
1028 Parser->State = BodyParserBodyStart;
1029
1030 //
1031 // Determine the message length accroding to RFC 2616.
1032 // 1. Check whether the message "MUST NOT" have a message-body.
1033 //
1034 Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);
1035 //
1036 // 2. Check whether the message using "chunked" transfer-coding.
1037 //
1038 Parser->IsChunked = HttpIoIsChunked (HeaderCount, Headers);
1039 //
1040 // 3. Check whether the message has a Content-Length header field.
1041 //
1042 Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);
1043 if (!EFI_ERROR (Status)) {
1044 Parser->ContentLengthIsValid = TRUE;
1045 }
1046 //
1047 // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".
1048 // 5. By server closing the connection
1049 //
1050
1051 //
1052 // Set state to skip body parser if the message shouldn't have a message body.
1053 //
1054 if (Parser->IgnoreBody) {
1055 Parser->State = BodyParserComplete;
1056 } else {
1057 Parser->Callback = Callback;
1058 Parser->Context = Context;
1059 }
1060
1061 *MsgParser = Parser;
1062 return EFI_SUCCESS;
1063 }
1064
1065 /**
1066 Parse message body.
1067
1068 Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.
1069
1070 @param[in, out] MsgParser Pointer to the message parser.
1071 @param[in] BodyLength Length in bytes of the Body.
1072 @param[in] Body Pointer to the buffer of the message-body to be parsed.
1073
1074 @retval EFI_SUCCESS Successfully parse the message-body.
1075 @retval EFI_INVALID_PARAMETER MsgParser is NULL or Body is NULL or BodyLength is 0.
1076 @retval Others Operation aborted.
1077
1078 **/
1079 EFI_STATUS
1080 EFIAPI
1081 HttpParseMessageBody (
1082 IN OUT VOID *MsgParser,
1083 IN UINTN BodyLength,
1084 IN CHAR8 *Body
1085 )
1086 {
1087 CHAR8 *Char;
1088 UINTN RemainderLengthInThis;
1089 UINTN LengthForCallback;
1090 EFI_STATUS Status;
1091 HTTP_BODY_PARSER *Parser;
1092
1093 if (BodyLength == 0 || Body == NULL) {
1094 return EFI_INVALID_PARAMETER;
1095 }
1096
1097 if (MsgParser == NULL) {
1098 return EFI_INVALID_PARAMETER;
1099 }
1100
1101 Parser = (HTTP_BODY_PARSER*) MsgParser;
1102
1103 if (Parser->IgnoreBody) {
1104 Parser->State = BodyParserComplete;
1105 if (Parser->Callback != NULL) {
1106 Status = Parser->Callback (
1107 BodyParseEventOnComplete,
1108 Body,
1109 0,
1110 Parser->Context
1111 );
1112 if (EFI_ERROR (Status)) {
1113 return Status;
1114 }
1115 }
1116 return EFI_SUCCESS;
1117 }
1118
1119 if (Parser->State == BodyParserBodyStart) {
1120 Parser->ParsedBodyLength = 0;
1121 if (Parser->IsChunked) {
1122 Parser->State = BodyParserChunkSizeStart;
1123 } else {
1124 Parser->State = BodyParserBodyIdentity;
1125 }
1126 }
1127
1128 //
1129 // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.
1130 //
1131 for (Char = Body; Char < Body + BodyLength; ) {
1132
1133 switch (Parser->State) {
1134 case BodyParserStateMax:
1135 return EFI_ABORTED;
1136
1137 case BodyParserComplete:
1138 if (Parser->Callback != NULL) {
1139 Status = Parser->Callback (
1140 BodyParseEventOnComplete,
1141 Char,
1142 0,
1143 Parser->Context
1144 );
1145 if (EFI_ERROR (Status)) {
1146 return Status;
1147 }
1148 }
1149 return EFI_SUCCESS;
1150
1151 case BodyParserBodyIdentity:
1152 //
1153 // Identity transfer-coding, just notify user to save the body data.
1154 //
1155 if (Parser->Callback != NULL) {
1156 Status = Parser->Callback (
1157 BodyParseEventOnData,
1158 Char,
1159 MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength),
1160 Parser->Context
1161 );
1162 if (EFI_ERROR (Status)) {
1163 return Status;
1164 }
1165 }
1166 Char += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
1167 Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
1168 if (Parser->ParsedBodyLength == Parser->ContentLength) {
1169 Parser->State = BodyParserComplete;
1170 }
1171 break;
1172
1173 case BodyParserChunkSizeStart:
1174 //
1175 // First byte of chunk-size, the chunk-size might be truncated.
1176 //
1177 Parser->CurrentChunkSize = 0;
1178 Parser->State = BodyParserChunkSize;
1179 case BodyParserChunkSize:
1180 if (!NET_IS_HEX_CHAR (*Char)) {
1181 if (*Char == ';') {
1182 Parser->State = BodyParserChunkExtStart;
1183 Char++;
1184 } else if (*Char == '\r') {
1185 Parser->State = BodyParserChunkSizeEndCR;
1186 Char++;
1187 } else {
1188 Parser->State = BodyParserStateMax;
1189 }
1190 break;
1191 }
1192
1193 if (Parser->CurrentChunkSize > (((~((UINTN) 0)) - 16) / 16)) {
1194 return EFI_INVALID_PARAMETER;
1195 }
1196 Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);
1197 Char++;
1198 break;
1199
1200 case BodyParserChunkExtStart:
1201 //
1202 // Ignore all the chunk extensions.
1203 //
1204 if (*Char == '\r') {
1205 Parser->State = BodyParserChunkSizeEndCR;
1206 }
1207 Char++;
1208 break;
1209
1210 case BodyParserChunkSizeEndCR:
1211 if (*Char != '\n') {
1212 Parser->State = BodyParserStateMax;
1213 break;
1214 }
1215 Parser->State = BodyParserChunkDataStart;
1216 Parser->CurrentChunkParsedSize = 0;
1217 Char++;
1218 break;
1219
1220 case BodyParserChunkDataStart:
1221 if (Parser->CurrentChunkSize == 0) {
1222 //
1223 // This is the last chunk, the trailer header is unsupported.
1224 //
1225 Parser->ContentLengthIsValid = TRUE;
1226 Parser->State = BodyParserComplete;
1227 break;
1228 }
1229
1230 //
1231 // First byte of chunk-data, the chunk data also might be truncated.
1232 //
1233 RemainderLengthInThis = BodyLength - (Char - Body);
1234 LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);
1235 if (Parser->Callback != NULL) {
1236 Status = Parser->Callback (
1237 BodyParseEventOnData,
1238 Char,
1239 LengthForCallback,
1240 Parser->Context
1241 );
1242 if (EFI_ERROR (Status)) {
1243 return Status;
1244 }
1245 }
1246 Char += LengthForCallback;
1247 Parser->ContentLength += LengthForCallback;
1248 Parser->CurrentChunkParsedSize += LengthForCallback;
1249 if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {
1250 Parser->State = BodyParserChunkDataEnd;
1251 }
1252 break;
1253
1254 case BodyParserChunkDataEnd:
1255 if (*Char == '\r') {
1256 Parser->State = BodyParserChunkDataEndCR;
1257 } else {
1258 Parser->State = BodyParserStateMax;
1259 }
1260 Char++;
1261 break;
1262
1263 case BodyParserChunkDataEndCR:
1264 if (*Char != '\n') {
1265 Parser->State = BodyParserStateMax;
1266 break;
1267 }
1268 Char++;
1269 Parser->State = BodyParserChunkSizeStart;
1270 break;
1271
1272 default:
1273 break;
1274 }
1275
1276 }
1277
1278 if (Parser->State == BodyParserStateMax) {
1279 return EFI_ABORTED;
1280 }
1281
1282 return EFI_SUCCESS;
1283 }
1284
1285 /**
1286 Check whether the message-body is complete or not.
1287
1288 @param[in] MsgParser Pointer to the message parser.
1289
1290 @retval TRUE Message-body is complete.
1291 @retval FALSE Message-body is not complete.
1292
1293 **/
1294 BOOLEAN
1295 EFIAPI
1296 HttpIsMessageComplete (
1297 IN VOID *MsgParser
1298 )
1299 {
1300 HTTP_BODY_PARSER *Parser;
1301
1302 Parser = (HTTP_BODY_PARSER*) MsgParser;
1303
1304 if (Parser->State == BodyParserComplete) {
1305 return TRUE;
1306 }
1307 return FALSE;
1308 }
1309
1310 /**
1311 Get the content length of the entity.
1312
1313 Note that in trunk transfer, the entity length is not valid until the whole message body is received.
1314
1315 @param[in] MsgParser Pointer to the message parser.
1316 @param[out] ContentLength Pointer to store the length of the entity.
1317
1318 @retval EFI_SUCCESS Successfully to get the entity length.
1319 @retval EFI_NOT_READY Entity length is not valid yet.
1320 @retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL.
1321
1322 **/
1323 EFI_STATUS
1324 EFIAPI
1325 HttpGetEntityLength (
1326 IN VOID *MsgParser,
1327 OUT UINTN *ContentLength
1328 )
1329 {
1330 HTTP_BODY_PARSER *Parser;
1331
1332 if (MsgParser == NULL || ContentLength == NULL) {
1333 return EFI_INVALID_PARAMETER;
1334 }
1335
1336 Parser = (HTTP_BODY_PARSER*) MsgParser;
1337
1338 if (!Parser->ContentLengthIsValid) {
1339 return EFI_NOT_READY;
1340 }
1341
1342 *ContentLength = Parser->ContentLength;
1343 return EFI_SUCCESS;
1344 }
1345
1346 /**
1347 Release the resource of the message parser.
1348
1349 @param[in] MsgParser Pointer to the message parser.
1350
1351 **/
1352 VOID
1353 EFIAPI
1354 HttpFreeMsgParser (
1355 IN VOID *MsgParser
1356 )
1357 {
1358 FreePool (MsgParser);
1359 }