]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.c
MdeModulePkg: Convert the UNIX to DOS end of line format
[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 BodyParserTrailer,
822 BodyParserLastCRLF,
823 BodyParserLastCRLFEnd,
824 BodyParserComplete,
825 BodyParserStateMax
826 } HTTP_BODY_PARSE_STATE;
827
828 typedef struct {
829 BOOLEAN IgnoreBody; // "MUST NOT" include a message-body
830 BOOLEAN IsChunked; // "chunked" transfer-coding.
831 BOOLEAN ContentLengthIsValid;
832 UINTN ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE
833
834 HTTP_BODY_PARSER_CALLBACK Callback;
835 VOID *Context;
836 UINTN ParsedBodyLength;
837 HTTP_BODY_PARSE_STATE State;
838 UINTN CurrentChunkSize;
839 UINTN CurrentChunkParsedSize;
840 } HTTP_BODY_PARSER;
841
842 /**
843
844 Convert an Ascii char to its uppercase.
845
846 @param[in] Char Ascii character.
847
848 @return Uppercase value of the input Char.
849
850 **/
851 CHAR8
852 HttpIoCharToUpper (
853 IN CHAR8 Char
854 )
855 {
856 if (Char >= 'a' && Char <= 'z') {
857 return Char - ('a' - 'A');
858 }
859
860 return Char;
861 }
862
863 /**
864 Convert an hexadecimal char to a value of type UINTN.
865
866 @param[in] Char Ascii character.
867
868 @return Value translated from Char.
869
870 **/
871 UINTN
872 HttpIoHexCharToUintn (
873 IN CHAR8 Char
874 )
875 {
876 if (Char >= '0' && Char <= '9') {
877 return Char - '0';
878 }
879
880 return (10 + HttpIoCharToUpper (Char) - 'A');
881 }
882
883 /**
884 Get the value of the content length if there is a "Content-Length" header.
885
886 @param[in] HeaderCount Number of HTTP header structures in Headers.
887 @param[in] Headers Array containing list of HTTP headers.
888 @param[out] ContentLength Pointer to save the value of the content length.
889
890 @retval EFI_SUCCESS Successfully get the content length.
891 @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
892
893 **/
894 EFI_STATUS
895 HttpIoParseContentLengthHeader (
896 IN UINTN HeaderCount,
897 IN EFI_HTTP_HEADER *Headers,
898 OUT UINTN *ContentLength
899 )
900 {
901 EFI_HTTP_HEADER *Header;
902
903 Header = HttpIoFindHeader (HeaderCount, Headers, "Content-Length");
904 if (Header == NULL) {
905 return EFI_NOT_FOUND;
906 }
907
908 *ContentLength = AsciiStrDecimalToUintn (Header->FieldValue);
909 return EFI_SUCCESS;
910 }
911
912 /**
913
914 Check whether the HTTP message is using the "chunked" transfer-coding.
915
916 @param[in] HeaderCount Number of HTTP header structures in Headers.
917 @param[in] Headers Array containing list of HTTP headers.
918
919 @return The message is "chunked" transfer-coding (TRUE) or not (FALSE).
920
921 **/
922 BOOLEAN
923 HttpIoIsChunked (
924 IN UINTN HeaderCount,
925 IN EFI_HTTP_HEADER *Headers
926 )
927 {
928 EFI_HTTP_HEADER *Header;
929
930
931 Header = HttpIoFindHeader (HeaderCount, Headers, "Transfer-Encoding");
932 if (Header == NULL) {
933 return FALSE;
934 }
935
936 if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {
937 return TRUE;
938 }
939
940 return FALSE;
941 }
942
943 /**
944 Check whether the HTTP message should have a message-body.
945
946 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
947 @param[in] StatusCode Response status code returned by the remote host.
948
949 @return The message should have a message-body (FALSE) or not (TRUE).
950
951 **/
952 BOOLEAN
953 HttpIoNoMessageBody (
954 IN EFI_HTTP_METHOD Method,
955 IN EFI_HTTP_STATUS_CODE StatusCode
956 )
957 {
958 //
959 // RFC 2616:
960 // All responses to the HEAD request method
961 // MUST NOT include a message-body, even though the presence of entity-
962 // header fields might lead one to believe they do. All 1xx
963 // (informational), 204 (no content), and 304 (not modified) responses
964 // MUST NOT include a message-body. All other responses do include a
965 // message-body, although it MAY be of zero length.
966 //
967 if (Method == HttpMethodHead) {
968 return TRUE;
969 }
970
971 if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||
972 (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||
973 (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||
974 (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))
975 {
976 return TRUE;
977 }
978
979 return FALSE;
980 }
981
982 /**
983 Initialize a HTTP message-body parser.
984
985 This function will create and initialize a HTTP message parser according to caller provided HTTP message
986 header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().
987
988 @param[in] Method The HTTP method (e.g. GET, POST) for this HTTP message.
989 @param[in] StatusCode Response status code returned by the remote host.
990 @param[in] HeaderCount Number of HTTP header structures in Headers.
991 @param[in] Headers Array containing list of HTTP headers.
992 @param[in] Callback Callback function that is invoked when parsing the HTTP message-body,
993 set to NULL to ignore all events.
994 @param[in] Context Pointer to the context that will be passed to Callback.
995 @param[out] MsgParser Pointer to the returned buffer to store the message parser.
996
997 @retval EFI_SUCCESS Successfully initialized the parser.
998 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
999 @retval EFI_INVALID_PARAMETER MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.
1000 @retval Others Failed to initialize the parser.
1001
1002 **/
1003 EFI_STATUS
1004 EFIAPI
1005 HttpInitMsgParser (
1006 IN EFI_HTTP_METHOD Method,
1007 IN EFI_HTTP_STATUS_CODE StatusCode,
1008 IN UINTN HeaderCount,
1009 IN EFI_HTTP_HEADER *Headers,
1010 IN HTTP_BODY_PARSER_CALLBACK Callback,
1011 IN VOID *Context,
1012 OUT VOID **MsgParser
1013 )
1014 {
1015 EFI_STATUS Status;
1016 HTTP_BODY_PARSER *Parser;
1017
1018 if (HeaderCount != 0 && Headers == NULL) {
1019 return EFI_INVALID_PARAMETER;
1020 }
1021
1022 if (MsgParser == NULL) {
1023 return EFI_INVALID_PARAMETER;
1024 }
1025
1026 Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));
1027 if (Parser == NULL) {
1028 return EFI_OUT_OF_RESOURCES;
1029 }
1030
1031 Parser->State = BodyParserBodyStart;
1032
1033 //
1034 // Determine the message length accroding to RFC 2616.
1035 // 1. Check whether the message "MUST NOT" have a message-body.
1036 //
1037 Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);
1038 //
1039 // 2. Check whether the message using "chunked" transfer-coding.
1040 //
1041 Parser->IsChunked = HttpIoIsChunked (HeaderCount, Headers);
1042 //
1043 // 3. Check whether the message has a Content-Length header field.
1044 //
1045 Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);
1046 if (!EFI_ERROR (Status)) {
1047 Parser->ContentLengthIsValid = TRUE;
1048 }
1049 //
1050 // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".
1051 // 5. By server closing the connection
1052 //
1053
1054 //
1055 // Set state to skip body parser if the message shouldn't have a message body.
1056 //
1057 if (Parser->IgnoreBody) {
1058 Parser->State = BodyParserComplete;
1059 } else {
1060 Parser->Callback = Callback;
1061 Parser->Context = Context;
1062 }
1063
1064 *MsgParser = Parser;
1065 return EFI_SUCCESS;
1066 }
1067
1068 /**
1069 Parse message body.
1070
1071 Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.
1072
1073 @param[in, out] MsgParser Pointer to the message parser.
1074 @param[in] BodyLength Length in bytes of the Body.
1075 @param[in] Body Pointer to the buffer of the message-body to be parsed.
1076
1077 @retval EFI_SUCCESS Successfully parse the message-body.
1078 @retval EFI_INVALID_PARAMETER MsgParser is NULL or Body is NULL or BodyLength is 0.
1079 @retval Others Operation aborted.
1080
1081 **/
1082 EFI_STATUS
1083 EFIAPI
1084 HttpParseMessageBody (
1085 IN OUT VOID *MsgParser,
1086 IN UINTN BodyLength,
1087 IN CHAR8 *Body
1088 )
1089 {
1090 CHAR8 *Char;
1091 UINTN RemainderLengthInThis;
1092 UINTN LengthForCallback;
1093 EFI_STATUS Status;
1094 HTTP_BODY_PARSER *Parser;
1095
1096 if (BodyLength == 0 || Body == NULL) {
1097 return EFI_INVALID_PARAMETER;
1098 }
1099
1100 if (MsgParser == NULL) {
1101 return EFI_INVALID_PARAMETER;
1102 }
1103
1104 Parser = (HTTP_BODY_PARSER*) MsgParser;
1105
1106 if (Parser->IgnoreBody) {
1107 Parser->State = BodyParserComplete;
1108 if (Parser->Callback != NULL) {
1109 Status = Parser->Callback (
1110 BodyParseEventOnComplete,
1111 Body,
1112 0,
1113 Parser->Context
1114 );
1115 if (EFI_ERROR (Status)) {
1116 return Status;
1117 }
1118 }
1119 return EFI_SUCCESS;
1120 }
1121
1122 if (Parser->State == BodyParserBodyStart) {
1123 Parser->ParsedBodyLength = 0;
1124 if (Parser->IsChunked) {
1125 Parser->State = BodyParserChunkSizeStart;
1126 } else {
1127 Parser->State = BodyParserBodyIdentity;
1128 }
1129 }
1130
1131 //
1132 // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.
1133 //
1134 for (Char = Body; Char < Body + BodyLength; ) {
1135
1136 switch (Parser->State) {
1137 case BodyParserStateMax:
1138 return EFI_ABORTED;
1139
1140 case BodyParserComplete:
1141 if (Parser->Callback != NULL) {
1142 Status = Parser->Callback (
1143 BodyParseEventOnComplete,
1144 Char,
1145 0,
1146 Parser->Context
1147 );
1148 if (EFI_ERROR (Status)) {
1149 return Status;
1150 }
1151 }
1152 return EFI_SUCCESS;
1153
1154 case BodyParserBodyIdentity:
1155 //
1156 // Identity transfer-coding, just notify user to save the body data.
1157 //
1158 if (Parser->Callback != NULL) {
1159 Status = Parser->Callback (
1160 BodyParseEventOnData,
1161 Char,
1162 MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength),
1163 Parser->Context
1164 );
1165 if (EFI_ERROR (Status)) {
1166 return Status;
1167 }
1168 }
1169 Char += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
1170 Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
1171 if (Parser->ParsedBodyLength == Parser->ContentLength) {
1172 Parser->State = BodyParserComplete;
1173 }
1174 break;
1175
1176 case BodyParserChunkSizeStart:
1177 //
1178 // First byte of chunk-size, the chunk-size might be truncated.
1179 //
1180 Parser->CurrentChunkSize = 0;
1181 Parser->State = BodyParserChunkSize;
1182 case BodyParserChunkSize:
1183 if (!NET_IS_HEX_CHAR (*Char)) {
1184 if (*Char == ';') {
1185 Parser->State = BodyParserChunkExtStart;
1186 Char++;
1187 } else if (*Char == '\r') {
1188 Parser->State = BodyParserChunkSizeEndCR;
1189 Char++;
1190 } else {
1191 Parser->State = BodyParserStateMax;
1192 }
1193 break;
1194 }
1195
1196 if (Parser->CurrentChunkSize > (((~((UINTN) 0)) - 16) / 16)) {
1197 return EFI_INVALID_PARAMETER;
1198 }
1199 Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);
1200 Char++;
1201 break;
1202
1203 case BodyParserChunkExtStart:
1204 //
1205 // Ignore all the chunk extensions.
1206 //
1207 if (*Char == '\r') {
1208 Parser->State = BodyParserChunkSizeEndCR;
1209 }
1210 Char++;
1211 break;
1212
1213 case BodyParserChunkSizeEndCR:
1214 if (*Char != '\n') {
1215 Parser->State = BodyParserStateMax;
1216 break;
1217 }
1218 Char++;
1219 if (Parser->CurrentChunkSize == 0) {
1220 //
1221 // The last chunk has been parsed and now assumed the state
1222 // of HttpBodyParse is ParserLastCRLF. So it need to decide
1223 // whether the rest message is trailer or last CRLF in the next round.
1224 //
1225 Parser->ContentLengthIsValid = TRUE;
1226 Parser->State = BodyParserLastCRLF;
1227 break;
1228 }
1229 Parser->State = BodyParserChunkDataStart;
1230 Parser->CurrentChunkParsedSize = 0;
1231 break;
1232
1233 case BodyParserLastCRLF:
1234 //
1235 // Judge the byte is belong to the Last CRLF or trailer, and then
1236 // configure the state of HttpBodyParse to corresponding state.
1237 //
1238 if (*Char == '\r') {
1239 Char++;
1240 Parser->State = BodyParserLastCRLFEnd;
1241 break;
1242 } else {
1243 Parser->State = BodyParserTrailer;
1244 break;
1245 }
1246
1247 case BodyParserLastCRLFEnd:
1248 if (*Char == '\n') {
1249 Parser->State = BodyParserComplete;
1250 break;
1251 } else {
1252 Parser->State = BodyParserStateMax;
1253 break;
1254 }
1255
1256 case BodyParserTrailer:
1257 if (*Char == '\r') {
1258 Parser->State = BodyParserChunkSizeEndCR;
1259 }
1260 Char++;
1261 break;
1262
1263 case BodyParserChunkDataStart:
1264 //
1265 // First byte of chunk-data, the chunk data also might be truncated.
1266 //
1267 RemainderLengthInThis = BodyLength - (Char - Body);
1268 LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);
1269 if (Parser->Callback != NULL) {
1270 Status = Parser->Callback (
1271 BodyParseEventOnData,
1272 Char,
1273 LengthForCallback,
1274 Parser->Context
1275 );
1276 if (EFI_ERROR (Status)) {
1277 return Status;
1278 }
1279 }
1280 Char += LengthForCallback;
1281 Parser->ContentLength += LengthForCallback;
1282 Parser->CurrentChunkParsedSize += LengthForCallback;
1283 if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {
1284 Parser->State = BodyParserChunkDataEnd;
1285 }
1286 break;
1287
1288 case BodyParserChunkDataEnd:
1289 if (*Char == '\r') {
1290 Parser->State = BodyParserChunkDataEndCR;
1291 } else {
1292 Parser->State = BodyParserStateMax;
1293 }
1294 Char++;
1295 break;
1296
1297 case BodyParserChunkDataEndCR:
1298 if (*Char != '\n') {
1299 Parser->State = BodyParserStateMax;
1300 break;
1301 }
1302 Char++;
1303 Parser->State = BodyParserChunkSizeStart;
1304 break;
1305
1306 default:
1307 break;
1308 }
1309
1310 }
1311
1312 if (Parser->State == BodyParserStateMax) {
1313 return EFI_ABORTED;
1314 }
1315
1316 return EFI_SUCCESS;
1317 }
1318
1319 /**
1320 Check whether the message-body is complete or not.
1321
1322 @param[in] MsgParser Pointer to the message parser.
1323
1324 @retval TRUE Message-body is complete.
1325 @retval FALSE Message-body is not complete.
1326
1327 **/
1328 BOOLEAN
1329 EFIAPI
1330 HttpIsMessageComplete (
1331 IN VOID *MsgParser
1332 )
1333 {
1334 HTTP_BODY_PARSER *Parser;
1335
1336 Parser = (HTTP_BODY_PARSER*) MsgParser;
1337
1338 if (Parser->State == BodyParserComplete) {
1339 return TRUE;
1340 }
1341 return FALSE;
1342 }
1343
1344 /**
1345 Get the content length of the entity.
1346
1347 Note that in trunk transfer, the entity length is not valid until the whole message body is received.
1348
1349 @param[in] MsgParser Pointer to the message parser.
1350 @param[out] ContentLength Pointer to store the length of the entity.
1351
1352 @retval EFI_SUCCESS Successfully to get the entity length.
1353 @retval EFI_NOT_READY Entity length is not valid yet.
1354 @retval EFI_INVALID_PARAMETER MsgParser is NULL or ContentLength is NULL.
1355
1356 **/
1357 EFI_STATUS
1358 EFIAPI
1359 HttpGetEntityLength (
1360 IN VOID *MsgParser,
1361 OUT UINTN *ContentLength
1362 )
1363 {
1364 HTTP_BODY_PARSER *Parser;
1365
1366 if (MsgParser == NULL || ContentLength == NULL) {
1367 return EFI_INVALID_PARAMETER;
1368 }
1369
1370 Parser = (HTTP_BODY_PARSER*) MsgParser;
1371
1372 if (!Parser->ContentLengthIsValid) {
1373 return EFI_NOT_READY;
1374 }
1375
1376 *ContentLength = Parser->ContentLength;
1377 return EFI_SUCCESS;
1378 }
1379
1380 /**
1381 Release the resource of the message parser.
1382
1383 @param[in] MsgParser Pointer to the message parser.
1384
1385 **/
1386 VOID
1387 EFIAPI
1388 HttpFreeMsgParser (
1389 IN VOID *MsgParser
1390 )
1391 {
1392 FreePool (MsgParser);
1393 }