Wednesday, April 16, 2008

Proxy HTTP request


Today I was working with HTTP protocol, I had to connect to web server over proxy using C and Sockets.
For this task you have to connect with the proxy and it'll redirect your request to your requested web server, if you are authenticated.
My Proxy uses Basic Authorization then I had to send the login information (userid and password) in base64 format. For this task I used OpenSSL one more time.
Once you are authenticated, and you have send a GET request correctly formated, proxy will send response to you.

Bellow I write a necesary code to do this connection. I hope it'll be usefull for you.


// Proxy.c : HTTP/HTTPS Request example
//

#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>

#ifdef WIN32
#include <winsock.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#endif

#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

#include "applink.c"

// OpenSSL functions
void OpenSSL_add_all_algorithms();
void ERR_load_crypto_strings();
void ENGINE_load_builtin_engines();

// my functions
char *base64(const unsigned char *input, int length);
BOOL sendGet(const char* host, const char* dir, const char* proxy,
unsigned short proxyPort, const char* uid, const char* pwd);
char* GetIpAddress(char* host);
long getLen(char* cadena, long *bytes);

#define MAXLEN 10000

int main (int argc, char* argv[])
{
    char ret[2500]="";
    boolean r=FALSE;

    // OpenSSL Init
    _fmode=_O_BINARY;
    CRYPTO_malloc_init();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    ENGINE_load_builtin_engines();

vERR_load_crypto_strings();

    // HTTPS connection example
    r= sendGet("www.ingdirect.es:443", "CONNECT www.ingdirect.es:443 ", "myproxy.es", 8080, "uid", "pwd");
    // Ejemplo HTTP
    r= sendGet("www.elmundo.es", "GET /", "myproxy.es", 8080, "uid", "pwd");

    return 0;
}


/*
*
* Sends a HTTP request
*
*/

BOOL sendGet(const char* host, const char* dir, const char* proxy,
unsigned short proxyPort, const char* uid, const char* pwd)
{
    int iProtocolPort = 0;
    char szBuffer[MAXLEN] = "";
    SOCKET sd;
    struct sockaddr_in sa;
    char proxySrv[16]="";
    char login[200]="";
    boolean loop=TRUE;
    int ret=0;
    FILE *file=NULL;
    int len=0;
    long bytes=0;

    #ifdef WIN32
        int err=0;
        WORD wVersionRequested;
        WSADATA wsaData;

        wVersionRequested = MAKEWORD( 1, 1 );
        err = WSAStartup(wVersionRequested, &wsaData );
        if ( err != 0 )
        {
            return FALSE;
        }
    #endif

    // Translate hostname to ip address
    strcpy(proxySrv, GetIpAddress((char*)proxy));

    // creating Socket
    sd = socket(PF_INET, SOCK_STREAM, 0);
    if (sd == INVALID_SOCKET)
    {
        // Can't open the socket
        return FALSE;
    }


    memset (&sa, '\0', sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr (proxySrv); /* Proxy Ip */
    sa.sin_port = htons(proxyPort); /* Proxy Port */


    // Connecting to Proxy
    if(connect(sd, (struct sockaddr*) &sa, sizeof(sa)))
    {
        // Error connecting proxy!
        return FALSE;
    }

    // Init Request
    sprintf(szBuffer,"%s HTTP/1.1\r\nHost:%s\r\n", dir,host);

    // setting login information
    strcat(szBuffer,"Proxy-authorization:Basic ");
    strcpy(login,uid);
    strcat(login, ":");
    strcat(login,pwd);
    strcpy(login, base64(login, strlen(login)));
    strcat(szBuffer,login);
    strcat(szBuffer, "\r\n\r\n");

    // Sending request
    send(sd,szBuffer,strlen(szBuffer),0);

    // Proxy response
    bytes=recv(sd, szBuffer, sizeof(szBuffer), 0);
    file=fopen("c:\\index.html","w");
    len=getLen(szBuffer, &bytes);


    fprintf(file, "%s", szBuffer);
    fflush(file);

    if(bytes>0 && strncmp(szBuffer+strlen(szBuffer)-4,"\r\n\r\n",4)!=0 && (len>0 && len>bytes)){
        // Writing proxy response
        do{
            ret=recv(sd, szBuffer, sizeof(szBuffer), 0);
            bytes+=ret;
            if(ret<0 || strncmp(szBuffer+strlen(szBuffer)-4,"\r\n\r\n",4)==0 || (len>0 && len<bytes)){
                loop=FALSE;
            }
            fprintf(file, "%s",szBuffer);
            fflush(file);
        }while(loop);
    }
    fclose(file);

    // closing connection
    #ifdef WIN32
        closesocket (sd);
        // free resources
        WSACleanup();

    #else
        close(sd);
    #endif

    return TRUE;
}

/*
*
* Returns a Base64 string
*
*/

char *base64(const unsigned char *input, int length)
{
    BIO *bmem, *b64;
    BUF_MEM *bptr;
    char *buff="";

    b64 = BIO_new(BIO_f_base64());
    bmem = BIO_new(BIO_s_mem());
    b64 = BIO_push(b64, bmem);
    BIO_write(b64, input, length);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);

    buff = (char *)malloc(bptr->length);
    memcpy(buff, bptr->data, bptr->length-1);
    buff[bptr->length-1] = 0;

    BIO_free_all(b64);

    return buff;
}

/*
*
* Translate hostname to ip address
*
*/

char* GetIpAddress(char *hostname)
{

    struct sockaddr_in SocketAddress;
    struct hostent *pHost = 0;
    char aszIPAddresses[16];
    int iCnt=0;

    pHost = gethostbyname(hostname);
    if(!pHost)
        return "";

    // Recupero la primera ip del Servidor
    memcpy(&SocketAddress.sin_addr, pHost->h_addr_list[0], pHost->h_length);
    strcpy(aszIPAddresses, inet_ntoa(SocketAddress.sin_addr));

    // Retorno la ip
    return aszIPAddresses;

}


/*
*
* Returns length data from Content-Length header
*
*/

long getLen(char* cadena, long *bytes){
    int t=0;
    char len[10];

    while(t<MAXLEN){
        if(strncmp(cadena+t,"Content-Length:",15)==0){
            int f=t+16;
            while(cadena[f]!='\r')
                f++;
            memcpy(len, cadena+t+16,f-(t+16));
            while(strncmp(cadena+f,"\r\n\r\n",4)!=0)
                f++;
            *bytes=*bytes-f;
            return atol(len);
        }
        t++;
    }

    return -1;

}