curl-library
Http Message flow through two proxies
Date: Sun, 12 Nov 2006 11:15:21 +0530
Hi,
We like to use libcurl to go through two proxies over https protocol.
Howerver, with the existing curl 7.16.0, we can't do it because it
doesn’t allow to user to build a Http CONNECT packet, that need to be
send over the proxy tunnel established with very first proxy to create a
second proxy tunnel. We can do the same to create a custom CONNECT
request packet and sent it through raw socket (connected with first
proxy), that we can get by CURLINFO_LASTSOCKET option. But this is fine
if data transfer is being over http protocol. But if we want it over
secure layer, anyway we need to use curl internal API Curl_read() and
Curl_write() to get/send the data. For that we need connectindex of the
last curl session. We have written a code for doing same functionality
that allows two proxies tunnel connections by using both internal and
external curl API calls. Can you look into the code and help us to
implement the same only by using the external curl call.
Second issue is that the same code works fine if it is build with curl
7.15.5 but it crashes with curl 7.16.0 after sometimes (after some read
and write calls). I took the snapshot attached with mail when it happened.
Thanks,
Ambuj
#include <iostream>
#include <curl/curl.h>
#include "urldata.h"
using namespace std;
#define CURL_7_16_0 /* code for curl-7.16.0 */
#ifdef CURL_7_16_0
#undef CURL_7_15_5
#else
#define CURL_7_15_5 /* code for curl-7.15.5 */
#endif
extern "C" int Curl_read(struct connectdata *conn, curl_socket_t sockfd,
char *buf, size_t buffersize,
ssize_t *n);
extern "C" CURLcode Curl_write(struct connectdata *conn,
curl_socket_t sockfd,
void *mem, size_t len,
ssize_t *written);
long connectindex;
SOCKET sock;
int responseCode;
CURL *curl;
CURLcode res;
HANDLE readThread;
void receiveDataOverProxyTunnel(long sockFd, char * receivedData ,int numBytesToRead,int &nSize, long uniqueConnectIndex,bool blockedRead)
{
struct SessionHandle* data = (struct SessionHandle*) curl;
int connectIndex = uniqueConnectIndex;
// get socket
SOCKET mySock = sockFd;
connectdata * data1;
if(blockedRead) {
// use select-based (blocked read)
#ifdef CURL_7_15_5
data1=data->state.connects[connectIndex]
#else
data1 = data->state.connc->connects[connectIndex];
#endif
/* Get data from SSL layer if available */
if (Curl_read(data1, mySock,receivedData, numBytesToRead, &nSize) == -1 && GetLastError() != WSAEWOULDBLOCK)
cout<<"Connect::receivedDataOverProxyTunnel :Curl_read() (non-blocking read) failed";
if (nSize != 0)
return;
// - set up the FD set
fd_set readFDS;
FD_ZERO(&readFDS);
FD_SET(mySock, &readFDS);
// - do select, wait for data to be available at socket
int retVal = select(0, &readFDS, NULL, NULL, 0);
if (retVal == SOCKET_ERROR) {
retVal = WSAGetLastError();
cout<<"Connect::receiveDataOverProxyTunnel: SOCKET ERROR";
}
else if (retVal > 0) {
// select returned, so read from socket
assert(FD_ISSET(mySock, &readFDS));
int retval = Curl_read(data1, mySock,
receivedData, numBytesToRead, &nSize);
DWORD errnu=GetLastError();
if(retval == -1 && errnu == WSAEWOULDBLOCK)
return;
else
if(nSize == SOCKET_ERROR || nSize==0) {
cout<<"Connect::receiveDataOverProxyTunnel: Curl_read() (blocked read) failed:";
}
}
else {
cout<<"Connect::receiveDataOverProxyTunnel:some other error";
}
}
else {
// non-blocking read
int retval = Curl_read(data1, mySock,
receivedData, numBytesToRead, &nSize);
if(retval == -1 && GetLastError() != WSAEWOULDBLOCK){
cout<<"Connect::receivedDataOverProxyTunnel :Curl_read() (non-blocking read) failed";
}
}
}
int
line_received( long sockFd, long connectIndex, char *buf, int size )
{
char *dst = buf;
if ( size == 0 )
return 0; /* no error */
size--;
int nBytes = 0;
while ( 0 < size ) {
receiveDataOverProxyTunnel(sockFd,dst, 1, nBytes, connectIndex,true); /* recv one-by-one */
switch ( nBytes ) {
case SOCKET_ERROR:
cout<<"recv() error\n";
return -1; /* error */
case 0:
size = 0; /* end of stream */
break;
default:
/* continue reading until last 1 char is EOL? */
if ( *dst == '\n' ) {
/* finished */
size = 0;
} else {
/* more... */
size--;
}
dst++;
}
}
*dst = '\0';
return 0;
}
void setConnectIndex() {
// get the connect index
connectindex = ((struct SessionHandle *)curl)->state.lastconnect;
//to receive the last socket used by curl session
curl_easy_getinfo(curl, CURLINFO_LASTSOCKET , &sock);
}
void sendDataOverProxyTunnel(char *buffer, int nBufferLength, int &bytesWritten,
long uniqueConnectIndex , long sock)
{
// init
struct SessionHandle* data = (struct SessionHandle*) curl;
// - get the curl connectindex
long connectIndex = uniqueConnectIndex;
connectdata * data1;
#ifdef CURL_7_15_5
data1=data->state.connects[connectIndex]
#else
data1 = data->state.connc->connects[connectIndex];
#endif
// - get socket
SOCKET mySock = sock;
int retVal;
// set up select, for blocked write
fd_set writeFDS;
int dataToBeSend = nBufferLength;
while ( dataToBeSend > 0)
{
FD_ZERO(&writeFDS);
FD_SET(mySock, &writeFDS);
// do select
retVal = select(0, NULL, &writeFDS, NULL, 0);
// select error
if (retVal == SOCKET_ERROR) {
retVal = WSAGetLastError();
}
else if (retVal > 0) {
// select returned, so read from socket
assert(FD_ISSET(mySock, &writeFDS));
int retVal = Curl_write(data1, mySock,
buffer + nBufferLength - dataToBeSend, dataToBeSend, &bytesWritten);
if((retVal == -1) || (bytesWritten == SOCKET_ERROR)) {
cout<<"Connect::sendDataOverProxyTunnel: Curl_write() failed:"<<GetLastError();
}
else
{
dataToBeSend = dataToBeSend - bytesWritten;
bytesWritten = nBufferLength - dataToBeSend;
}
}
}
}
int customConnectMethod()
{
char buf[2048];
int result;
char protocol[10], host[50],filename[128];
int nBytes;
//Creating CONNECT Method packet
sprintf(buf,"CONNECT %s:%d HTTP/1.1\r\n", "127.0.0.1", "4900");
sscanf("https://kalyan.dummy.com/", "%15[^\n:]://%[^\n/]%[^\n]", protocol,host,filename);
sprintf(buf + strlen(buf),"Host: %s\r\nAccept: */*\r\n",host);
sprintf(buf + strlen(buf),"\r\n"); /* finish the header part */
/* sending data over socket */
sendDataOverProxyTunnel(buf, strlen(buf),nBytes,connectindex,sock);
/* get response */
if (line_received(sock, connectindex, buf, sizeof(buf) ) < 0){
cout<<"failed to read http response for CONNECT.\n";
return -1;
}
/* check status */
if (!strchr(buf, ' ')) {
cout<<"Unexpected http response: "<< buf<<endl;
return -1;
}
result = atoi(strchr(buf,' '));
switch ( result ) {
case 200:
/* Conguraturation, connected via http proxy server! */
cout<<"connected, start user session.\n";
nBytes = 0;
break;
case 302: /* redirect */
cout<<"Connection Redirected";
nBytes = -1;
break;
case 401: /* WWW-Auth required */
case 407: /* Proxy-Auth required */
cout<<"Proxy Authentication Required\n";
nBytes = -1;
break;
default:
/* Not allowed */
cout<<"http proxy is not allowed.\n";
nBytes = -1;
break;
}
/* skip to end of response header */
do {
if ( line_received(sock, connectindex, buf, sizeof(buf) ) ) {
cout<<"Can't skip response headers\n";
return -1;
}
} while ( strcmp(buf,"\r\n") != 0 );
return nBytes;
}
DWORD WINAPI MsgRecvThread(LPVOID pThreadData)
{
char * receivedData= new char[1024];
int numBytesToRead;
int nSize;
while(1){
receiveDataOverProxyTunnel(sock,receivedData,numBytesToRead,nSize,connectindex,true);
cout<<"received data:"<<receivedData<<endl;
// process the received data here.
}
}
int main(void)
{
curl = curl_easy_init(); // Start a libcurl easy session
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); // set to display verbose information
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://kalyan.dummy.com/"); // set URL to deal with
curl_easy_setopt(curl, CURLOPT_PROXY, "172.22.0.110:3128"); // first proxy
curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, "proxy:password"); // First proxy user and password
curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY , 1); // instruct to setup the connection olny, no data transfer
res = curl_easy_perform(curl); // to create a socket and connect it with its peer
setConnectIndex(); /* set connectindex and socket used by this connection */
customConnectMethod(); /* send CONNECT request over this socket for second proxy */
cout<<"response :"<<res;
DWORD dwReadStatId;
/* create thread to recv data from the channel */
readThread= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) MsgRecvThread,
(LPVOID)0,0,&dwReadStatId);
while(1){
char * buffer = new char[8 * 1024];
int bytesWritten;
int nBufferLength;
// prepare the packet and sent it over channel
/* send raw data from already established channel */
sendDataOverProxyTunnel(buffer,nBufferLength,bytesWritten,connectindex,sock);
}
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}