]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/HttpDxe/HttpUtilities.c
NetworkPkg: Update the DnsDhcp.c to use BSD license
[mirror_edk2.git] / NetworkPkg / HttpDxe / HttpUtilities.c
CommitLineData
47f51a06
YT
1/** @file\r
2\r
3Implementation of help functions to parse HTTP message header.\r
4\r
5Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "HttpDriver.h"\r
17\r
18/**\r
19 Get the next string, which is distinguished by specified seperator. \r
20\r
21 @param[in] String Pointer to the string.\r
22 @param[in] Seperator Specified seperator used to distinguish where is the beginning \r
23 of next string.\r
24\r
25 @return Pointer to the next string.\r
26 @return NULL if not find or String is NULL.\r
27\r
28**/\r
29CHAR8 *\r
30AsciiStrGetNextToken (\r
31 IN CONST CHAR8 *String,\r
32 IN CHAR8 Seperator\r
33 )\r
34{\r
35 CONST CHAR8 *Token;\r
36\r
37 Token = String;\r
38 while (TRUE) {\r
39 if (*Token == 0) {\r
40 return NULL;\r
41 }\r
42 if (*Token == Seperator) {\r
43 return (CHAR8 *) (Token + 1);\r
44 }\r
45 Token++;\r
46 }\r
47}\r
48\r
49/**\r
50 Free existing HeaderFields.\r
51\r
52 @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free.\r
53 @param[in] FieldCount The number of header pairs in HeaderFields.\r
54\r
55**/\r
56VOID\r
57FreeHeaderFields (\r
58 IN EFI_HTTP_HEADER *HeaderFields,\r
59 IN UINTN FieldCount\r
60 )\r
61{\r
62 UINTN Index;\r
63 \r
64 if (HeaderFields != NULL) {\r
65 for (Index = 0; Index < FieldCount; Index++) {\r
66 if(HeaderFields[Index].FieldName != NULL) {\r
67 FreePool (HeaderFields[Index].FieldName);\r
68 }\r
69 if(HeaderFields[Index].FieldValue != NULL) {\r
70 FreePool (HeaderFields[Index].FieldValue);\r
71 }\r
72 }\r
73\r
74 FreePool (HeaderFields);\r
75 }\r
76}\r
77\r
78/**\r
79 Find required header field in HeaderFields.\r
80\r
81 @param[in] HeaderFields Pointer to array of key/value header pairs.\r
82 @param[in] FieldCount The number of header pairs.\r
83 @param[in] FieldName Pointer to header field's name.\r
84\r
85 @return Pointer to the queried header field.\r
86 @return NULL if not find this required header field.\r
87\r
88**/\r
89EFI_HTTP_HEADER *\r
90FindHttpHeader (\r
91 IN EFI_HTTP_HEADER *HeaderFields,\r
92 IN UINTN FieldCount,\r
93 IN CHAR8 *FieldName\r
94 )\r
95{\r
96 UINTN Index;\r
97\r
98 for (Index = 0; Index < FieldCount; Index++) {\r
99 if (AsciiStrCmp (FieldName, HeaderFields[Index].FieldName) == 0) {\r
100 //\r
101 // Find the required header field.\r
102 //\r
103 return &HeaderFields[Index];\r
104 }\r
105 }\r
106 return NULL;\r
107}\r
108\r
109/**\r
110 Check whether header field called FieldName is in DeleteList.\r
111\r
112 @param[in] DeleteList Pointer to array of key/value header pairs.\r
113 @param[in] DeleteCount The number of header pairs.\r
114 @param[in] FieldName Pointer to header field's name.\r
115\r
116 @return TRUE if FieldName is not in DeleteList, that means this header field is valid.\r
117 @return FALSE if FieldName is in DeleteList, that means this header field is invalid.\r
118\r
119**/\r
120BOOLEAN\r
121IsValidHttpHeader (\r
122 IN CHAR8 *DeleteList[],\r
123 IN UINTN DeleteCount,\r
124 IN CHAR8 *FieldName\r
125 )\r
126{\r
127 UINTN Index;\r
128\r
129 for (Index = 0; Index < DeleteCount; Index++) {\r
130 if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) {\r
131 return FALSE;\r
132 }\r
133 }\r
134 \r
135 return TRUE;\r
136}\r
137\r
138/**\r
139 Set FieldName and FieldValue into specified HttpHeader.\r
140\r
141 @param[in] HttpHeader Specified HttpHeader.\r
142 @param[in] FieldName FieldName of this HttpHeader.\r
143 @param[in] FieldValue FieldValue of this HttpHeader.\r
144\r
145\r
146 @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully.\r
147 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
148\r
149**/\r
150EFI_STATUS\r
151SetFieldNameAndValue (\r
152 IN EFI_HTTP_HEADER *HttpHeader,\r
153 IN CHAR8 *FieldName, \r
154 IN CHAR8 *FieldValue\r
155 )\r
156{ \r
157 UINTN FieldNameSize;\r
158 UINTN FieldValueSize;\r
159\r
160 if (HttpHeader->FieldName != NULL) {\r
161 FreePool (HttpHeader->FieldName);\r
162 }\r
163 if (HttpHeader->FieldValue != NULL) {\r
164 FreePool (HttpHeader->FieldValue);\r
165 }\r
166\r
167 FieldNameSize = AsciiStrSize (FieldName);\r
168 HttpHeader->FieldName = AllocateZeroPool (FieldNameSize);\r
169 if (HttpHeader->FieldName == NULL) {\r
170 return EFI_OUT_OF_RESOURCES;\r
171 }\r
172 CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize);\r
173 HttpHeader->FieldName[FieldNameSize - 1] = 0;\r
174\r
175 FieldValueSize = AsciiStrSize (FieldValue);\r
176 HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize);\r
177 if (HttpHeader->FieldValue == NULL) {\r
178 FreePool (HttpHeader->FieldName);\r
179 return EFI_OUT_OF_RESOURCES;\r
180 }\r
181 CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize);\r
182 HttpHeader->FieldValue[FieldValueSize - 1] = 0;\r
183\r
184 return EFI_SUCCESS;\r
185}\r
186\r
187/**\r
188 Get one key/value header pair from the raw string.\r
189\r
190 @param[in] String Pointer to the raw string.\r
191 @param[out] FieldName Pointer to header field's name.\r
192 @param[out] FieldValue Pointer to header field's value.\r
193\r
194 @return Pointer to the next raw string.\r
195 @return NULL if no key/value header pair from this raw string.\r
196\r
197**/\r
198CHAR8 *\r
199GetFieldNameAndValue (\r
200 IN CHAR8 *String,\r
201 OUT CHAR8 **FieldName,\r
202 OUT CHAR8 **FieldValue\r
203 )\r
204{\r
205 CHAR8 *FieldNameStr;\r
206 CHAR8 *FieldValueStr;\r
207 CHAR8 *StrPtr;\r
208\r
209 if (String == NULL || FieldName == NULL || FieldValue == NULL) {\r
210 return NULL;\r
211 }\r
212 \r
213 *FieldName = NULL;\r
214 *FieldValue = NULL;\r
215 FieldNameStr = NULL;\r
216 FieldValueStr = NULL;\r
217 StrPtr = NULL;\r
218\r
219 //\r
220 // Each header field consists of a name followed by a colon (":") and the field value.\r
221 //\r
222 FieldNameStr = String;\r
223 FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':');\r
224 if (FieldValueStr == NULL) {\r
225 return NULL;\r
226 }\r
227 \r
228 *(FieldValueStr - 1) = 0; /// Replace ':' with 0\r
229 \r
230 //\r
231 // The field value MAY be preceded by any amount of LWS, though a single SP is preferred.\r
232 //\r
233 while (TRUE) {\r
234 if(*FieldValueStr == ' ' || *FieldValueStr == '\t') {\r
235 FieldValueStr ++;\r
236 } else if (*FieldValueStr == '\r' && *(FieldValueStr + 1) == '\n' && \r
237 (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t')) {\r
238 FieldValueStr = FieldValueStr + 3;\r
239 } else {\r
240 break;\r
241 }\r
242 }\r
243\r
244 //\r
245 // Header fields can be extended over multiple lines by preceding each extra\r
246 // line with at least one SP or HT.\r
247 //\r
248 StrPtr = FieldValueStr;\r
249 do {\r
250 StrPtr = AsciiStrGetNextToken (StrPtr, '\r');\r
251 if (StrPtr == NULL || *StrPtr != '\n') {\r
252 return NULL;\r
253 }\r
254 \r
255 StrPtr++;\r
256 } while (*StrPtr == ' ' || *StrPtr == '\t');\r
257\r
258 //\r
259 // Replace '\r' with 0.\r
260 //\r
261 *(StrPtr - 2) = 0;\r
262\r
263 //\r
264 // Get FieldName and FieldValue.\r
265 //\r
266 *FieldName = FieldNameStr;\r
267 *FieldValue = FieldValueStr;\r
268 \r
269 return StrPtr;\r
270}\r
271\r
272/**\r
273 This function is used to manage the headers portion of an HTTP message by providing \r
274 the ability to add, remove, or replace HTTP headers.\r
275\r
276 @param[in] SeedMessageSize Size in bytes of the initial HTTP header. This can be zero. \r
277 @param[in] SeedMessage Initial raw unformatted HTTP header to be used as a base for \r
278 building a new unformatted HTTP header. If NULL, SeedMessageSize \r
279 is ignored. The buffer containing this message will be allocated \r
280 and released by the caller. \r
281 @param[in] DeleteCount Number of null-terminated HTTP header field names in DeleteList.\r
282 @param[in] DeleteList List of null-terminated HTTP header field names to remove from SeedMessage. \r
283 Only the field names are in this list because the field values are irrelevant \r
284 to this operation. If NULL, DeleteCount is ignored. The buffer containing the \r
285 list will be allocated and released by the caller.\r
286 @param[in] AppendCount Number of header fields in AppendList. \r
287 @param[in] AppendList List of HTTP headers to populate NewMessage with. If SeedMessage is not NULL, \r
288 AppendList will be appended to the existing list from SeedMessage in NewMessage.\r
289 @param[out] NewMessageSize Pointer to the size in bytes of the new unformatted HTTP header in NewMessage. \r
290 @param[out] NewMessage Pointer to a new unformatted HTTP header. The storage for this NewMessage is \r
291 allocated by the driver publishing this protocol, and must be freed by the caller. \r
292 \r
293 @retval EFI_SUCCESS Add, remove, and replace operations succeeded.\r
294 @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage.\r
295 \r
296**/\r
297EFI_STATUS\r
298HttpUtilitiesBuild(\r
299 IN UINTN SeedMessageSize,\r
300 IN VOID *SeedMessage, OPTIONAL\r
301 IN UINTN DeleteCount,\r
302 IN CHAR8 *DeleteList[], OPTIONAL\r
303 IN UINTN AppendCount,\r
304 IN EFI_HTTP_HEADER *AppendList[], OPTIONAL\r
305 OUT UINTN *NewMessageSize,\r
306 OUT VOID **NewMessage\r
307 )\r
308{\r
309 EFI_STATUS Status; \r
310 EFI_HTTP_HEADER *SeedHeaderFields;\r
311 UINTN SeedFieldCount;\r
312 UINTN Index;\r
313 EFI_HTTP_HEADER *TempHeaderFields;\r
314 UINTN TempFieldCount;\r
315 EFI_HTTP_HEADER *NewHeaderFields;\r
316 UINTN NewFieldCount;\r
317 EFI_HTTP_HEADER *HttpHeader;\r
318 UINTN StrLength;\r
319 UINT8 *NewMessagePtr;\r
320\r
321 SeedHeaderFields = NULL;\r
322 SeedFieldCount = 0;\r
323 TempHeaderFields = NULL;\r
324 TempFieldCount = 0;\r
325 NewHeaderFields = NULL;\r
326 NewFieldCount = 0;\r
327\r
328 HttpHeader = NULL; \r
329 StrLength = 0;\r
330 NewMessagePtr = NULL;\r
331 *NewMessageSize = 0;\r
332 Status = EFI_SUCCESS;\r
333\r
334 if (SeedMessage != NULL) {\r
335 Status = HttpUtilitiesParse (\r
336 SeedMessage,\r
337 SeedMessageSize,\r
338 &SeedHeaderFields,\r
339 &SeedFieldCount\r
340 );\r
341 if (EFI_ERROR (Status)){\r
342 goto ON_EXIT;\r
343 }\r
344 }\r
345\r
346 //\r
347 // Handle DeleteList\r
348 //\r
349 if(SeedFieldCount != 0 && DeleteCount != 0) {\r
350 TempHeaderFields = AllocateZeroPool (SeedFieldCount * sizeof(EFI_HTTP_HEADER));\r
351 if (TempHeaderFields == NULL) {\r
352 Status = EFI_OUT_OF_RESOURCES;\r
353 goto ON_EXIT;\r
354 }\r
355 \r
356 for (Index = 0, TempFieldCount = 0; Index < SeedFieldCount; Index++) {\r
357 //\r
358 // Check whether each SeedHeaderFields member is in DeleteList\r
359 //\r
360 if (IsValidHttpHeader(DeleteList, DeleteCount, SeedHeaderFields[Index].FieldName)) {\r
361 Status = SetFieldNameAndValue(\r
362 &TempHeaderFields[TempFieldCount],\r
363 SeedHeaderFields[Index].FieldName,\r
364 SeedHeaderFields[Index].FieldValue\r
365 );\r
366 if (EFI_ERROR (Status)){\r
367 goto ON_EXIT;\r
368 }\r
369 TempFieldCount++;\r
370 }\r
371 }\r
372 } else {\r
373 TempHeaderFields = SeedHeaderFields;\r
374 TempFieldCount = SeedFieldCount;\r
375 }\r
376\r
377 //\r
378 // Handle AppendList\r
379 //\r
380 NewHeaderFields = AllocateZeroPool ((TempFieldCount + AppendCount) * sizeof(EFI_HTTP_HEADER));\r
381 if (NewHeaderFields == NULL) {\r
382 Status = EFI_OUT_OF_RESOURCES;\r
383 goto ON_EXIT;\r
384 }\r
385\r
386 for (Index = 0; Index < TempFieldCount; Index++) {\r
387 Status = SetFieldNameAndValue(\r
388 &NewHeaderFields[Index],\r
389 TempHeaderFields[Index].FieldName,\r
390 TempHeaderFields[Index].FieldValue\r
391 );\r
392 if (EFI_ERROR (Status)){\r
393 goto ON_EXIT;\r
394 }\r
395 }\r
396 \r
397 NewFieldCount = TempFieldCount;\r
398\r
399 for (Index = 0; Index < AppendCount; Index++) {\r
400 HttpHeader = FindHttpHeader(NewHeaderFields, NewFieldCount, AppendList[Index]->FieldName);\r
401 if(HttpHeader != NULL) {\r
402 Status = SetFieldNameAndValue(\r
403 HttpHeader,\r
404 AppendList[Index]->FieldName,\r
405 AppendList[Index]->FieldValue\r
406 );\r
407 if (EFI_ERROR (Status)){\r
408 goto ON_EXIT;\r
409 }\r
410 } else {\r
411 Status = SetFieldNameAndValue\r
412 (&NewHeaderFields[NewFieldCount],\r
413 AppendList[Index]->FieldName,\r
414 AppendList[Index]->FieldValue\r
415 );\r
416 if (EFI_ERROR (Status)){\r
417 goto ON_EXIT;\r
418 }\r
419 NewFieldCount++;\r
420 }\r
421 }\r
422\r
423 //\r
424 // Calculate NewMessageSize, then build NewMessage\r
425 //\r
426 for (Index = 0; Index < NewFieldCount; Index++) {\r
427 HttpHeader = &NewHeaderFields[Index];\r
428\r
429 StrLength = AsciiStrLen (HttpHeader->FieldName);\r
430 *NewMessageSize += StrLength;\r
431\r
432 StrLength = sizeof(": ") - 1;\r
433 *NewMessageSize += StrLength;\r
434\r
435 StrLength = AsciiStrLen (HttpHeader->FieldValue);\r
436 *NewMessageSize += StrLength;\r
437\r
438 StrLength = sizeof(HTTP_CRLF_STR) - 1;\r
439 *NewMessageSize += StrLength;\r
440 }\r
441 StrLength = sizeof(HTTP_CRLF_STR) - 1;\r
442 *NewMessageSize += StrLength;\r
443 //\r
444 // Final 0 for end flag.\r
445 //\r
446 *NewMessageSize += 1;\r
447\r
448 *NewMessage = AllocateZeroPool (*NewMessageSize);\r
449 if (*NewMessage == NULL) {\r
450 Status = EFI_OUT_OF_RESOURCES;\r
451 goto ON_EXIT;\r
452 }\r
453\r
454 NewMessagePtr = (UINT8 *)(*NewMessage);\r
455\r
456 for (Index = 0; Index < NewFieldCount; Index++) {\r
457 HttpHeader = &NewHeaderFields[Index];\r
458\r
459 StrLength = AsciiStrLen (HttpHeader->FieldName);\r
460 CopyMem (NewMessagePtr, HttpHeader->FieldName, StrLength);\r
461 NewMessagePtr += StrLength;\r
462\r
463 StrLength = sizeof(": ") - 1;\r
464 CopyMem (NewMessagePtr, ": ", StrLength);\r
465 NewMessagePtr += StrLength;\r
466\r
467 StrLength = AsciiStrLen (HttpHeader->FieldValue);\r
468 CopyMem (NewMessagePtr, HttpHeader->FieldValue, StrLength);\r
469 NewMessagePtr += StrLength;\r
470\r
471 StrLength = sizeof(HTTP_CRLF_STR) - 1;\r
472 CopyMem (NewMessagePtr, HTTP_CRLF_STR, StrLength);\r
473 NewMessagePtr += StrLength;\r
474 }\r
475 StrLength = sizeof(HTTP_CRLF_STR) - 1;\r
476 CopyMem (NewMessagePtr, HTTP_CRLF_STR, StrLength);\r
477 NewMessagePtr += StrLength;\r
478\r
479 *NewMessagePtr = 0;\r
480\r
481 ASSERT (*NewMessageSize == (UINTN) NewMessagePtr - (UINTN) (*NewMessage) + 1);\r
482\r
483 //\r
484 // Free allocated buffer \r
485 //\r
486ON_EXIT:\r
487 if(SeedHeaderFields != NULL) {\r
488 FreeHeaderFields(SeedHeaderFields, SeedFieldCount);\r
489 }\r
490 \r
491 if(TempHeaderFields != NULL) {\r
492 FreeHeaderFields(TempHeaderFields, TempFieldCount);\r
493 }\r
494\r
495 if(NewHeaderFields != NULL) {\r
496 FreeHeaderFields(NewHeaderFields, NewFieldCount);\r
497 }\r
498 \r
499 return Status;\r
500}\r
501\r
502/**\r
503 This function is used to transform data stored in HttpMessage into a list of fields \r
504 paired with their corresponding values.\r
505\r
506 @param[in] HttpMessage Contains raw unformatted HTTP header string. The buffer for this string will \r
507 be allocated and released by the caller.\r
508 @param[in] HttpMessageSize Size in bytes of raw unformatted HTTP header. \r
509 @param[out] HeaderFields Array of key/value header pairs. The storage for all header pairs is allocated\r
510 by the driver publishing this protocol, and must be freed by the caller. \r
511 @param[out] FieldCount Number of headers in HeaderFields.\r
512 \r
513 @retval EFI_SUCCESS Parse HTTP header into array of key/value pairs succeeded.\r
514 @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage.\r
515 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:\r
516 HttpMessage is NULL.\r
517 HeaderFields is NULL.\r
518 FieldCount is NULL.\r
519 \r
520**/\r
521EFI_STATUS\r
522HttpUtilitiesParse(\r
523 IN CHAR8 *HttpMessage,\r
524 IN UINTN HttpMessageSize,\r
525 OUT EFI_HTTP_HEADER **HeaderFields,\r
526 OUT UINTN *FieldCount\r
527 )\r
528{\r
529 EFI_STATUS Status;\r
530 CHAR8 *TempHttpMessage;\r
531 CHAR8 *Token;\r
532 CHAR8 *NextToken;\r
533 CHAR8 *FieldName;\r
534 CHAR8 *FieldValue;\r
535 UINTN Index;\r
536\r
537 if (HttpMessage == NULL || HeaderFields == NULL || FieldCount == NULL) {\r
538 return EFI_INVALID_PARAMETER;\r
539 }\r
540 \r
541 Status = EFI_SUCCESS;\r
542 TempHttpMessage = NULL;\r
543 *FieldCount = 0;\r
544 Token = NULL;\r
545 NextToken = NULL;\r
546 FieldName = NULL;\r
547 FieldValue = NULL;\r
548 Index = 0; \r
549\r
550 TempHttpMessage = AllocateZeroPool (HttpMessageSize);\r
551 if (TempHttpMessage == NULL) {\r
552 return EFI_OUT_OF_RESOURCES;\r
553 }\r
554\r
555 CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize);\r
556 \r
557 //\r
558 // Get header number\r
559 //\r
560 Token = TempHttpMessage;\r
561 while (TRUE) {\r
562 FieldName = NULL;\r
563 FieldValue = NULL;\r
564 NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue);\r
565 Token = NextToken;\r
566 if (FieldName == NULL || FieldValue == NULL) {\r
567 break;\r
568 }\r
569\r
570 (*FieldCount)++;\r
571 }\r
572\r
573 if(*FieldCount == 0) {\r
574 Status = EFI_INVALID_PARAMETER;\r
575 goto ON_EXIT;\r
576 }\r
577 \r
578 //\r
579 // Allocate buffer for header\r
580 //\r
581 *HeaderFields = AllocateZeroPool ((*FieldCount) * sizeof(EFI_HTTP_HEADER));\r
582 if (*HeaderFields == NULL) {\r
583 *FieldCount = 0;\r
584 Status = EFI_OUT_OF_RESOURCES;\r
585 goto ON_EXIT;\r
586 }\r
587 \r
588 CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize);\r
589 \r
590 //\r
591 // Set Field and Value to each header\r
592 //\r
593 Token = TempHttpMessage;\r
594 while (Index < *FieldCount) {\r
595 FieldName = NULL;\r
596 FieldValue = NULL;\r
597 NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue);\r
598 Token = NextToken;\r
599 if (FieldName == NULL || FieldValue == NULL) {\r
600 break;\r
601 }\r
602\r
603 Status = SetFieldNameAndValue(&(*HeaderFields)[Index], FieldName, FieldValue);\r
604 if(EFI_ERROR(Status)){\r
605 *FieldCount = 0;\r
606 FreeHeaderFields (*HeaderFields, Index);\r
607 goto ON_EXIT;\r
608 }\r
609 \r
610 Index++;\r
611 }\r
612\r
613 //\r
614 // Free allocated buffer \r
615 //\r
616ON_EXIT:\r
617 if (TempHttpMessage != NULL) {\r
618 FreePool(TempHttpMessage);\r
619 }\r
620 \r
621 return Status;\r
622}\r