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: Thu, 28 Apr 2005 08:56:05 -0700

Bugs item #1188280, was opened at 2005-04-22 19:44
Message generated for change (Comment added) made by balajilx
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: 6
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: balaji (balajilx)
Date: 2005-04-28 15:55

Message:
Logged In: YES
user_id=1264790

sure, please let me know
1. what code makes it non-portable. ( i guess i have used only
string functions)
2. what are those nine test cases
3. i agree that there is no need to allocate memory i can
continue parsing until \r\n to get the content length.
4. please let me know the indenting style.
5. The additional header fields are there for a reason. The
HOST header is required by some proxies else they
return an error stating host not specified. This i learned from
tests in the past. Please feel free to test it against some
proxies in http://aliveproxy.com.
6. chunked bodies should not be a problem coz of 1.0 is
my understanding.

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

Comment By: Daniel Stenberg (bagder)
Date: 2005-04-28 08:50

Message:
Logged In: YES
user_id=1110

I'll admit I have serious problems with this patch.

1. It doesn't build on non-windows (easily fixed though)

2. It breaks nine test cases (easily fixed as well, just
tedious)

3. It sends a whole range of new headers in the CONNECT I
disagree with

4. It *allocates memory* in the header parser which seems
unnessary?

5. it doesn't follow curl's source indenting style, which
makes it harder to read/follow

6. It doesn't support chunked-encoded bodies (which won't
show up as long as it sends a 1.0 CONNECT though)

I know this patch serves a purpose but I just don't like the
look of the fix. I think I'd rather have a proper fix done
later than applying this half-baked fix sooner.

Would you be willing to work on this?

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

Comment By: balaji (balajilx)
Date: 2005-04-27 17:15

Message:
Logged In: YES
user_id=1264790

i have attached the diff file of http.c with version 7.13.2 http.c

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

Comment By: Daniel Stenberg (bagder)
Date: 2005-04-23 21:42

Message:
Logged In: YES
user_id=1110

It didn't apply very good, there have been many changes done
since 7.11.1.

Is this even still a problem with 7.13.2? If so, can you
make a patch against that instead please?

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

Comment By: balaji (balajilx)
Date: 2005-04-22 23:05

Message:
Logged In: YES
user_id=1264790

i have attached the diff file for http.c along with this

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

Comment By: Daniel Stenberg (bagder)
Date: 2005-04-22 22:23

Message:
Logged In: YES
user_id=1110

The tool you look for is 'diff':

 The GNU diff and GNU patch tools exist for virtually all
platforms, including
 all kinds of Unixes and Windows:

 For unix-like operating systems:

        http://www.fsf.org/software/patch/patch.html
        http://www.gnu.org/directory/diffutils.html

 For Windows:

        http://gnuwin32.sourceforge.net/packages/patch.htm
        http://gnuwin32.sourceforge.net/packages/diffutils.htm

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

Comment By: balaji (balajilx)
Date: 2005-04-22 22:22

Message:
Logged In: YES
user_id=1264790

is it ok if i upload the http.c file or is there any tool
that would generate a patch file with two given
http.c files?

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

Comment By: Daniel Stenberg (bagder)
Date: 2005-04-22 22: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-28

These mail archives are generated by hypermail.

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

File upload with ASP.NET