int httpUtil::parse_request(const char* source,uint32_t len) { if(!source||len==0) { return -1; } clear_request(); char* pheadEnd=0; char* pentity=0; pheadEnd=strstr(source,"\r\n\r\n"); if(pheadEnd==0) { return -1; } uint32_t headlen=pheadEnd+4-source; pentity=pheadEnd+4; if(headlen==0) { return -1; } char* headbuff=new char[headlen+1]; if(!headbuff) { return -1; } memset(headbuff,0,headlen+1); memcpy(headbuff,source,headlen); int getRet=0,PostRet=0; getRet=get_header_field(headbuff,_request_firstLine,"GET "); PostRet=get_header_field(headbuff,_request_firstLine,"POST "); if(getRet==0) { _request_method="GET"; } else if(PostRet==0) { _request_method="POST"; } get_header_field(headbuff,_request_accept,"Accept: "); get_header_field(headbuff,_request_referer,"Referer: "); get_header_field(headbuff,_request_acceptEncoding,"Accept-Encoding: "); get_header_field(headbuff,_request_userAgent,"User-Agent: "); get_header_field(headbuff,_request_host,"Host: "); get_header_field(headbuff,_request_cookie,"Cookie: "); get_header_field(headbuff,_request_acceptLanguage,"Accept-Language: "); get_header_field(headbuff,_request_connection,"Connection: "); dissect_request_firstLine(); uint32_t entitylen=len-headlen; if(entitylen==0) { _request_payload=""; } else _request_payload=pentity; delete []headbuff; return 0; }
int proxy_thread( struct thread_data *td ) { int remote_fd = -1; int state; int n, client_fd; int result = -1; uint32 client_ip; ssize_t writed; int already_authorized = 0; // if some http data coming together with request char *data = NULL; int data_len = 0; #define BUF_SIZE 1504 #define REQ_SIZE 15004 #define clear_buffer() memset(buffer, 0, BUF_SIZE) #define clear_request() memset(request, 0, REQ_SIZE); #define clear_req() memset(&req, 0, sizeof(req)); char buffer[BUF_SIZE]; char last_host[BUF_SIZE]; request = malloc(REQ_SIZE); struct sockaddr_in remote_addr; struct hostent *remote_host; struct timeval timeout; fd_set rfds; FENTER; ASSERT(request); client_fd = td->client_fd; client_ip = td->client_ip; /* fetch the http request headers */ FD_ZERO( &rfds ); FD_SET( (unsigned int) client_fd, &rfds ); timeout.tv_sec = 15; timeout.tv_usec = 0; if ( select(client_fd + 1, &rfds, NULL, NULL, &timeout ) <= 0) { ERR("select() timeout", 0); result = 11; goto exit; } char *end_of_req = NULL; char *preq = request; clear_req(); clear_request(); clear_buffer(); if ( ( n = read(client_fd, buffer, sizeof(buffer)-4) ) <= 0 ) { ERR("read() fail", 0); result = 12; goto exit; } DBG("- recv %d bytes", n); // append memcpy(preq, buffer, n); preq += n; process_request: while ( !(end_of_req = strstr(request, "\r\n\r\n")) ) { DBG("- request without %s", ANSI_WHITE"CRLFCRLF"ANSI_RESET); clear_buffer(); if ( ( n = read(client_fd, buffer, sizeof(buffer)-4) ) <= 0 ) { ERR("read() fail", 0); result = 12; goto exit; } DBG("- recv %d bytes", n); // append memcpy(preq, buffer, n); preq += n; } end_of_req += 4; // first byte after CRLFCRLF if ( (unsigned int)(end_of_req - request) == strlen(request) ) { DBG("no data found in this request", 0); } else { data_len = preq - end_of_req; DBG("%d data bytes found in this request", data_len); data = malloc(data_len); if (!data) { ERR("malloc() %d", data_len); goto exit; } memcpy(data, end_of_req, data_len); } //#define DUMP #ifdef DUMP LOG_HEXDUMP("RECEIVED FROM CLIENT", (unsigned char*)request, preq-request); #else DBG("RECEIVED FROM CLIENT %d bytes", preq-request); #endif if (!parse_request()) { WARN("bad request:", request); return -1; }; if (!already_authorized) { if (is_proxy_authorized()) { already_authorized = 1; } else { proxy_unauthorized(td); goto exit; } } if ( is_syscmd() ) { if ( is_sys_authorized() ) { process_sys_cmd(); goto exit; } else { sys_unauthorized(td); goto exit; } } //LOG_HEXDUMP("BEFORE", (unsigned char*)req.headers, BUF_SIZE); remove_header("Proxy-Authorization"); remove_header("Proxy-Connection"); //LOG_HEXDUMP("AFTER", (unsigned char*)req.headers, BUF_SIZE); /* resolve the http server hostname */ if ( !(req.hostname) || !( remote_host = gethostbyname( req.hostname ) ) ) { WARN("- fail to resolve '%s'", req.hostname); result = 19; goto exit; } if ( req.connect ) { if( td->connect == 1 && req.port != 443 ) { result = 20; goto exit; } } /* connect to the remote server, if not already connected */ if ( 1 || strcmp(req.hostname, last_host) ) { shutdown( remote_fd, 2 ); close( remote_fd ); remote_fd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP ); if (remote_fd < 0) { WARN("- socket() fail", 0); result = 21; goto exit; } remote_addr.sin_family = AF_INET; remote_addr.sin_port = htons( (unsigned short) req.port ); memcpy( (void *) &remote_addr.sin_addr, (void *) remote_host->h_addr, remote_host->h_length ); if ( connect( remote_fd, (struct sockaddr *) &remote_addr, sizeof( remote_addr ) ) < 0 ) { WARN("- connect() to '%s:%d' fail", req.hostname, req.port); result = 22; goto exit; } DBG("- connected to %s:%d", req.url_host, req.port); memset( last_host, 0, sizeof( last_host ) ); strncpy( last_host, req.hostname, sizeof( last_host ) - 1 ); } else { INFO("reuse current connection", 0); } if ( req.connect ) { /* send HTTP/1.0 200 OK */ snprintf(buffer, sizeof(buffer), "HTTP/1.0 200 OK\r\n\r\n"); if ( (writed = write(client_fd, buffer, 19)) != 19 ) { WARN("write() fail: %d", writed); result = 23; goto exit; } req.sent_to_client += writed; } else { /* Construct and send modified request */ size_t server_req_size = strlen(req.method) + strlen(req.url) + strlen(req.http_ver) + strlen(req.headers) + 16; char *server_req = malloc(server_req_size); if (!server_req) { ERR("malloc() fail: %d", server_req_size); result = 235; goto exit; } memset(server_req, 0, server_req_size); n = snprintf(server_req, server_req_size - 4, "%s %s %s%s", req.method, req.url, req.http_ver, req.headers); if ((writed = write(remote_fd, server_req, n)) != n) { WARN("write() fail: %d", writed); result = 24; goto exit; } req.sent_to_server += writed; #ifdef DUMP LOG_HEXDUMP("SENDED TO SERVER", (unsigned char*)server_req, n); #else DBG("SENDED TO SERVER %d bytes", n); #endif free(server_req); if ( data ) { // Some additional data was found in initial request if ((writed = write(remote_fd, data, data_len)) != data_len) { WARN("write() fail: %d", writed); result = 35; goto exit; } req.sent_to_server += writed; #ifdef DUMP LOG_HEXDUMP("SENDED TO SERVER (data)", (unsigned char*)data, data_len); #else DBG("SENDED TO SERVER %d bytes (data)", n); #endif // clear data free(data); data = NULL; data_len = 0; } } /* tunnel the data between the client and the server */ state = 0; while( 1 ) { FD_ZERO( &rfds ); FD_SET( (unsigned int) client_fd, &rfds ); FD_SET( (unsigned int) remote_fd, &rfds ); n = ( client_fd > remote_fd ) ? client_fd : remote_fd; if ( select( n + 1, &rfds, NULL, NULL, NULL ) < 0 ) { ERR("select() fail", 0); result = 25; goto exit; } if ( FD_ISSET( remote_fd, &rfds ) ) { clear_buffer(); if ( ( n = read( remote_fd, buffer, BUF_SIZE-1) ) <= 0 ) { WARN("read() fail: %d", n); result = 26; goto exit; } #ifdef DUMP LOG_HEXDUMP("RECEIVED FROM SERVER (tunneled)", (unsigned char*)buffer, n); #else DBG("RECEIVED FROM SERVER (tunneled) %d bytes", n); #endif state = 1; /* client finished sending data */ if ( (writed = write( client_fd, buffer, n )) != n ) { WARN("write() fail: %d", writed); result = 27; goto exit; } req.sent_to_client += n; #ifdef DUMP LOG_HEXDUMP("SENDED TO CLIENT (tunneled)", (unsigned char*)buffer, n); #else DBG("SENDED TO CLIENT (tunneled) %d bytes", n); #endif } if ( FD_ISSET( client_fd, &rfds ) ) { clear_buffer(); if ( (n = read( client_fd, buffer, BUF_SIZE-1)) <= 0 ) { WARN("read() fail: %d", n); result = 28; goto exit; } #ifdef DUMP LOG_HEXDUMP("RECEIVED FROM CLIENT (tunneled)", (unsigned char*)buffer, n); #else DBG("RECEIVED FROM CLIENT (tunneled) %d bytes", n); #endif if ( state && !req.connect ) { /* new http request */ INFO("- process new http request", 0); syslog_request(); LOG_HEXDUMP("request", (unsigned char*)buffer, n); memset(&req, 0, sizeof(req)); int req_len = MIN((size_t)n, REQ_SIZE-1); strncpy(request, buffer, req_len); preq = request + req_len; goto process_request; } if ((writed = write( remote_fd, buffer, n)) != n ) { WARN("write() fail: %d", writed); result = 29; goto exit; } req.sent_to_server += n; #ifdef DUMP LOG_HEXDUMP("SENDED TO SERVER (tunneled)", (unsigned char*)buffer, n); #else DBG("SENDED TO SERVER (tunneled) %d bytes", n); #endif } } /* not reached */ exit: shutdown( client_fd, 2 ); shutdown( remote_fd, 2 ); close( client_fd ); close( remote_fd ); syslog_request(); closelog(); if (request) free(request); FLEAVEA("exit with %d code sent to client: %d, to server: %d", result, req.sent_to_client, req.sent_to_server); return( result ); }