1 /** @file
2   Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
3 
4   Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials
6   are licensed and made available under the terms and conditions of the BSD License
7   which accompanies this distribution.  The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "HttpUtilitiesDxe.h"
16 
17 EFI_HTTP_UTILITIES_PROTOCOL mHttpUtilitiesProtocol = {
18   HttpUtilitiesBuild,
19   HttpUtilitiesParse
20 };
21 
22 
23 /**
24   Create HTTP header based on a combination of seed header, fields
25   to delete, and fields to append.
26 
27   The Build() function is used to manage the headers portion of an
28   HTTP message by providing the ability to add, remove, or replace
29   HTTP headers.
30 
31   @param[in]  This                Pointer to EFI_HTTP_UTILITIES_PROTOCOL instance.
32   @param[in]  SeedMessageSize     Size of the initial HTTP header. This can be zero.
33   @param[in]  SeedMessage         Initial HTTP header to be used as a base for
34                                   building a new HTTP header. If NULL,
35                                   SeedMessageSize is ignored.
36   @param[in]  DeleteCount         Number of null-terminated HTTP header field names
37                                   in DeleteList.
38   @param[in]  DeleteList          List of null-terminated HTTP header field names to
39                                   remove from SeedMessage. Only the field names are
40                                   in this list because the field values are irrelevant
41                                   to this operation.
42   @param[in]  AppendCount         Number of header fields in AppendList.
43   @param[in]  AppendList          List of HTTP headers to populate NewMessage with.
44                                   If SeedMessage is not NULL, AppendList will be
45                                   appended to the existing list from SeedMessage in
46                                   NewMessage.
47   @param[out] NewMessageSize      Pointer to number of header fields in NewMessage.
48   @param[out] NewMessage          Pointer to a new list of HTTP headers based on.
49 
50   @retval EFI_SUCCESS             Add, remove, and replace operations succeeded.
51   @retval EFI_OUT_OF_RESOURCES    Could not allocate memory for NewMessage.
52   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
53                                   This is NULL.
54 **/
55 EFI_STATUS
56 EFIAPI
HttpUtilitiesBuild(IN EFI_HTTP_UTILITIES_PROTOCOL * This,IN UINTN SeedMessageSize,IN VOID * SeedMessage,OPTIONAL IN UINTN DeleteCount,IN CHAR8 * DeleteList[],OPTIONAL IN UINTN AppendCount,IN EFI_HTTP_HEADER * AppendList[],OPTIONAL OUT UINTN * NewMessageSize,OUT VOID ** NewMessage)57 HttpUtilitiesBuild (
58   IN     EFI_HTTP_UTILITIES_PROTOCOL *This,
59   IN     UINTN                       SeedMessageSize,
60   IN     VOID                        *SeedMessage, OPTIONAL
61   IN     UINTN                       DeleteCount,
62   IN     CHAR8                       *DeleteList[], OPTIONAL
63   IN     UINTN                       AppendCount,
64   IN     EFI_HTTP_HEADER             *AppendList[], OPTIONAL
65      OUT UINTN                       *NewMessageSize,
66      OUT VOID                        **NewMessage
67   )
68 {
69   EFI_STATUS                Status;
70   EFI_HTTP_HEADER           *SeedHeaderFields;
71   UINTN                     SeedFieldCount;
72   UINTN                     Index;
73   EFI_HTTP_HEADER           *TempHeaderFields;
74   UINTN                     TempFieldCount;
75   EFI_HTTP_HEADER           *NewHeaderFields;
76   UINTN                     NewFieldCount;
77   EFI_HTTP_HEADER           *HttpHeader;
78   UINTN                     StrLength;
79   UINT8                     *NewMessagePtr;
80 
81   SeedHeaderFields = NULL;
82   SeedFieldCount   = 0;
83   TempHeaderFields = NULL;
84   TempFieldCount   = 0;
85   NewHeaderFields  = NULL;
86   NewFieldCount    = 0;
87 
88   HttpHeader       = NULL;
89   StrLength        = 0;
90   NewMessagePtr    = NULL;
91   *NewMessageSize  = 0;
92   Status           = EFI_SUCCESS;
93 
94   if (This == NULL) {
95     return EFI_INVALID_PARAMETER;
96   }
97 
98   if (SeedMessage != NULL) {
99     Status = This->Parse (
100                      This,
101                      SeedMessage,
102                      SeedMessageSize,
103                      &SeedHeaderFields,
104                      &SeedFieldCount
105                      );
106     if (EFI_ERROR (Status)) {
107       goto ON_EXIT;
108     }
109   }
110 
111   //
112   // Handle DeleteList
113   //
114   if (SeedFieldCount != 0 && DeleteCount != 0) {
115     TempHeaderFields = AllocateZeroPool (SeedFieldCount * sizeof(EFI_HTTP_HEADER));
116     if (TempHeaderFields == NULL) {
117       Status = EFI_OUT_OF_RESOURCES;
118       goto ON_EXIT;
119     }
120 
121     for (Index = 0, TempFieldCount = 0; Index < SeedFieldCount; Index++) {
122       //
123       // Check whether each SeedHeaderFields member is in DeleteList
124       //
125       if (IsValidHttpHeader( DeleteList, DeleteCount, SeedHeaderFields[Index].FieldName)) {
126         Status = SetFieldNameAndValue (
127                    &TempHeaderFields[TempFieldCount],
128                    SeedHeaderFields[Index].FieldName,
129                    SeedHeaderFields[Index].FieldValue
130                    );
131         if (EFI_ERROR (Status)) {
132           goto ON_EXIT;
133         }
134         TempFieldCount++;
135       }
136     }
137   } else {
138     TempHeaderFields = SeedHeaderFields;
139     TempFieldCount = SeedFieldCount;
140   }
141 
142   //
143   // Handle AppendList
144   //
145   NewHeaderFields = AllocateZeroPool ((TempFieldCount + AppendCount) * sizeof (EFI_HTTP_HEADER));
146   if (NewHeaderFields == NULL) {
147     Status = EFI_OUT_OF_RESOURCES;
148     goto ON_EXIT;
149   }
150 
151   for (Index = 0; Index < TempFieldCount; Index++) {
152     Status = SetFieldNameAndValue (
153                &NewHeaderFields[Index],
154                TempHeaderFields[Index].FieldName,
155                TempHeaderFields[Index].FieldValue
156                );
157     if (EFI_ERROR (Status)) {
158       goto ON_EXIT;
159     }
160   }
161 
162   NewFieldCount = TempFieldCount;
163 
164   for (Index = 0; Index < AppendCount; Index++) {
165     HttpHeader = FindHttpHeader (NewHeaderFields, NewFieldCount, AppendList[Index]->FieldName);
166     if (HttpHeader != NULL) {
167       Status = SetFieldNameAndValue (
168                  HttpHeader,
169                  AppendList[Index]->FieldName,
170                  AppendList[Index]->FieldValue
171                  );
172       if (EFI_ERROR (Status)) {
173         goto ON_EXIT;
174       }
175     } else {
176       Status = SetFieldNameAndValue (
177                  &NewHeaderFields[NewFieldCount],
178                  AppendList[Index]->FieldName,
179                  AppendList[Index]->FieldValue
180                  );
181       if (EFI_ERROR (Status)) {
182         goto ON_EXIT;
183       }
184       NewFieldCount++;
185     }
186   }
187 
188   //
189   // Calculate NewMessageSize, then build NewMessage
190   //
191   for (Index = 0; Index < NewFieldCount; Index++) {
192     HttpHeader = &NewHeaderFields[Index];
193 
194     StrLength = AsciiStrLen (HttpHeader->FieldName);
195     *NewMessageSize += StrLength;
196 
197     StrLength = sizeof(": ") - 1;
198     *NewMessageSize += StrLength;
199 
200     StrLength = AsciiStrLen (HttpHeader->FieldValue);
201     *NewMessageSize += StrLength;
202 
203     StrLength = sizeof("\r\n") - 1;
204     *NewMessageSize += StrLength;
205   }
206   StrLength = sizeof("\r\n") - 1;
207   *NewMessageSize += StrLength;
208 
209   //
210   // Final 0 for end flag
211   //
212   *NewMessageSize += 1;
213 
214   *NewMessage = AllocateZeroPool (*NewMessageSize);
215   if (*NewMessage == NULL) {
216     Status = EFI_OUT_OF_RESOURCES;
217     goto ON_EXIT;
218   }
219 
220   NewMessagePtr = (UINT8 *)(*NewMessage);
221 
222   for (Index = 0; Index < NewFieldCount; Index++) {
223     HttpHeader = &NewHeaderFields[Index];
224 
225     StrLength = AsciiStrLen (HttpHeader->FieldName);
226     CopyMem (NewMessagePtr, HttpHeader->FieldName, StrLength);
227     NewMessagePtr += StrLength;
228 
229     StrLength = sizeof(": ") - 1;
230     CopyMem (NewMessagePtr, ": ", StrLength);
231     NewMessagePtr += StrLength;
232 
233     StrLength = AsciiStrLen (HttpHeader->FieldValue);
234     CopyMem (NewMessagePtr, HttpHeader->FieldValue, StrLength);
235     NewMessagePtr += StrLength;
236 
237     StrLength = sizeof("\r\n") - 1;
238     CopyMem (NewMessagePtr, "\r\n", StrLength);
239     NewMessagePtr += StrLength;
240   }
241   StrLength = sizeof("\r\n") - 1;
242   CopyMem (NewMessagePtr, "\r\n", StrLength);
243   NewMessagePtr += StrLength;
244 
245   *NewMessagePtr = 0;
246 
247   ASSERT (*NewMessageSize == (UINTN)NewMessagePtr - (UINTN)(*NewMessage) + 1);
248 
249   //
250   // Free allocated buffer
251   //
252 ON_EXIT:
253   if (SeedHeaderFields != NULL) {
254     FreeHeaderFields(SeedHeaderFields, SeedFieldCount);
255   }
256 
257   if (TempHeaderFields != NULL) {
258     FreeHeaderFields(TempHeaderFields, TempFieldCount);
259   }
260 
261   if (NewHeaderFields != NULL) {
262     FreeHeaderFields(NewHeaderFields, NewFieldCount);
263   }
264 
265   return Status;
266 }
267 
268 
269 /**
270   Parses HTTP header and produces an array of key/value pairs.
271 
272   The Parse() function is used to transform data stored in HttpHeader
273   into a list of fields paired with their corresponding values.
274 
275   @param[in]  This                Pointer to EFI_HTTP_UTILITIES_PROTOCOL instance.
276   @param[in]  HttpMessage         Contains raw unformatted HTTP header string.
277   @param[in]  HttpMessageSize     Size of HTTP header.
278   @param[out] HeaderFields        Array of key/value header pairs.
279   @param[out] FieldCount          Number of headers in HeaderFields.
280 
281   @retval EFI_SUCCESS             Allocation succeeded.
282   @retval EFI_NOT_STARTED         This EFI HTTP Protocol instance has not been
283                                   initialized.
284   @retval EFI_INVALID_PARAMETER   One or more of the following conditions is TRUE:
285                                   This is NULL.
286                                   HttpMessage is NULL.
287                                   HeaderFields is NULL.
288                                   FieldCount is NULL.
289 **/
290 EFI_STATUS
291 EFIAPI
HttpUtilitiesParse(IN EFI_HTTP_UTILITIES_PROTOCOL * This,IN CHAR8 * HttpMessage,IN UINTN HttpMessageSize,OUT EFI_HTTP_HEADER ** HeaderFields,OUT UINTN * FieldCount)292 HttpUtilitiesParse (
293   IN  EFI_HTTP_UTILITIES_PROTOCOL  *This,
294   IN  CHAR8                        *HttpMessage,
295   IN  UINTN                        HttpMessageSize,
296   OUT EFI_HTTP_HEADER              **HeaderFields,
297   OUT UINTN                        *FieldCount
298   )
299 {
300   EFI_STATUS                Status;
301   CHAR8                     *TempHttpMessage;
302   CHAR8                     *Token;
303   CHAR8                     *NextToken;
304   CHAR8                     *FieldName;
305   CHAR8                     *FieldValue;
306   UINTN                     Index;
307 
308   Status          = EFI_SUCCESS;
309   TempHttpMessage = NULL;
310   Token           = NULL;
311   NextToken       = NULL;
312   FieldName       = NULL;
313   FieldValue      = NULL;
314   Index           = 0;
315 
316   if (This == NULL || HttpMessage == NULL || HeaderFields == NULL || FieldCount == NULL) {
317     return EFI_INVALID_PARAMETER;
318   }
319 
320   TempHttpMessage = AllocateZeroPool (HttpMessageSize);
321   if (TempHttpMessage == NULL) {
322     return EFI_OUT_OF_RESOURCES;
323   }
324 
325   CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize);
326 
327   //
328   // Get header number
329   //
330   *FieldCount = 0;
331   Token = TempHttpMessage;
332   while (TRUE) {
333     FieldName     = NULL;
334     FieldValue    = NULL;
335     NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue);
336     Token     = NextToken;
337     if (FieldName == NULL || FieldValue == NULL) {
338       break;
339     }
340 
341     (*FieldCount)++;
342   }
343 
344   if (*FieldCount == 0) {
345     Status =  EFI_INVALID_PARAMETER;
346     goto ON_EXIT;
347   }
348 
349   //
350   // Allocate buffer for header
351   //
352   *HeaderFields = AllocateZeroPool ((*FieldCount) * sizeof(EFI_HTTP_HEADER));
353   if (*HeaderFields == NULL) {
354     *FieldCount = 0;
355     Status = EFI_OUT_OF_RESOURCES;
356     goto ON_EXIT;
357   }
358 
359   CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize);
360 
361   //
362   // Set Field and Value to each header
363   //
364   Token = TempHttpMessage;
365   while (Index < *FieldCount) {
366     FieldName     = NULL;
367     FieldValue    = NULL;
368     NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue);
369     Token     = NextToken;
370     if (FieldName == NULL || FieldValue == NULL) {
371       break;
372     }
373 
374     Status = SetFieldNameAndValue (&(*HeaderFields)[Index], FieldName, FieldValue);
375     if (EFI_ERROR (Status)) {
376       *FieldCount = 0;
377       FreeHeaderFields (*HeaderFields, Index);
378       goto ON_EXIT;
379     }
380 
381     Index++;
382   }
383 
384   //
385   // Free allocated buffer
386   //
387 ON_EXIT:
388   if (TempHttpMessage != NULL) {
389     FreePool (TempHttpMessage);
390   }
391 
392   return Status;
393 }