rendered paste body/** * HTTP/HTTPS protocol support * * Copyright (C) 2000-2005 by * Jeffrey Fulmer - <jeff@joedog.org> * This file is distributed as part of Siege * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <setup.h>#include <http.h>#include <stdio.h>#include <stdarg.h>#include <cookie.h>#include <string.h>#include <joedog/defs.h>#define MAXFILE 10240#define REQBUF 40960 private char *__parse_pair(char **str);/** * HTTPS tunnel; set up a secure tunnel with the * proxy server. CONNECT server:port HTTP/1.0 */BOOLEANhttps_tunnel_request(CONN *C, char *host, int port){ size_t rlen, n; char request[256]; if(C->prot == HTTPS && my.proxy.required){ snprintf( request, sizeof(request), "CONNECT %s:%d HTTP/1.0\015\012" "User-agent: Proxy-User\015\012" "\015\012", host, port ); rlen = strlen(request); if(my.debug || my.get){fprintf(stdout, "%s", request); fflush(stdout);} if((n = socket_write(C, request, rlen)) != rlen){ joe_error( "socket_write: ERROR" ); return FALSE; } } else { return FALSE; } return TRUE;}inthttps_tunnel_response(CONN *C){ int x, n; char c; char line[256]; int code = 100; while(TRUE){ x = 0; memset( &line, 0, sizeof( line )); while((n = read(C->sock, &c, 1)) == 1){ line[x] = c; if(my.debug || my.get){ printf("%c", c ); fflush(stdout); } if((line[0] == '\n') || (line[1] == '\n')){ return code; } if( line[x] == '\n' ) break; x ++; } line[x]=0; if( strncasecmp( line, "http", 4 ) == 0 ){ code = atoi(line + 9); } }}/** * returns int, ( < 0 == error ) * formats and sends an HTTP/1.0 request */inthttp_get(CONN *C, char *host, int port, char *path){ int rlen; char *protocol; char *keepalive; char authwww[128]; char authpxy[128]; char request[REQBUF]; char portstr[16]; char fullpath[4096]; char cookie[MAX_COOKIE_SIZE+10]; memset(cookie, 0, sizeof cookie); memset(request, 0, sizeof request); memset(portstr, 0, sizeof portstr); /* Request path based on proxy settings */ if(my.proxy.required){ sprintf( fullpath, "%s://%s:%d%s", C->prot == HTTP?"http":"https", host, port, path ); } else{ sprintf(fullpath, "%s", path); } if((port==80 && C->prot==HTTP) || (port==443 && C->prot==HTTPS)){ portstr[0] = '\0'; } else { snprintf(portstr, sizeof portstr, ":%d", port); } /* HTTP protocol string */ protocol = (my.protocol == TRUE)?"HTTP/1.1":"HTTP/1.0"; keepalive = (C->connection.keepalive == TRUE)?"keep-alive":"close"; get_cookie_header(pthread_self(), host, cookie); if( C->auth.www ){ rlen = snprintf( authwww, sizeof(authwww), "Authorization: %s %s\015\012", (C->auth.type.www==BASIC)?"Basic":"Digest", my.auth.encode ); } if( C->auth.proxy ){ rlen = snprintf( authpxy, sizeof(authpxy), "Proxy-Authorization: %s %s\015\012", (C->auth.type.www==BASIC)?"Basic":"Digest", my.proxy.encode ); } /** * build a request string to pass to the server */ rlen = snprintf( request, sizeof( request ), "GET %s %s\015\012" "Host: %s%s\015\012" "%s" "%s" "%s" "Accept: */*\015\012" "Accept-Encoding: * \015\012" "User-Agent: %s\015\012%s" "Connection: %s\015\012\015\012", fullpath, protocol, host, portstr, (C->auth.www==TRUE)?authwww:"", (C->auth.proxy==TRUE)?authpxy:"", (strlen(cookie) > 8)?cookie:"", my.uagent, my.extra, keepalive ); if(my.debug || my.get){ printf("%s\n", request); fflush(stdout); } if(rlen < 0 || rlen > (int)sizeof(request)) joe_fatal("http_get: request buffer overrun!"); if((socket_write(C, request, rlen)) < 0){ return -1; } return 0;}/** * returns int, ( < 0 == error ) * formats and sends an HTTP/1.0 request */inthttp_post( CONN *C, char *host, int port, char *path, char *data, size_t len ){ int rlen; char authwww[128]; char authpxy[128]; char request[REQBUF]; char portstr[16]; char *protocol; char *keepalive; char cookie[MAX_COOKIE_SIZE]; char fullpath[4096]; memset(cookie, 0, sizeof(cookie)); memset(request, 0, sizeof(request)); memset(portstr, 0, sizeof portstr); if( my.proxy.required ){ sprintf( fullpath, "%s://%s:%d%s", C->prot == 0?"http":"https", host, port, path ); } else{ sprintf(fullpath, "%s", path); } if((port==80 && C->prot==HTTP) || (port==443 && C->prot==HTTPS)){ portstr[0] = '\0'; ; } else { snprintf(portstr, sizeof portstr, ":%d", port); } /* HTTP protocol string */ protocol = (my.protocol == TRUE)?"HTTP/1.1":"HTTP/1.0"; keepalive = (C->connection.keepalive == TRUE)?"keep-alive":"close"; get_cookie_header(pthread_self(), host, cookie); if( C->auth.www ){ rlen = snprintf( authwww, sizeof(authwww), "Authorization: %s %s\015\012", (C->auth.type.www==BASIC)?"Basic":"Digest", my.auth.encode ); } if( C->auth.proxy ){ rlen = snprintf( authpxy, sizeof(authpxy), "Proxy-Authorization: %s %s\015\012", (C->auth.type.www==BASIC)?"Basic":"Digest", my.proxy.encode ); } /* build a request string to pass to the server */ rlen = snprintf( request, sizeof(request), "POST %s %s\015\012" "Host: %s%s\015\012" "%s" "%s" "%s" "Accept: */*\015\012" "Accept-Encoding: * \015\012" "User-Agent: %s\015\012%s" "Connection: %s\015\012" "Content-type: application/x-www-form-urlencoded\015\012" "Content-length: %ld\015\012\015\012", fullpath, protocol, host, portstr, (C->auth.www==TRUE)?authwww:"", (C->auth.proxy==TRUE)?authpxy:"", (strlen(cookie) > 8)?cookie:"", my.uagent, my.extra, keepalive, (long)len ); if( rlen + len < sizeof(request)){ memcpy(request + rlen, data, len); request[rlen+len] = 0; } rlen += len; if(my.debug || my.get){ printf("%s\n", request); fflush(stdout); } if(rlen<0 || rlen>(int)sizeof(request) ) joe_fatal("http_post: request buffer overrun!"); if((socket_write(C, request, rlen)) < 0){ return -1; } return 0;}/** * returns HEADERS struct * reads from http/https socket and parses * header information into the struct. */HEADERS *http_read_headers(CONN *C, char *host){ int x; /* while loop index */ int n; /* assign socket_read */ char c; /* assign char read */ int len = 0; /* size length of line */ char *line = NULL; /* assign chars read */ HEADERS *h; /* struct to hold it all */ h = xcalloc(sizeof(HEADERS), 1); while(TRUE){ x = 0; memset(&line, 0, sizeof(line)); while((n = socket_read(C, &c, 1)) == 1){ if(x >= len){ len += 2048; line = (char*)xrealloc(line, len); } line[x] = c; if(my.debug || my.get){ printf("%c", c ); fflush(stdout); } if((line[0] == '\n') || (line[1] == '\n')){ xfree(line); return h; } if(line[x] == '\n') break; x ++; } line[x]=0; /* strip trailing CR */ if (x > 0 && line[x-1] == '\r') line[x-1]=0; if( strncasecmp( line, "http", 4 ) == 0 ){ strncpy( h->head, line, 8 ); h->code = atoi( line + 9 ); } if( strncasecmp( line, "content-length: ", 16 ) == 0 ){ C->content.length = atoi( line + 16 ); } if(strncasecmp( line, "set-cookie: ", 12 ) == 0){ if(my.cookies){ char* cookie = line + 12; int cookie_len = strlen(cookie); if(cookie_len + 1 > (int)sizeof(h->cookie)){ cookie_len = (int)sizeof(h->cookie) - 1; } memset(h->cookie, 0, sizeof(h->cookie)); strncpy(h->cookie, line+12, cookie_len); add_cookie(pthread_self(), host, h->cookie); } } if(strncasecmp(line, "connection: ", 12) == 0){ if(strncasecmp(line+12, "keep-alive", 10) == 0){ h->keepalive = 1; } else if(strncasecmp(line+12, "close", 5) == 0){ h->keepalive = 0; } } if(strncasecmp(line, "keep-alive: ", 12) == 0){ char *tmp = ""; char *option = "", *value = ""; char *newline = (char*)line; while((tmp = __parse_pair(&newline)) != NULL){ option = tmp; while(*tmp && !ISSPACE((int)*tmp) && !ISSEPARATOR(*tmp)) tmp++; *tmp++=0; while(ISSPACE((int)*tmp) || ISSEPARATOR(*tmp)) tmp++; value = tmp; while(*tmp) tmp++; if(!strncasecmp( option, "timeout", 7)){ C->connection.timeout = atoi(value); } if(!strncasecmp(option, "max", 3)){ C->connection.max = atoi(value); } } } if(strncasecmp(line, "location: ", 10) == 0){ size_t len = strlen(line); h->redirect = xmalloc(len); memcpy(h->redirect, line+10, len-10); h->redirect[len-10] = 0; } if(strncasecmp(line, "www-authenticate: ", 18) == 0){ char *tmp; int tmp_len; h->auth.www = TRUE; if(strncasecmp(line+19, "digest", 6) == 0){ h->auth.type.www = DIGEST; } else { h->auth.type.www = BASIC; } tmp = strchr( line, ':' ); if (tmp == NULL) { printf("I shat myself so hard..\n"); return NULL; } if(h->auth.www && tmp) { tmp++; tmp_len = strlen(tmp); if(tmp[0] == '"' && tmp[tmp_len - 1] == '"'){ tmp++; tmp_len -= 2; tmp[tmp_len] = '\0'; } if(tmp_len + 1 > (int)sizeof(h->auth.realm.www)){ tmp_len = (int)sizeof(h->auth.realm.www) - 1; } strncpy( h->auth.realm.www, tmp, tmp_len ); } } if(strncasecmp(line, "proxy-authenticate: ", 20) == 0){ char *tmp; int tmp_len; h->auth.proxy = TRUE; if(strncasecmp(line+21, "digest", 6) == 0){ h->auth.type.proxy = DIGEST; } else { h->auth.type.proxy = BASIC; } tmp = strchr(line, '='); if(h->auth.proxy && tmp) { tmp++; tmp_len = strlen(tmp); if(tmp[0] == '"' && tmp[tmp_len - 1] == '"'){ tmp++; tmp_len -= 2; tmp[tmp_len] = '\0'; } if(tmp_len + 1 > (int)sizeof(h->auth.realm.proxy)){ tmp_len = (int)sizeof(h->auth.realm.proxy) - 1; } strncpy(h->auth.realm.proxy, tmp, tmp_len); } } if(strncasecmp(line, "transfer-encoding: ", 19) == 0){ if(strncasecmp(line+20, "chunked", 7)){ C->content.transfer = CHUNKED; } else if(strncasecmp( line+20, "trailer", 7)){ C->content.transfer = TRAILER; } else { C->content.transfer = NONE; } } if(n == 0){ xfree(line); return h; } if(n < 0){ xfree(line); xfree(h); return(NULL); } } /* end of while TRUE */ xfree(line); return h;}inthttp_chunk_size(CONN *C){ int n; char *end; size_t length; memset(C->chkbuf, 0, sizeof(C->chkbuf)); if((n = socket_readline(C, C->chkbuf, sizeof(C->chkbuf))) < 1){ joe_error("http.c: unable to determine chunk size"); return -1; } if(((C->chkbuf[0] == '\n')||(strlen(C->chkbuf)==0)||(C->chkbuf[0] == '\r'))){ return -1; } errno = 0; if(!isxdigit((unsigned)*C->chkbuf)) return -1; length = strtoul(C->chkbuf, &end, 16); if((errno == ERANGE) || (end == C->chkbuf)){ joe_error("http.c: bad chunk line %s\n", C->chkbuf); return 0; } else { return length; } return -1;} /** * returns ssize_t */ssize_thttp_read(CONN *C){ int n = 0; int chunk = 0; size_t bytes = 0; size_t length = 0; static char body[MAXFILE]; if( C == NULL ) joe_fatal("C is NULL!\n"); if(C->content.length > 0){ length = (C->content.length < MAXFILE)?C->content.length:MAXFILE; do { memset(body, 0, sizeof(body)); if(( n = socket_read(C, body, length)) == 0 ) break; bytes += n; length = (C->content.length - bytes < MAXFILE)?C->content.length-bytes:MAXFILE; } while(bytes < C->content.length); } else if(my.chunked && C->content.transfer == CHUNKED) { int tries = 0; while(tries < 256) { chunk = http_chunk_size(C); if(chunk == 0) break; else if(chunk < 0) { tries ++; continue; } do { int n; memset(body, 0, MAXFILE); n = socket_read(C, body, (chunk>MAXFILE)?MAXFILE:chunk); chunk -= n; bytes += n; } while(chunk > 0); } } else { do { memset(body, 0, sizeof(body)); if((n = socket_read(C, body, sizeof(body))) == 0) break; bytes += n; } while(TRUE); } return(bytes);}/** * parses option=value pairs from an * http header, see keep-alive: above * while(( tmp = __parse_pair( &newline )) != NULL ){ * do_something( tmp ); * } */private char *__parse_pair(char **str){ int okay = 0; char *p = *str; char *pair = NULL; if( !str || !*str ) return NULL; /** * strip the header label */ while( *p && *p != ' ' ) p++; *p++=0; if( !*p ){ *str = p; return NULL; } pair = p; while( *p && *p != ';' && *p != ',' ){ if( !*p ){ *str = p; return NULL; } if( *p == '=' ) okay = 1; p++; } *p++ = 0; *str = p; if( okay ) return pair; else return NULL;}