/** * Clean heap allocated block, and free it. * * @param buf : buffer to zero-ize * @param buflen : buf length */ void proxenet_xclean(void* buf, size_t buflen) { proxenet_xzero(buf, buflen); proxenet_xfree(buf); return; }
int create_http_socket(char* http_request, sock_t* server_sock, sock_t* client_sock, ssl_context_t* ssl_ctx) { http_request_t http_infos; int retcode; char* err; char sport[7] = {0, }; err = NULL; proxenet_xzero(&http_infos, sizeof(http_request_t)); http_infos.method = NULL; http_infos.hostname = NULL; http_infos.request_uri = NULL; /* get target from string and establish client socket to dest */ if (get_url_information(http_request, &http_infos) == false) { xlog(LOG_ERROR, "%s\n", "Failed to extract valid parameters from URL."); return -1; } ssl_ctx->use_ssl = http_infos.is_ssl; snprintf(sport, 6, "%d", http_infos.port); retcode = create_connect_socket(http_infos.hostname, sport, &err); if (retcode < 0) { if (err) generic_http_error_page(*server_sock, err); else generic_http_error_page(*server_sock, "Unknown error in <i>create_connect_socket</i>"); retcode = -1; } else { *client_sock = retcode; /* if ssl, set up ssl interception */ if (http_infos.is_ssl) { /* 1. set up proxy->server ssl session */ if(proxenet_ssl_init_client_context(&(ssl_ctx->client)) < 0) { retcode = -1; goto http_sock_end; } proxenet_ssl_wrap_socket(&(ssl_ctx->client.context), client_sock); if (proxenet_ssl_handshake(&(ssl_ctx->client.context)) < 0) { xlog(LOG_ERROR, "%s\n", "proxy->server: handshake"); retcode = -1; goto http_sock_end; } #ifdef DEBUG xlog(LOG_DEBUG, "%s\n", "SSL handshake with server done"); #endif proxenet_write(*server_sock, "HTTP/1.0 200 Connection established\r\n\r\n", 39); /* 2.set up proxy->browser ssl session */ if(proxenet_ssl_init_server_context(&(ssl_ctx->server)) < 0) { retcode = -1; goto http_sock_end; } proxenet_ssl_wrap_socket(&(ssl_ctx->server.context), server_sock); if (proxenet_ssl_handshake(&(ssl_ctx->server.context)) < 0) { xlog(LOG_ERROR, "handshake proxy->client '%s:%d' failed\n", http_infos.hostname, http_infos.port); retcode = -1; goto http_sock_end; } #ifdef DEBUG xlog(LOG_DEBUG, "%s\n", "SSL Handshake with client done"); #endif } } http_sock_end: proxenet_xfree(http_infos.method); proxenet_xfree(http_infos.hostname); proxenet_xfree(http_infos.request_uri); return retcode; }
/** * * request MUST be like * METHOD proto://hostname[:port][/location][?param=value....]\r\n * cf. RFC2616 */ bool get_url_information(char* request, http_request_t* http) { char *start_pos, *cur_pos, *end_pos; unsigned int str_len; str_len = -1; cur_pos = NULL; /* find method */ start_pos = index(request, ' '); if (start_pos == NULL) { xlog(LOG_ERROR, "%s\n", "Malformed HTTP Header"); return false; } end_pos = index(start_pos+1, ' '); if (end_pos==NULL) { xlog(LOG_ERROR, "%s\n", "Malformed HTTP Header"); return false; } str_len = start_pos-request ; http->method = (char*)proxenet_xmalloc(str_len +1); memcpy(http->method, request, str_len); ++start_pos; /* get proto */ if (!strncmp(start_pos,"http://", 7)) { http->proto = "http"; http->port = 80; start_pos += 7; } else if (!strncmp(start_pos,"https://", 8)) { http->proto = "https"; http->port = 443; http->is_ssl = true; start_pos += 8; } else if (!strcmp(http->method, "CONNECT")) { http->proto = "https"; http->port = 443; http->is_ssl = true; } else { xlog(LOG_ERROR, "%s\n", "Malformed HTTP/HTTPS URL, unknown proto"); xlog(LOG_DEBUG, "%s\n", request); proxenet_xfree(http->method); return false; } cur_pos = start_pos; /* get hostname */ for(; *cur_pos && *cur_pos!=':' && *cur_pos!='/' && cur_pos<end_pos; cur_pos++); str_len = cur_pos - start_pos; http->hostname = (char*)proxenet_xmalloc(str_len+1); memcpy(http->hostname, start_pos, str_len); /* get port if set explicitly (i.e ':<port_num>'), otherwise default */ if(*cur_pos == ':') { cur_pos++; http->port = (unsigned short)atoi(cur_pos); for(;*cur_pos!='/' && cur_pos<end_pos;cur_pos++); } /* get request_uri (no need to parse) */ str_len = end_pos - cur_pos; if (str_len > 0) { http->request_uri = (char*) proxenet_xmalloc(str_len+1); memcpy(http->request_uri, cur_pos, str_len); } else { http->request_uri = (char*) proxenet_xmalloc(2); *(http->request_uri) = '/'; } #ifdef DEBUG xlog(LOG_DEBUG, "URL: %s %s://%s:%d%s\n", http->method, http->proto, http->hostname, http->port, http->request_uri); #endif return true; }
static int proxenet_apply_plugins(request_t *request) { plugin_t *p = NULL; char *old_data = NULL;; char* (*plugin_function)(plugin_t*, request_t*) = NULL; for (p=plugins_list; p!=NULL; p=p->next) { if (p->state == INACTIVE) continue; switch (p->type) { #ifdef _PYTHON_PLUGIN case _PYTHON_: plugin_function = proxenet_python_plugin; break; #endif #ifdef _C_PLUGIN case _C_: plugin_function = proxenet_c_plugin; break; #endif #ifdef _RUBY_PLUGIN case _RUBY_: plugin_function = proxenet_ruby_plugin; break; #endif #ifdef _PERL_PLUGIN case _PERL_: plugin_function = proxenet_perl_plugin; break; #endif #ifdef _LUA_PLUGIN case _LUA_: plugin_function = proxenet_lua_plugin; break; #endif default: xlog(LOG_CRITICAL, "Type %d not supported (yet)\n", p->type); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "Calling %s plugin id %d: %s:%s\n", supported_plugins_str[p->type], p->id, p->name, request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION); #endif old_data = request->data; request->data = (*plugin_function)(p, request); if (request->data) { /* * If new_data is different, it means a new buffer was allocated by * (*plugin_function)(). The old_data can then be free-ed. */ proxenet_xfree(old_data); } else { /* Otherwise (data different or error), use the original data */ request->data = old_data; } /* Additionnal check for dummy plugin coder */ if (!request->data || request->size == 0) { xlog(LOG_CRITICAL, "Plugin '%s' is invalid", p->name); p->state = INACTIVE; return -1; } } return 0; }
/** * This function is called by all threads to treat to process the request and response. * It will also apply the plugins. */ void proxenet_process_http_request(sock_t server_socket) { sock_t client_socket; request_t req; int retcode; fd_set rfds; struct timespec ts; ssl_context_t ssl_context; bool is_ssl; sigset_t emptyset; size_t n; client_socket = retcode = n = -1; proxenet_xzero(&req, sizeof(request_t)); proxenet_xzero(&ssl_context, sizeof(ssl_context_t)); /* wait for any event on sockets */ for(;;) { if (server_socket < 0) { xlog(LOG_ERROR, "%s\n", "Sock browser->proxy died unexpectedly"); break; } ts.tv_sec = HTTP_TIMEOUT_SOCK; ts.tv_nsec = 0; FD_ZERO(&rfds); FD_SET(server_socket, &rfds); if (client_socket > 0) FD_SET(client_socket, &rfds); sigemptyset(&emptyset); retcode = pselect(FD_SETSIZE, &rfds, NULL, NULL, &ts, &emptyset); if (retcode < 0) { if (errno == EINTR) { continue; } else { xlog(LOG_CRITICAL, "[thread] pselect returned %d: %s\n", retcode, strerror(errno)); break; } } if (retcode == 0) { break; } is_ssl = ssl_context.use_ssl; /* is there data from web browser to proxy ? */ if( FD_ISSET(server_socket, &rfds ) ) { n = - 1; if(is_ssl) { n = proxenet_read_all(server_socket, &req.data, &(ssl_context.server.context)); } else { n = proxenet_read_all(server_socket, &req.data, NULL); } #ifdef DEBUG xlog(LOG_DEBUG, "[%d] Got %dB from client (%s)\n", req.id, n, (is_ssl)?"SSL":"PLAIN"); #endif if (n <= 0) break; req.size = n; /* is connection to server not established ? -> new request */ if (client_socket < 0) { retcode = create_http_socket(&req, &server_socket, &client_socket, &ssl_context); if (retcode < 0) { xlog(LOG_ERROR, "[%d] Failed to create %s->server socket\n", req.id, PROGNAME); proxenet_xfree(req.data); client_socket = -1; break; } if (ssl_context.use_ssl) { if (ssl_context.server.is_valid && ssl_context.client.is_valid) { #ifdef DEBUG xlog(LOG_DEBUG, "[%d] SSL interception established\n", req.id); #endif proxenet_xfree(req.data); continue; } xlog(LOG_ERROR, "[%d] Failed to establish interception\n", req.id); proxenet_xfree(req.data); client_socket = -1; break; } } req.type = REQUEST; req.id = get_new_request_id(); /* check if request is valid */ if (!cfg->proxy.host) { if (!is_ssl) { if (!is_valid_http_request(&req.data, &req.size)) { proxenet_xfree(req.data); client_socket = -1; break; } } else { set_https_infos(&req); } } if (cfg->verbose) { xlog(LOG_INFO, "New request %d to '%s:%d'\n", req.id, req.http_infos.hostname, req.http_infos.port); if (cfg->verbose > 1) xlog(LOG_INFO, "%s %s://%s:%d%s %s\n", req.http_infos.method, req.http_infos.proto, req.http_infos.hostname, req.http_infos.port, req.http_infos.uri, req.http_infos.version); } #ifdef DEBUG xlog(LOG_DEBUG, "[%d] Sending buffer %d bytes (%s) - pre-plugins\n", req.id, req.size, (req.http_infos.is_ssl)?"SSL":"PLAIN"); #endif /* hook request with all plugins in plugins_list */ if ( proxenet_apply_plugins(&req) < 0) { /* extremist action: any error on any plugin discard the whole request */ req.id = 0; proxenet_xfree( req.data ); continue; } #ifdef DEBUG xlog(LOG_DEBUG, "[%d] Sending buffer %d bytes (%s) - post-plugins\n", req.id, req.size, (req.http_infos.is_ssl)?"SSL":"PLAIN"); #endif /* send modified data */ if (is_ssl) { retcode = proxenet_ssl_write(client_socket, req.data, req.size, &(ssl_context.client.context)); } else { retcode = proxenet_write(client_socket, req.data, req.size); } proxenet_xfree(req.data); if (retcode < 0) { xlog(LOG_ERROR, "[%d] %s\n", req.id, "Failed to write to server"); if (req.id) req.id = 0; break; } } /* end FD_ISSET(data_from_browser) */ /* is there data from remote server to proxy ? */ if( client_socket > 0 && FD_ISSET(client_socket, &rfds ) ) { n = -1; if (is_ssl) n = proxenet_read_all(client_socket, &req.data, &ssl_context.client.context); else n = proxenet_read_all(client_socket, &req.data, NULL); #ifdef DEBUG xlog(LOG_DEBUG, "[%d] Got %dB from server\n", req.id, n); #endif if (n <= 0) break; /* update request data structure */ req.type = RESPONSE; req.size = n; /* execute response hooks */ if ( proxenet_apply_plugins(&req) < 0) { /* extremist action: any error on any plugin discard the whole request */ req.id = 0; proxenet_xfree(req.data); continue; } /* send modified data to client */ if (is_ssl) retcode = proxenet_ssl_write(server_socket, req.data, req.size, &ssl_context.server.context); else retcode = proxenet_write(server_socket, req.data, req.size); if (retcode < 0) { xlog(LOG_ERROR, "[%d] %s\n", req.id, "proxy->client: write failed"); } proxenet_xfree(req.data); } /* end FD_ISSET(data_from_server) */ } /* end for(;;) { select() } */ if (req.id) { #ifdef DEBUG xlog(LOG_DEBUG, "Free-ing request %d\n", req.id); #endif proxenet_xfree(req.http_infos.method); proxenet_xfree(req.http_infos.hostname); proxenet_xfree(req.http_infos.uri); proxenet_xfree(req.http_infos.version); } /* close client socket */ if (client_socket > 0) { #ifdef DEBUG xlog(LOG_DEBUG, "Closing proxy->server socket #%d\n", client_socket); #endif if (ssl_context.client.is_valid) { proxenet_ssl_finish(&(ssl_context.client), false); close_socket_ssl(client_socket, &ssl_context.client.context); } else { close_socket(client_socket); } } /* close local socket */ if (server_socket > 0) { #ifdef DEBUG xlog(LOG_DEBUG, "Closing browser->proxy socket #%d\n", server_socket); #endif if (ssl_context.server.is_valid) { proxenet_ssl_finish(&(ssl_context.server), true); close_socket_ssl(server_socket, &ssl_context.server.context); } else { close_socket(server_socket); } } /* and that's all folks */ return; }
/** * Establish a connection from proxenet -> server. If proxy forwarding configured, then process * request to other proxy. * */ int create_http_socket(request_t* req, sock_t* server_sock, sock_t* client_sock, ssl_context_t* ssl_ctx) { int retcode; char *host, *port; char sport[6] = {0, }; http_request_t* http_infos = &req->http_infos; bool use_proxy = (cfg->proxy.host != NULL) ; /* get target from string and establish client socket to dest */ if (get_url_information(req->data, http_infos) == false) { xlog(LOG_ERROR, "%s\n", "Failed to extract valid parameters from URL."); return -1; } #ifdef DEBUG char* full_uri = get_request_full_uri(req); xlog(LOG_DEBUG, "URL: %s\n", full_uri); proxenet_xfree(full_uri); #endif ssl_ctx->use_ssl = http_infos->is_ssl; snprintf(sport, 5, "%u", http_infos->port); /* do we forward to another proxy ? */ if (use_proxy) { host = cfg->proxy.host; port = cfg->proxy.port; } else { host = http_infos->hostname; port = sport; } retcode = create_connect_socket(host, port); if (retcode < 0) { if (errno) generic_http_error_page(*server_sock, strerror(errno)); else generic_http_error_page(*server_sock, "Unknown error in <i>create_connect_socket</i>"); retcode = -1; } else { *client_sock = retcode; /* if ssl, set up ssl interception */ if (http_infos->is_ssl) { if (use_proxy) { char *connect_buf = NULL; /* 0. set up proxy->proxy ssl session (i.e. forward CONNECT request) */ retcode = proxenet_write(*client_sock, req->data, req->size); if (retcode < 0) { xlog(LOG_ERROR, "%s failed to CONNECT to proxy\n", PROGNAME); return -1; } /* read response */ retcode = proxenet_read_all(*client_sock, &connect_buf, NULL); if (retcode < 0) { xlog(LOG_ERROR, "%s Failed to read from proxy\n", PROGNAME); return -1; } /* expect HTTP 200 */ if ( (strncmp(connect_buf, "HTTP/1.0 200", 12) != 0) && (strncmp(connect_buf, "HTTP/1.1 200", 12) != 0)) { xlog(LOG_ERROR, "%s->proxy: bad HTTP version\n", PROGNAME); if (cfg->verbose) xlog(LOG_ERROR, "Received %s\n", connect_buf); return -1; } } /* 1. set up proxy->server ssl session */ if(proxenet_ssl_init_client_context(&(ssl_ctx->client)) < 0) { return -1; } proxenet_ssl_wrap_socket(&(ssl_ctx->client.context), client_sock); if (proxenet_ssl_handshake(&(ssl_ctx->client.context)) < 0) { xlog(LOG_ERROR, "%s->server: handshake\n", PROGNAME); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "%s %d %d\n", "SSL handshake with server done", *client_sock, *server_sock); #endif if (proxenet_write(*server_sock, "HTTP/1.0 200 Connection established\r\n\r\n", 39) < 0){ return -1; } /* 2. set up proxy->browser ssl session */ if(proxenet_ssl_init_server_context(&(ssl_ctx->server)) < 0) { return -1; } proxenet_ssl_wrap_socket(&(ssl_ctx->server.context), server_sock); if (proxenet_ssl_handshake(&(ssl_ctx->server.context)) < 0) { xlog(LOG_ERROR, "handshake %s->client '%s:%d' failed\n", PROGNAME, http_infos->hostname, http_infos->port); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "%s\n", "SSL Handshake with client done"); #endif } } return retcode; }
/** * This function is called by all threads to treat to process the request and response. * It will also apply the plugins. * * @param server_socket is the socket received by the main thread from the web browser (acting like server * to web browser) */ void proxenet_process_http_request(sock_t server_socket) { sock_t client_socket; request_t req; int retcode, n; fd_set rfds; struct timespec ts; ssl_context_t ssl_context; bool is_ssl; sigset_t emptyset; bool is_new_http_connection = false; client_socket = retcode = n = -1; proxenet_xzero(&req, sizeof(request_t)); proxenet_xzero(&ssl_context, sizeof(ssl_context_t)); /* wait for any event on sockets */ while(proxy_state == ACTIVE) { if (server_socket < 0) { xlog(LOG_ERROR, "sock browser->%s (#%d) died unexpectedly\n", PROGNAME, server_socket); break; } ts.tv_sec = HTTP_TIMEOUT_SOCK; ts.tv_nsec = 0; FD_ZERO(&rfds); FD_SET(server_socket, &rfds); if (client_socket > 0) FD_SET(client_socket, &rfds); sigemptyset(&emptyset); retcode = pselect(FD_SETSIZE, &rfds, NULL, NULL, &ts, &emptyset); if (retcode < 0) { if (errno == EINTR) { continue; } else { xlog(LOG_CRITICAL, "[thread] pselect returned %d: %s\n", retcode, strerror(errno)); break; } } if (retcode == 0) { continue; } is_ssl = ssl_context.use_ssl; /* is there data from web browser to proxy ? */ if( FD_ISSET(server_socket, &rfds ) ) { if(is_ssl && req.do_intercept) { n = proxenet_read_all(server_socket, &req.data, &(ssl_context.server.context)); } else { n = proxenet_read_all(server_socket, &req.data, NULL); } #ifdef DEBUG xlog(LOG_DEBUG, "Got %dB from client (%s srv_sock=#%d intercept_flag=%s)\n", n, is_ssl?"SSL":"PLAIN", server_socket, req.do_intercept?"true":"false"); #endif if (n < 0) { xlog(LOG_ERROR, "%s\n", "read() failed, end thread"); break; } if (n == 0){ #ifdef DEBUG xlog(LOG_DEBUG, "%s\n", "Socket EOF from client"); #endif break; } /* from here, n can only be positive */ req.size = (size_t) n; bytes_sent += n; if (req.id > 0 && !req.do_intercept){ #ifdef DEBUG xlog(LOG_DEBUG, "Intercept disabled for browser->'%s'\n", req.http_infos.hostname); #endif goto send_to_server; } /* proxy keep-alive */ if (req.id > 0){ request_t* old_req = (request_t*)proxenet_xmalloc(sizeof(request_t)); memcpy(old_req, &req, sizeof(request_t)); char* host = proxenet_xstrdup2( req.http_infos.hostname ); free_http_infos(&(req.http_infos)); if (update_http_infos(&req) < 0){ xlog(LOG_ERROR, "Failed to update HTTP information for request %d\n", req.id); proxenet_xfree( host ); proxenet_xfree( old_req ); req.id = 0; break; } if (strcmp( host, req.http_infos.hostname )){ /* reset the client connection parameters */ if (cfg->verbose) xlog(LOG_INFO, "Reusing sock=%d (old request=%d, old sock=%d) %s/%s\n", server_socket, req.id, client_socket, host, req.http_infos.hostname ); proxenet_close_socket(client_socket, &(ssl_context.client)); free_http_infos(&(req.http_infos)); client_socket = -1; } proxenet_xclean( old_req, sizeof(request_t) ); proxenet_xfree( host ); } req.type = REQUEST; req.id = get_new_request_id(); /* is connection to server not established ? -> new request */ if ( client_socket < 0) { retcode = create_http_socket(&req, &server_socket, &client_socket, &ssl_context); if (retcode < 0) { xlog(LOG_ERROR, "Failed to create %s->server socket\n", PROGNAME); proxenet_xfree(req.data); req.id = 0; break; } if (ssl_context.use_ssl) { req.is_ssl = true; if (req.do_intercept == false) { #ifdef DEBUG xlog(LOG_DEBUG, "SSL interception client <-> %s <-> server disabled\n", PROGNAME); #endif proxenet_xfree(req.data); req.type = REQUEST; req.id = get_new_request_id(); continue; } else if (ssl_context.server.is_valid && ssl_context.client.is_valid) { #ifdef DEBUG xlog(LOG_DEBUG, "SSL interception client <-> %s <-> server established\n", PROGNAME); #endif proxenet_xfree(req.data); is_new_http_connection = true; continue; } xlog(LOG_ERROR, "%s\n", "Failed to establish interception"); proxenet_xfree(req.data); break; } is_new_http_connection = true; } /* if proxenet does not relay to another proxy */ if (!cfg->proxy.host) { if (is_new_http_connection) { if (is_ssl) { /* * SSL request fields still have the values gathered in the CONNECT * Those values must be updated to reflect the real request */ free_http_infos(&(req.http_infos)); retcode = update_http_infos(&req); } else { /* * Format requests * GET http://foo/bar.blah HTTP/1.1 ... * into * GET /bar.blah HTTP/1.1 ... */ retcode = format_http_request(&req.data, &req.size); } } else { /* if here, at least 1 request has been to server */ /* so simply forward */ /* e.g. using HTTP/1.1 100 Continue */ #ifdef DEBUG xlog(LOG_DEBUG, "Resuming stream '%d'->'%d'\n", client_socket, server_socket); #endif free_http_infos(&(req.http_infos)); retcode = update_http_infos(&req); } if (retcode < 0){ xlog(LOG_ERROR, "Failed to update %s information in request %d\n", is_ssl?"HTTPS":"HTTP", req.id); proxenet_xfree(req.data); break; } } if(cfg->ie_compat){ if (is_ssl) retcode = ie_compat_read_post_body(server_socket, &req, &(ssl_context.server.context)); else retcode = ie_compat_read_post_body(server_socket, &req, NULL); if (retcode < 0){ xlog(LOG_ERROR, "%s\n", "Extending IE POST: failed"); proxenet_xfree(req.data); break; } } /* apply plugins for requests (from browser to server) */ if (cfg->verbose) { xlog(LOG_INFO, "%s request to '%s:%d'\n", is_ssl?"SSL":"plain", req.http_infos.hostname, req.http_infos.port); if (cfg->verbose > 1) xlog(LOG_INFO, "%s %s %s\n", req.http_infos.method, req.http_infos.uri, req.http_infos.version); } #ifdef DEBUG xlog(LOG_DEBUG, "Request %d pre-plugins: buflen:%lu\n", req.id, req.size); #endif /* hook request with all plugins in plugins_list */ if ( proxenet_apply_plugins(&req) < 0) { /* extremist action: any error on any plugin discard the whole request */ proxenet_xfree( req.data ); break; } #ifdef DEBUG xlog(LOG_DEBUG, "Request %d post-plugins: buflen:%lu\n", req.id, req.size); if(cfg->verbose > 2) proxenet_hexdump(req.data, req.size); #endif send_to_server: /* send modified data */ if (is_ssl && req.do_intercept) { retcode = proxenet_ssl_write(&(ssl_context.client.context), req.data, req.size); } else { retcode = proxenet_write(client_socket, req.data, req.size); } /* reset data */ proxenet_xfree(req.data); req.size = 0; if (retcode < 0) { xlog(LOG_ERROR, "[%d] %s\n", req.id, "Failed to write to server"); break; } #ifdef DEBUG xlog(LOG_DEBUG, "Written %d bytes to server (socket=%s socket #%d)\n", retcode, is_ssl?"SSL":"PLAIN", client_socket); #endif } /* end FD_ISSET(data_from_browser) */ /* is there data from remote server to proxy ? */ if( client_socket > 0 && FD_ISSET(client_socket, &rfds ) ) { if(req.is_ssl && req.do_intercept) { n = proxenet_read_all(client_socket, &req.data, &ssl_context.client.context); } else { n = proxenet_read_all(client_socket, &req.data, NULL); } if (n < 0){ xlog(LOG_ERROR, "read() %s on cli_sock=#%d failed: %d\n", is_ssl?"SSL":"PLAIN", client_socket, n); break; } if (n==0){ #ifdef DEBUG xlog(LOG_DEBUG, "Socket EOF from server (cli_sock=#%d)\n", client_socket); #endif break; } /* update request data structure */ req.type = RESPONSE; /* from here, n can only be positive */ req.size = (size_t) n; bytes_recv += n; if (req.do_intercept==false){ #ifdef DEBUG xlog(LOG_DEBUG, "Intercept disabled for '%s'->browser\n", req.http_infos.hostname); #endif goto send_to_client; } /* apply plugins for response (from server to browser) */ #ifdef DEBUG xlog(LOG_DEBUG, "Response %d pre-plugins: buflen:%lu\n", req.id, req.size); #endif /* execute response hooks */ if ( proxenet_apply_plugins(&req) < 0) { /* extremist action: any error on any plugin discard the whole request */ proxenet_xfree(req.data); break; } #ifdef DEBUG xlog(LOG_DEBUG, "Response %d post-plugins: buflen:%lu\n", req.id, req.size); if(cfg->verbose > 2) proxenet_hexdump(req.data, req.size); #endif send_to_client: /* send modified data to client */ if (req.is_ssl && req.do_intercept) retcode = proxenet_ssl_write(&(ssl_context.server.context), req.data, req.size); else retcode = proxenet_write(server_socket, req.data, req.size); if (retcode < 0) { xlog(LOG_ERROR, "[%d] %s\n", req.id, "proxy->client: write failed"); proxenet_xfree(req.data); break; } #ifdef DEBUG xlog(LOG_DEBUG, "Written %d bytes to browser (socket=%s socket #%d)\n", retcode, is_ssl?"SSL":"PLAIN", client_socket); #endif proxenet_xfree(req.data); } /* end FD_ISSET(data_from_server) */ } /* end for(;;) { select() } */ if (req.id) { if (cfg->verbose) xlog(LOG_INFO, "End of request %d, cleaning context\n", req.id); free_http_infos(&(req.http_infos)); } /* close client socket */ if (client_socket > 0) { if (cfg->verbose >= 2) xlog(LOG_INFO, "Closing %s->server (fd=#%d)\n", PROGNAME, client_socket); proxenet_close_socket(client_socket, &(ssl_context.client)); } /* close local socket */ if (server_socket > 0) { if (cfg->verbose >= 2) xlog(LOG_INFO, "Closing browser->%s (fd=#%d)\n", PROGNAME, server_socket); proxenet_close_socket(server_socket, &(ssl_context.server)); } #ifdef DEBUG xlog(LOG_DEBUG, "Request %d: Structures closed, leaving\n", req.id); #endif /* and that's all folks */ return; }
static int proxenet_apply_plugins(request_t *request) { plugin_t *p = NULL; char *old_data = NULL; char *res = NULL; char* (*plugin_function)(plugin_t*, request_t*) = NULL; for (p=plugins_list; p!=NULL; p=p->next) { if (p->state == INACTIVE) continue; switch (p->type) { #ifdef _PYTHON_PLUGIN case _PYTHON_: plugin_function = proxenet_python_plugin; break; #endif #ifdef _C_PLUGIN case _C_: plugin_function = proxenet_c_plugin; break; #endif #ifdef _RUBY_PLUGIN case _RUBY_: plugin_function = proxenet_ruby_plugin; break; #endif #ifdef _PERL_PLUGIN case _PERL_: plugin_function = proxenet_perl_plugin; break; #endif #ifdef _LUA_PLUGIN case _LUA_: plugin_function = proxenet_lua_plugin; break; #endif #ifdef _TCL_PLUGIN case _TCL_: plugin_function = proxenet_tcl_plugin; break; #endif #ifdef _JAVA_PLUGIN case _JAVA_: plugin_function = proxenet_java_plugin; break; #endif default: xlog(LOG_CRITICAL, "Type %d not supported (yet)\n", p->type); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "Calling '%s:%s' with rid=%d (%s)\n", p->name, request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION, request->id, supported_plugins_str[p->type] ); #endif old_data = request->data; #ifdef DEBUG struct timeval tstart, tend; proxenet_xzero(&tstart, sizeof(struct timeval)); proxenet_xzero(&tend, sizeof(struct timeval)); gettimeofday(&tstart, NULL); #endif res = (*plugin_function)(p, request); #ifdef DEBUG gettimeofday(&tend, NULL); unsigned long sec = tend.tv_sec-tstart.tv_sec; unsigned long msec = (tend.tv_usec-tstart.tv_usec)/1000; unsigned long usec = (tend.tv_usec-tstart.tv_usec)%1000; xlog(LOG_DEBUG, "[%s] '%s:%s' executed in %dsec, %dms, %dus\n", supported_plugins_str[p->type], p->name, request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION, sec, msec, usec); #endif /* * We consider a plugin execution has failed if one of those conditions is found: * - the plugin_*_apply() function returned NULL * - the new request size is 0 */ if (res == NULL || request->size==0) { /* * If the plugin function returned NULL, it means that an error occured * somewhere in the VM run. To be safe, we disable the plugin, and restore * the previous context. */ request->data = old_data; xlog(LOG_ERROR, "Plugin '%s' failed, disabling...\n", p->name); proxenet_plugin_set_state(p, INACTIVE); continue; } request->data = res; if (request->data) { /* * If new_data is different, it means a new buffer was allocated by * (*plugin_function)(). The old_data can then be free-ed. */ proxenet_xfree(old_data); #ifdef DEBUG xlog(LOG_DEBUG, "New data from '%s:%s' on rid=%d\n", request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION, p->name, p->id); #endif } else { /* Otherwise (data different or error), use the original data */ request->data = old_data; #ifdef DEBUG xlog(LOG_DEBUG, "No new data from '%s:%s' on rid=%d\n", request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION, p->name, p->id); #endif } } return 0; }
/** * This function updates all the fields of the current request_t with the new values found in the * request. Since those values will be useful many times, they are strdup-ed in a specific structure * (http_request_t). Those values *must* be free-ed later on. * * @return 0 if successful, -1 if any error occurs. */ int update_http_infos(request_t *req) { char *ptr, *buf, c; buf = req->data; /* method */ ptr = strchr(buf, ' '); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP method in request"); if (cfg->verbose) xlog(LOG_ERROR, "Buffer sent:\n%s\n", buf); return -1; } c = *ptr; *ptr = '\0'; req->http_infos.method = proxenet_xstrdup2(buf); if (!req->http_infos.method){ xlog(LOG_ERROR, "%s\n", "strdup(method) failed, cannot pursue..."); return -1; } *ptr = c; if (!strcmp(req->http_infos.method, "CONNECT")){ int offset; req->is_ssl = true; req->http_infos.proto = HTTPS_STRING; req->http_infos.port = HTTPS_DEFAULT_PORT; offset = ptr - buf + 1; if( get_hostname_from_uri(req, offset) < 0 ){ xlog(LOG_ERROR, "%s\n", "Failed to get hostname (URI)"); goto failed_hostname; } req->http_infos.path = proxenet_xstrdup2("/"); req->http_infos.version = proxenet_xstrdup2("HTTP/1.0"); req->http_infos.uri = get_request_full_uri(req); if(!req->http_infos.uri){ xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed"); goto failed_uri; } return 0; } /* hostname and port */ if (req->is_ssl){ req->http_infos.port = HTTPS_DEFAULT_PORT; req->http_infos.proto = HTTPS_STRING; } else { req->http_infos.port = HTTP_DEFAULT_PORT; req->http_infos.proto = HTTP_STRING; } if( get_hostname_from_header(req) < 0 ){ xlog(LOG_ERROR, "%s\n", "Failed to get hostname (Host header)"); goto failed_hostname; } /* path */ buf = ptr+1; if (!strncmp(buf, HTTP_PROTO_STRING, strlen(HTTP_PROTO_STRING))){ buf = strchr(buf + 8, '/'); } ptr = strchr(buf, ' '); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP path in request"); goto failed_path; } c = *ptr; *ptr = '\0'; req->http_infos.path = proxenet_xstrdup2(buf); if (!req->http_infos.path){ xlog(LOG_ERROR, "%s\n", "strdup(path) failed, cannot pursue..."); goto failed_path; } *ptr = c; buf = ptr+1; /* version */ ptr = strchr(req->data, '\r'); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP version"); goto failed_version; } c = *ptr; *ptr = '\0'; req->http_infos.version = proxenet_xstrdup2(buf); if (!req->http_infos.version){ xlog(LOG_ERROR, "%s\n", "strdup(version) failed, cannot pursue..."); goto failed_version; } *ptr = c; /* refresh uri */ req->http_infos.uri = get_request_full_uri(req); if(!req->http_infos.uri){ xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed"); goto failed_uri; } if (cfg->verbose) { xlog(LOG_INFO, "New request %d to '%s'\n", req->id, req->http_infos.uri); if (cfg->verbose >= 2) { xlog(LOG_INFO, "Request HTTP information:\n" "method=%s\n" "proto=%s\n" "hostname=%s\n" "port=%d\n" "path=%s\n" "version=%s\n" , req->http_infos.method, req->http_infos.proto, req->http_infos.hostname, req->http_infos.port, req->http_infos.path, req->http_infos.version); } } return 0; failed_uri: proxenet_xfree(req->http_infos.version); failed_version: proxenet_xfree(req->http_infos.path); failed_path: proxenet_xfree(req->http_infos.hostname); failed_hostname: proxenet_xfree(req->http_infos.method); return -1; }
static int proxenet_apply_plugins(request_t *request) { plugin_t *p = NULL; char *old_data = NULL; char* (*plugin_function)(plugin_t*, request_t*) = NULL; for (p=plugins_list; p!=NULL; p=p->next) { if (p->state == INACTIVE) continue; switch (p->type) { #ifdef _PYTHON_PLUGIN case _PYTHON_: plugin_function = proxenet_python_plugin; break; #endif #ifdef _C_PLUGIN case _C_: plugin_function = proxenet_c_plugin; break; #endif #ifdef _RUBY_PLUGIN case _RUBY_: plugin_function = proxenet_ruby_plugin; break; #endif #ifdef _PERL_PLUGIN case _PERL_: plugin_function = proxenet_perl_plugin; break; #endif #ifdef _LUA_PLUGIN case _LUA_: plugin_function = proxenet_lua_plugin; break; #endif #ifdef _TCL_PLUGIN case _TCL_: plugin_function = proxenet_tcl_plugin; break; #endif #ifdef _JAVA_PLUGIN case _JAVA_: plugin_function = proxenet_java_plugin; break; #endif default: xlog(LOG_CRITICAL, "Type %d not supported (yet)\n", p->type); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "Calling '%s:%s' with rid=%d (%s)\n", p->name, request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION, request->id, supported_plugins_str[p->type] ); #endif old_data = request->data; #ifdef DEBUG struct timeval tstart, tend; proxenet_xzero(&tstart, sizeof(struct timeval)); proxenet_xzero(&tend, sizeof(struct timeval)); gettimeofday(&tstart, NULL); #endif request->data = (*plugin_function)(p, request); #ifdef DEBUG gettimeofday(&tend, NULL); xlog(LOG_DEBUG, "[%s] '%s:%s' executed in %lusec, %lums\n", supported_plugins_str[p->type], p->name, request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION, tend.tv_sec-tstart.tv_sec, tend.tv_usec-tstart.tv_usec); #endif if (request->data) { /* * If new_data is different, it means a new buffer was allocated by * (*plugin_function)(). The old_data can then be free-ed. */ proxenet_xfree(old_data); #ifdef DEBUG xlog(LOG_DEBUG, "New data from '%s:%s' on rid=%d\n", request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION, p->name, p->id); #endif } else { /* Otherwise (data different or error), use the original data */ request->data = old_data; #ifdef DEBUG xlog(LOG_DEBUG, "No new data from '%s:%s' on rid=%d\n", request->type==REQUEST?CFG_REQUEST_PLUGIN_FUNCTION:CFG_RESPONSE_PLUGIN_FUNCTION, p->name, p->id); #endif } /* Additionnal check for dummy plugin coder */ if (!request->data || !request->size) { xlog(LOG_CRITICAL, "Plugin '%s' is invalid, disabling\n", p->name); p->state = INACTIVE; if (cfg->verbose){ if(!request->data) xlog(LOG_CRITICAL, "Plugin '%s' returned a NULL value\n", p->name); else xlog(LOG_CRITICAL, "Plugin '%s' returned a NULL size\n", p->name); } return -1; } } return 0; }