cURL / Mailing Lists / curl-library / Single Mail

curl-library

socket error with NTLM auth proxy and multi interface

From: Pingzhong Li <PLi_at_fiberlink.com>
Date: Tue, 4 Oct 2011 09:34:39 -0400

Hi, expert,

The libcurl version we are using is 7.21.1. The libcurl is compiled with openssl, and it is not compiled with WINTLS ( which I would think it is SSPI).

We are using the libcurl for the http communication. We are using the multi interface and there is only 1 easy interface wrapped inside the multi interface. Here are steps we did:

1. We first do curl_easy_perform to send a request and receive response with easy interface to make sure we could connect with server

2. we then put the easy interface inside a multi interface

3. Then we would send out the request and then libcurl would connect again since multi interface won't reuse connection from easy interface and we do make sure all the data is send out before we start checking if we received any data or not

4. On the receive side, we would use a function to check if there is any data or not to receive by using select on socket and then we could call the curl_multi_perform.

5. After all the receive is done, then we would send data again. It would be same as step 3 except the connection established at 3 would be reused

6. After the send is done, it would be same 4 for receive.

7. Repeat step 5 and 6.

The code works fine on direct connection, proxy without authentication and basic and digest proxy. However it doesn't work for NTLM proxy. We are keeping get the socket error on select call on first receive call at step 4 there.

Here is the code that we are checking for data available. It is used for both send and receive to make sure that socket is available for read or write:
      Int dataAvailable(int iTimeout = -1)
      {

            struct timeval timeout;
            timeout.tv_sec = 1;
            timeout.tv_usec = 0;
            int maxfd = -1;
            long curl_timeo = -1;
            CURLMcode mres;

            fd_set fdread;
            fd_set fdwrite;
            fd_set fdexcep;

            FD_ZERO(&fdread);
            FD_ZERO(&fdwrite);
            FD_ZERO(&fdexcep);
            if(iTimeout > 0)
            {
            timeout.tv_sec = iTimeout / 1000000;
            timeout.tv_usec = iTimeout % 1000000;
            }
            mres = curl_multi_timeout(m_curlMultiHandle, &curl_timeo);
            if(mres != CURLM_OK)
            {
                  //some error logging here
                  return -1;
            }
            if(curl_timeo >= 0)
            {
              timeout.tv_sec = curl_timeo / 1000;
              if(timeout.tv_sec > 1)
                  timeout.tv_sec = 1;
              else
                  timeout.tv_usec = (curl_timeo % 1000) * 1000;
            }
            mres = curl_multi_fdset(m_curlMultiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
            if(mres != CURLM_OK)
            {
                  //some error logging here
                  return -1;
            }

           int nRet = 0;

            nRet = (select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout) );
            if(nRet == SOCKET_ERROR)
            {

//log the socket last error ::WSAGetLastError() );
//it could be 10038 or 10022.

            }

            return nRet;
      }

This method is called by both send and recv. The problem for us is that we would get the SOCKET_ERROR on select method for receiving. The last socket error could either 10038 (not a socket) or 10022 (invalid arguments).

Here is what send looks like:
      bool send( const std::string& data )
      {

            CURLMcode mres;
            m_debugFileStruct.m_uploadSize = 0;
            m_iStillRunning = -1;

            //setup libcurl easy interface and mulit interface and call back for receiving the http resonse
            mres = curl_multi_perform(m_curlMultiHandle, &m_iStillRunning);
            if(mres != CURLM_OK)
            {
                  return false;
            }
            while(m_iStillRunning)
            {

                  //here we are checking if all the data is send out or not
                  if( (int)m_debugFileStruct.m_uploadSize == (int)data.length())
                  {
                        break;
                  }

                  if(dataAvailable(-1, szLogHeader) >= 0)
                  {
                        mres = curl_multi_perform(m_curlMultiHandle, &m_iStillRunning);
                  }
                  else
                  {
                        return false;
                  }
            }
            return true;
      }

Here is recv side:

int recv( int timeout )
      {
            CURLMcode mres ;
            CURLMsg *msg; /* for picking up messages with the transfer status */
            int msgs_left; /* how many messages are left */

            //here mres is CURLM_OK from logs
            mres = curl_multi_perform(m_curlMultiHandle, &m_iStillRunning);

            int nDataAvail = dataAvailable(timeout);
            if(nDataAvail == 0)
            {

                  return NO_DATA;
            }
            else if(nDataAvail < 0)
            {
                  return ERROR;
            }
            mres = curl_multi_perform(m_curlMultiHandle, &m_iStillRunning);
            if(mres != CURLM_OK)
            {
                  Return ERROR
            }
            i = 0;
            while(m_iStillRunning && i < 2 )
            {
                  i++;
                  nDataAvail = dataAvailable(timeout);
                  if(nDataAvail > 0 )
                  {
                              mres = curl_multi_perform(m_curlMultiHandle, &m_iStillRunning);
                              if(mres != CURLM_OK)
                              {
                                    Return ERROR; }
                  }
                  else if (nDataAvail == 0)
                  {
                        break;
                  }
                  else
                  {
                        //error here
                        Return ERROR;
                  }
            }

      }

I originally thought I could ignore the socket error, however the iStillRunning got from curl_multi_perform is still 1 and from logging, our call back buffer already has all the bytes of response (which mean 750 bytes), here is from libcurl verbose logging:
[0111-09-03 17:35:49] } [data not shown]
[0111-09-03 17:35:51] < HTTP/1.1 200 OK
[0111-09-03 17:35:51] < Date: Mon, 03 Oct 2011 21:35:50 GMT
[0111-09-03 17:35:51] < Content-Length: 750
[0111-09-03 17:35:51] < Content-Type: text/xml; charset=utf-8
[0111-09-03 17:35:51] < Access-Control-Allow-Origin: *
[0111-09-03 17:35:51] < Access-Control-Allow-Headers: Content-Type
[0111-09-03 17:35:51] * Keep sending data to get tossed away!

There is 200 http status code response, however there is one line said:
* Keep sending data to get tossed away!.

From the source code, looks like that logging will only appear for NTLM auth proxy.

Any idea what's wrong here? Could anyone shed some lights on this?

Thanks,
Pingzhong

________________________________
Fiberlink Disclaimer: The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any review, retransmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited. If you received this in error, please contact the sender and delete the material from any computer.

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2011-10-04