/** * 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 updates all the fields of the current request_t with the new values found in the * request. Since those values will be useful many times, they are strdup-ed in a specific structure * (http_infos_t). Those values *must* be free-ed later on. * * @return 0 if successful, -1 if any error occurs. */ int parse_http_request(request_t *req) { char *ptr, *buf, c; int offset; buf = req->data; /* method */ ptr = strchr(buf, ' '); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP method in request"); if (cfg->verbose) xlog(LOG_ERROR, "Buffer sent:\n%s\n", buf); return -1; } c = *ptr; *ptr = '\0'; req->http_infos.method = proxenet_xstrdup2(buf); if (!req->http_infos.method){ xlog(LOG_ERROR, "%s\n", "strdup(method) failed, cannot pursue..."); return -1; } *ptr = c; req->http_infos.proto_type = HTTP; if (!strcmp(req->http_infos.method, "CONNECT")){ /* * We can receive a CONNECT if * - the connection uses HTTPS * - the client tries to upgrade to WebSocket/Secure WebSocket */ char *upgrade_header = get_header_by_name(buf, "Upgrade:"); if (upgrade_header){ if (strcmp(upgrade_header, "WebSocket")==0){ xlog(LOG_INFO, "%s\n", "Upgrading to WebSocket"); req->http_infos.proto_type = WS; req->is_ssl = false; req->http_infos.proto = WS_STRING; req->http_infos.port = HTTP_DEFAULT_PORT; req->http_infos.proto_type = WS; } proxenet_xfree(upgrade_header); } else { req->is_ssl = true; req->http_infos.proto = HTTPS_STRING; req->http_infos.port = HTTPS_DEFAULT_PORT; req->http_infos.proto_type = HTTPS; } offset = ptr - buf + 1; if( get_hostname_from_uri(req, offset) < 0 ){ xlog(LOG_ERROR, "%s\n", "Failed to get hostname (URI)"); goto failed_hostname; } req->http_infos.path = proxenet_xstrdup2("/"); req->http_infos.version = proxenet_xstrdup2("HTTP/1.0"); req->http_infos.uri = get_request_full_uri(req); if(!req->http_infos.uri){ xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed"); goto failed_uri; } return 0; } /* hostname and port */ if (req->is_ssl){ req->http_infos.port = HTTPS_DEFAULT_PORT; req->http_infos.proto = HTTPS_STRING; } else { req->http_infos.port = HTTP_DEFAULT_PORT; req->http_infos.proto = HTTP_STRING; } if( get_hostname_from_header(req) < 0 ){ xlog(LOG_ERROR, "%s\n", "Failed to get hostname (Host header)"); goto failed_hostname; } /* path */ buf = ptr+1; if (!strncmp(buf, HTTP_PROTO_STRING, strlen(HTTP_PROTO_STRING))){ buf = strchr(buf + 8, '/'); } ptr = strchr(buf, ' '); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP path in request"); goto failed_path; } c = *ptr; *ptr = '\0'; req->http_infos.path = proxenet_xstrdup2(buf); if (!req->http_infos.path){ xlog(LOG_ERROR, "%s\n", "strdup(path) failed, cannot pursue..."); goto failed_path; } *ptr = c; buf = ptr+1; /* version */ ptr = strchr(req->data, '\r'); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP version"); goto failed_version; } c = *ptr; *ptr = '\0'; req->http_infos.version = proxenet_xstrdup2(buf); if (!req->http_infos.version){ xlog(LOG_ERROR, "%s\n", "strdup(version) failed, cannot pursue..."); goto failed_version; } *ptr = c; /* refresh uri */ req->http_infos.uri = get_request_full_uri(req); if(!req->http_infos.uri){ xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed"); goto failed_uri; } if (cfg->verbose) { xlog(LOG_INFO, "New request %d to '%s'\n", req->id, req->http_infos.uri); if (cfg->verbose > 1) { xlog(LOG_INFO, "Request HTTP information:\n" "method=%s\n" "proto=%s\n" "hostname=%s\n" "port=%d\n" "path=%s\n" "version=%s\n" , req->http_infos.method, req->http_infos.proto, req->http_infos.hostname, req->http_infos.port, req->http_infos.path, req->http_infos.version); } } return 0; failed_uri: proxenet_xfree(req->http_infos.version); failed_version: proxenet_xfree(req->http_infos.path); failed_path: proxenet_xfree(req->http_infos.hostname); failed_hostname: proxenet_xfree(req->http_infos.method); return -1; }
/** * This function updates all the fields of the current request_t with the new values found in the * request. Since those values will be useful many times, they are strdup-ed in a specific structure * (http_request_t). Those values *must* be free-ed later on. * * @return 0 if successful, -1 if any error occurs. */ int update_http_infos(request_t *req) { char *ptr, *buf, c; buf = req->data; /* method */ ptr = strchr(buf, ' '); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP method in request"); if (cfg->verbose) xlog(LOG_ERROR, "Buffer sent:\n%s\n", buf); return -1; } c = *ptr; *ptr = '\0'; req->http_infos.method = proxenet_xstrdup2(buf); if (!req->http_infos.method){ xlog(LOG_ERROR, "%s\n", "strdup(method) failed, cannot pursue..."); return -1; } *ptr = c; if (!strcmp(req->http_infos.method, "CONNECT")){ int offset; req->is_ssl = true; req->http_infos.proto = HTTPS_STRING; req->http_infos.port = HTTPS_DEFAULT_PORT; offset = ptr - buf + 1; if( get_hostname_from_uri(req, offset) < 0 ){ xlog(LOG_ERROR, "%s\n", "Failed to get hostname (URI)"); goto failed_hostname; } req->http_infos.path = proxenet_xstrdup2("/"); req->http_infos.version = proxenet_xstrdup2("HTTP/1.0"); req->http_infos.uri = get_request_full_uri(req); if(!req->http_infos.uri){ xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed"); goto failed_uri; } return 0; } /* hostname and port */ if (req->is_ssl){ req->http_infos.port = HTTPS_DEFAULT_PORT; req->http_infos.proto = HTTPS_STRING; } else { req->http_infos.port = HTTP_DEFAULT_PORT; req->http_infos.proto = HTTP_STRING; } if( get_hostname_from_header(req) < 0 ){ xlog(LOG_ERROR, "%s\n", "Failed to get hostname (Host header)"); goto failed_hostname; } /* path */ buf = ptr+1; if (!strncmp(buf, HTTP_PROTO_STRING, strlen(HTTP_PROTO_STRING))){ buf = strchr(buf + 8, '/'); } ptr = strchr(buf, ' '); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP path in request"); goto failed_path; } c = *ptr; *ptr = '\0'; req->http_infos.path = proxenet_xstrdup2(buf); if (!req->http_infos.path){ xlog(LOG_ERROR, "%s\n", "strdup(path) failed, cannot pursue..."); goto failed_path; } *ptr = c; buf = ptr+1; /* version */ ptr = strchr(req->data, '\r'); if (!ptr){ xlog(LOG_ERROR, "%s\n", "Cannot find HTTP version"); goto failed_version; } c = *ptr; *ptr = '\0'; req->http_infos.version = proxenet_xstrdup2(buf); if (!req->http_infos.version){ xlog(LOG_ERROR, "%s\n", "strdup(version) failed, cannot pursue..."); goto failed_version; } *ptr = c; /* refresh uri */ req->http_infos.uri = get_request_full_uri(req); if(!req->http_infos.uri){ xlog(LOG_ERROR, "%s\n", "get_request_full_uri() failed"); goto failed_uri; } if (cfg->verbose) { xlog(LOG_INFO, "New request %d to '%s'\n", req->id, req->http_infos.uri); if (cfg->verbose >= 2) { xlog(LOG_INFO, "Request HTTP information:\n" "method=%s\n" "proto=%s\n" "hostname=%s\n" "port=%d\n" "path=%s\n" "version=%s\n" , req->http_infos.method, req->http_infos.proto, req->http_infos.hostname, req->http_infos.port, req->http_infos.path, req->http_infos.version); } } return 0; failed_uri: proxenet_xfree(req->http_infos.version); failed_version: proxenet_xfree(req->http_infos.path); failed_path: proxenet_xfree(req->http_infos.hostname); failed_hostname: proxenet_xfree(req->http_infos.method); return -1; }