cURL
Haxx ad
libcurl

curl's project page on SourceForge.net

Sponsors:
Haxx

cURL > Mailing List > Monthly Index > Single Mail

curl-tracker mailing list Archives

[ curl-Bugs-1188280 ] Fix for proxies sending 407 with content-length and body

From: SourceForge.net <noreply_at_sourceforge.net>
Date: Fri, 22 Apr 2005 15:07:39 -0700

Bugs item #1188280, was opened at 2005-04-22 21:44
Message generated for change (Comment added) made by bagder
You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=100976&aid=1188280&group_id=976

Category: http
>Group: bad behaviour
Status: Open
Resolution: None
Priority: 5
Submitted By: balaji (balajilx)
Assigned to: Daniel Stenberg (bagder)
Summary: Fix for proxies sending 407 with content-length and body

Initial Comment:
Dear All,
    Following is a fix for proxies that send a content-
length with the reply 407 (ISA proxy for instance). The
Current logic fails under this scenario because it
will not read the reply completely before sending
the connect request for authorization again. This results
in the proxy closing the connection.

I have added a function Curl_checkHeaderReceived to
check whether the header has been received and to
check for content length in the header to enable full
receipt of the body before sending another connect
request with authorization string.

TODO: Logic has to be improved to support TE
CHUNKED if any 1.1 proxy sends a chunked transfer
encoding with body length in first 4 bytes of body.

Regards
L.Balaji

static bool Curl_checkHeaderReceived(char * ptr,int
bufsize,unsigned int * puiHdrLength,unsigned long
*pulContentLength)
{
        /*
        * condition for header received is a \r\n in a
line by itself.
        */
        int i=0;
        char * lineBegin=ptr;
        char * sztoken = NULL;
        char * ptrLine = NULL;
        *puiHdrLength=0;
        *pulContentLength=0;
        
        for(i=0;i< bufsize;i++)
        {
                if( ptr[i]=='\n' )
                {
                        if(strncmp
(lineBegin,"\r\n",2) == 0)
                        {
                        
        *puiHdrLength = i+1;
                                return
TRUE;
                        }

                        if(strnicmp
(lineBegin,"Content-Length:",15) == 0)
                        {
                                ptrLine =
(char *)calloc(1,(i-(lineBegin-ptr))+1);
                                if(ptrLine)
                                {
                                
        strncpy(ptrLine,lineBegin,i-(lineBegin-ptr));
                                
                                
        sztoken=strtok(ptrLine,":");
                                        if
(sztoken)
                                        {
                                        
        sztoken = strtok(NULL,":");
                                        
        if(sztoken)
                                        
                *pulContentLength = atol(sztoken);
                                        }
                                
        free(ptrLine);
                                }
                        }

                        if(i+1 < bufsize )
                        {
                                lineBegin=
(ptr+i+1);
                        }
                }
        }
        return FALSE;
}

/*
 * ConnectHTTPProxyTunnel() requires that we're
connected to a HTTP proxy. This
 * function will issue the necessary commands to get a
seamless tunnel through
 * this proxy. After that, the socket can be used just as
a normal socket.
 */

CURLcode Curl_ConnectHTTPProxyTunnel(struct
connectdata *conn,
                                     int sockindex,
                                     char *hostname,
                                     int remote_port)
{
  int httpcode=0;
  int subversion=0;
  struct SessionHandle *data=conn->data;
  CURLcode result;
  int res;

  size_t nread; /* total size read */
  int perline; /* count bytes per line */
  bool keepon=TRUE;
  ssize_t gotbytes;
  char *ptr;
  long timeout = 3600; /* default timeout in seconds */
  struct timeval interval;
  fd_set rkeepfd;
  fd_set readfd;
  char *line_start;
  char *host_port;
  unsigned int uiHdrLength=0;
  unsigned long ulContentLength=0;
  curl_socket_t tunnelsocket = conn->sock[sockindex];

#define SELECT_OK 0
#define SELECT_ERROR 1
#define SELECT_TIMEOUT 2
  int error = SELECT_OK;

  infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
hostname, remote_port);

  do {
    bool auth; /* we don't really have to know when the
auth phase is done,
                  but this variable will be set to true then */

    if(conn->newurl) {
      /* This only happens if we've looped here due to
authentication reasons,
         and we don't really use the newly cloned URL
here then. Just free()
         it. */
      free(conn->newurl);
      conn->newurl = NULL;
    }

    host_port = aprintf("%s:%d", hostname, remote_port);
    if(!host_port)
      return CURLE_OUT_OF_MEMORY;

    /* Setup the proxy-authorization header, if any */
    result = http_auth_headers(conn, (char
*)"CONNECT", host_port, &auth);
    if(CURLE_OK == result) {

      /* OK, now send the connect request to the proxy */
      result =
        Curl_sendf(tunnelsocket, conn,
                   "CONNECT %s:%d HTTP/1.0\015
\012HOST: %s:%d\015\012Content-Length: 0\015
\012Proxy-Connection: Keep-Alive\015\012Pragma: no-
cache\015\012"
                   "%s"
                   "%s"
                   "\r\n",
                   hostname, remote_port,
                                   
hostname, remote_port,
                   (conn->bits.proxy_user_passwd &&conn-
>allocptr.proxyuserpwd) ?
                   conn->allocptr.proxyuserpwd:"",
                   data->set.useragent?conn-
>allocptr.uagent:""
                   );
      if(result)
        failf(data, "Failed sending CONNECT to proxy");
    }
    free(host_port);
    if(result)
      return result;

    FD_ZERO (&readfd); /* clear it */
    FD_SET (tunnelsocket, &readfd); /* read socket */

    /* get this in a backup variable to be able to restore it
on each lap in
       the select() loop */
    rkeepfd = readfd;

    ptr=data->state.buffer;
    line_start = ptr;

    nread=0;
    perline=0;
    keepon=TRUE;

    while((nread<BUFSIZE) && (keepon && !error)) {
      readfd = rkeepfd; /* set every lap */
      interval.tv_sec = 1; /* timeout each second and
check the timeout */
      interval.tv_usec = 0;

      if(data->set.timeout) {
        /* if timeout is requested, find out how much
remaining time we have */
        timeout = data->set.timeout - /* timeout time */
          Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /*
spent time */
        if(timeout <=0 ) {
          failf(data, "Proxy connection aborted due to
timeout");
          error = SELECT_TIMEOUT; /* already too little
time */
          break;
        }
      }
      
      switch (select (tunnelsocket+1, &readfd, NULL,
NULL, &interval)) {
      case -1: /* select() error, stop reading */
        error = SELECT_ERROR;
        failf(data, "Proxy CONNECT aborted due to select
() error");
        break;
      case 0: /* timeout */
        break;
      default:
        /*
         * This code previously didn't use the kerberos
sec_read() code
         * to read, but when we use Curl_read() it may do
so. Do confirm
         * that this is still ok and then remove this
comment!
         */
        res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-
nread, &gotbytes);
        if(res< 0)
          /* EWOULDBLOCK */
          continue; /* go loop yourself */
        else if(res)
          keepon = FALSE;
        else if(gotbytes <= 0) {
          keepon = FALSE;
          error = SELECT_ERROR;
          failf(data, "Proxy CONNECT aborted");
        }
        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.
           *
           * TODO: To make this code work less error-
prone, we need to make
           * sure that we read and create full lines before
we compare them,
           * as there is really nothing that stops the proxy
from delivering
           * the response lines in multiple parts, each part
consisting of
           * only a little piece of the line(s). */
          unsigned int i;
                 
          nread += gotbytes;
                  /*
                  * Begin changes L.Balaji
04/22/2005
                  */
                  /*
                  * Make sure that we have
received the entire header and body
                  * before we proceed. The proxy
might send the data in pieces
                  * or might even have a body.
                  * L.Balaji 04/22/2005
                  */
                  if( Curl_checkHeaderReceived
(ptr,nread, &uiHdrLength,&ulContentLength) )
                  {
                          /*
                          * Check whether we
have got any content-length header.
                          * TODO: add support
for HTTP 1.1 TE CHUNKED to read the size from
                          * body.
                          * L.Balaji 04/22/2005
                          */
                          if( ulContentLength >
0 )
                          {
                                  /*
                                  * Check if
we have received entire body else continue.
                                  * L.Balaji
04/22/2005
                                  */
                                  if(
(ulContentLength+uiHdrLength )> nread )
                                  {
                                          
ptr = ptr + nread;
                                          
continue;
                                  }
                          }
                  }
                  else
                  {
                          ptr = ptr + nread;
                          continue;
                  }
                  ptr = line_start;
                 /*
                  * End changes L.Balaji
04/22/2005
                  */
          for(i = 0; i < nread; ptr++, i++) {
            perline++; /* amount of bytes in this line so far */
            if(*ptr=='\n') {
              char letter;
              int writetype;
            
              /* output debug output if that is requested */
              if(data->set.verbose)
                Curl_debug(data, CURLINFO_HEADER_IN,
line_start, perline);

              /* send the header to the callback */
              writetype = CLIENTWRITE_HEADER;
              if(data->set.http_include_header)
                writetype |= CLIENTWRITE_BODY;

              result = Curl_client_write(data, writetype,
line_start, 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 */
                keepon=FALSE;
                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 == httpcode)) ||
                 (checkprefix("Proxy-authenticate:",
line_start) &&
                  (407 == httpcode))) {
                result = Curl_http_auth(conn, httpcode,
line_start);
                if(result)
                  return result;
              }
              else if(2 == sscanf(line_start, "HTTP/1.%d %
d",
                                  &subversion,
                                  &httpcode)) {
                /* store the HTTP code */
                data->info.httpproxycode =
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 */
            }
          }
        }
        break;
      } /* switch */
    } /* while there's buffer left and loop is requested */

    if(error)
      return CURLE_RECV_ERROR;

    /* Deal with the possibly already received
authenticate headers. 'newurl'
       is set to a new URL if we must loop. */
    Curl_http_auth_act(conn);
  
  } while(conn->newurl);

  if(200 != httpcode) {
    failf(data, "Received HTTP code %d from proxy after
CONNECT", httpcode);
    return CURLE_RECV_ERROR;
  }
  
  /* If a proxy-authorization header was used for the
proxy, then we should
     make sure that it isn't accidentally used for the
document request
     after we've connected. So let's free and clear it here.
*/
  Curl_safefree(conn->allocptr.proxyuserpwd);
  conn->allocptr.proxyuserpwd = NULL;

  Curl_http_auth_stage(data, 401); /* move on to the
host auth */

  infof (data, "Proxy replied OK to CONNECT request\n");
  return CURLE_OK;
}

----------------------------------------------------------------------

>Comment By: Daniel Stenberg (bagder)
Date: 2005-04-23 00:07

Message:
Logged In: YES
user_id=1110

Can you please provide this as a proper patch and attach it
to this report? Getting source code off the tracker like the
one you pasted here is painful.

I think the _best_ fix would be fix the code to use
Curl_readwrite() even when reading the CONNECT response. But
that is an even larger work.

----------------------------------------------------------------------

You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=100976&aid=1188280&group_id=976
_______________________________________________
http://cool.haxx.se/cgi-bin/mailman/listinfo/curl-tracker
Received on 2005-04-23

These mail archives are generated by hypermail.

donate! Page updated November 12, 2010.
web site info

File upload with ASP.NET