• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 - 2016, 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
UriPercentDecode(IN CHAR8 * Buffer,IN UINT32 BufferLength,OUT CHAR8 * ResultBuffer,OUT UINT32 * ResultLength)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
NetHttpParseAuthorityChar(IN CHAR8 Char,IN HTTP_URL_PARSE_STATE State,IN BOOLEAN * IsRightBracket)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
NetHttpParseAuthority(IN CHAR8 * Url,IN BOOLEAN FoundAt,IN OUT HTTP_URL_PARSER * UrlParser)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
NetHttpParseUrlChar(IN CHAR8 Char,IN HTTP_URL_PARSE_STATE 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
HttpParseUrl(IN CHAR8 * Url,IN UINT32 Length,IN BOOLEAN IsConnectMethod,OUT VOID ** UrlParser)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 accoring 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
HttpUrlGetHostName(IN CHAR8 * Url,IN VOID * UrlParser,OUT CHAR8 ** HostName)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     return Status;
527   }
528 
529   Name[ResultLength] = '\0';
530   *HostName = Name;
531   return EFI_SUCCESS;
532 }
533 
534 
535 /**
536   Get the IPv4 address from a HTTP URL.
537 
538   This function will return the IPv4 address according to the Url and previous parse result.
539 
540   @param[in]    Url                The pointer to a HTTP URL string.
541   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
542   @param[out]   Ip4Address         Pointer to a buffer to store the IP address.
543 
544   @retval EFI_SUCCESS              Successfully get the required component.
545   @retval EFI_INVALID_PARAMETER    Uri is NULL or Ip4Address is NULL or UrlParser is invalid.
546   @retval EFI_NOT_FOUND            No IPv4 address component in the URL.
547   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
548 
549 **/
550 EFI_STATUS
551 EFIAPI
HttpUrlGetIp4(IN CHAR8 * Url,IN VOID * UrlParser,OUT EFI_IPv4_ADDRESS * Ip4Address)552 HttpUrlGetIp4 (
553   IN      CHAR8              *Url,
554   IN      VOID               *UrlParser,
555      OUT  EFI_IPv4_ADDRESS   *Ip4Address
556   )
557 {
558   CHAR8                *Ip4String;
559   EFI_STATUS           Status;
560   UINT32               ResultLength;
561   HTTP_URL_PARSER      *Parser;
562 
563   if (Url == NULL || UrlParser == NULL || Ip4Address == NULL) {
564     return EFI_INVALID_PARAMETER;
565   }
566 
567   Parser = (HTTP_URL_PARSER*) UrlParser;
568 
569   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
570     return EFI_INVALID_PARAMETER;
571   }
572 
573   Ip4String = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_HOST].Length + 1);
574   if (Ip4String == NULL) {
575     return EFI_OUT_OF_RESOURCES;
576   }
577 
578   Status = UriPercentDecode (
579              Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset,
580              Parser->FieldData[HTTP_URI_FIELD_HOST].Length,
581              Ip4String,
582              &ResultLength
583              );
584   if (EFI_ERROR (Status)) {
585     return Status;
586   }
587 
588   Ip4String[ResultLength] = '\0';
589   Status = NetLibAsciiStrToIp4 (Ip4String, Ip4Address);
590   FreePool (Ip4String);
591 
592   return Status;
593 }
594 
595 /**
596   Get the IPv6 address from a HTTP URL.
597 
598   This function will return the IPv6 address according to the Url and previous parse result.
599 
600   @param[in]    Url                The pointer to a HTTP URL string.
601   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
602   @param[out]   Ip6Address         Pointer to a buffer to store the IP address.
603 
604   @retval EFI_SUCCESS              Successfully get the required component.
605   @retval EFI_INVALID_PARAMETER    Uri is NULL or Ip6Address is NULL or UrlParser is invalid.
606   @retval EFI_NOT_FOUND            No IPv6 address component in the URL.
607   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
608 
609 **/
610 EFI_STATUS
611 EFIAPI
HttpUrlGetIp6(IN CHAR8 * Url,IN VOID * UrlParser,OUT EFI_IPv6_ADDRESS * Ip6Address)612 HttpUrlGetIp6 (
613   IN      CHAR8              *Url,
614   IN      VOID               *UrlParser,
615      OUT  EFI_IPv6_ADDRESS   *Ip6Address
616   )
617 {
618   CHAR8                *Ip6String;
619   CHAR8                *Ptr;
620   UINT32               Length;
621   EFI_STATUS           Status;
622   UINT32               ResultLength;
623   HTTP_URL_PARSER      *Parser;
624 
625   if (Url == NULL || UrlParser == NULL || Ip6Address == NULL) {
626     return EFI_INVALID_PARAMETER;
627   }
628 
629   Parser = (HTTP_URL_PARSER*) UrlParser;
630 
631   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_HOST)) == 0) {
632     return EFI_INVALID_PARAMETER;
633   }
634 
635   //
636   // IP-literal = "[" ( IPv6address / IPvFuture  ) "]"
637   //
638   Length = Parser->FieldData[HTTP_URI_FIELD_HOST].Length;
639   if (Length < 2) {
640     return EFI_INVALID_PARAMETER;
641   }
642 
643   Ptr    = Url + Parser->FieldData[HTTP_URI_FIELD_HOST].Offset;
644   if ((Ptr[0] != '[') || (Ptr[Length - 1] != ']')) {
645     return EFI_INVALID_PARAMETER;
646   }
647 
648   Ip6String = AllocatePool (Length);
649   if (Ip6String == NULL) {
650     return EFI_OUT_OF_RESOURCES;
651   }
652 
653   Status = UriPercentDecode (
654              Ptr + 1,
655              Length - 2,
656              Ip6String,
657              &ResultLength
658              );
659   if (EFI_ERROR (Status)) {
660     return Status;
661   }
662 
663   Ip6String[ResultLength] = '\0';
664   Status = NetLibAsciiStrToIp6 (Ip6String, Ip6Address);
665   FreePool (Ip6String);
666 
667   return Status;
668 }
669 
670 /**
671   Get the port number from a HTTP URL.
672 
673   This function will return the port number according to the Url and previous parse result.
674 
675   @param[in]    Url                The pointer to a HTTP URL string.
676   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
677   @param[out]   Port               Pointer to a buffer to store the port number.
678 
679   @retval EFI_SUCCESS              Successfully get the required component.
680   @retval EFI_INVALID_PARAMETER    Uri is NULL or Port is NULL or UrlParser is invalid.
681   @retval EFI_NOT_FOUND            No port number in the URL.
682   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
683 
684 **/
685 EFI_STATUS
686 EFIAPI
HttpUrlGetPort(IN CHAR8 * Url,IN VOID * UrlParser,OUT UINT16 * Port)687 HttpUrlGetPort (
688   IN      CHAR8              *Url,
689   IN      VOID               *UrlParser,
690      OUT  UINT16             *Port
691   )
692 {
693   CHAR8         *PortString;
694   EFI_STATUS    Status;
695   UINT32        ResultLength;
696   HTTP_URL_PARSER      *Parser;
697 
698   if (Url == NULL || UrlParser == NULL || Port == NULL) {
699     return EFI_INVALID_PARAMETER;
700   }
701 
702   Parser = (HTTP_URL_PARSER*) UrlParser;
703 
704   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PORT)) == 0) {
705     return EFI_INVALID_PARAMETER;
706   }
707 
708   PortString = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PORT].Length + 1);
709   if (PortString == NULL) {
710     return EFI_OUT_OF_RESOURCES;
711   }
712 
713   Status = UriPercentDecode (
714              Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset,
715              Parser->FieldData[HTTP_URI_FIELD_PORT].Length,
716              PortString,
717              &ResultLength
718              );
719   if (EFI_ERROR (Status)) {
720     return Status;
721   }
722 
723   PortString[ResultLength] = '\0';
724   *Port = (UINT16) AsciiStrDecimalToUintn (Url + Parser->FieldData[HTTP_URI_FIELD_PORT].Offset);
725 
726   return  EFI_SUCCESS;
727 }
728 
729 /**
730   Get the Path from a HTTP URL.
731 
732   This function will return the Path according to the Url and previous parse result,and
733   it is the caller's responsibility to free the buffer returned in *Path.
734 
735   @param[in]    Url                The pointer to a HTTP URL string.
736   @param[in]    UrlParser          URL Parse result returned by NetHttpParseUrl().
737   @param[out]   Path               Pointer to a buffer to store the Path.
738 
739   @retval EFI_SUCCESS              Successfully get the required component.
740   @retval EFI_INVALID_PARAMETER    Uri is NULL or HostName is NULL or UrlParser is invalid.
741   @retval EFI_NOT_FOUND            No hostName component in the URL.
742   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
743 
744 **/
745 EFI_STATUS
746 EFIAPI
HttpUrlGetPath(IN CHAR8 * Url,IN VOID * UrlParser,OUT CHAR8 ** Path)747 HttpUrlGetPath (
748   IN      CHAR8              *Url,
749   IN      VOID               *UrlParser,
750      OUT  CHAR8              **Path
751   )
752 {
753   CHAR8                *PathStr;
754   EFI_STATUS           Status;
755   UINT32               ResultLength;
756   HTTP_URL_PARSER      *Parser;
757 
758   if (Url == NULL || UrlParser == NULL || Path == NULL) {
759     return EFI_INVALID_PARAMETER;
760   }
761 
762   Parser = (HTTP_URL_PARSER*) UrlParser;
763 
764   if ((Parser->FieldBitMap & BIT (HTTP_URI_FIELD_PATH)) == 0) {
765     return EFI_NOT_FOUND;
766   }
767 
768   PathStr = AllocatePool (Parser->FieldData[HTTP_URI_FIELD_PATH].Length + 1);
769   if (PathStr == NULL) {
770     return EFI_OUT_OF_RESOURCES;
771   }
772 
773   Status = UriPercentDecode (
774              Url + Parser->FieldData[HTTP_URI_FIELD_PATH].Offset,
775              Parser->FieldData[HTTP_URI_FIELD_PATH].Length,
776              PathStr,
777              &ResultLength
778              );
779   if (EFI_ERROR (Status)) {
780     return Status;
781   }
782 
783   PathStr[ResultLength] = '\0';
784   *Path = PathStr;
785   return EFI_SUCCESS;
786 }
787 
788 /**
789   Release the resource of the URL parser.
790 
791   @param[in]    UrlParser            Pointer to the parser.
792 
793 **/
794 VOID
795 EFIAPI
HttpUrlFreeParser(IN VOID * UrlParser)796 HttpUrlFreeParser (
797   IN      VOID               *UrlParser
798   )
799 {
800   FreePool (UrlParser);
801 }
802 
803 /**
804   Find a specified header field according to the field name.
805 
806   @param[in]   HeaderCount      Number of HTTP header structures in Headers list.
807   @param[in]   Headers          Array containing list of HTTP headers.
808   @param[in]   FieldName        Null terminated string which describes a field name.
809 
810   @return    Pointer to the found header or NULL.
811 
812 **/
813 EFI_HTTP_HEADER *
814 EFIAPI
HttpFindHeader(IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,IN CHAR8 * FieldName)815 HttpFindHeader (
816   IN  UINTN                HeaderCount,
817   IN  EFI_HTTP_HEADER      *Headers,
818   IN  CHAR8                *FieldName
819   )
820 {
821   UINTN                 Index;
822 
823   if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {
824     return NULL;
825   }
826 
827   for (Index = 0; Index < HeaderCount; Index++){
828     //
829     // Field names are case-insensitive (RFC 2616).
830     //
831     if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
832       return &Headers[Index];
833     }
834   }
835   return NULL;
836 }
837 
838 typedef enum {
839   BodyParserBodyStart,
840   BodyParserBodyIdentity,
841   BodyParserChunkSizeStart,
842   BodyParserChunkSize,
843   BodyParserChunkSizeEndCR,
844   BodyParserChunkExtStart,
845   BodyParserChunkDataStart,
846   BodyParserChunkDataEnd,
847   BodyParserChunkDataEndCR,
848   BodyParserTrailer,
849   BodyParserLastCRLF,
850   BodyParserLastCRLFEnd,
851   BodyParserComplete,
852   BodyParserStateMax
853 } HTTP_BODY_PARSE_STATE;
854 
855 typedef struct {
856   BOOLEAN                       IgnoreBody;    // "MUST NOT" include a message-body
857   BOOLEAN                       IsChunked;     // "chunked" transfer-coding.
858   BOOLEAN                       ContentLengthIsValid;
859   UINTN                         ContentLength; // Entity length (not the message-body length), invalid until ContentLengthIsValid is TRUE
860 
861   HTTP_BODY_PARSER_CALLBACK     Callback;
862   VOID                          *Context;
863   UINTN                         ParsedBodyLength;
864   HTTP_BODY_PARSE_STATE         State;
865   UINTN                         CurrentChunkSize;
866   UINTN                         CurrentChunkParsedSize;
867 } HTTP_BODY_PARSER;
868 
869 /**
870 
871   Convert an Ascii char to its uppercase.
872 
873   @param[in]       Char           Ascii character.
874 
875   @return          Uppercase value of the input Char.
876 
877 **/
878 CHAR8
HttpIoCharToUpper(IN CHAR8 Char)879 HttpIoCharToUpper (
880   IN      CHAR8                    Char
881   )
882 {
883   if (Char >= 'a' && Char <= 'z') {
884     return  Char - ('a' - 'A');
885   }
886 
887   return Char;
888 }
889 
890 /**
891   Convert an hexadecimal char to a value of type UINTN.
892 
893   @param[in]       Char           Ascii character.
894 
895   @return          Value translated from Char.
896 
897 **/
898 UINTN
HttpIoHexCharToUintn(IN CHAR8 Char)899 HttpIoHexCharToUintn (
900   IN CHAR8           Char
901   )
902 {
903   if (Char >= '0' && Char <= '9') {
904     return Char - '0';
905   }
906 
907   return (10 + HttpIoCharToUpper (Char) - 'A');
908 }
909 
910 /**
911   Get the value of the content length if there is a "Content-Length" header.
912 
913   @param[in]    HeaderCount        Number of HTTP header structures in Headers.
914   @param[in]    Headers            Array containing list of HTTP headers.
915   @param[out]   ContentLength      Pointer to save the value of the content length.
916 
917   @retval EFI_SUCCESS              Successfully get the content length.
918   @retval EFI_NOT_FOUND            No "Content-Length" header in the Headers.
919 
920 **/
921 EFI_STATUS
HttpIoParseContentLengthHeader(IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,OUT UINTN * ContentLength)922 HttpIoParseContentLengthHeader (
923   IN     UINTN                HeaderCount,
924   IN     EFI_HTTP_HEADER      *Headers,
925      OUT UINTN                *ContentLength
926   )
927 {
928   EFI_HTTP_HEADER       *Header;
929 
930   Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);
931   if (Header == NULL) {
932     return EFI_NOT_FOUND;
933   }
934 
935   *ContentLength = AsciiStrDecimalToUintn (Header->FieldValue);
936   return EFI_SUCCESS;
937 }
938 
939 /**
940 
941   Check whether the HTTP message is using the "chunked" transfer-coding.
942 
943   @param[in]    HeaderCount        Number of HTTP header structures in Headers.
944   @param[in]    Headers            Array containing list of HTTP headers.
945 
946   @return       The message is "chunked" transfer-coding (TRUE) or not (FALSE).
947 
948 **/
949 BOOLEAN
HttpIoIsChunked(IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers)950 HttpIoIsChunked (
951   IN   UINTN                    HeaderCount,
952   IN   EFI_HTTP_HEADER          *Headers
953   )
954 {
955   EFI_HTTP_HEADER       *Header;
956 
957 
958   Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);
959   if (Header == NULL) {
960     return FALSE;
961   }
962 
963   if (AsciiStriCmp (Header->FieldValue, "identity") != 0) {
964     return TRUE;
965   }
966 
967   return FALSE;
968 }
969 
970 /**
971   Check whether the HTTP message should have a message-body.
972 
973   @param[in]    Method             The HTTP method (e.g. GET, POST) for this HTTP message.
974   @param[in]    StatusCode         Response status code returned by the remote host.
975 
976   @return       The message should have a message-body (FALSE) or not (TRUE).
977 
978 **/
979 BOOLEAN
HttpIoNoMessageBody(IN EFI_HTTP_METHOD Method,IN EFI_HTTP_STATUS_CODE StatusCode)980 HttpIoNoMessageBody (
981   IN   EFI_HTTP_METHOD          Method,
982   IN   EFI_HTTP_STATUS_CODE     StatusCode
983   )
984 {
985   //
986   // RFC 2616:
987   // All responses to the HEAD request method
988   // MUST NOT include a message-body, even though the presence of entity-
989   // header fields might lead one to believe they do. All 1xx
990   // (informational), 204 (no content), and 304 (not modified) responses
991   // MUST NOT include a message-body. All other responses do include a
992   // message-body, although it MAY be of zero length.
993   //
994   if (Method == HttpMethodHead) {
995     return TRUE;
996   }
997 
998   if ((StatusCode == HTTP_STATUS_100_CONTINUE) ||
999       (StatusCode == HTTP_STATUS_101_SWITCHING_PROTOCOLS) ||
1000       (StatusCode == HTTP_STATUS_204_NO_CONTENT) ||
1001       (StatusCode == HTTP_STATUS_304_NOT_MODIFIED))
1002   {
1003     return TRUE;
1004   }
1005 
1006   return FALSE;
1007 }
1008 
1009 /**
1010   Initialize a HTTP message-body parser.
1011 
1012   This function will create and initialize a HTTP message parser according to caller provided HTTP message
1013   header information. It is the caller's responsibility to free the buffer returned in *UrlParser by HttpFreeMsgParser().
1014 
1015   @param[in]    Method             The HTTP method (e.g. GET, POST) for this HTTP message.
1016   @param[in]    StatusCode         Response status code returned by the remote host.
1017   @param[in]    HeaderCount        Number of HTTP header structures in Headers.
1018   @param[in]    Headers            Array containing list of HTTP headers.
1019   @param[in]    Callback           Callback function that is invoked when parsing the HTTP message-body,
1020                                    set to NULL to ignore all events.
1021   @param[in]    Context            Pointer to the context that will be passed to Callback.
1022   @param[out]   MsgParser          Pointer to the returned buffer to store the message parser.
1023 
1024   @retval EFI_SUCCESS              Successfully initialized the parser.
1025   @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
1026   @retval EFI_INVALID_PARAMETER    MsgParser is NULL or HeaderCount is not NULL but Headers is NULL.
1027   @retval Others                   Failed to initialize the parser.
1028 
1029 **/
1030 EFI_STATUS
1031 EFIAPI
HttpInitMsgParser(IN EFI_HTTP_METHOD Method,IN EFI_HTTP_STATUS_CODE StatusCode,IN UINTN HeaderCount,IN EFI_HTTP_HEADER * Headers,IN HTTP_BODY_PARSER_CALLBACK Callback,IN VOID * Context,OUT VOID ** MsgParser)1032 HttpInitMsgParser (
1033   IN     EFI_HTTP_METHOD               Method,
1034   IN     EFI_HTTP_STATUS_CODE          StatusCode,
1035   IN     UINTN                         HeaderCount,
1036   IN     EFI_HTTP_HEADER               *Headers,
1037   IN     HTTP_BODY_PARSER_CALLBACK     Callback,
1038   IN     VOID                          *Context,
1039     OUT  VOID                          **MsgParser
1040   )
1041 {
1042   EFI_STATUS            Status;
1043   HTTP_BODY_PARSER      *Parser;
1044 
1045   if (HeaderCount != 0 && Headers == NULL) {
1046     return EFI_INVALID_PARAMETER;
1047   }
1048 
1049   if (MsgParser == NULL) {
1050     return EFI_INVALID_PARAMETER;
1051   }
1052 
1053   Parser = AllocateZeroPool (sizeof (HTTP_BODY_PARSER));
1054   if (Parser == NULL) {
1055     return EFI_OUT_OF_RESOURCES;
1056   }
1057 
1058   Parser->State = BodyParserBodyStart;
1059 
1060   //
1061   // Determine the message length according to RFC 2616.
1062   // 1. Check whether the message "MUST NOT" have a message-body.
1063   //
1064   Parser->IgnoreBody = HttpIoNoMessageBody (Method, StatusCode);
1065   //
1066   // 2. Check whether the message using "chunked" transfer-coding.
1067   //
1068   Parser->IsChunked  = HttpIoIsChunked (HeaderCount, Headers);
1069   //
1070   // 3. Check whether the message has a Content-Length header field.
1071   //
1072   Status = HttpIoParseContentLengthHeader (HeaderCount, Headers, &Parser->ContentLength);
1073   if (!EFI_ERROR (Status)) {
1074     Parser->ContentLengthIsValid = TRUE;
1075   }
1076   //
1077   // 4. Range header is not supported now, so we won't meet media type "multipart/byteranges".
1078   // 5. By server closing the connection
1079   //
1080 
1081   //
1082   // Set state to skip body parser if the message shouldn't have a message body.
1083   //
1084   if (Parser->IgnoreBody) {
1085     Parser->State = BodyParserComplete;
1086   } else {
1087     Parser->Callback = Callback;
1088     Parser->Context  = Context;
1089   }
1090 
1091   *MsgParser = Parser;
1092   return EFI_SUCCESS;
1093 }
1094 
1095 /**
1096   Parse message body.
1097 
1098   Parse BodyLength of message-body. This function can be called repeatedly to parse the message-body partially.
1099 
1100   @param[in, out]    MsgParser            Pointer to the message parser.
1101   @param[in]         BodyLength           Length in bytes of the Body.
1102   @param[in]         Body                 Pointer to the buffer of the message-body to be parsed.
1103 
1104   @retval EFI_SUCCESS                Successfully parse the message-body.
1105   @retval EFI_INVALID_PARAMETER      MsgParser is NULL or Body is NULL or BodyLength is 0.
1106   @retval Others                     Operation aborted.
1107 
1108 **/
1109 EFI_STATUS
1110 EFIAPI
HttpParseMessageBody(IN OUT VOID * MsgParser,IN UINTN BodyLength,IN CHAR8 * Body)1111 HttpParseMessageBody (
1112   IN OUT VOID              *MsgParser,
1113   IN     UINTN             BodyLength,
1114   IN     CHAR8             *Body
1115   )
1116 {
1117   CHAR8                 *Char;
1118   UINTN                 RemainderLengthInThis;
1119   UINTN                 LengthForCallback;
1120   EFI_STATUS            Status;
1121   HTTP_BODY_PARSER      *Parser;
1122 
1123   if (BodyLength == 0 || Body == NULL) {
1124     return EFI_INVALID_PARAMETER;
1125   }
1126 
1127   if (MsgParser == NULL) {
1128     return EFI_INVALID_PARAMETER;
1129   }
1130 
1131   Parser = (HTTP_BODY_PARSER*) MsgParser;
1132 
1133   if (Parser->IgnoreBody) {
1134     Parser->State = BodyParserComplete;
1135     if (Parser->Callback != NULL) {
1136       Status = Parser->Callback (
1137                  BodyParseEventOnComplete,
1138                  Body,
1139                  0,
1140                  Parser->Context
1141                  );
1142       if (EFI_ERROR (Status)) {
1143         return Status;
1144       }
1145     }
1146     return EFI_SUCCESS;
1147   }
1148 
1149   if (Parser->State == BodyParserBodyStart) {
1150     Parser->ParsedBodyLength = 0;
1151     if (Parser->IsChunked) {
1152       Parser->State = BodyParserChunkSizeStart;
1153     } else {
1154       Parser->State = BodyParserBodyIdentity;
1155     }
1156   }
1157 
1158   //
1159   // The message body might be truncated in anywhere, so we need to parse is byte-by-byte.
1160   //
1161   for (Char = Body; Char < Body + BodyLength; ) {
1162 
1163     switch (Parser->State) {
1164     case BodyParserStateMax:
1165       return EFI_ABORTED;
1166 
1167     case BodyParserBodyIdentity:
1168       //
1169       // Identity transfer-coding, just notify user to save the body data.
1170       //
1171       if (Parser->Callback != NULL) {
1172         Status = Parser->Callback (
1173                    BodyParseEventOnData,
1174                    Char,
1175                    MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength),
1176                    Parser->Context
1177                    );
1178         if (EFI_ERROR (Status)) {
1179           return Status;
1180         }
1181       }
1182       Char += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
1183       Parser->ParsedBodyLength += MIN (BodyLength, Parser->ContentLength - Parser->ParsedBodyLength);
1184       if (Parser->ParsedBodyLength == Parser->ContentLength) {
1185         Parser->State = BodyParserComplete;
1186         if (Parser->Callback != NULL) {
1187           Status = Parser->Callback (
1188                      BodyParseEventOnComplete,
1189                      Char,
1190                      0,
1191                      Parser->Context
1192                      );
1193           if (EFI_ERROR (Status)) {
1194             return Status;
1195           }
1196         }
1197       }
1198       break;
1199 
1200     case BodyParserChunkSizeStart:
1201       //
1202       // First byte of chunk-size, the chunk-size might be truncated.
1203       //
1204       Parser->CurrentChunkSize = 0;
1205       Parser->State = BodyParserChunkSize;
1206     case BodyParserChunkSize:
1207       if (!NET_IS_HEX_CHAR (*Char)) {
1208         if (*Char == ';') {
1209           Parser->State = BodyParserChunkExtStart;
1210           Char++;
1211         } else if (*Char == '\r') {
1212           Parser->State = BodyParserChunkSizeEndCR;
1213           Char++;
1214         } else {
1215           Parser->State = BodyParserStateMax;
1216         }
1217         break;
1218       }
1219 
1220       if (Parser->CurrentChunkSize > (((~((UINTN) 0)) - 16) / 16)) {
1221         return EFI_INVALID_PARAMETER;
1222       }
1223       Parser->CurrentChunkSize = Parser->CurrentChunkSize * 16 + HttpIoHexCharToUintn (*Char);
1224       Char++;
1225       break;
1226 
1227     case BodyParserChunkExtStart:
1228       //
1229       // Ignore all the chunk extensions.
1230       //
1231       if (*Char == '\r') {
1232         Parser->State = BodyParserChunkSizeEndCR;
1233        }
1234       Char++;
1235       break;
1236 
1237     case BodyParserChunkSizeEndCR:
1238       if (*Char != '\n') {
1239         Parser->State = BodyParserStateMax;
1240         break;
1241       }
1242       Char++;
1243       if (Parser->CurrentChunkSize == 0) {
1244         //
1245         // The last chunk has been parsed and now assumed the state
1246         // of HttpBodyParse is ParserLastCRLF. So it need to decide
1247         // whether the rest message is trailer or last CRLF in the next round.
1248         //
1249         Parser->ContentLengthIsValid = TRUE;
1250         Parser->State = BodyParserLastCRLF;
1251         break;
1252       }
1253       Parser->State = BodyParserChunkDataStart;
1254       Parser->CurrentChunkParsedSize = 0;
1255       break;
1256 
1257     case BodyParserLastCRLF:
1258       //
1259       // Judge the byte is belong to the Last CRLF or trailer, and then
1260       // configure the state of HttpBodyParse to corresponding state.
1261       //
1262       if (*Char == '\r') {
1263         Char++;
1264         Parser->State = BodyParserLastCRLFEnd;
1265         break;
1266       } else {
1267         Parser->State = BodyParserTrailer;
1268         break;
1269       }
1270 
1271     case BodyParserLastCRLFEnd:
1272       if (*Char == '\n') {
1273         Parser->State = BodyParserComplete;
1274         Char++;
1275         if (Parser->Callback != NULL) {
1276           Status = Parser->Callback (
1277                      BodyParseEventOnComplete,
1278                      Char,
1279                      0,
1280                      Parser->Context
1281                      );
1282           if (EFI_ERROR (Status)) {
1283             return Status;
1284           }
1285         }
1286         break;
1287       } else {
1288         Parser->State = BodyParserStateMax;
1289         break;
1290       }
1291 
1292     case BodyParserTrailer:
1293       if (*Char == '\r') {
1294         Parser->State = BodyParserChunkSizeEndCR;
1295       }
1296       Char++;
1297       break;
1298 
1299     case BodyParserChunkDataStart:
1300       //
1301       // First byte of chunk-data, the chunk data also might be truncated.
1302       //
1303       RemainderLengthInThis = BodyLength - (Char - Body);
1304       LengthForCallback = MIN (Parser->CurrentChunkSize - Parser->CurrentChunkParsedSize, RemainderLengthInThis);
1305       if (Parser->Callback != NULL) {
1306         Status = Parser->Callback (
1307                    BodyParseEventOnData,
1308                    Char,
1309                    LengthForCallback,
1310                    Parser->Context
1311                    );
1312         if (EFI_ERROR (Status)) {
1313           return Status;
1314         }
1315       }
1316       Char += LengthForCallback;
1317       Parser->ContentLength += LengthForCallback;
1318       Parser->CurrentChunkParsedSize += LengthForCallback;
1319       if (Parser->CurrentChunkParsedSize == Parser->CurrentChunkSize) {
1320         Parser->State = BodyParserChunkDataEnd;
1321       }
1322       break;
1323 
1324     case BodyParserChunkDataEnd:
1325       if (*Char == '\r') {
1326         Parser->State = BodyParserChunkDataEndCR;
1327       } else {
1328         Parser->State = BodyParserStateMax;
1329       }
1330       Char++;
1331       break;
1332 
1333     case BodyParserChunkDataEndCR:
1334       if (*Char != '\n') {
1335         Parser->State = BodyParserStateMax;
1336         break;
1337       }
1338       Char++;
1339       Parser->State = BodyParserChunkSizeStart;
1340       break;
1341 
1342     default:
1343       break;
1344     }
1345 
1346   }
1347 
1348   if (Parser->State == BodyParserStateMax) {
1349     return EFI_ABORTED;
1350   }
1351 
1352   return EFI_SUCCESS;
1353 }
1354 
1355 /**
1356   Check whether the message-body is complete or not.
1357 
1358   @param[in]    MsgParser            Pointer to the message parser.
1359 
1360   @retval TRUE                       Message-body is complete.
1361   @retval FALSE                      Message-body is not complete.
1362 
1363 **/
1364 BOOLEAN
1365 EFIAPI
HttpIsMessageComplete(IN VOID * MsgParser)1366 HttpIsMessageComplete (
1367   IN VOID              *MsgParser
1368   )
1369 {
1370   HTTP_BODY_PARSER      *Parser;
1371 
1372   Parser = (HTTP_BODY_PARSER*) MsgParser;
1373 
1374   if (Parser->State == BodyParserComplete) {
1375     return TRUE;
1376   }
1377   return FALSE;
1378 }
1379 
1380 /**
1381   Get the content length of the entity.
1382 
1383   Note that in trunk transfer, the entity length is not valid until the whole message body is received.
1384 
1385   @param[in]    MsgParser            Pointer to the message parser.
1386   @param[out]   ContentLength        Pointer to store the length of the entity.
1387 
1388   @retval EFI_SUCCESS                Successfully to get the entity length.
1389   @retval EFI_NOT_READY              Entity length is not valid yet.
1390   @retval EFI_INVALID_PARAMETER      MsgParser is NULL or ContentLength is NULL.
1391 
1392 **/
1393 EFI_STATUS
1394 EFIAPI
HttpGetEntityLength(IN VOID * MsgParser,OUT UINTN * ContentLength)1395 HttpGetEntityLength (
1396   IN  VOID              *MsgParser,
1397   OUT UINTN             *ContentLength
1398   )
1399 {
1400   HTTP_BODY_PARSER      *Parser;
1401 
1402   if (MsgParser == NULL || ContentLength == NULL) {
1403     return EFI_INVALID_PARAMETER;
1404   }
1405 
1406   Parser = (HTTP_BODY_PARSER*) MsgParser;
1407 
1408   if (!Parser->ContentLengthIsValid) {
1409     return EFI_NOT_READY;
1410   }
1411 
1412   *ContentLength = Parser->ContentLength;
1413   return EFI_SUCCESS;
1414 }
1415 
1416 /**
1417   Release the resource of the message parser.
1418 
1419   @param[in]    MsgParser            Pointer to the message parser.
1420 
1421 **/
1422 VOID
1423 EFIAPI
HttpFreeMsgParser(IN VOID * MsgParser)1424 HttpFreeMsgParser (
1425   IN  VOID           *MsgParser
1426   )
1427 {
1428   FreePool (MsgParser);
1429 }
1430 
1431 
1432 /**
1433   Get the next string, which is distinguished by specified separator.
1434 
1435   @param[in]  String             Pointer to the string.
1436   @param[in]  Separator          Specified separator used to distinguish where is the beginning
1437                                  of next string.
1438 
1439   @return     Pointer to the next string.
1440   @return     NULL if not find or String is NULL.
1441 
1442 **/
1443 CHAR8 *
1444 EFIAPI
AsciiStrGetNextToken(IN CONST CHAR8 * String,IN CHAR8 Separator)1445 AsciiStrGetNextToken (
1446   IN CONST CHAR8 *String,
1447   IN       CHAR8 Separator
1448   )
1449 {
1450   CONST CHAR8 *Token;
1451 
1452   Token = String;
1453   while (TRUE) {
1454     if (*Token == 0) {
1455       return NULL;
1456     }
1457     if (*Token == Separator) {
1458       return (CHAR8 *)(Token + 1);
1459     }
1460     Token++;
1461   }
1462 }
1463 
1464 /**
1465   Set FieldName and FieldValue into specified HttpHeader.
1466 
1467   @param[in,out]  HttpHeader      Specified HttpHeader.
1468   @param[in]  FieldName           FieldName of this HttpHeader, a NULL terminated ASCII string.
1469   @param[in]  FieldValue          FieldValue of this HttpHeader, a NULL terminated ASCII string.
1470 
1471 
1472   @retval EFI_SUCCESS             The FieldName and FieldValue are set into HttpHeader successfully.
1473   @retval EFI_OUT_OF_RESOURCES    Failed to allocate resources.
1474 
1475 **/
1476 EFI_STATUS
1477 EFIAPI
HttpSetFieldNameAndValue(IN OUT EFI_HTTP_HEADER * HttpHeader,IN CONST CHAR8 * FieldName,IN CONST CHAR8 * FieldValue)1478 HttpSetFieldNameAndValue (
1479   IN  OUT   EFI_HTTP_HEADER       *HttpHeader,
1480   IN  CONST CHAR8                 *FieldName,
1481   IN  CONST CHAR8                 *FieldValue
1482   )
1483 {
1484   UINTN                       FieldNameSize;
1485   UINTN                       FieldValueSize;
1486 
1487   if (HttpHeader->FieldName != NULL) {
1488     FreePool (HttpHeader->FieldName);
1489   }
1490   if (HttpHeader->FieldValue != NULL) {
1491     FreePool (HttpHeader->FieldValue);
1492   }
1493 
1494   FieldNameSize = AsciiStrSize (FieldName);
1495   HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);
1496   if (HttpHeader->FieldName == NULL) {
1497     return EFI_OUT_OF_RESOURCES;
1498   }
1499   CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);
1500   HttpHeader->FieldName[FieldNameSize - 1] = 0;
1501 
1502   FieldValueSize = AsciiStrSize (FieldValue);
1503   HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);
1504   if (HttpHeader->FieldValue == NULL) {
1505     return EFI_OUT_OF_RESOURCES;
1506   }
1507   CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);
1508   HttpHeader->FieldValue[FieldValueSize - 1] = 0;
1509 
1510   return EFI_SUCCESS;
1511 }
1512 
1513 /**
1514   Get one key/value header pair from the raw string.
1515 
1516   @param[in]  String             Pointer to the raw string.
1517   @param[out] FieldName          Points directly to field name within 'HttpHeader'.
1518   @param[out] FieldValue         Points directly to field value within 'HttpHeader'.
1519 
1520   @return     Pointer to the next raw string.
1521   @return     NULL if no key/value header pair from this raw string.
1522 
1523 **/
1524 CHAR8 *
1525 EFIAPI
HttpGetFieldNameAndValue(IN CHAR8 * String,OUT CHAR8 ** FieldName,OUT CHAR8 ** FieldValue)1526 HttpGetFieldNameAndValue (
1527   IN     CHAR8   *String,
1528      OUT CHAR8   **FieldName,
1529      OUT CHAR8   **FieldValue
1530   )
1531 {
1532   CHAR8  *FieldNameStr;
1533   CHAR8  *FieldValueStr;
1534   CHAR8  *StrPtr;
1535 
1536   if (String == NULL || FieldName == NULL || FieldValue == NULL) {
1537     return NULL;
1538   }
1539 
1540   *FieldName    = NULL;
1541   *FieldValue   = NULL;
1542   FieldNameStr  = NULL;
1543   FieldValueStr = NULL;
1544   StrPtr        = NULL;
1545 
1546   //
1547   // Each header field consists of a name followed by a colon (":") and the field value.
1548   //
1549   FieldNameStr = String;
1550   FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');
1551   if (FieldValueStr == NULL) {
1552     return NULL;
1553   }
1554 
1555   //
1556   // Replace ':' with 0
1557   //
1558   *(FieldValueStr - 1) = 0;
1559 
1560   //
1561   // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.
1562   //
1563   while (TRUE) {
1564     if (*FieldValueStr == ' ' || *FieldValueStr == '\t') {
1565       FieldValueStr ++;
1566     } else if (*FieldValueStr == '\r' && *(FieldValueStr + 1) == '\n' &&
1567                (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t')) {
1568       FieldValueStr = FieldValueStr + 3;
1569     } else {
1570       break;
1571     }
1572   }
1573 
1574   //
1575   // Header fields can be extended over multiple lines by preceding each extra
1576   // line with at least one SP or HT.
1577   //
1578   StrPtr = FieldValueStr;
1579   do {
1580     StrPtr = AsciiStrGetNextToken (StrPtr, '\r');
1581     if (StrPtr == NULL || *StrPtr != '\n') {
1582       return NULL;
1583     }
1584 
1585     StrPtr++;
1586   } while (*StrPtr == ' ' || *StrPtr == '\t');
1587 
1588   //
1589   // Replace '\r' with 0
1590   //
1591   *(StrPtr - 2) = 0;
1592 
1593   //
1594   // Get FieldName and FieldValue.
1595   //
1596   *FieldName = FieldNameStr;
1597   *FieldValue = FieldValueStr;
1598 
1599   return StrPtr;
1600 }
1601 
1602 /**
1603   Free existing HeaderFields.
1604 
1605   @param[in]  HeaderFields       Pointer to array of key/value header pairs waitting for free.
1606   @param[in]  FieldCount         The number of header pairs in HeaderFields.
1607 
1608 **/
1609 VOID
1610 EFIAPI
HttpFreeHeaderFields(IN EFI_HTTP_HEADER * HeaderFields,IN UINTN FieldCount)1611 HttpFreeHeaderFields (
1612   IN  EFI_HTTP_HEADER  *HeaderFields,
1613   IN  UINTN            FieldCount
1614   )
1615 {
1616   UINTN                       Index;
1617 
1618   if (HeaderFields != NULL) {
1619     for (Index = 0; Index < FieldCount; Index++) {
1620       if (HeaderFields[Index].FieldName != NULL) {
1621         FreePool (HeaderFields[Index].FieldName);
1622       }
1623       if (HeaderFields[Index].FieldValue != NULL) {
1624         FreePool (HeaderFields[Index].FieldValue);
1625       }
1626     }
1627 
1628     FreePool (HeaderFields);
1629   }
1630 }
1631 
1632 /**
1633   Generate HTTP request message.
1634 
1635   This function will allocate memory for the whole HTTP message and generate a
1636   well formatted HTTP Request message in it, include the Request-Line, header
1637   fields and also the message body. It is the caller's responsibility to free
1638   the buffer returned in *RequestMsg.
1639 
1640   @param[in]   Message            Pointer to the EFI_HTTP_MESSAGE structure which
1641                                   contains the required information to generate
1642                                   the HTTP request message.
1643   @param[in]   Url                The URL of a remote host.
1644   @param[out]  RequestMsg         Pointer to the created HTTP request message.
1645                                   NULL if any error occured.
1646   @param[out]  RequestMsgSize     Size of the RequestMsg (in bytes).
1647 
1648   @return EFI_SUCCESS             If HTTP request string was created successfully
1649   @retval EFI_OUT_OF_RESOURCES    Failed to allocate resources.
1650   @retval EFI_INVALID_PARAMETER   The input arguments are invalid
1651 
1652 **/
1653 EFI_STATUS
1654 EFIAPI
HttpGenRequestMessage(IN CONST EFI_HTTP_MESSAGE * Message,IN CONST CHAR8 * Url,OUT CHAR8 ** RequestMsg,OUT UINTN * RequestMsgSize)1655 HttpGenRequestMessage (
1656   IN     CONST EFI_HTTP_MESSAGE        *Message,
1657   IN     CONST CHAR8                   *Url,
1658      OUT CHAR8                         **RequestMsg,
1659      OUT UINTN                         *RequestMsgSize
1660   )
1661 {
1662   EFI_STATUS                       Status;
1663   UINTN                            StrLength;
1664   CHAR8                            *RequestPtr;
1665   UINTN                            HttpHdrSize;
1666   UINTN                            MsgSize;
1667   BOOLEAN                          Success;
1668   VOID                             *HttpHdr;
1669   EFI_HTTP_HEADER                  **AppendList;
1670   UINTN                            Index;
1671   EFI_HTTP_UTILITIES_PROTOCOL      *HttpUtilitiesProtocol;
1672 
1673 
1674   ASSERT (Message != NULL);
1675 
1676   *RequestMsg           = NULL;
1677   Status                = EFI_SUCCESS;
1678   HttpHdrSize           = 0;
1679   MsgSize               = 0;
1680   Success               = FALSE;
1681   HttpHdr               = NULL;
1682   AppendList            = NULL;
1683   HttpUtilitiesProtocol = NULL;
1684 
1685   //
1686   // 1. If we have a Request, we cannot have a NULL Url
1687   // 2. If we have a Request, HeaderCount can not be non-zero
1688   // 3. If we do not have a Request, HeaderCount should be zero
1689   // 4. If we do not have Request and Headers, we need at least a message-body
1690   //
1691   if ((Message->Data.Request != NULL && Url == NULL) ||
1692       (Message->Data.Request != NULL && Message->HeaderCount == 0) ||
1693       (Message->Data.Request == NULL && Message->HeaderCount != 0) ||
1694       (Message->Data.Request == NULL && Message->HeaderCount == 0 && Message->BodyLength == 0)) {
1695     return EFI_INVALID_PARAMETER;
1696   }
1697 
1698   if (Message->HeaderCount != 0) {
1699     //
1700     // Locate the HTTP_UTILITIES protocol.
1701     //
1702     Status = gBS->LocateProtocol (
1703                     &gEfiHttpUtilitiesProtocolGuid,
1704                     NULL,
1705                     (VOID **)&HttpUtilitiesProtocol
1706                     );
1707 
1708     if (EFI_ERROR (Status)) {
1709       DEBUG ((DEBUG_ERROR,"Failed to locate Http Utilities protocol. Status = %r.\n", Status));
1710       return Status;
1711     }
1712 
1713     //
1714     // Build AppendList to send into HttpUtilitiesBuild
1715     //
1716     AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount));
1717     if (AppendList == NULL) {
1718       return EFI_OUT_OF_RESOURCES;
1719     }
1720 
1721     for(Index = 0; Index < Message->HeaderCount; Index++){
1722       AppendList[Index] = &Message->Headers[Index];
1723     }
1724 
1725     //
1726     // Build raw HTTP Headers
1727     //
1728     Status = HttpUtilitiesProtocol->Build (
1729                 HttpUtilitiesProtocol,
1730                 0,
1731                 NULL,
1732                 0,
1733                 NULL,
1734                 Message->HeaderCount,
1735                 AppendList,
1736                 &HttpHdrSize,
1737                 &HttpHdr
1738                 );
1739 
1740     if (AppendList != NULL) {
1741       FreePool (AppendList);
1742     }
1743 
1744     if (EFI_ERROR (Status) || HttpHdr == NULL){
1745       return Status;
1746     }
1747   }
1748 
1749   //
1750   // If we have headers to be sent, account for it.
1751   //
1752   if (Message->HeaderCount != 0) {
1753     MsgSize = HttpHdrSize;
1754   }
1755 
1756   //
1757   // If we have a request line, account for the fields.
1758   //
1759   if (Message->Data.Request != NULL) {
1760     MsgSize += HTTP_METHOD_MAXIMUM_LEN + AsciiStrLen (HTTP_VERSION_CRLF_STR) + AsciiStrLen (Url);
1761   }
1762 
1763 
1764   //
1765   // If we have a message body to be sent, account for it.
1766   //
1767   MsgSize += Message->BodyLength;
1768 
1769   //
1770   // memory for the string that needs to be sent to TCP
1771   //
1772   *RequestMsg = AllocateZeroPool (MsgSize);
1773   if (*RequestMsg == NULL) {
1774     Status = EFI_OUT_OF_RESOURCES;
1775     goto Exit;
1776   }
1777 
1778   RequestPtr = *RequestMsg;
1779   //
1780   // Construct header request
1781   //
1782   if (Message->Data.Request != NULL) {
1783     switch (Message->Data.Request->Method) {
1784     case HttpMethodGet:
1785       StrLength = sizeof (HTTP_METHOD_GET) - 1;
1786       CopyMem (RequestPtr, HTTP_METHOD_GET, StrLength);
1787       RequestPtr += StrLength;
1788       break;
1789     case HttpMethodPut:
1790       StrLength = sizeof (HTTP_METHOD_PUT) - 1;
1791       CopyMem (RequestPtr, HTTP_METHOD_PUT, StrLength);
1792       RequestPtr += StrLength;
1793       break;
1794     case HttpMethodPatch:
1795       StrLength = sizeof (HTTP_METHOD_PATCH) - 1;
1796       CopyMem (RequestPtr, HTTP_METHOD_PATCH, StrLength);
1797       RequestPtr += StrLength;
1798       break;
1799     case HttpMethodPost:
1800       StrLength = sizeof (HTTP_METHOD_POST) - 1;
1801       CopyMem (RequestPtr, HTTP_METHOD_POST, StrLength);
1802       RequestPtr += StrLength;
1803       break;
1804     case HttpMethodHead:
1805       StrLength = sizeof (HTTP_METHOD_HEAD) - 1;
1806       CopyMem (RequestPtr, HTTP_METHOD_HEAD, StrLength);
1807       RequestPtr += StrLength;
1808       break;
1809     case HttpMethodDelete:
1810       StrLength = sizeof (HTTP_METHOD_DELETE) - 1;
1811       CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength);
1812       RequestPtr += StrLength;
1813       break;
1814     default:
1815       ASSERT (FALSE);
1816       Status = EFI_INVALID_PARAMETER;
1817       goto Exit;
1818     }
1819 
1820     StrLength = AsciiStrLen(EMPTY_SPACE);
1821     CopyMem (RequestPtr, EMPTY_SPACE, StrLength);
1822     RequestPtr += StrLength;
1823 
1824     StrLength = AsciiStrLen (Url);
1825     CopyMem (RequestPtr, Url, StrLength);
1826     RequestPtr += StrLength;
1827 
1828     StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1;
1829     CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength);
1830     RequestPtr += StrLength;
1831 
1832     if (HttpHdr != NULL) {
1833       //
1834       // Construct header
1835       //
1836       CopyMem (RequestPtr, HttpHdr, HttpHdrSize);
1837       RequestPtr += HttpHdrSize;
1838     }
1839   }
1840 
1841   //
1842   // Construct body
1843   //
1844   if (Message->Body != NULL) {
1845     CopyMem (RequestPtr, Message->Body, Message->BodyLength);
1846     RequestPtr += Message->BodyLength;
1847   }
1848 
1849   //
1850   // Done
1851   //
1852   (*RequestMsgSize) = (UINTN)(RequestPtr) - (UINTN)(*RequestMsg);
1853   Success     = TRUE;
1854 
1855 Exit:
1856 
1857   if (!Success) {
1858     if (*RequestMsg != NULL) {
1859       FreePool (*RequestMsg);
1860     }
1861     *RequestMsg = NULL;
1862     return Status;
1863   }
1864 
1865   if (HttpHdr != NULL) {
1866     FreePool (HttpHdr);
1867   }
1868 
1869   return EFI_SUCCESS;
1870 }
1871 
1872 /**
1873   Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined
1874   in UEFI 2.5 specification.
1875 
1876   @param[in]  StatusCode         The status code value in HTTP message.
1877 
1878   @return                        Value defined in EFI_HTTP_STATUS_CODE .
1879 
1880 **/
1881 EFI_HTTP_STATUS_CODE
1882 EFIAPI
HttpMappingToStatusCode(IN UINTN StatusCode)1883 HttpMappingToStatusCode (
1884   IN UINTN                  StatusCode
1885   )
1886 {
1887   switch (StatusCode) {
1888   case 100:
1889     return HTTP_STATUS_100_CONTINUE;
1890   case 101:
1891     return HTTP_STATUS_101_SWITCHING_PROTOCOLS;
1892   case 200:
1893     return HTTP_STATUS_200_OK;
1894   case 201:
1895     return HTTP_STATUS_201_CREATED;
1896   case 202:
1897     return HTTP_STATUS_202_ACCEPTED;
1898   case 203:
1899     return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION;
1900   case 204:
1901     return HTTP_STATUS_204_NO_CONTENT;
1902   case 205:
1903     return HTTP_STATUS_205_RESET_CONTENT;
1904   case 206:
1905     return HTTP_STATUS_206_PARTIAL_CONTENT;
1906   case 300:
1907     return HTTP_STATUS_300_MULTIPLE_CHIOCES;
1908   case 301:
1909     return HTTP_STATUS_301_MOVED_PERMANENTLY;
1910   case 302:
1911     return HTTP_STATUS_302_FOUND;
1912   case 303:
1913     return HTTP_STATUS_303_SEE_OTHER;
1914   case 304:
1915     return HTTP_STATUS_304_NOT_MODIFIED;
1916   case 305:
1917     return HTTP_STATUS_305_USE_PROXY;
1918   case 307:
1919     return HTTP_STATUS_307_TEMPORARY_REDIRECT;
1920   case 400:
1921     return HTTP_STATUS_400_BAD_REQUEST;
1922   case 401:
1923     return HTTP_STATUS_401_UNAUTHORIZED;
1924   case 402:
1925     return HTTP_STATUS_402_PAYMENT_REQUIRED;
1926   case 403:
1927     return HTTP_STATUS_403_FORBIDDEN;
1928   case 404:
1929     return HTTP_STATUS_404_NOT_FOUND;
1930   case 405:
1931     return HTTP_STATUS_405_METHOD_NOT_ALLOWED;
1932   case 406:
1933     return HTTP_STATUS_406_NOT_ACCEPTABLE;
1934   case 407:
1935     return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED;
1936   case 408:
1937     return HTTP_STATUS_408_REQUEST_TIME_OUT;
1938   case 409:
1939     return HTTP_STATUS_409_CONFLICT;
1940   case 410:
1941     return HTTP_STATUS_410_GONE;
1942   case 411:
1943     return HTTP_STATUS_411_LENGTH_REQUIRED;
1944   case 412:
1945     return HTTP_STATUS_412_PRECONDITION_FAILED;
1946   case 413:
1947     return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE;
1948   case 414:
1949     return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE;
1950   case 415:
1951     return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE;
1952   case 416:
1953     return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED;
1954   case 417:
1955     return HTTP_STATUS_417_EXPECTATION_FAILED;
1956   case 500:
1957     return HTTP_STATUS_500_INTERNAL_SERVER_ERROR;
1958   case 501:
1959     return HTTP_STATUS_501_NOT_IMPLEMENTED;
1960   case 502:
1961     return HTTP_STATUS_502_BAD_GATEWAY;
1962   case 503:
1963     return HTTP_STATUS_503_SERVICE_UNAVAILABLE;
1964   case 504:
1965     return HTTP_STATUS_504_GATEWAY_TIME_OUT;
1966   case 505:
1967     return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED;
1968 
1969   default:
1970     return HTTP_STATUS_UNSUPPORTED_STATUS;
1971   }
1972 }
1973 
1974 /**
1975   Check whether header field called FieldName is in DeleteList.
1976 
1977   @param[in]  DeleteList        Pointer to array of key/value header pairs.
1978   @param[in]  DeleteCount       The number of header pairs.
1979   @param[in]  FieldName         Pointer to header field's name.
1980 
1981   @return     TRUE if FieldName is not in DeleteList, that means this header field is valid.
1982   @return     FALSE if FieldName is in DeleteList, that means this header field is invalid.
1983 
1984 **/
1985 BOOLEAN
1986 EFIAPI
HttpIsValidHttpHeader(IN CHAR8 * DeleteList[],IN UINTN DeleteCount,IN CHAR8 * FieldName)1987 HttpIsValidHttpHeader (
1988   IN  CHAR8            *DeleteList[],
1989   IN  UINTN            DeleteCount,
1990   IN  CHAR8            *FieldName
1991   )
1992 {
1993   UINTN                       Index;
1994 
1995   for (Index = 0; Index < DeleteCount; Index++) {
1996     if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {
1997       return FALSE;
1998     }
1999   }
2000 
2001   return TRUE;
2002 }
2003 
2004