void reload_cmd(sock_t fd, char *options, unsigned int nb_options) { char *msg; if (get_active_threads_size() > 0) { msg = "Threads still active, cannot reload"; proxenet_write(fd, (void*)msg, strlen(msg)); return; } proxy_state = SLEEPING; proxenet_destroy_plugins_vm(); proxenet_delete_list_plugins(); if( proxenet_initialize_plugins_list() < 0) { msg = "Failed to reinitilize plugins"; proxenet_write(fd, (void*)msg, strlen(msg)); proxy_state = INACTIVE; return; } proxenet_initialize_plugins(); proxy_state = ACTIVE; msg = "Plugins list successfully reloaded\n"; proxenet_write(fd, (void*)msg, strlen(msg)); return; }
void threads_cmd(sock_t fd, char *options, unsigned int nb_options) { char msg[BUFSIZE] = {0, }; char *ptr; int n; ptr = strtok(options, " \n"); if (!ptr){ n = get_active_threads_size(); n = snprintf(msg, 128, "%d active thread%c/%d max thread%c\n", n, (n>1)?'s':' ', cfg->nb_threads, (cfg->nb_threads>1)?'s':' ' ); proxenet_write(fd, (void*)msg, n); return; } if (strcmp(ptr, "inc")==0 && cfg->nb_threads<MAX_THREADS) n = snprintf(msg, BUFSIZE, "Nb threads level is now %d\n", ++cfg->nb_threads); else if (strcmp(ptr, "dec")==0 && cfg->nb_threads>1) n = snprintf(msg, BUFSIZE, "Nb threads level is now %d\n", --cfg->nb_threads); else n = snprintf(msg, BUFSIZE, "Invalid action\n Syntax\n threads (inc|dec)\n"); proxenet_write(fd, (void*)msg, n); return; }
void quit_cmd(sock_t fd, char *options, unsigned int nb_options) { char *msg = "Leaving gracefully\n"; proxenet_write(fd, (void*)msg, strlen(msg)); proxy_state = INACTIVE; return; }
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; }
void info_cmd(sock_t fd, char *options, unsigned int nb_options) { char msg[BUFSIZE] = {0, }; char *msg2 = NULL; snprintf(msg, BUFSIZE, "Infos:\n" "- Listening interface: %s/%s\n" "- Supported IP version: %s\n" "- Logging to %s\n" "- Running/Max threads: %d/%d\n" "- SSL private key: %s\n" "- SSL certificate: %s\n" "- Proxy: %s [%s]\n" "- Plugins directory: %s\n" , cfg->iface, cfg->port, (cfg->ip_version==AF_INET)? "IPv4": (cfg->ip_version==AF_INET6)?"IPv6": "ANY", (cfg->logfile)?cfg->logfile:"stdout", get_active_threads_size(), cfg->nb_threads, cfg->keyfile, cfg->certfile, cfg->proxy.host ? cfg->proxy.host : "None", cfg->proxy.host ? cfg->proxy.port : "direct", cfg->plugins_path ); proxenet_write(fd, (void*)msg, strlen(msg)); if (proxenet_plugin_list_size()) { msg2 = proxenet_build_plugins_list(); proxenet_write(fd, (void*)msg2, strlen(msg2)); proxenet_xfree(msg2); } else { proxenet_write(fd, (void*)"No plugin loaded\n", 17); } return; }
void proxenet_print_plugins_list(int fd) { char *list_str; int list_len; list_len = 2048; list_str = (char*)alloca(list_len); memset(list_str, 0, list_len); if (!proxenet_build_plugins_list(list_str, &list_len)) { xlog(LOG_ERROR, "%s\n", "Failed to build plugins list string"); return; } if (fd<0) xlog(LOG_INFO, "%s", list_str); else { proxenet_write(fd, list_str, list_len); proxenet_write(fd, "\n", 1); } return; }
int proxenet_handle_control_event(sock_t* sock) { char read_buf[BUFSIZE] = {0, }; char *ptr = NULL; int retcode = -1; struct command_t *cmd = NULL; retcode = proxenet_read(*sock, read_buf, BUFSIZE-1); if (retcode < 0) { xlog(LOG_ERROR, "Failed to read control command: %s\n", strerror(errno)); return -1; } if (retcode == 0) { return -1; } if (read_buf[0] == '\n') { goto cmd_end; } if ( (cmd=get_command(read_buf)) == NULL ) { proxenet_write(*sock, CONTROL_INVALID, strlen(CONTROL_INVALID)); goto cmd_end; } #ifdef DEBUG xlog(LOG_DEBUG, "Receiving control command: \"%s\" \n", cmd->name); #endif ptr = &read_buf[strlen(cmd->name)]; cmd->func(*sock, ptr, cmd->nb_opt_max); cmd_end: proxenet_write(*sock, CONTROL_PROMPT, strlen(CONTROL_PROMPT)); return 0; }
void plugin_cmd(sock_t fd, char *options, unsigned int nb_options) { char msg[BUFSIZE] = {0, }; char *ptr, *plist_str; int n, res; ptr = strtok(options, " \n"); if (!ptr){ n = snprintf(msg, BUFSIZE, "Invalid action\nSyntax\n plugin [list]|[toggle <num>]\n"); proxenet_write(fd, (void*)msg, n); return; } if (strcmp(ptr, "list") == 0) { plist_str = proxenet_build_plugins_list(); proxenet_write(fd, (void*)plist_str, strlen(plist_str)); proxenet_xfree(plist_str); return; } else if (strcmp(ptr, "toggle") == 0) { ptr = strtok(NULL, " \n"); if (!ptr) return; n = atoi(ptr); if (0 < n && n <= proxenet_plugin_list_size() ) { res = proxenet_toggle_plugin(n); n = snprintf(msg, BUFSIZE, "Plugin %d is now %sACTIVE\n", n, res?"":"IN"); proxenet_write(fd, (void*)msg, n); return; } } n = snprintf(msg, BUFSIZE, "Invalid action\nSyntax\n plugin [list]|[toggle <num>]\n"); proxenet_write(fd, (void*)msg, n); return; }
void pause_cmd(sock_t fd, char *options, unsigned int nb_options) { char *msg; if (proxy_state==SLEEPING) { msg = "sleep-mode -> 0\n"; proxy_state = ACTIVE; } else { msg = "sleep-mode -> 1\n"; proxy_state = SLEEPING; } xlog(LOG_INFO, "%s", msg); proxenet_write(fd, (void*)msg, strlen(msg)); return; }
void verbose_cmd(sock_t fd, char *options, unsigned int nb_options) { char msg[BUFSIZE] = {0, }; char *ptr; int n; ptr = strtok(options, " \n"); if (!ptr){ n = snprintf(msg, BUFSIZE, "Verbose level is at %d\n", cfg->verbose); proxenet_write(fd, (void*)msg, n); return; } if (strcmp(ptr, "inc")==0 && cfg->verbose<MAX_VERBOSE_LEVEL) n = snprintf(msg, BUFSIZE, "Verbose level is now %d\n", ++cfg->verbose); else if (strcmp(ptr, "dec")==0 && cfg->verbose>0) n = snprintf(msg, BUFSIZE, "Verbose level is now %d\n", --cfg->verbose); else n = snprintf(msg, BUFSIZE, "Invalid action\n Syntax\n verbose (inc|dec)\n"); proxenet_write(fd, (void*)msg, n); return; }
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; }
/** * 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; }
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; }
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; }
/** * 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; }