/** * Add old IE support (Compatibility View) for POST requests by forcing a 2nd read on the * server socket, to make IE send the POST body. * Tests revealed that this mode does not change the behaviour for recent browser. Therefore, * it can be used all the time. Option -i allows to disable it. * * @return 0 if successful, -1 otherwise */ int ie_compat_read_post_body(sock_t sock, request_t* req, proxenet_ssl_context_t* sslctx) { int nb; size_t old_len, body_len; char *body, *clen; /* to speed-up, disregard requests without body */ if (strcmp(req->http_infos.method, "POST")!=0 && \ strcmp(req->http_infos.method, "PUT")!=0) return 0; /* read Content-Length header */ clen = get_header_by_name(req->data, "Content-Length: "); if (!clen){ xlog(LOG_ERROR, "%s\n", "Extending IE POST: No Content-Length"); return -1; } /* if Content-Length is zero */ body_len = (size_t)atoi(clen); proxenet_xfree(clen); if (body_len==0){ return 0; } /* if everything has already been sent (i.e. end of already received buffer is not CRLF*2) */ if (req->data[ req->size-4 ] != '\r') return 0; if (req->data[ req->size-3 ] != '\n') return 0; if (req->data[ req->size-2 ] != '\r') return 0; if (req->data[ req->size-1 ] != '\n') return 0; /* read data (if any) */ nb = proxenet_read_all(sock, &body, sslctx); if (nb<0){ xlog(LOG_ERROR, "%s\n", "Extending IE POST: failed to read"); return -1; } /* and extend the request buffer */ if (nb>0){ old_len = req->size; req->size = old_len + (size_t)nb; req->data = proxenet_xrealloc(req->data, req->size); memcpy(req->data + old_len, body, (size_t)nb); } proxenet_xfree(body); #ifdef DEBUG xlog(LOG_DEBUG, "Extending request for IE: new_size=%d (content-length=%d)\n", req->size, body_len); #endif return 0; }
int create_https_socket(request_t *req, sock_t *cli_sock, sock_t *srv_sock, ssl_context_t* ctx, bool use_http_proxy) { char *connect_buf = NULL; http_infos_t* http_infos = &req->http_infos; int retcode = -1; /* disable all interception if ssl intercept was explicitely disabled by config */ if (cfg->ssl_intercept == false) req->do_intercept = false; if (use_http_proxy) { /* 0. set up proxy->proxy ssl session (i.e. forward CONNECT request) */ retcode = proxenet_write(*cli_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(*cli_sock, &connect_buf, NULL); if (retcode < 0) { xlog(LOG_ERROR, "%s Failed to read from proxy\n", PROGNAME); proxenet_xfree(connect_buf); 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); proxenet_xfree(connect_buf); if (cfg->verbose) xlog(LOG_ERROR, "Received %s\n", connect_buf); return -1; } proxenet_xfree(connect_buf); } if (req->do_intercept){ /* 1. set up proxy->server ssl session with hostname */ if(proxenet_ssl_init_client_context(&(ctx->client), http_infos->hostname) < 0) { return -1; } proxenet_ssl_wrap_socket(&(ctx->client.context), cli_sock); retcode = proxenet_ssl_handshake(&(ctx->client.context)); if (retcode < 0) { xlog(LOG_ERROR, "handshake %s->server failed [code: %#x]\n", PROGNAME, retcode); xlog(LOG_ERROR, "Client SSL handshake failed for '%s:%d'.\n", http_infos->hostname, http_infos->port, retcode); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "SSL handshake with %s done, cli_sock=%d\n", use_http_proxy?"proxy":"server", *cli_sock); #endif } if (proxenet_write(*srv_sock, "HTTP/1.0 200 Connection established\r\n\r\n", 39) < 0){ return -1; } if (req->do_intercept) { /* 2. set up proxy->browser ssl session with hostname */ if(proxenet_ssl_init_server_context(&(ctx->server), http_infos->hostname) < 0) { return -1; } proxenet_ssl_wrap_socket(&(ctx->server.context), srv_sock); retcode = proxenet_ssl_handshake(&(ctx->server.context)); if (retcode < 0) { xlog(LOG_ERROR, "handshake %s->client failed [code: %#x]\n", PROGNAME, retcode); xlog(LOG_ERROR, "Server SSL handshake failed for '%s:%d'.\n", http_infos->hostname, http_infos->port, retcode); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "SSL handshake with client done, srv_sock=%d\n", *srv_sock); #endif } 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; }
/** * Establish a connection from proxenet -> server. * If proxy forwarding configured, then this function performs the negociation with the other proxy. * If the host applies for SSL intercept rules, this function also handles the SSL handshake. * * @return 0 if successful, -1 otherwise */ 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[7] = {0, }; http_request_t* http_infos = &req->http_infos; bool use_proxy = (cfg->proxy.host != NULL); bool use_http_proxy = use_proxy && (cfg->is_socks_proxy == false); bool use_socks_proxy = use_proxy && cfg->is_socks_proxy; char errmsg[512]={0,}; if (update_http_infos(req) < 0){ xlog(LOG_ERROR, "%s\n", "Failed to extract valid parameters from URL."); return -1; } ssl_ctx->use_ssl = req->is_ssl; proxenet_xsnprintf(sport, sizeof(sport), "%hu", 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; } #ifdef DEBUG xlog(LOG_DEBUG, "Relay request %s to '%s:%s'\n", use_http_proxy ? "via HTTP proxy" : "direct", host, port); #endif retcode = proxenet_open_socket(host, port); if (retcode < 0) { proxenet_xsnprintf(errmsg, sizeof(errmsg), "Cannot connect to %s:%s<br><br>Reason: %s", host, port, errno?strerror(errno):"<i>proxenet_open_socket()</i> failed"); generic_http_error_page(*server_sock, errmsg); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "Socket to %s '%s:%s': fd=%d\n", use_http_proxy?"HTTP proxy":(use_socks_proxy?"SOCKS4 proxy":"server"), host, port, retcode); #endif *client_sock = retcode; req->do_intercept = ( (cfg->intercept_mode==INTERCEPT_ONLY && \ fnmatch(cfg->intercept_pattern, http_infos->hostname, 0)==0) || \ (cfg->intercept_mode==INTERCEPT_EXCEPT && \ fnmatch(cfg->intercept_pattern, http_infos->hostname, 0)==FNM_NOMATCH) ); #ifdef DEBUG xlog(LOG_DEBUG, "Server '%s' %s match interception '%s' with pattern '%s'\n", http_infos->hostname, req->do_intercept?"do":"do not", cfg->intercept_mode==INTERCEPT_ONLY?"INTERCEPT_ONLY":"INTERCEPT_EXCEPT", cfg->intercept_pattern); #endif if(use_socks_proxy){ char*rhost = http_infos->hostname; int rport = http_infos->port; retcode = proxenet_socks_connect(*client_sock, rhost, rport, true); if( retcode<0 ){ proxenet_xsnprintf(errmsg, sizeof(errmsg), "Failed to open SOCKS4 tunnel to %s:%s.\n", host, port); generic_http_error_page(*server_sock, errmsg); xlog(LOG_ERROR, "%s", errmsg); return -1; } } /* set up ssl layer */ if (req->is_ssl) { /* adjust do_intercept if we do not SSL intercept was explicitely disabled */ req->do_intercept = cfg->ssl_intercept; if (use_http_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; } #ifdef DEBUG xlog(LOG_DEBUG, "HTTP Connect Ok with '%s:%s', cli_sock=%d\n", host, port, *client_sock); #endif } if (req->do_intercept){ /* 1. set up proxy->server ssl session with hostname */ if(proxenet_ssl_init_client_context(&(ssl_ctx->client), http_infos->hostname) < 0) { return -1; } proxenet_ssl_wrap_socket(&(ssl_ctx->client.context), client_sock); retcode = proxenet_ssl_handshake(&(ssl_ctx->client.context)); if (retcode < 0) { xlog(LOG_ERROR, "handshake %s->server failed [code: %#x]\n", PROGNAME, retcode); xlog(LOG_ERROR, "Client SSL handshake failed for '%s:%d'.\n", http_infos->hostname, http_infos->port, retcode); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "SSL handshake with %s done, cli_sock=%d\n", use_http_proxy?"proxy":"server", *client_sock); #endif } if (proxenet_write(*server_sock, "HTTP/1.0 200 Connection established\r\n\r\n", 39) < 0){ return -1; } if (req->do_intercept) { /* 2. set up proxy->browser ssl session with hostname */ if(proxenet_ssl_init_server_context(&(ssl_ctx->server), http_infos->hostname) < 0) { return -1; } proxenet_ssl_wrap_socket(&(ssl_ctx->server.context), server_sock); retcode = proxenet_ssl_handshake(&(ssl_ctx->server.context)); if (retcode < 0) { xlog(LOG_ERROR, "handshake %s->client failed [code: %#x]\n", PROGNAME, retcode); xlog(LOG_ERROR, "Server SSL handshake failed for '%s:%d'.\n", http_infos->hostname, http_infos->port, retcode); return -1; } #ifdef DEBUG xlog(LOG_DEBUG, "SSL handshake with client done, srv_sock=%d\n", *server_sock); #endif } } return retcode; }