From 46f96ed5e3127394a0cd4dcb961404b792f743b4 Mon Sep 17 00:00:00 2001 From: Jakub Slawinski Date: Thu, 10 Jul 2014 10:24:59 +0200 Subject: Initial project structure based on Active Port Forwarder 0.8.4. --- src/http_proxy_client.c | 565 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 565 insertions(+) create mode 100644 src/http_proxy_client.c (limited to 'src/http_proxy_client.c') diff --git a/src/http_proxy_client.c b/src/http_proxy_client.c new file mode 100644 index 0000000..157d009 --- /dev/null +++ b/src/http_proxy_client.c @@ -0,0 +1,565 @@ +/* + * active port forwarder - software for secure forwarding + * Copyright (C) 2003-2007 jeremian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "http_proxy_functions.h" +#include "http_proxy_client.h" +#include "thread_management.h" +#include "network.h" +#include "stats.h" +#include "logging.h" +#include "base64.h" + +#ifdef HAVE_LIBPTHREAD +typedef struct { + int sockfd; + char *host; + char *serv; + HttpProxyOptions* hpo; + char type; + SSL_CTX* ctx; +} proxy_argT; + +/* + * Function name: clean_return + * Description: Closes the connection and exits the thread. + * Arguments: sockfd - the descriptor of the connection + */ + +static void +clean_return(int sockfd) +{ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http proxy: clean_return"); + close(sockfd); + pthread_exit(NULL); +} + +/* + * Function name: http_proxy_client + * Description: Function responsible for the client part of the http proxy connection. + * Arguments: vptr - the structure with all the information needed for http proxy tunnel + */ + +void* +http_proxy_client(void *vptr) +{ + char tab[9000]; + connection conn; + header hdr; + int j, n, maxfdp1; + fd_set rset, allset; + struct timeval tv; + int timeout = 5; + int tmp; + char *host, *serv, *proxyname, *proxyport, *credentials, *name = ""; + char b64cred[100]; + char type, authtype, https; + SSL_CTX* ctx; + proxy_argT *proxy_argptr; + + start_critical_section(); + proxy_argptr = (proxy_argT *) vptr; + + host = proxy_argptr->host; + serv = proxy_argptr->serv; + proxyname = HttpProxyOptions_get_proxyname(proxy_argptr->hpo); + proxyport = HttpProxyOptions_get_proxyport(proxy_argptr->hpo); + credentials = HttpProxyOptions_get_proxyauth_cred(proxy_argptr->hpo); + type = proxy_argptr->type; + authtype = HttpProxyOptions_get_proxyauth_type(proxy_argptr->hpo); + conn.sockfd = proxy_argptr->sockfd; + https = HttpProxyOptions_is_https(proxy_argptr->hpo); + ctx = proxy_argptr->ctx; + + broadcast_condition(); + end_critical_section(); + + conn.postFd = SslFd_new(); + conn.getFd = SslFd_new(); + conn.tmpFd = SslFd_new(); + if ((conn.postFd == NULL) || (conn.getFd == NULL) || (conn.tmpFd == NULL)) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "http%s proxy: Can't allocate memory... exiting.", name); + exit(1); + } + + if (https) { + name = "s"; + SslFd_set_ssl(conn.postFd, SSL_new(ctx)); + SslFd_set_ssl(conn.getFd, SSL_new(ctx)); + SslFd_set_ssl(conn.tmpFd, SSL_new(ctx)); + if ((SslFd_get_ssl(conn.postFd) == NULL) || + (SslFd_get_ssl(conn.getFd) == NULL) || + (SslFd_get_ssl(conn.tmpFd) == NULL)) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "http%s proxy: Can't allocate memory... exiting.", name); + exit(1); + } + } + + if (authtype == PROXYAUTH_TYPE_WRONG) { + aflog(LOG_T_MAIN, LOG_I_WARNING, + "Wrong type of proxy authorizaton --> switching to no authorization"); + credentials = NULL; + } + + if (credentials) { + if (b64_ntop((unsigned char*)credentials, strlen(credentials), b64cred, 100) == -1) { + aflog(LOG_T_MAIN, LOG_I_ERR, + "Cannot encode credentials for proxy authorization"); + b64cred[0] = 0; + } + else { + if (authtype == PROXYAUTH_TYPE_NOTSET) { + authtype = PROXYAUTH_TYPE_BASIC; + } + } + } + + FD_ZERO(&allset); + tv.tv_usec = 0; + tv.tv_sec = timeout; + + memset(conn.id, 0, 10); + for (j = 0; j < 9; ++j) { + conn.id[j] = myrand(65, 90); + } + conn.id[9] = 0; + + /* postfd */ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: connecting (postfd)...", name); + if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { + clean_return(conn.sockfd); + } + SslFd_set_fd(conn.postFd, tmp); + if (https) { + if (SSL_set_fd(SslFd_get_ssl(conn.postFd), SslFd_get_fd(conn.postFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: Problem with initializing ssl"); + clean_return(conn.sockfd); + } + if (SSL_connect(SslFd_get_ssl(conn.postFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: SSL_connect has failed"); + clean_return(conn.sockfd); + } + } + + memset(tab, 0, 9000); + switch (authtype) { + case PROXYAUTH_TYPE_BASIC: + sprintf(tab, + "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Content-Length: 90000\r\n" + "Connection: close\r\n" + "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); + break; + default: + sprintf(tab, + "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Content-Length: 90000\r\n" + "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); + } + j = strlen (tab); + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: writing POST request...", name); + if (http_write(https, conn.postFd, (unsigned char*) tab, j) <= 0) { + clean_return(conn.sockfd); + } + + /* getfd */ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: connecting (getfd)...", name); + if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { + clean_return(conn.sockfd); + } + SslFd_set_fd(conn.getFd, tmp); + if (https) { + if (SSL_set_fd(SslFd_get_ssl(conn.getFd), SslFd_get_fd(conn.getFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: Problem with initializing ssl"); + clean_return(conn.sockfd); + } + if (SSL_connect(SslFd_get_ssl(conn.getFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: SSL_connect has failed"); + clean_return(conn.sockfd); + } + } + + memset(tab, 0, 9000); + switch (authtype) { + case PROXYAUTH_TYPE_BASIC: + sprintf(tab, + "GET http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Connection: close\r\n" + "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); + break; + default: + sprintf(tab, + "GET http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); + } + j = strlen (tab); + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: writing GET request...", name); + if (http_write(https, conn.getFd, (unsigned char*) tab, j) <= 0) { + clean_return(conn.sockfd); + } + + set_fd(conn.sockfd, &maxfdp1, &allset); + set_fd(SslFd_get_fd(conn.postFd), &maxfdp1, &allset); + set_fd(SslFd_get_fd(conn.getFd), &maxfdp1, &allset); + conn.state = C_OPEN; + + memset(tab, 0, 9000); + + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: parsing header from getfd", name); + if (parse_header(conn.getFd, tab, &hdr, https)) { + clean_return(conn.sockfd); + } + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: checking hdr.type", name); + if (hdr.type != H_TYPE_OK) { + clean_return(conn.sockfd); + } + if (hdr.length) { + conn.received += hdr.length; + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: reading message...", name); + if (read_message(conn.sockfd, hdr.length, &conn, tab, hdr.ptr)) { + clean_return(conn.sockfd); + } + } + + while (1) { + if ((conn.state == C_OPEN) && (!(FD_ISSET(conn.sockfd, &allset)))) { + FD_SET(conn.sockfd, &allset); + } + rset = allset; + + if (select(maxfdp1, &rset, NULL, NULL, &tv) == 0) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: timeout", name); + tv.tv_sec = timeout; + if (conn.sent_ptr+1 >= 90000) { + if (conn.state == C_CLOSED) { + continue; + } + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: send T", name); + http_write(https, conn.postFd, (unsigned char*) "T", 1); + conn.sent_ptr = 0; + clear_sslFd(conn.postFd, &allset); + /* postfd */ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: connecting (postfd)...", name); + if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { + clean_return(conn.sockfd); + } + SslFd_set_fd(conn.postFd, tmp); + if (https) { + if (SSL_set_fd(SslFd_get_ssl(conn.postFd), SslFd_get_fd(conn.postFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: Problem with initializing ssl"); + clean_return(conn.sockfd); + } + if (SSL_connect(SslFd_get_ssl(conn.postFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: SSL_connect has failed"); + clean_return(conn.sockfd); + } + } + + memset(tab, 0, 9000); + switch (authtype) { + case PROXYAUTH_TYPE_BASIC: + sprintf(tab, + "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Content-Length: 90000\r\n" + "Connection: close\r\n" + "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); + break; + default: + sprintf(tab, + "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Content-Length: 90000\r\n" + "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); + } + j = strlen (tab); + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: writing POST request...", name); + if (http_write(https, conn.postFd, (unsigned char *) tab, j) <= 0) { + clean_return(conn.sockfd); + } + conn.sent_ptr = 0; + conn.ptr = 0; + conn.length = 0; + conn.state = C_CLOSED; + + set_fd(SslFd_get_fd(conn.postFd), &maxfdp1, &allset); + } + else { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: send T", name); + http_write(https, conn.postFd, (unsigned char *) "T", 1); + conn.sent_ptr += 1; + } + continue; + } + + /* sockfd */ + if (FD_ISSET(conn.sockfd, &rset)) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: FD_ISSET(conn.sockfd)", name); + n = read(conn.sockfd, conn.buf+5, 8995); + if (n <= 0) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: send Q", name); + http_write(https, conn.postFd, (unsigned char *) "Q", 1); + clean_return(conn.sockfd); + } + if ((conn.state == C_CLOSED) && (conn.sent_ptr > 70000)) { + FD_CLR(conn.sockfd, &allset); + } + conn.buf[0] = 'M'; + tmp = htonl(n); + memcpy(&conn.buf[1], &tmp, 4); + if (conn.sent_ptr+5 + n >= 90000) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: send message", name); + http_write(https, conn.postFd, (unsigned char *) conn.buf, 90000 - conn.sent_ptr); + conn.ptr = 90000 - conn.sent_ptr; + conn.length = 5+n - conn.ptr; + conn.sent_ptr = 0; + clear_sslFd(conn.postFd, &allset); + + /* postfd */ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: connecting (postfd)...", name); + if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { + clean_return(conn.sockfd); + } + SslFd_set_fd(conn.postFd, tmp); + if (https) { + if (SSL_set_fd(SslFd_get_ssl(conn.postFd), SslFd_get_fd(conn.postFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: Problem with initializing ssl"); + clean_return(conn.sockfd); + } + if (SSL_connect(SslFd_get_ssl(conn.postFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: SSL_connect has failed"); + clean_return(conn.sockfd); + } + } + + memset(tab, 0, 9000); + switch (authtype) { + case PROXYAUTH_TYPE_BASIC: + sprintf(tab, + "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Content-Length: 90000\r\n" + "Connection: close\r\n" + "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); + break; + default: + sprintf(tab, + "POST http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Content-Length: 90000\r\n" + "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); + } + j = strlen (tab); + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: writing POST request...", name); + if (http_write(https, conn.postFd, (unsigned char *) tab, j) <= 0) { + clean_return(conn.sockfd); + } + if (conn.length > 0) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: writing old data...", name); + if (http_write(https, conn.postFd, (unsigned char *) (conn.buf+conn.ptr), conn.length) <= 0) { + clean_return(conn.sockfd); + } + } + conn.sent_ptr = conn.length; + conn.ptr = 0; + conn.length = 0; + conn.state = C_CLOSED; + + set_fd(SslFd_get_fd(conn.postFd), &maxfdp1, &allset); + } + else { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: send message", name); + http_write(https, conn.postFd, (unsigned char *) conn.buf, 5+n); + conn.sent_ptr += 5+n; + } + } + + /* getfd */ + if (FD_ISSET(SslFd_get_fd(conn.getFd), &rset)) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: FD_ISSET(conn.getfd)", name); + n = http_read(https, conn.getFd, (unsigned char*) tab, 9000); + conn.received += n; + if (n == 0) { + conn.received = 0; + clear_sslFd(conn.getFd, &allset); + + /* getfd */ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: connecting (getfd)...", name); + if (ip_connect(&tmp, proxyname, proxyport, type, NULL, NULL)) { + clean_return(conn.sockfd); + } + SslFd_set_fd(conn.getFd, tmp); + if (https) { + if (SSL_set_fd(SslFd_get_ssl(conn.getFd), SslFd_get_fd(conn.getFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: Problem with initializing ssl"); + clean_return(conn.sockfd); + } + if (SSL_connect(SslFd_get_ssl(conn.getFd)) != 1) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "https proxy: SSL_connect has failed"); + clean_return(conn.sockfd); + } + } + + memset(tab, 0, 9000); + switch (authtype) { + case PROXYAUTH_TYPE_BASIC: + sprintf(tab, + "GET http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Connection: close\r\n" + "Proxy-Authorization: Basic %s\r\n\r\n", host, serv, conn.id, host, serv, b64cred); + break; + default: + sprintf(tab, + "GET http://%s:%s/yahpt.html?id=%s HTTP/1.1\r\n" + "Host: %s:%s\r\n" + "Connection: close\r\n\r\n", host, serv, conn.id, host, serv); + } + j = strlen (tab); + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: writing GET request...", name); + if (http_write(https, conn.getFd, (unsigned char *) tab, j) <= 0) { + clean_return(conn.sockfd); + } + memset(tab, 0, 9000); + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: parsing header from getfd", name); + if (parse_header(conn.getFd, tab, &hdr, https)) { + clean_return(conn.sockfd); + } + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: checking hdr.type", name); + if (hdr.type != H_TYPE_OK) { + clean_return(conn.sockfd); + } + + set_fd(SslFd_get_fd(conn.getFd), &maxfdp1, &allset); + if (hdr.length) { + conn.received += hdr.length; + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: reading message...", name); + if (read_message(conn.sockfd, hdr.length, &conn, tab, hdr.ptr)) { + clean_return(conn.sockfd); + } + } + } + else { + if (read_message(conn.sockfd, n, &conn, tab, 0)) { + clean_return(conn.sockfd); + } + } + } + + /* postfd */ + if (FD_ISSET(SslFd_get_fd(conn.postFd), &rset)) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: FD_ISSET(conn.postfd) --> clean_return", name); + clear_sslFd(conn.postFd, &allset); + clean_return(conn.sockfd); + } + } + clean_return(conn.sockfd); +} + +/* + * Function name: initialize_http_proxy_client + * Description: Initializes the thread responsible for http proxy connection. + * Arguments: sockfd - the new connection descriptor will be stored here + * cr - the pointer to ClientRealm structure + * ctx - the pointer to SSL_CTX structure + * Returns: 0 - success, + * !0 - failure. + */ + +int +initialize_http_proxy_client(int* sockfd, ClientRealm* cr, SSL_CTX* ctx) +{ + int retval; + int sockets[2]; + pthread_t proxy_thread; + static proxy_argT arg; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) { + return 1; + } + (*sockfd) = sockets[0]; + + if (HttpProxyOptions_get_proxyname(ClientRealm_get_httpProxyOptions(cr)) == NULL) { + return 1; + } + + start_critical_section(); + + arg.host = ClientRealm_get_serverName(cr); + arg.serv = ClientRealm_get_managePort(cr); + arg.hpo = ClientRealm_get_httpProxyOptions(cr); + arg.type = ClientRealm_get_ipFamily(cr); + arg.sockfd = sockets[1]; + arg.ctx = ctx; + + retval = pthread_create(&proxy_thread, NULL, &http_proxy_client, &arg); + + wait_for_condition(); + + end_critical_section(); + + return retval; +} + +#endif -- cgit v1.1