]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/HttpDxe/HttpImpl.c
NetworkPkg/HttpDxe: HTTPS support over IPv4 and IPv6
[mirror_edk2.git] / NetworkPkg / HttpDxe / HttpImpl.c
index 6fcb0b7c6e27a0ddfa1fd64ac6bdb2cb16e24c8d..77aa64a2b99668ef6b65920e6820d3c665615332 100644 (file)
@@ -241,6 +241,7 @@ EfiHttpRequest (
   HTTP_PROTOCOL                 *HttpInstance;\r
   BOOLEAN                       Configure;\r
   BOOLEAN                       ReConfigure;\r
+  BOOLEAN                       TlsConfigure;\r
   CHAR8                         *RequestMsg;\r
   CHAR8                         *Url;\r
   UINTN                         UrlLen;\r
@@ -260,6 +261,7 @@ EfiHttpRequest (
   HostNameStr = NULL;\r
   Wrap = NULL;\r
   FileUrl = NULL;\r
+  TlsConfigure = FALSE;\r
 \r
   if ((This == NULL) || (Token == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -345,6 +347,32 @@ EfiHttpRequest (
 \r
 \r
     UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen);\r
+\r
+    //\r
+    // From the information in Url, the HTTP instance will \r
+    // be able to determine whether to use http or https.\r
+    //\r
+    HttpInstance->UseHttps = IsHttpsUrl (Url);\r
+\r
+    //\r
+    // Check whether we need to create Tls child and open the TLS protocol.\r
+    //\r
+    if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) {\r
+      //\r
+      // Use TlsSb to create Tls child and open the TLS protocol.\r
+      //\r
+      HttpInstance->TlsChildHandle = TlsCreateChild (\r
+                                       HttpInstance->Service->ImageHandle,\r
+                                       &(HttpInstance->Tls),\r
+                                       &(HttpInstance->TlsConfiguration)\r
+                                       );\r
+      if (HttpInstance->TlsChildHandle == NULL) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      TlsConfigure = TRUE;\r
+    }\r
+\r
     UrlParser = NULL;\r
     Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);\r
     if (EFI_ERROR (Status)) {\r
@@ -359,7 +387,11 @@ EfiHttpRequest (
 \r
     Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);\r
     if (EFI_ERROR (Status)) {\r
-      RemotePort = HTTP_DEFAULT_PORT;\r
+      if (HttpInstance->UseHttps) {\r
+        RemotePort = HTTPS_DEFAULT_PORT;\r
+      } else {\r
+        RemotePort = HTTP_DEFAULT_PORT;\r
+      }\r
     }\r
     //\r
     // If Configure is TRUE, it indicates the first time to call Request();\r
@@ -376,9 +408,13 @@ EfiHttpRequest (
       ReConfigure = FALSE;\r
     } else {\r
       if ((HttpInstance->RemotePort == RemotePort) &&\r
-        (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) {\r
+          (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0) && \r
+          (!HttpInstance->UseHttps || (HttpInstance->UseHttps && \r
+                                       !TlsConfigure && \r
+                                       HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) {\r
         //\r
         // Host Name and port number of the request URL are the same with previous call to Request().\r
+        // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.\r
         // Check whether previous TCP packet sent out.\r
         //\r
 \r
@@ -482,6 +518,16 @@ EfiHttpRequest (
     } else {\r
       ASSERT (HttpInstance->Tcp6 != NULL);\r
     }\r
+\r
+    if (HttpInstance->UseHttps && !TlsConfigure) {\r
+      Status = TlsCloseSession (HttpInstance);\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error1;\r
+      }\r
+      \r
+      TlsCloseTxRxEvent (HttpInstance);\r
+    }\r
+    \r
     HttpCloseConnection (HttpInstance);\r
     EfiHttpCancel (This, NULL);\r
   }\r
@@ -500,13 +546,18 @@ EfiHttpRequest (
   if (Request != NULL) {\r
     Wrap->TcpWrap.Method = Request->Method;\r
   }\r
-\r
-  Status = HttpInitTcp (HttpInstance, Wrap, Configure);\r
+  \r
+  Status = HttpInitSession (\r
+             HttpInstance, \r
+             Wrap, \r
+             Configure || ReConfigure, \r
+             TlsConfigure\r
+             );\r
   if (EFI_ERROR (Status)) {\r
     goto Error2;\r
-  }  \r
+  }\r
 \r
-  if (!Configure) {\r
+  if (!Configure && !ReConfigure && !TlsConfigure) {\r
     //\r
     // For the new HTTP token, create TX TCP token events.    \r
     //\r
@@ -593,9 +644,14 @@ Error4:
   }  \r
 \r
 Error3:\r
-  HttpCloseConnection (HttpInstance);\r
+  if (HttpInstance->UseHttps) {\r
+    TlsCloseSession (HttpInstance);\r
+    TlsCloseTxRxEvent (HttpInstance);\r
+  }\r
 \r
 Error2:\r
+  HttpCloseConnection (HttpInstance);\r
+  \r
   HttpCloseTcpConnCloseEvent (HttpInstance);\r
   if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) {\r
     gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event);\r
@@ -731,22 +787,30 @@ HttpCancel (
     }\r
   }\r
 \r
-  //\r
-  // Then check the tokens queued by EfiHttpResponse().\r
-  //\r
-  Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);\r
-  if (EFI_ERROR (Status)) {\r
-    if (Token != NULL) {\r
-      if (Status == EFI_ABORTED) {\r
-        return EFI_SUCCESS;\r
+  if (!HttpInstance->UseHttps) {\r
+    //\r
+    // Then check the tokens queued by EfiHttpResponse(), except for Https.\r
+    //\r
+    Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token);\r
+    if (EFI_ERROR (Status)) {\r
+      if (Token != NULL) {\r
+        if (Status == EFI_ABORTED) {\r
+          return EFI_SUCCESS;\r
+        } else {\r
+          return EFI_NOT_FOUND;\r
+        }\r
       } else {\r
-        return EFI_NOT_FOUND;\r
+        return Status;\r
       }\r
+    }\r
+  } else {\r
+    if (!HttpInstance->LocalAddressIsIPv6) {\r
+      HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);\r
     } else {\r
-      return Status;\r
+      HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);\r
     }\r
   }\r
-\r
+  \r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -882,6 +946,7 @@ HttpResponseWorker (
   NET_MAP_ITEM                  *Item;\r
   HTTP_TOKEN_WRAP               *ValueInItem;\r
   UINTN                         HdrLen;\r
+  NET_FRAGMENT                  Fragment;\r
 \r
   if (Wrap == NULL || Wrap->HttpInstance == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -899,16 +964,10 @@ HttpResponseWorker (
   BufferSize                = 0;\r
   EndofHeader               = NULL;\r
   ValueInItem               = NULL;\r
+  Fragment.Len              = 0;\r
+  Fragment.Bulk             = NULL;\r
  \r
   if (HttpMsg->Data.Response != NULL) {\r
-    //\r
-    // Need receive the HTTP headers, prepare buffer.\r
-    //\r
-    Status = HttpCreateTcpRxEventForHeader (HttpInstance);\r
-    if (EFI_ERROR (Status)) {\r
-      goto Error;\r
-    }\r
-\r
     //\r
     // Check whether we have cached header from previous call.\r
     //\r
@@ -1200,9 +1259,116 @@ HttpResponseWorker (
   //\r
   // We still need receive more data when there is no cache data and MsgParser is not NULL;\r
   //\r
-  Status = HttpTcpReceiveBody (Wrap, HttpMsg);\r
-  if (EFI_ERROR (Status)) {\r
-    goto Error2;\r
+  if (!HttpInstance->UseHttps) {\r
+    Status = HttpTcpReceiveBody (Wrap, HttpMsg);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+    \r
+  } else {\r
+    if (HttpInstance->TimeoutEvent == NULL) {\r
+      //\r
+      // Create TimeoutEvent for response\r
+      //\r
+      Status = gBS->CreateEvent (\r
+                      EVT_TIMER,\r
+                      TPL_CALLBACK,\r
+                      NULL,\r
+                      NULL,\r
+                      &HttpInstance->TimeoutEvent\r
+                      );\r
+      if (EFI_ERROR (Status)) {\r
+        goto Error2;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Start the timer, and wait Timeout seconds to receive the body packet.\r
+    //\r
+    Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+  \r
+    Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);\r
+\r
+    gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);\r
+    \r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+\r
+    //\r
+    // Check whether we receive a complete HTTP message.\r
+    //\r
+    Status = HttpParseMessageBody (\r
+               HttpInstance->MsgParser,\r
+               (UINTN) Fragment.Len,\r
+               (CHAR8 *) Fragment.Bulk\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error2;\r
+    }\r
+\r
+    if (HttpIsMessageComplete (HttpInstance->MsgParser)) {\r
+      //\r
+      // Free the MsgParse since we already have a full HTTP message.\r
+      //\r
+      HttpFreeMsgParser (HttpInstance->MsgParser);\r
+      HttpInstance->MsgParser = NULL;\r
+    }\r
+\r
+    //\r
+    // We receive part of header of next HTTP msg.\r
+    //\r
+    if (HttpInstance->NextMsg != NULL) {\r
+      HttpMsg->BodyLength = MIN ((UINTN) (HttpInstance->NextMsg - (CHAR8 *) Fragment.Bulk), HttpMsg->BodyLength);\r
+      CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);\r
+      \r
+      HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;\r
+      if (HttpInstance->CacheLen != 0) {\r
+        if (HttpInstance->CacheBody != NULL) {\r
+          FreePool (HttpInstance->CacheBody);\r
+        }\r
+        \r
+        HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);\r
+        if (HttpInstance->CacheBody == NULL) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+          goto Error2;\r
+        }\r
+        \r
+        CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);\r
+        HttpInstance->CacheOffset = 0;\r
+\r
+        HttpInstance->NextMsg = HttpInstance->CacheBody + (UINTN) (HttpInstance->NextMsg - (CHAR8 *) (Fragment.Bulk + HttpMsg->BodyLength));\r
+      }\r
+    } else {\r
+      HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength);\r
+      CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);\r
+      HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;\r
+      if (HttpInstance->CacheLen != 0) {\r
+        if (HttpInstance->CacheBody != NULL) {\r
+          FreePool (HttpInstance->CacheBody);\r
+        }\r
+        \r
+        HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);\r
+        if (HttpInstance->CacheBody == NULL) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+          goto Error2;\r
+        }\r
+\r
+        CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);\r
+        HttpInstance->CacheOffset = 0;\r
+      }\r
+    }\r
+\r
+    if (Fragment.Bulk != NULL) {\r
+      FreePool (Fragment.Bulk);\r
+      Fragment.Bulk = NULL;\r
+    }\r
+\r
+    goto Exit;\r
   }\r
 \r
   return Status;\r
@@ -1234,15 +1400,26 @@ Error:
   if (Item != NULL) {\r
     NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);\r
   }\r
-  \r
-  HttpTcpTokenCleanup (Wrap);\r
+\r
+  if (!HttpInstance->UseHttps) {\r
+    HttpTcpTokenCleanup (Wrap);\r
+  } else {\r
+    FreePool (Wrap);\r
+  }\r
   \r
   if (HttpHeaders != NULL) {\r
     FreePool (HttpHeaders);\r
+    HttpHeaders = NULL;\r
+  }\r
+\r
+  if (Fragment.Bulk != NULL) {\r
+    FreePool (Fragment.Bulk);\r
+    Fragment.Bulk = NULL;\r
   }\r
 \r
   if (HttpMsg->Headers != NULL) {\r
     FreePool (HttpMsg->Headers);\r
+    HttpMsg->Headers = NULL;\r
   }\r
 \r
   if (HttpInstance->CacheBody != NULL) {\r
@@ -1353,9 +1530,16 @@ EfiHttpResponse (
   Wrap->HttpInstance = HttpInstance;\r
   Wrap->HttpToken    = Token;\r
 \r
-  Status = HttpCreateTcpRxEvent (Wrap);\r
-  if (EFI_ERROR (Status)) {\r
-    goto Error;\r
+  //\r
+  // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to \r
+  // receive the https response. A special TlsRxToken is used for receiving TLS \r
+  // related messages. It should be a blocking response.\r
+  //\r
+  if (!HttpInstance->UseHttps) {\r
+    Status = HttpCreateTcpRxEvent (Wrap);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
   }\r
 \r
   Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap);\r