diff options
Diffstat (limited to 'src/http_proxy_server.c')
-rw-r--r-- | src/http_proxy_server.c | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/src/http_proxy_server.c b/src/http_proxy_server.c new file mode 100644 index 0000000..17baf5a --- /dev/null +++ b/src/http_proxy_server.c @@ -0,0 +1,585 @@ +/* + * active port forwarder - software for secure forwarding + * Copyright (C) 2003-2007 jeremian <jeremian [at] poczta.fm> + * + * 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 <config.h> + +#include "make_ssl_handshake.h" +#include "http_proxy_server.h" +#include "thread_management.h" +#include "stats.h" +#include "logging.h" +#include "network.h" + +#ifdef HAVE_LIBPTHREAD + +typedef struct { + int sockfd; + char *host; + char *serv; + socklen_t *addrlenp; + char type; + int limit; + char https; + SSL_CTX* ctx; +} sproxy_argT; + +/* + * Function name: afserver_connect + * Description: Connects new http proxy connection to the afserver. + * Arguments: sockfd - the file descriptor which will be used for communication with afserver + * afserverfd - the afserver's file descriptor + * cliaddr - pointer to sockaddr structure + * addrlenp - pointer to the length of the sockaddr structure + * type - the type of the connection + */ + +int +afserver_connect(int* sockfd, int afserverfd, struct sockaddr* cliaddr, socklen_t* addrlenp, char type) +{ + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) { + return 1; + } + if (write(afserverfd, &sockets[0], 4) != 4) { + return 2; + } + if (write(afserverfd, &type, 1) != 1) { + return 3; + } + if (write(afserverfd, addrlenp, 4) != 4) { + return 4; + } + if (write(afserverfd, cliaddr, *addrlenp) != *addrlenp) { + return 5; + } + (*sockfd) = sockets[1]; + return 0; +} + +/* + * Function name: http_proxy_server + * Description: Function responsible for the server part of the http proxy connection. + * Arguments: vptr - the structure with all the information needed for http proxy tunnel. + */ + +void* +http_proxy_server(void *vptr) +{ + int listenfd, afserverfd; + SslFd* connFd; + struct sockaddr* cliaddr; + char tab[9000]; + connection* table; + header hdr; + int i, n, maxfdp1; + fd_set rset, allset; + struct timeval tv; + int maxclients, tmp; + int timeout = 5; + socklen_t *addrlenp; + socklen_t addrlen; + char type, nothttp, https; + char *host, *serv, *name = ""; + SSL_CTX* ctx; + sproxy_argT *proxy_argptr; + + start_critical_section(); + proxy_argptr = (sproxy_argT *) vptr; + + afserverfd = proxy_argptr->sockfd; + host = proxy_argptr->host; + serv = proxy_argptr->serv; + addrlenp = proxy_argptr->addrlenp; + type = proxy_argptr->type; + maxclients = proxy_argptr->limit+1; + https = proxy_argptr->https; + ctx = proxy_argptr->ctx; + + if (https) { + name = "s"; + } + + table = calloc(maxclients, sizeof(connection)); + if (table == NULL) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "http%s proxy: Can't allocate memory... exiting.", name); + exit(1); + } + for (i = 0; i < maxclients; ++i) { + table[i].postFd = SslFd_new(); + table[i].getFd = SslFd_new(); + table[i].tmpFd = SslFd_new(); + if ((table[i].postFd == NULL) || (table[i].getFd == NULL) || (table[i].tmpFd == NULL)) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "http%s proxy: Can't allocate memory... exiting.", name); + exit(1); + } + if (https) { + SslFd_set_ssl(table[i].postFd, SSL_new(ctx)); + SslFd_set_ssl(table[i].getFd, SSL_new(ctx)); + SslFd_set_ssl(table[i].tmpFd, SSL_new(ctx)); + if ((SslFd_get_ssl(table[i].postFd) == NULL) || + (SslFd_get_ssl(table[i].getFd) == NULL) || + (SslFd_get_ssl(table[i].tmpFd) == NULL)) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "http%s proxy: Can't allocate memory... exiting.", name); + exit(1); + } + } + } + connFd = SslFd_new(); + if (connFd == NULL) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "http%s proxy: Can't allocate memory... exiting.", name); + exit(1); + } + if (https) { + SslFd_set_ssl(connFd, SSL_new(ctx)); + if (SslFd_get_ssl(connFd) == NULL) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "http%s proxy: Can't allocate memory... exiting.", name); + exit(1); + } + } + + if (ip_listen(&listenfd, host, serv, addrlenp, type)) { + aflog(LOG_T_INIT, LOG_I_CRIT, + "http%s proxy: Can't listen on %s:%s", name, host, serv); + exit(1); + } + cliaddr = malloc(*addrlenp); + addrlen = (*addrlenp); + addrlenp = &addrlen; + + broadcast_condition(); + end_critical_section(); + + FD_ZERO(&allset); + FD_SET(listenfd, &allset); + maxfdp1 = listenfd + 1; + tv.tv_usec = 0; + tv.tv_sec = timeout; + + while (1) { + 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; + for (i = 0; i < maxclients; ++i) { + if ((table[i].state == C_CLOSED) || (table[i].state & C_GET_WAIT) || (table[i].type == 1)) { + continue; + } + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: send T to table[%d].getfd", name, i); + if (table[i].sent_ptr+1 >= 90000) { + http_write(https, table[i].getFd, (unsigned char*) "T", 1); + table[i].sent_ptr = 0; + clear_sslFd(table[i].getFd, &allset); + FD_CLR(table[i].sockfd, &allset); + table[i].state |= C_GET_WAIT; + } + else { + http_write(https, table[i].getFd, (unsigned char*) "T", 1); + table[i].sent_ptr += 1; + } + } + continue; + } + + /* http proxy tunnels */ + for (i = 0; i < maxclients; ++i) { + if ((table[i].state == C_CLOSED) || (table[i].type == 1)) { + continue; + } + + /* sockfd */ + if ((!(table[i].state & C_GET_WAIT)) && (FD_ISSET(table[i].sockfd, &rset))) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: FD_ISSET(table[%d].sockfd)", name, i); + n = read(table[i].sockfd, table[i].buf+5, 8995); + if (n <= 0) { + http_write(https, table[i].getFd, (unsigned char*) "Q", 1); + delete_user(table, i, &allset); + continue; + } + table[i].buf[0] = 'M'; + tmp = htonl(n); + memcpy(&table[i].buf[1], &tmp, 4); + if (table[i].sent_ptr+5 + n >= 90000) { + http_write(https, table[i].getFd, (unsigned char*) table[i].buf, 90000 - table[i].sent_ptr); + table[i].ptr = 90000 - table[i].sent_ptr; + table[i].length = 5+n - table[i].ptr; + table[i].sent_ptr = 0; + clear_sslFd(table[i].getFd, &allset); + FD_CLR(table[i].sockfd, &allset); + table[i].state |= C_GET_WAIT; + continue; + } + else { + http_write(https, table[i].getFd, (unsigned char*) table[i].buf, n+5); + table[i].sent_ptr += n+5; + } + } + + /* getfd */ + if (FD_ISSET(SslFd_get_fd(table[i].getFd), &rset)) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: FD_ISSET(table[%d].getfd)", name, i); + delete_user(table, i, &allset); + continue; + } + + /* postfd */ + if (FD_ISSET(SslFd_get_fd(table[i].postFd), &rset)) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: FD_ISSET(table[%d].postfd)", name, i); + n = http_read(https, table[i].postFd, (unsigned char*) tab, 9000); + if (n != 0) { + table[i].received += n; + if (read_message(table[i].sockfd, n, &table[i], tab, 0)) { + delete_user(table, i, &allset); + } + } + if ((n == 0) || (table[i].received == 90000)) { + table[i].received = 0; + clear_sslFd(table[i].postFd, &allset); + table[i].state |= C_POST_WAIT; + + if (!(table[i].state & C_GET_WAIT)) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: send A to table[%d].getfd", name, i); + if (table[i].sent_ptr+1 >= 90000) { + http_write(https, table[i].getFd, (unsigned char*) "A", 1); + table[i].sent_ptr = 0; + clear_sslFd(table[i].getFd, &allset); + FD_CLR(table[i].sockfd, &allset); + table[i].state |= C_GET_WAIT; + } + else { + http_write(https, table[i].getFd, (unsigned char*) "A", 1); + table[i].sent_ptr += 1; + } + } + else { + table[i].state |= C_DELAYED_A; + } + + if (table[i].tmpstate == 1) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: get old POST request...", name); + table[i].state &= ~C_POST_WAIT; + SslFd_swap_content(table[i].postFd, table[i].tmpFd); + set_fd(SslFd_get_fd(table[i].postFd), &maxfdp1, &allset); + table[i].tmpstate = 0; + if (table[i].tmpheader.length) { + table[i].received += table[i].tmpheader.length; + if (read_message(table[i].sockfd, table[i].tmpheader.length, &table[i], + table[i].tmpbuf, table[i].tmpheader.ptr)) { + delete_user(table, i, &allset); + } + } + } + continue; + } + } + } + + /* direct tunnels */ + for (i = 0; i < maxclients; ++i) { + if ((table[i].state == C_OPEN) && (table[i].type == 1)) { + + if (FD_ISSET(table[i].sockfd, &rset)) { + n = read(table[i].sockfd, table[i].buf, 9000); + if (n > 0) { + writen(SslFd_get_fd(table[i].postFd), (unsigned char*) table[i].buf, n); + } + else { + delete_user(table, i, &allset); + continue; + } + } + + if (FD_ISSET(SslFd_get_fd(table[i].postFd), &rset)) { + n = read(SslFd_get_fd(table[i].postFd), tab, 9000); + if (n > 0) { + writen(table[i].sockfd, (unsigned char*) tab, n); + } + else { + delete_user(table, i, &allset); + continue; + } + } + + } + } + + /* listen */ + if (FD_ISSET(listenfd, &rset)) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: FD_ISSET(listenfd)", name); + tmp = accept(listenfd, cliaddr, addrlenp); + if (tmp != -1) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: New connection...", name); + SslFd_set_fd(connFd, tmp); + if (https) { + make_ssl_initialize(connFd); + if (make_ssl_accept(connFd)) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "https proxy: DENIED by SSL_accept"); + close(SslFd_get_fd(connFd)); + SSL_clear(SslFd_get_ssl(connFd)); + continue; + } + } + } + else { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: New connection --> EAGAIN", name); + continue; + } + memset(tab, 0, 9000); + nothttp = 0; + if (parse_header(connFd, tab, &hdr, https)) { + nothttp = 1; + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: no http header...", name); + } + n = -1; + for (i = 0; i < maxclients; ++i) { + if (table[i].state == C_CLOSED) { + if (n == -1) + n = i; + } + else { + if ((!nothttp) && (strcmp(table[i].id, hdr.id) == 0)) { + break; + } + } + } + if (i < maxclients) { /* the client exists */ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: the client exist...", name); + if (hdr.type == H_TYPE_GET) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: type GET...", name); + if (!(table[i].state & C_GET_WAIT)) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: not waiting for GET...", name); + table[i].sent_ptr = 0; + FD_CLR(table[i].sockfd, &allset); + clear_sslFd(table[i].getFd, &allset); + table[i].state |= C_GET_WAIT; + } + if (!(table[i].state & C_OPEN)) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: not opened...", name); + if (afserver_connect(&table[i].sockfd, afserverfd, cliaddr, addrlenp, 1)) { + memset(tab, 0, 9000); + sprintf(tab,"HTTP/1.1 400 Bad Request\r\n\r\n"); + n = strlen (tab); + http_write(https, connFd, (unsigned char*) tab, n); + close_fd((&(connFd->fd))); + SSL_clear(SslFd_get_ssl(connFd)); + clear_sslFd(table[i].postFd, &allset); + table[i].state = C_CLOSED; + continue; + } + table[i].state |= C_OPEN; + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: OPEN...", name); + } + table[i].state &= ~C_GET_WAIT; + table[i].sent_ptr = 0; + SslFd_swap_content(table[i].getFd, connFd); + set_fd(table[i].sockfd, &maxfdp1, &allset); + set_fd(SslFd_get_fd(table[i].getFd), &maxfdp1, &allset); + memset(tab, 0, 9000); + sprintf(tab, + "HTTP/1.1 200 OK\r\n" + "Content-Length: 90000\r\n" + "Connection: close\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache, no-store, must-revalidate\r\n" + "Expires: 0\r\n" + "Content-Type: text/html\r\n\r\n"); + n = strlen(tab); + if (writen(SslFd_get_fd(table[i].getFd), (unsigned char*) tab, n) <= 0) { + delete_user(table, i, &allset); + continue; + } + if (table[i].length) { + if (writen(SslFd_get_fd(table[i].getFd), + (unsigned char*) (table[i].buf+table[i].ptr), table[i].length) <= 0) { + delete_user(table, i, &allset); + continue; + } + } + table[i].sent_ptr = table[i].length; + table[i].ptr = 0; + table[i].length = 0; + if (table[i].state & C_DELAYED_A) { + aflog(LOG_T_MAIN, LOG_I_DDEBUG, + "http%s proxy: send A to table[%d].getfd", name, i); + http_write(https, table[i].getFd, (unsigned char*) "A", 1); + table[i].sent_ptr += 1; + table[i].state &= ~C_DELAYED_A; + } + } + else if (hdr.type == H_TYPE_POST) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: type POST...", name); + if (!(table[i].state & C_POST_WAIT)) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: unexpected POST request...", name); + if (table[i].tmpstate == 0) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: buffering POST request...", name); + table[i].tmpstate = 1; + SslFd_swap_content(table[i].tmpFd, connFd); + memcpy(table[i].tmpbuf, tab, 9000); + table[i].tmpheader = hdr; + } + else { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: no space to buffer POST request (received from first postfd: %d)", name, + table[i].received); + delete_user(table, i, &allset); + } + } + else { + if (hdr.length) { + table[i].received += hdr.length; + if (read_message(table[i].sockfd, hdr.length, &table[i], tab, hdr.ptr)) { + delete_user(table, i, &allset); + } + } + table[i].state &= ~C_POST_WAIT; + SslFd_swap_content(table[i].postFd, connFd); + set_fd(SslFd_get_fd(table[i].postFd), &maxfdp1, &allset); + } + } + else { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: unrecognized type...", name); + delete_user(table, i, &allset); + } + } + else if (n != -1) { /* there are free slots */ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: there are free slots...", name); + if (!nothttp) { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: http header...", name); + if (hdr.type == H_TYPE_POST) { /* POST request must be first */ + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: type POST...", name); + table[n].state = C_GET_WAIT; + memcpy(table[n].id,hdr.id, 9); + SslFd_swap_content(table[n].postFd, connFd); + set_fd(SslFd_get_fd(table[n].postFd), &maxfdp1, &allset); + } + else { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: closing this connection... (not POST type)", name); + close_fd((&(connFd->fd))); + if (https) { + SSL_clear(SslFd_get_ssl(connFd)); + } + continue; + } + } + else { + table[n].state = C_OPEN; + SslFd_set_fd(table[n].postFd, SslFd_get_fd(connFd)); + table[n].type = 1; + set_fd(SslFd_get_fd(table[n].postFd), &maxfdp1, &allset); + if (afserver_connect(&table[n].sockfd, afserverfd, cliaddr, addrlenp, 0)) { + clear_sslFd(table[n].postFd, &allset); + table[n].state = C_CLOSED; + continue; + } + set_fd(table[n].sockfd, &maxfdp1, &allset); + write(table[n].sockfd, tab, hdr.allreaded); + } + } + else { + aflog(LOG_T_MAIN, LOG_I_DEBUG, + "http%s proxy: closing this connection... (no free slots)", name); + close_fd((&(connFd->fd))); + continue; + } + } + } + + return 0; +} + +/* + * Function name: initialize_http_proxy_server + * Description: Initializes the thread responsible for http proxy connection. + * Arguments: sockfd - the new connection descriptor will be stored here + * host - the name of the host on which we will be listening on + * serv - the port on which we will be listening on + * addrlenp - pointer to the length of the sockaddr structure + * type - the type of the connection + * limit - the limit for user's connections + * https - if the connection should be https instead of http + * ctx - the pointer to SSL_CTX structure + * Returns: 0 - success, + * !0 - failure. + */ + +int +initialize_http_proxy_server(int* sockfd, const char *host, const char *serv, socklen_t *addrlenp, const char type, int limit, char https, SSL_CTX* ctx) +{ + int retval; + int sockets[2]; + pthread_t proxy_thread; + static sproxy_argT arg; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) { + return 1; + } + (*sockfd) = sockets[0]; + + start_critical_section(); + + arg.host = (char*) host; + arg.serv = (char*) serv; + arg.addrlenp = addrlenp; + arg.limit = limit; + arg.type = (char) type; + arg.sockfd = sockets[1]; + arg.https = https; + arg.ctx = ctx; + + retval = pthread_create(&proxy_thread, NULL, &http_proxy_server, &arg); + + wait_for_condition(); + + end_critical_section(); + + return retval; +} + +#endif |