/** * initialize config and allocate buffers * * @return -1 if an error occured * @return 0 on success */ int proxenet_init_config(int argc, char** argv) { cfg = ¤t_config; proxenet_xzero(cfg, sizeof(conf_t)); if (parse_options(argc, argv) < 0) { xlog(LOG_CRITICAL, "%s\n", "Failed to parse arguments"); proxenet_free_config(); proxenet_xzero(cfg, sizeof(conf_t)); return -1; } return 0; }
sock_t proxenet_bind_control_socket() { sock_t control_sock = -1; struct sockaddr_un sun_local; proxenet_xzero(&sun_local, sizeof(struct sockaddr_un)); /* create control socket */ control_sock = socket(AF_UNIX, SOCK_STREAM, 0); if (control_sock < 0) { return -1; } sun_local.sun_family = AF_UNIX; strcpy(sun_local.sun_path, CFG_CONTROL_SOCK_PATH); unlink(sun_local.sun_path); /* and bind+listen */ if ( (bind(control_sock, (struct sockaddr *)&sun_local, SUN_LEN(&sun_local)) < 0) || (listen(control_sock, 1) < 0 ) ) { close(control_sock); return -1; } xlog(LOG_INFO, "Control interface listening on '%s'\n", sun_local.sun_path); return control_sock; }
/** * Prints a few information about the proxenet session. */ static void print_global_stats() { struct tm *t1, *t2; char buf[128]; float bytes_sent_kb, bytes_sent_mb; float bytes_recv_kb, bytes_recv_mb; t1 = localtime(&start_time); t2 = localtime(&end_time); xlog(LOG_INFO, "%s\n", "Statistics:"); proxenet_xzero(buf, sizeof(buf)); strftime(buf, sizeof(buf), "%F %T", t1); xlog(LOG_INFO, "Start time: %s\n", buf); proxenet_xzero(buf, sizeof(buf)); strftime(buf, sizeof(buf), "%F %T", t2); xlog(LOG_INFO, "End time: %s\n", buf); xlog(LOG_INFO, "Session duration: %lu seconds\n", (end_time-start_time)); bytes_sent_kb = bytes_sent/1024.0; bytes_sent_mb = bytes_sent_kb/1024.0; xlog(LOG_INFO, "Number of bytes sent: %luB (%.2fkB, %.2fMB)\n", bytes_sent, bytes_sent_kb, bytes_sent_mb); bytes_recv_kb = bytes_recv/1024.0; bytes_recv_mb = bytes_recv_kb/1024.0; xlog(LOG_INFO, "Number of bytes received: %luB (%.2fkB, %5.2fMB)\n", bytes_recv,bytes_recv_kb, bytes_recv_mb); xlog(LOG_INFO, "Number of unique requests: %lu\n", (request_id-1)); return; }
void initialize_sigmask(struct sigaction *saction) { proxenet_xzero(saction, sizeof(struct sigaction)); saction->sa_handler = sighandler; saction->sa_flags = SA_RESTART|SA_NOCLDSTOP; sigemptyset(&(saction->sa_mask)); sigaction(SIGINT, saction, NULL); sigaction(SIGTERM, saction, NULL); sigaction(SIGCHLD, saction, NULL); saction->sa_handler = SIG_IGN; sigaction(SIGPIPE, saction, NULL); return; }
/** * malloc(3) wrapper. Checks size and zero-fill buffer. * * Note: (re-)allocation is a "succeed-or-die" process in proxenet. * * @param size: buffer size to allocate on heap * @return ptr: allocated zero-filled buffer pointer */ void* proxenet_xmalloc(size_t size) { void *ptr; if (size > SIZE_MAX / sizeof(size_t)) { xlog(LOG_CRITICAL, "proxenet_xmalloc: try to allocate incorrect size (%d byte)\n", size); abort(); } ptr = malloc(size); if ( ptr == NULL ) { xlog(LOG_CRITICAL, "%s\n", "proxenet_xmalloc: fail to allocate space"); abort(); } proxenet_xzero(ptr, size); return ptr; }
void help_cmd(sock_t fd, char *options, unsigned int nb_options) { struct command_t *cmd; char *msg; unsigned int msglen = 20 + 80 + 3; msg = "Command list:\n"; proxenet_write(fd, (void*)msg, strlen(msg)); for (cmd=known_commands; cmd && cmd->name; cmd++) { msg = alloca(msglen+1); proxenet_xzero(msg, msglen+1); snprintf(msg, msglen+1, "%-15s\t%s\n", cmd->name, cmd->desc); proxenet_write(fd, (void*)msg, strlen(msg)); } return; }
static int proxenet_perl_load_file(plugin_t* plugin) { char *pathname = NULL; size_t pathlen = 0; SV* sv = NULL; int nb_res = -1; SV* package_sv = NULL; char *required = NULL; char *package_name = NULL; size_t package_len, len = 0; int ret = -1; pathlen = strlen(cfg->plugins_path) + 1 + strlen(plugin->filename) + 1; pathname = (char*) alloca(pathlen+1); proxenet_xzero(pathname, pathlen+1); snprintf(pathname, pathlen, "%s/%s", cfg->plugins_path, plugin->filename); #ifdef DEBUG xlog(LOG_DEBUG, "[Perl] Loading '%s'\n", pathname); #endif /* Load the file through perl's require mechanism */ dSP; ENTER; SAVETMPS; PUSHMARK(SP); PUTBACK; sv = newSVpvf("$package = require q%c%s%c", 0, pathname, 0); nb_res = eval_sv(sv_2mortal(sv), G_EVAL); if (nb_res != 1) { xlog(LOG_ERROR, "[Perl] Invalid number of response returned while loading '%s' (got %d, expected 1)\n", pathname, nb_res); } else if (SvTRUE(ERRSV)) { xlog(LOG_ERROR, "[Perl] Eval error for '%s': %s\n", pathname, SvPV_nolen(ERRSV)); } else { /* Get the package name from the package (which should follow the convention...) */ package_sv = get_sv("package", 0); /* Check if the SV* stores a string */ if (!SvPOK(package_sv)) { xlog(LOG_ERROR, "[Perl] Invalid convention for '%s': the package should return a string\n", pathname); } else { required = (char*) SvPV_nolen(package_sv); package_len = strlen(required); package_name = (char*) alloca(package_len+1); proxenet_xzero(package_name, package_len+1); memcpy(package_name, required, package_len); #ifdef DEBUG xlog(LOG_DEBUG, "[Perl] Package of name '%s' loaded\n", package_name); #endif /* Save the functions' full name to call them later */ len = package_len + 2 + strlen(CFG_REQUEST_PLUGIN_FUNCTION); plugin->pre_function = proxenet_xmalloc(len + 1); snprintf(plugin->pre_function, len+1, "%s::%s", package_name, CFG_REQUEST_PLUGIN_FUNCTION); len = package_len + 2 + strlen(CFG_RESPONSE_PLUGIN_FUNCTION); plugin->post_function = proxenet_xmalloc(len + 1); snprintf(plugin->post_function, len+1, "%s::%s", package_name, CFG_RESPONSE_PLUGIN_FUNCTION); ret = 0; } } SPAGAIN; PUTBACK; FREETMPS; LEAVE; return ret; }
/** * 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; }
void xloop(sock_t sock, sock_t ctl_sock) { fd_set sock_set; int retcode; pthread_attr_t pattr; int tid; sock_t conn; sigset_t curmask, oldmask; sock_t ctl_cli_sock = -1; /* prepare threads */ proxenet_xzero(threads, sizeof(pthread_t) * MAX_THREADS); if (pthread_attr_init(&pattr)) { xlog(LOG_ERROR, "%s\n", "Failed to pthread_attr_init"); return; } pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE); /* block useful signal */ sigemptyset(&curmask); sigaddset(&curmask, SIGTERM); sigaddset(&curmask, SIGINT); sigaddset(&curmask, SIGCHLD); if (pthread_sigmask(SIG_BLOCK, &curmask, &oldmask) < 0) { xlog(LOG_ERROR, "sigprocmask failed : %s\n", strerror(errno)); return; } /* proxenet is now running :) */ proxy_state = ACTIVE; /* big loop */ while (proxy_state != INACTIVE) { conn = -1; retcode = -1; FD_ZERO(&sock_set); FD_SET(sock, &sock_set); FD_SET(ctl_sock, &sock_set); if (ctl_cli_sock > 0) FD_SET(ctl_cli_sock, &sock_set); purge_zombies(); /* set asynchronous listener */ struct timespec timeout = { .tv_sec = 5, .tv_nsec = 0 }; retcode = pselect(FD_SETSIZE, &sock_set, NULL, NULL, &timeout, &oldmask); if (retcode < 0) { if (errno != EINTR) { xlog(LOG_ERROR, "[main] pselect() returned %d: %s\n", retcode, strerror(errno)); proxy_state = INACTIVE; break; } else { continue; } } if (retcode == 0) continue; if (proxy_state == INACTIVE) break; /* event on the listening socket -> new request */ if( FD_ISSET(sock, &sock_set) && proxy_state != SLEEPING) { #ifdef DEBUG xlog(LOG_DEBUG, "%s\n", "Incoming listening event"); #endif tid = get_new_thread_id(); if(tid < 0) { continue; } struct sockaddr addr; socklen_t addrlen = 0; proxenet_xzero(&addr, sizeof(struct sockaddr)); conn = accept(sock, &addr, &addrlen); if (conn < 0) { if(errno != EINTR) xlog(LOG_ERROR, "[main] accept() failed: %s\n", strerror(errno)); continue; } retcode = proxenet_start_new_thread(conn, tid, &threads[tid], &pattr); if (retcode < 0) { xlog(LOG_ERROR, "[main] %s\n", "Error while spawn new thread"); continue; } } /* end if _socket_event */ /* event on control listening socket */ if( FD_ISSET(ctl_sock, &sock_set) ) { #ifdef DEBUG xlog(LOG_DEBUG, "%s\n", "Incoming control event"); #endif struct sockaddr_un sun_cli; socklen_t sun_cli_len = 0; int new_conn = -1; proxenet_xzero(&sun_cli, sizeof(struct sockaddr_un)); new_conn = accept(ctl_sock, (struct sockaddr *)&sun_cli, &sun_cli_len); if (new_conn < 0) { xlog(LOG_ERROR, "[main] control accept() failed: %s\n", strerror(errno)); continue; } if (ctl_cli_sock < 0) { ctl_cli_sock = new_conn; xlog(LOG_INFO, "%s\n", "New connection on Control socket"); proxenet_write(ctl_cli_sock, CONTROL_MOTD, strlen(CONTROL_MOTD)); proxenet_write(ctl_cli_sock, CONTROL_PROMPT, strlen(CONTROL_PROMPT)); } else { if(new_conn > 0) { xlog(LOG_ERROR, "%s\n", "Denied control connection: already established"); if(close_socket(new_conn) < 0) { xlog(LOG_ERROR, "Failed to close socket: %s\n", strerror(errno)); } } } }/* end if _control_listening_event */ /* event on control socket */ if( ctl_cli_sock > 0 && FD_ISSET(ctl_cli_sock, &sock_set) ) { if (proxenet_handle_control_event(&ctl_cli_sock) < 0) { close_socket(ctl_cli_sock); ctl_cli_sock = -1; } } /* end if _control_event */ } /* endof while(!INACTIVE) */ kill_zombies(); proxenet_destroy_plugins_vm(); pthread_attr_destroy(&pattr); return; } /** * * @param signum */ void sighandler(int signum) { #ifdef DEBUG xlog(LOG_DEBUG, "Received signal %s [%d]\n", strsignal(signum), signum); #endif switch(signum) { case SIGTERM: case SIGINT: if (proxy_state != INACTIVE) proxy_state = INACTIVE; cfg->try_exit++; xlog(LOG_INFO, "%s, %d/%d\n", "Trying to leave", cfg->try_exit, cfg->try_exit_max); if (cfg->try_exit == cfg->try_exit_max) { xlog(LOG_CRITICAL, "%s\n", "Failed to exit properly"); abort(); } break; case SIGCHLD: purge_zombies(); break; } }
/** * 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; }
/** * 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; }
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; }