Revert "Imported Upstream version 7.53.1"
[platform/upstream/curl.git] / lib / http_proxy.c
index 7fde11d..082b73a 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
 #include "http.h"
 #include "url.h"
 #include "select.h"
+#include "rawstr.h"
 #include "progress.h"
 #include "non-ascii.h"
 #include "connect.h"
 #include "curlx.h"
-#include "vtls/vtls.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/*
- * Perform SSL initialization for HTTPS proxy.  Sets
- * proxy_ssl_connected connection bit when complete.  Can be
- * called multiple times.
- */
-static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex)
-{
-#ifdef USE_SSL
-  CURLcode result = CURLE_OK;
-  DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS);
-  if(!conn->bits.proxy_ssl_connected[sockindex]) {
-    /* perform SSL initialization for this socket */
-    result =
-      Curl_ssl_connect_nonblocking(conn, sockindex,
-                                   &conn->bits.proxy_ssl_connected[sockindex]);
-    if(result)
-      conn->bits.close = TRUE; /* a failed connection is marked for closure to
-                                  prevent (bad) re-use or similar */
-  }
-  return result;
-#else
-  (void) conn;
-  (void) sockindex;
-  return CURLE_NOT_BUILT_IN;
-#endif
-}
-
-CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
+CURLcode Curl_proxy_connect(struct connectdata *conn)
 {
-  if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
-    const CURLcode result = https_proxy_connect(conn, sockindex);
-    if(result)
-      return result;
-    if(!conn->bits.proxy_ssl_connected[sockindex])
-      return result; /* wait for HTTPS proxy SSL initialization to complete */
-  }
-
   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
 #ifndef CURL_DISABLE_PROXY
     /* for [protocol] tunneled through HTTP proxy */
@@ -98,31 +63,21 @@ CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
      * original pointer
      *
      * This function might be called several times in the multi interface case
-     * if the proxy's CONNECT response is not instant.
+     * if the proxy's CONNTECT response is not instant.
      */
     prot_save = conn->data->req.protop;
     memset(&http_proxy, 0, sizeof(http_proxy));
     conn->data->req.protop = &http_proxy;
     connkeep(conn, "HTTP proxy CONNECT");
-
-    /* for the secondary socket (FTP), use the "connect to host"
-     * but ignore the "connect to port" (use the secondary port)
-     */
-
     if(conn->bits.conn_to_host)
       hostname = conn->conn_to_host.name;
-    else if(sockindex == SECONDARYSOCKET)
-      hostname = conn->secondaryhostname;
     else
       hostname = conn->host.name;
-
-    if(sockindex == SECONDARYSOCKET)
-      remote_port = conn->secondary_port;
-    else if(conn->bits.conn_to_port)
+    if(conn->bits.conn_to_port)
       remote_port = conn->conn_to_port;
     else
       remote_port = conn->remote_port;
-    result = Curl_proxyCONNECT(conn, sockindex, hostname,
+    result = Curl_proxyCONNECT(conn, FIRSTSOCKET, hostname,
                                remote_port, FALSE);
     conn->data->req.protop = prot_save;
     if(CURLE_OK != result)
@@ -159,7 +114,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
   curl_off_t cl=0;
   bool closeConnection = FALSE;
   bool chunked_encoding = FALSE;
-  time_t check;
+  long check;
 
 #define SELECT_OK      0
 #define SELECT_ERROR   1
@@ -204,10 +159,10 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
       free(host_port);
 
       if(!result) {
-        char *host = NULL;
+        char *host=(char *)"";
         const char *proxyconn="";
         const char *useragent="";
-        const char *http = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ?
+        const char *http = (conn->proxytype == CURLPROXY_HTTP_1_0) ?
           "1.0" : "1.1";
         bool ipv6_ip = conn->bits.ipv6_ip;
         char *hostheader;
@@ -247,13 +202,13 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
                            "%s", /* Proxy-Connection */
                            hostheader,
                            http,
-                           host?host:"",
+                           host,
                            conn->allocptr.proxyuserpwd?
                            conn->allocptr.proxyuserpwd:"",
                            useragent,
                            proxyconn);
 
-        if(host)
+        if(host && *host)
           free(host);
         free(hostheader);
 
@@ -290,7 +245,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
     }
 
     if(!blocking) {
-      if(!Curl_conn_data_pending(conn, sockindex))
+      if(0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
         /* return so we'll be called again polling-style */
         return CURLE_OK;
       else {
@@ -309,22 +264,13 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
       char *ptr;
       char *line_start;
 
-      ptr = data->state.buffer;
+      ptr=data->state.buffer;
       line_start = ptr;
 
-      nread = 0;
-      perline = 0;
+      nread=0;
+      perline=0;
 
-      while(nread < BUFSIZE && keepon && !error) {
-        int writetype;
-
-        if(Curl_pgrsUpdate(conn))
-          return CURLE_ABORTED_BY_CALLBACK;
-
-        if(ptr >= &data->state.buffer[BUFSIZE]) {
-          failf(data, "CONNECT response too large!");
-          return CURLE_RECV_ERROR;
-        }
+      while((nread<BUFSIZE) && (keepon && !error)) {
 
         check = Curl_timeleft(data, NULL, TRUE);
         if(check <= 0) {
@@ -333,233 +279,254 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
           break;
         }
 
-        /* Read one byte at a time to avoid a race condition. Wait at most one
-           second before looping to ensure continuous pgrsUpdates. */
-        result = Curl_read(conn, tunnelsocket, ptr, 1, &gotbytes);
-        if(result == CURLE_AGAIN) {
-          if(SOCKET_READABLE(tunnelsocket, check<1000L?check:1000) == -1) {
-            error = SELECT_ERROR;
-            failf(data, "Proxy CONNECT aborted due to select/poll error");
-            break;
-          }
-          continue;
-        }
-        else if(result) {
-          keepon = FALSE;
+        /* loop every second at least, less if the timeout is near */
+        switch (Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD,
+                                  check<1000L?check:1000)) {
+        case -1: /* select() error, stop reading */
+          error = SELECT_ERROR;
+          failf(data, "Proxy CONNECT aborted due to select/poll error");
           break;
-        }
-        else if(gotbytes <= 0) {
-          if(data->set.proxyauth && data->state.authproxy.avail) {
-            /* proxy auth was requested and there was proxy auth available,
-               then deem this as "mere" proxy disconnect */
-            conn->bits.proxy_connect_closed = TRUE;
-            infof(data, "Proxy CONNECT connection closed\n");
-          }
-          else {
-            error = SELECT_ERROR;
-            failf(data, "Proxy CONNECT aborted");
-          }
-          keepon = FALSE;
+        case 0: /* timeout */
           break;
-        }
-
-        /* We got a byte of data */
-        nread++;
-
-        if(keepon > TRUE) {
-          /* This means we are currently ignoring a response-body */
-
-          nread = 0; /* make next read start over in the read buffer */
-          ptr = data->state.buffer;
-          if(cl) {
-            /* A Content-Length based body: simply count down the counter
-               and make sure to break out of the loop when we're done! */
-            cl--;
-            if(cl <= 0) {
-              keepon = FALSE;
-              break;
+        default:
+          DEBUGASSERT(ptr+BUFSIZE-nread <= data->state.buffer+BUFSIZE+1);
+          result = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
+                             &gotbytes);
+          if(result==CURLE_AGAIN)
+            continue; /* go loop yourself */
+          else if(result)
+            keepon = FALSE;
+          else if(gotbytes <= 0) {
+            keepon = FALSE;
+            if(data->set.proxyauth && data->state.authproxy.avail) {
+              /* proxy auth was requested and there was proxy auth available,
+                 then deem this as "mere" proxy disconnect */
+              conn->bits.proxy_connect_closed = TRUE;
+              infof(data, "Proxy CONNECT connection closed\n");
             }
-          }
-          else {
-            /* chunked-encoded body, so we need to do the chunked dance
-               properly to know when the end of the body is reached */
-            CHUNKcode r;
-            ssize_t tookcareof = 0;
-
-            /* now parse the chunked piece of data so that we can
-               properly tell when the stream ends */
-            r = Curl_httpchunk_read(conn, ptr, 1, &tookcareof);
-            if(r == CHUNKE_STOP) {
-              /* we're done reading chunks! */
-              infof(data, "chunk reading DONE\n");
-              keepon = FALSE;
-              /* we did the full CONNECT treatment, go COMPLETE */
-              conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+            else {
+              error = SELECT_ERROR;
+              failf(data, "Proxy CONNECT aborted");
             }
           }
-          continue;
-        }
-
-        perline++; /* amount of bytes in this line so far */
-
-        /* if this is not the end of a header line then continue */
-        if(*ptr != 0x0a) {
-          ptr++;
-          continue;
-        }
-
-        /* convert from the network encoding */
-        result = Curl_convert_from_network(data, line_start, perline);
-        /* Curl_convert_from_network calls failf if unsuccessful */
-        if(result)
-          return result;
-
-        /* output debug if that is requested */
-        if(data->set.verbose)
-          Curl_debug(data, CURLINFO_HEADER_IN,
-                     line_start, (size_t)perline, conn);
-
-        /* send the header to the callback */
-        writetype = CLIENTWRITE_HEADER;
-        if(data->set.include_header)
-          writetype |= CLIENTWRITE_BODY;
-
-        result = Curl_client_write(conn, writetype, line_start, perline);
-
-        data->info.header_size += (long)perline;
-        data->req.headerbytecount += (long)perline;
-
-        if(result)
-          return result;
-
-        /* Newlines are CRLF, so the CR is ignored as the line isn't
-           really terminated until the LF comes. Treat a following CR
-           as end-of-headers as well.*/
-
-        if(('\r' == line_start[0]) ||
-           ('\n' == line_start[0])) {
-          /* end of response-headers from the proxy */
-          nread = 0; /* make next read start over in the read
-                        buffer */
-          ptr = data->state.buffer;
-          if((407 == k->httpcode) && !data->state.authproblem) {
-            /* If we get a 407 response code with content length
-               when we have no auth problem, we must ignore the
-               whole response-body */
-            keepon = 2;
-
-            if(cl) {
-              infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
-                    " bytes of response-body\n", cl);
-            }
-            else if(chunked_encoding) {
-              CHUNKcode r;
-
-              infof(data, "Ignore chunked response-body\n");
-
-              /* We set ignorebody true here since the chunked
-                 decoder function will acknowledge that. Pay
-                 attention so that this is cleared again when this
-                 function returns! */
-              k->ignorebody = TRUE;
-
-              if(line_start[1] == '\n') {
-                /* this can only be a LF if the letter at index 0
-                   was a CR */
-                line_start++;
+          else {
+            /*
+             * We got a whole chunk of data, which can be anything from one
+             * byte to a set of lines and possibly just a piece of the last
+             * line.
+             */
+            int i;
+
+            nread += gotbytes;
+
+            if(keepon > TRUE) {
+              /* This means we are currently ignoring a response-body */
+
+              nread = 0; /* make next read start over in the read buffer */
+              ptr=data->state.buffer;
+              if(cl) {
+                /* A Content-Length based body: simply count down the counter
+                   and make sure to break out of the loop when we're done! */
+                cl -= gotbytes;
+                if(cl<=0) {
+                  keepon = FALSE;
+                  break;
+                }
               }
-
-              /* now parse the chunked piece of data so that we can
-                 properly tell when the stream ends */
-              r = Curl_httpchunk_read(conn, line_start + 1, 1, &gotbytes);
-              if(r == CHUNKE_STOP) {
-                /* we're done reading chunks! */
-                infof(data, "chunk reading DONE\n");
-                keepon = FALSE;
-                /* we did the full CONNECT treatment, go to
-                   COMPLETE */
-                conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+              else {
+                /* chunked-encoded body, so we need to do the chunked dance
+                   properly to know when the end of the body is reached */
+                CHUNKcode r;
+                ssize_t tookcareof=0;
+
+                /* now parse the chunked piece of data so that we can
+                   properly tell when the stream ends */
+                r = Curl_httpchunk_read(conn, ptr, gotbytes, &tookcareof);
+                if(r == CHUNKE_STOP) {
+                  /* we're done reading chunks! */
+                  infof(data, "chunk reading DONE\n");
+                  keepon = FALSE;
+                  /* we did the full CONNECT treatment, go COMPLETE */
+                  conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+                }
+                else
+                  infof(data, "Read %zd bytes of chunk, continue\n",
+                        tookcareof);
               }
             }
-            else {
-              /* without content-length or chunked encoding, we
-                 can't keep the connection alive since the close is
-                 the end signal so we bail out at once instead */
-              keepon = FALSE;
-            }
-          }
-          else
-            keepon = FALSE;
-          /* we did the full CONNECT treatment, go to COMPLETE */
-          conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
-          continue;
-        }
-
-        line_start[perline] = 0; /* zero terminate the buffer */
-        if((checkprefix("WWW-Authenticate:", line_start) &&
-            (401 == k->httpcode)) ||
-           (checkprefix("Proxy-authenticate:", line_start) &&
-            (407 == k->httpcode))) {
-
-          bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
-          char *auth = Curl_copy_header_value(line_start);
-          if(!auth)
-            return CURLE_OUT_OF_MEMORY;
-
-          result = Curl_http_input_auth(conn, proxy, auth);
-
-          free(auth);
-
-          if(result)
-            return result;
-        }
-        else if(checkprefix("Content-Length:", line_start)) {
-          if(k->httpcode/100 == 2) {
-            /* A server MUST NOT send any Transfer-Encoding or
-               Content-Length header fields in a 2xx (Successful)
-               response to CONNECT. (RFC 7231 section 4.3.6) */
-            failf(data, "Content-Length: in %03d response",
-                  k->httpcode);
-            return CURLE_RECV_ERROR;
-          }
-
-          cl = curlx_strtoofft(line_start +
-                               strlen("Content-Length:"), NULL, 10);
-        }
-        else if(Curl_compareheader(line_start, "Connection:", "close"))
-          closeConnection = TRUE;
-        else if(Curl_compareheader(line_start,
-                                   "Transfer-Encoding:",
-                                   "chunked")) {
-          if(k->httpcode/100 == 2) {
-            /* A server MUST NOT send any Transfer-Encoding or
-               Content-Length header fields in a 2xx (Successful)
-               response to CONNECT. (RFC 7231 section 4.3.6) */
-            failf(data, "Transfer-Encoding: in %03d response", k->httpcode);
-            return CURLE_RECV_ERROR;
+            else
+              for(i = 0; i < gotbytes; ptr++, i++) {
+                perline++; /* amount of bytes in this line so far */
+                if(*ptr == 0x0a) {
+                  char letter;
+                  int writetype;
+
+                  /* convert from the network encoding */
+                  result = Curl_convert_from_network(data, line_start,
+                                                     perline);
+                  /* Curl_convert_from_network calls failf if unsuccessful */
+                  if(result)
+                    return result;
+
+                  /* output debug if that is requested */
+                  if(data->set.verbose)
+                    Curl_debug(data, CURLINFO_HEADER_IN,
+                               line_start, (size_t)perline, conn);
+
+                  /* send the header to the callback */
+                  writetype = CLIENTWRITE_HEADER;
+                  if(data->set.include_header)
+                    writetype |= CLIENTWRITE_BODY;
+
+                  result = Curl_client_write(conn, writetype, line_start,
+                                             perline);
+
+                  data->info.header_size += (long)perline;
+                  data->req.headerbytecount += (long)perline;
+
+                  if(result)
+                    return result;
+
+                  /* Newlines are CRLF, so the CR is ignored as the line isn't
+                     really terminated until the LF comes. Treat a following CR
+                     as end-of-headers as well.*/
+
+                  if(('\r' == line_start[0]) ||
+                     ('\n' == line_start[0])) {
+                    /* end of response-headers from the proxy */
+                    nread = 0; /* make next read start over in the read
+                                  buffer */
+                    ptr=data->state.buffer;
+                    if((407 == k->httpcode) && !data->state.authproblem) {
+                      /* If we get a 407 response code with content length
+                         when we have no auth problem, we must ignore the
+                         whole response-body */
+                      keepon = 2;
+
+                      if(cl) {
+                        infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
+                              " bytes of response-body\n", cl);
+
+                        /* remove the remaining chunk of what we already
+                           read */
+                        cl -= (gotbytes - i);
+
+                        if(cl<=0)
+                          /* if the whole thing was already read, we are done!
+                           */
+                          keepon=FALSE;
+                      }
+                      else if(chunked_encoding) {
+                        CHUNKcode r;
+                        /* We set ignorebody true here since the chunked
+                           decoder function will acknowledge that. Pay
+                           attention so that this is cleared again when this
+                           function returns! */
+                        k->ignorebody = TRUE;
+                        infof(data, "%zd bytes of chunk left\n", gotbytes-i);
+
+                        if(line_start[1] == '\n') {
+                          /* this can only be a LF if the letter at index 0
+                             was a CR */
+                          line_start++;
+                          i++;
+                        }
+
+                        /* now parse the chunked piece of data so that we can
+                           properly tell when the stream ends */
+                        r = Curl_httpchunk_read(conn, line_start+1,
+                                                  gotbytes -i, &gotbytes);
+                        if(r == CHUNKE_STOP) {
+                          /* we're done reading chunks! */
+                          infof(data, "chunk reading DONE\n");
+                          keepon = FALSE;
+                          /* we did the full CONNECT treatment, go to
+                             COMPLETE */
+                          conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+                        }
+                        else
+                          infof(data, "Read %zd bytes of chunk, continue\n",
+                                gotbytes);
+                      }
+                      else {
+                        /* without content-length or chunked encoding, we
+                           can't keep the connection alive since the close is
+                           the end signal so we bail out at once instead */
+                        keepon=FALSE;
+                      }
+                    }
+                    else {
+                      keepon = FALSE;
+                      if(200 == data->info.httpproxycode) {
+                        if(gotbytes - (i+1))
+                          failf(data, "Proxy CONNECT followed by %zd bytes "
+                                "of opaque data. Data ignored (known bug #39)",
+                                gotbytes - (i+1));
+                      }
+                    }
+                    /* we did the full CONNECT treatment, go to COMPLETE */
+                    conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
+                    break; /* breaks out of for-loop, not switch() */
+                  }
+
+                  /* keep a backup of the position we are about to blank */
+                  letter = line_start[perline];
+                  line_start[perline]=0; /* zero terminate the buffer */
+                  if((checkprefix("WWW-Authenticate:", line_start) &&
+                      (401 == k->httpcode)) ||
+                     (checkprefix("Proxy-authenticate:", line_start) &&
+                      (407 == k->httpcode))) {
+
+                    bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
+                    char *auth = Curl_copy_header_value(line_start);
+                    if(!auth)
+                      return CURLE_OUT_OF_MEMORY;
+
+                    result = Curl_http_input_auth(conn, proxy, auth);
+
+                    free(auth);
+
+                    if(result)
+                      return result;
+                  }
+                  else if(checkprefix("Content-Length:", line_start)) {
+                    cl = curlx_strtoofft(line_start +
+                                         strlen("Content-Length:"), NULL, 10);
+                  }
+                  else if(Curl_compareheader(line_start,
+                                             "Connection:", "close"))
+                    closeConnection = TRUE;
+                  else if(Curl_compareheader(line_start,
+                                             "Transfer-Encoding:",
+                                             "chunked")) {
+                    infof(data, "CONNECT responded chunked\n");
+                    chunked_encoding = TRUE;
+                    /* init our chunky engine */
+                    Curl_httpchunk_init(conn);
+                  }
+                  else if(Curl_compareheader(line_start,
+                                             "Proxy-Connection:", "close"))
+                    closeConnection = TRUE;
+                  else if(2 == sscanf(line_start, "HTTP/1.%d %d",
+                                      &subversion,
+                                      &k->httpcode)) {
+                    /* store the HTTP code from the proxy */
+                    data->info.httpproxycode = k->httpcode;
+                  }
+                  /* put back the letter we blanked out before */
+                  line_start[perline]= letter;
+
+                  perline=0; /* line starts over here */
+                  line_start = ptr+1; /* this skips the zero byte we wrote */
+                }
+              }
           }
-          infof(data, "CONNECT responded chunked\n");
-          chunked_encoding = TRUE;
-          /* init our chunky engine */
-          Curl_httpchunk_init(conn);
-        }
-        else if(Curl_compareheader(line_start, "Proxy-Connection:", "close"))
-          closeConnection = TRUE;
-        else if(2 == sscanf(line_start, "HTTP/1.%d %d",
-                            &subversion,
-                            &k->httpcode)) {
-          /* store the HTTP code from the proxy */
-          data->info.httpproxycode = k->httpcode;
-        }
-
-        perline = 0; /* line starts over here */
-        ptr = data->state.buffer;
-        line_start = ptr;
+          break;
+        } /* switch */
+        if(Curl_pgrsUpdate(conn))
+          return CURLE_ABORTED_BY_CALLBACK;
       } /* while there's buffer left and loop is requested */
 
-      if(Curl_pgrsUpdate(conn))
-        return CURLE_ABORTED_BY_CALLBACK;
-
       if(error)
         return CURLE_RECV_ERROR;
 
@@ -573,7 +540,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
         if(conn->bits.close)
           /* the connection has been marked for closure, most likely in the
              Curl_http_auth_act() function and thus we can kill it at once
-             below */
+             below
+          */
           closeConnection = TRUE;
       }
 
@@ -634,7 +602,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
 
   data->state.authproxy.done = TRUE;
 
-  infof(data, "Proxy replied OK to CONNECT request\n");
+  infof (data, "Proxy replied OK to CONNECT request\n");
   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
                                          document request  */