From 06219414a6917f52246147ccd14181f9b99c6bbb Mon Sep 17 00:00:00 2001 From: henri Date: Mon, 22 Apr 2013 19:28:53 +0000 Subject: [PATCH] Initial version with a _very_ raw support for HTTP proxy chains (using the CONNECT method). This is mostly intended to validate the concepts, data models and programming approach. --- nsock/include/nsock.h | 5 + nsock/src/Makefile.in | 4 +- nsock/src/nsock_connect.c | 21 +++ nsock/src/nsock_internal.h | 8 + nsock/src/nsock_iod.c | 7 + nsock/src/nsock_pool.c | 2 + nsock/src/nsock_proxy.c | 325 +++++++++++++++++++++++++++++++++++++ nsock/src/nsock_proxy.h | 108 ++++++++++++ 8 files changed, 478 insertions(+), 2 deletions(-) create mode 100644 nsock/src/nsock_proxy.c create mode 100644 nsock/src/nsock_proxy.h diff --git a/nsock/include/nsock.h b/nsock/include/nsock.h index a68c18c67..26d2e7a9c 100644 --- a/nsock/include/nsock.h +++ b/nsock/include/nsock.h @@ -187,6 +187,7 @@ enum nsock_loopstatus { NSOCK_LOOP_QUIT }; +int nsock_set_proxychain(nsock_pool nsp, const char *proxystr); enum nsock_loopstatus nsock_loop(nsock_pool nsp, int msec_timeout); @@ -498,6 +499,10 @@ nsock_event_id nsock_connect_unixsock_datagram(nsock_pool nsp, nsock_iod nsiod, nsock_event_id nsock_connect_tcp(nsock_pool nsp, nsock_iod nsiod, nsock_ev_handler handler, int timeout_msecs, void *userdata, struct sockaddr *ss, size_t sslen, unsigned short port); +nsock_event_id nsock_connect_tcp_direct(nsock_pool nsp, nsock_iod nsiod, nsock_ev_handler handler, + int timeout_msecs, void *userdata, struct sockaddr *ss, + size_t sslen, unsigned short port); + /* Request an SCTP association to another system (by IP address). The in_addr is * normal network byte order, but the port number should be given in HOST BYTE * ORDER. ss should be a sockaddr_storage, sockaddr_in6, or sockaddr_in as diff --git a/nsock/src/Makefile.in b/nsock/src/Makefile.in index 2cb6b1e9d..59e11b8cb 100644 --- a/nsock/src/Makefile.in +++ b/nsock/src/Makefile.in @@ -27,9 +27,9 @@ NBASEDIR=@NBASEDIR@ TARGET = libnsock.a -SRCS = error.c filespace.c gh_list.c nsock_connect.c nsock_core.c nsock_iod.c nsock_read.c nsock_timers.c nsock_write.c nsock_ssl.c nsock_event.c nsock_pool.c netutils.c nsock_pcap.c nsock_engines.c engine_select.c engine_epoll.c engine_kqueue.c engine_poll.c nsock_log.c @COMPAT_SRCS@ +SRCS = error.c filespace.c gh_list.c nsock_connect.c nsock_core.c nsock_iod.c nsock_proxy.c nsock_read.c nsock_timers.c nsock_write.c nsock_ssl.c nsock_event.c nsock_pool.c netutils.c nsock_pcap.c nsock_engines.c engine_select.c engine_epoll.c engine_kqueue.c engine_poll.c nsock_log.c @COMPAT_SRCS@ -OBJS = error.o filespace.o gh_list.o nsock_connect.o nsock_core.o nsock_iod.o nsock_read.o nsock_timers.o nsock_write.o nsock_ssl.o nsock_event.o nsock_pool.o netutils.o nsock_pcap.o nsock_engines.o engine_select.o engine_epoll.o engine_kqueue.o engine_poll.o nsock_log.o @COMPAT_OBJS@ +OBJS = error.o filespace.o gh_list.o nsock_connect.o nsock_core.o nsock_iod.o nsock_proxy.o nsock_read.o nsock_timers.o nsock_write.o nsock_ssl.o nsock_event.o nsock_pool.o netutils.o nsock_pcap.o nsock_engines.o engine_select.o engine_epoll.o engine_kqueue.o engine_poll.o nsock_log.o @COMPAT_OBJS@ DEPS = error.h filespace.h gh_list.h nsock_internal.h netutils.h nsock_pcap.h nsock_log.h ../include/nsock.h $(NBASEDIR)/libnbase.a diff --git a/nsock/src/nsock_connect.c b/nsock/src/nsock_connect.c index a5a0ea56a..72a690b26 100644 --- a/nsock/src/nsock_connect.c +++ b/nsock/src/nsock_connect.c @@ -58,12 +58,14 @@ #include "nsock.h" #include "nsock_internal.h" #include "nsock_log.h" +#include "nsock_proxy.h" #include "netutils.h" #include #include #include + /* Create the actual socket (nse->iod->sd) underlying the iod. This unblocks the * socket, binds to the localaddr address, sets IP options, and sets the * broadcast flag. Trying to change these functions after making this call will @@ -261,7 +263,26 @@ nsock_event_id nsock_connect_unixsock_datagram(nsock_pool nsp, nsock_iod nsiod, * sizeof the structure you are passing in. */ nsock_event_id nsock_connect_tcp(nsock_pool nsp, nsock_iod ms_iod, nsock_ev_handler handler, int timeout_msecs, void *userdata, struct sockaddr *saddr, size_t sslen, unsigned short port) { + msiod *nsi = (msiod *)ms_iod; + if (nsi->px_ctx) { + memcpy(&nsi->px_ctx->target_ss, saddr, sslen); + nsi->px_ctx->target_sslen = sslen; + nsi->px_ctx->target_port = port; + nsi->px_ctx->target_handler = handler; + + saddr = (struct sockaddr *)&nsi->px_ctx->px_current->ss; + sslen = nsi->px_ctx->px_current->sslen; + port = nsi->px_ctx->px_current->port; + handler = nsock_proxy_ev_handler; + } + + return nsock_connect_tcp_direct(nsp, ms_iod, handler, timeout_msecs, userdata, saddr, sslen, port); +} + +nsock_event_id nsock_connect_tcp_direct(nsock_pool nsp, nsock_iod ms_iod, nsock_ev_handler handler, + int timeout_msecs, void *userdata, struct sockaddr *saddr, + size_t sslen, unsigned short port) { msiod *nsi = (msiod *)ms_iod; mspool *ms = (mspool *)nsp; msevent *nse; diff --git a/nsock/src/nsock_internal.h b/nsock/src/nsock_internal.h index 4343ef4be..cdb7d4160 100644 --- a/nsock/src/nsock_internal.h +++ b/nsock/src/nsock_internal.h @@ -76,6 +76,7 @@ #include "filespace.h" #include "nsock.h" /* The public interface -- I need it for some enum defs */ #include "nsock_ssl.h" +#include "nsock_proxy.h" #if HAVE_SYS_TIME_H #include @@ -217,6 +218,10 @@ typedef struct { /* The SSL Context (options and such) */ SSL_CTX *sslctx; #endif + + /* Proxy chain (or NULL) */ + struct proxy_node *px_chain; + } mspool; @@ -306,6 +311,9 @@ typedef struct { /* Pointer to mspcap struct (used only if pcap support is included) */ void *pcap; + + struct proxy_chain_context *px_ctx; + } msiod; diff --git a/nsock/src/nsock_iod.c b/nsock/src/nsock_iod.c index 590aac784..31904e91c 100644 --- a/nsock/src/nsock_iod.c +++ b/nsock/src/nsock_iod.c @@ -69,6 +69,7 @@ #endif #include + /* nsock_iod is like a "file descriptor" for the nsock library. You use it to * request events. And here is how you create an nsock_iod. nsi_new returns @@ -137,6 +138,12 @@ nsock_iod nsi_new2(nsock_pool nsockp, int sd, void *userdata) { nsi->ssl_session = NULL; #endif + if (nsp->px_chain) { + nsi->px_ctx = proxy_chain_context_new(nsp); + } else { + nsi->px_ctx = NULL; + } + nsi->id = nsp->next_iod_serial++; if (nsi->id == 0) nsi->id = nsp->next_iod_serial++; diff --git a/nsock/src/nsock_pool.c b/nsock/src/nsock_pool.c index b8c8b478e..517912044 100644 --- a/nsock/src/nsock_pool.c +++ b/nsock/src/nsock_pool.c @@ -185,6 +185,8 @@ nsock_pool nsp_new(void *userdata) { nsp->sslctx = NULL; #endif + nsp->px_chain = NULL; + return (nsock_pool)nsp; } diff --git a/nsock/src/nsock_proxy.c b/nsock/src/nsock_proxy.c new file mode 100644 index 000000000..074766ee8 --- /dev/null +++ b/nsock/src/nsock_proxy.c @@ -0,0 +1,325 @@ +/*************************************************************************** + * nsock_proxy.c -- This contains the functions relating to proxies. * + * * + ***********************IMPORTANT NSOCK LICENSE TERMS*********************** + * * + * The nsock parallel socket event library is (C) 1999-2012 Insecure.Com * + * LLC This library is free software; you may redistribute and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; Version 2. This guarantees * + * your right to use, modify, and redistribute this software under certain * + * conditions. If this license is unacceptable to you, Insecure.Com LLC * + * may be willing to sell alternative licenses (contact * + * sales@insecure.com ). * + * * + * As a special exception to the GPL terms, Insecure.Com LLC grants * + * permission to link the code of this program with any version of the * + * OpenSSL library which is distributed under a license identical to that * + * listed in the included docs/licenses/OpenSSL.txt file, and distribute * + * linked combinations including the two. You must obey the GNU GPL in all * + * respects for all of the code used other than OpenSSL. If you modify * + * this file, you may extend this exception to your version of the file, * + * but you are not obligated to do so. * + * * + * If you received these files with a written license agreement stating * + * terms other than the (GPL) terms above, then that alternative license * + * agreement takes precedence over this comment. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes (none * + * have been found so far). * + * * + * Source code also allows you to port Nmap to new platforms, fix bugs, * + * and add new features. You are highly encouraged to send your changes * + * to nmap-dev@insecure.org for possible incorporation into the main * + * distribution. By sending these changes to Fyodor or one of the * + * Insecure.Org development mailing lists, it is assumed that you are * + * offering the Nmap Project (Insecure.Com LLC) the unlimited, * + * non-exclusive right to reuse, modify, and relicense the code. Nmap * + * will always be available Open Source, but this is important because the * + * inability to relicense code has caused devastating problems for other * + * Free Software projects (such as KDE and NASM). We also occasionally * + * relicense the code to third parties as discussed above. If you wish to * + * specify special license conditions of your contributions, just say so * + * when you send them. * + * * + * 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 v2.0 for more details * + * (http://www.gnu.org/licenses/gpl-2.0.html). * + * * + ***************************************************************************/ + +/* $Id $ */ + +#include "nsock.h" +#include "nsock_internal.h" +#include +#include + +/* TODO first! + * --- + * o Parse proxy spec string + * o Deal with actual proxy chains (cf. ev_handler) + * o Deal with errors + * o Generic proxy interface (to handle many proxy types) + * --- + * o Manage timeouts + */ + +struct proxy_parser { + int done; + struct proxy_node *value; + char *str; + char *saveptr; + char *tokens; +}; + + +static struct proxy_parser *proxy_parser_new(const char *proxychainstr); +static void proxy_parser_next(struct proxy_parser *parser); +static void proxy_parser_delete(struct proxy_parser *parser); + +static struct proxy_node *proxy_node_new(char *proxystr); +static void proxy_node_delete(struct proxy_node *proxy); +static void proxy_chain_delete(struct proxy_node *chain); + +static void forward_event(mspool *nsp, msevent *nse, void *udata); + +void proxy_http_node_init(struct proxy_node *proxy, char *proxystr); +static void proxy_http_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata); + + + +/* XXX for the sake of stability we might want to have + * a write-once only API. So that proxyinfo pointers that + * are used in the IODs remain valid... + * + * A proxy chain is a comma-separated list of proxy specification strings: + * proto://[user:pass@]host[:port] + */ +int nsock_set_proxychain(nsock_pool nspool, const char *proxystr) { + mspool *nsp = (mspool *)nspool; + + if (nsp->px_chain) { + proxy_chain_delete(nsp->px_chain); + nsp->px_chain = NULL; + } + + /* Pass NULL to reset the proxy chain. */ + if (proxystr) { + struct proxy_node **ppx; + struct proxy_parser *parser; + + ppx = &nsp->px_chain; + for (parser = proxy_parser_new(proxystr); !parser->done; proxy_parser_next(parser)) { + *ppx = parser->value; + ppx = &parser->value->next; + } + proxy_parser_delete(parser); + } + return 0; +} + +struct proxy_chain_context *proxy_chain_context_new(nsock_pool nspool) { + mspool *nsp = (mspool *)nspool; + struct proxy_chain_context *ctx; + + ctx = (struct proxy_chain_context *)safe_malloc(sizeof(struct proxy_chain_context)); + ctx->px_state = PROXY_STATE_INITIAL; + ctx->px_current = nsp->px_chain; + return ctx; +} + + +struct proxy_parser *proxy_parser_new(const char *proxychainstr) { + struct proxy_parser *parser; + + parser = (struct proxy_parser *)safe_malloc(sizeof(struct proxy_parser)); + parser->done = 0; + parser->value = NULL; + + parser->str = strdup(proxychainstr); + + parser->tokens = strtok_r(parser->str, ",", &parser->saveptr); + if (parser->tokens) { + parser->value = proxy_node_new(parser->tokens); + } else { + parser->done = 1; + } + + return parser; +} + +void proxy_parser_next(struct proxy_parser *parser) { + + parser->tokens = strtok_r(NULL, ",", &parser->saveptr); + if (parser->tokens) { + parser->value = proxy_node_new(parser->tokens); + } else { + parser->done = 1; + } +} + +void proxy_parser_delete(struct proxy_parser *parser) { + if (parser) { + free(parser->str); + free(parser); + } +} + +/* XXX + * This function is just an ugly PoC. + * + * A clean version should handle: + * - both v4 and v6 adresses + * - hostnames (how do we want to resolve them though??) + * - user:pass@ prefix before host specification + */ +static struct proxy_node *proxy_node_new(char *proxystr) { + struct proxy_node *proxy; + + proxy = (struct proxy_node *)safe_zalloc(sizeof(struct proxy_node)); + + if (strncasecmp(proxystr, "http://", 7) != 0) { + fatal("Invalid protocol in proxy specification string: %s", proxystr); + } + + proxy->px_type = PROXY_TYPE_HTTP; + proxy->next = NULL; + proxy_http_node_init(proxy, proxystr); + + return proxy; +} + +static void proxy_node_delete(struct proxy_node *proxy) { + if (proxy) + free(proxy); +} + +static void proxy_chain_delete(struct proxy_node *chain) { + struct proxy_node *p, *next; + + for (p = chain; p != NULL; p = next) { + next = p->next; + proxy_node_delete(p); + } +} + +void forward_event(mspool *nsp, msevent *nse, void *udata) { + enum nse_type cached_type; + enum nse_status cached_status; + + cached_type = nse->type; + cached_status = nse->status; + + nse->type = NSE_TYPE_CONNECT; + nse->status = NSE_STATUS_SUCCESS; + + if (nsp->tracelevel > 0) + nsock_trace(nsp, "Forwarding event upstream: SUCCESS TCP connect (IOD #%li) EID %li", + nse->iod->id, nse->id); + + nse->iod->px_ctx->target_handler(nsp, nse, udata); + + nse->type = cached_type; + nse->status = cached_status; +} + +void nsock_proxy_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata) { + msevent *nse = (msevent *)nsevent; + + if (nse->status != NSE_STATUS_SUCCESS) + fatal("Error, but this is debug only!"); + + switch (nse->iod->px_ctx->px_current->px_type) { + case PROXY_TYPE_HTTP: + proxy_http_ev_handler(nspool, nsevent, udata); + break; + + default: + fatal("Invalid proxy type (%d)", nse->iod->px_ctx->px_current->px_type); + } +} + +void proxy_http_node_init(struct proxy_node *proxy, char *proxystr) { + struct sockaddr_in *sin; + char *strhost, *strport, *saveptr; + + strhost = strtok_r(proxystr + 7, ":", &saveptr); + strport = strtok_r(NULL, ":", &saveptr); + if (strport == NULL) + strport = "8080"; + + printf("init HTTP node http://%s:%s\n", strhost, strport); + + sin = (struct sockaddr_in *)&proxy->ss; + sin->sin_family = AF_INET; + inet_pton(AF_INET, strhost, &sin->sin_addr); + + proxy->sslen = sizeof(struct sockaddr_in); + proxy->port = (unsigned short)atoi(strport); +} + +void proxy_http_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata) { + mspool *nsp = (mspool *)nspool; + msevent *nse = (msevent *)nsevent; + struct sockaddr_storage *ss; + size_t sslen; + unsigned short port; + + switch (nse->iod->px_ctx->px_state) { + case PROXY_STATE_INITIAL: + nse->iod->px_ctx->px_state = PROXY_STATE_HTTP_TCP_CONNECTED; + + if (nse->iod->px_ctx->px_current->next == NULL) { + ss = &nse->iod->px_ctx->target_ss; + sslen = nse->iod->px_ctx->target_sslen; + port = nse->iod->px_ctx->target_port; + + assert(inet_ntop_ez(ss, sslen)); + } else { + ss = &nse->iod->px_ctx->px_current->next->ss; + sslen = nse->iod->px_ctx->px_current->next->sslen; + port = nse->iod->px_ctx->px_current->next->port; + } + nsock_printf(nspool, (nsock_iod)nse->iod, nsock_proxy_ev_handler, + 4000, udata, "CONNECT %s:%d HTTP/1.1\r\n\r\n", + inet_ntop_ez(ss, sslen), (int)port); + nsock_readlines(nspool, (nsock_iod)nse->iod, nsock_proxy_ev_handler, 4000, udata, 1); + break; + + case PROXY_STATE_HTTP_TCP_CONNECTED: + if (nse->type == NSE_TYPE_READ) { + char *res; + int reslen; + + res = nse_readbuf(nse, &reslen); + + /* TODO string check!! */ + if ((reslen >= 15) && strstr(res, "200 OK")) { + nse->iod->px_ctx->px_state = PROXY_STATE_HTTP_TUNNEL_ESTABLISHED; + } + + if (nse->iod->px_ctx->px_current->next == NULL) { + forward_event(nsp, nse, udata); + } else { + nse->iod->px_ctx->px_current = nse->iod->px_ctx->px_current->next; + nse->iod->px_ctx->px_state = PROXY_STATE_INITIAL; + + nsock_proxy_ev_handler(nsp, nse, udata); + } + } + break; + + case PROXY_STATE_HTTP_TUNNEL_ESTABLISHED: + forward_event(nsp, nse, udata); + break; + + default: + fatal("Invalid proxy state!"); + } +} + diff --git a/nsock/src/nsock_proxy.h b/nsock/src/nsock_proxy.h new file mode 100644 index 000000000..15ca40d3a --- /dev/null +++ b/nsock/src/nsock_proxy.h @@ -0,0 +1,108 @@ +/*************************************************************************** + * nsock_proxy.h -- PRIVATE interface definitions for proxy handling. * + * * + ***********************IMPORTANT NSOCK LICENSE TERMS*********************** + * * + * The nsock parallel socket event library is (C) 1999-2012 Insecure.Com * + * LLC This library is free software; you may redistribute and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; Version 2. This guarantees * + * your right to use, modify, and redistribute this software under certain * + * conditions. If this license is unacceptable to you, Insecure.Com LLC * + * may be willing to sell alternative licenses (contact * + * sales@insecure.com ). * + * * + * As a special exception to the GPL terms, Insecure.Com LLC grants * + * permission to link the code of this program with any version of the * + * OpenSSL library which is distributed under a license identical to that * + * listed in the included docs/licenses/OpenSSL.txt file, and distribute * + * linked combinations including the two. You must obey the GNU GPL in all * + * respects for all of the code used other than OpenSSL. If you modify * + * this file, you may extend this exception to your version of the file, * + * but you are not obligated to do so. * + * * + * If you received these files with a written license agreement stating * + * terms other than the (GPL) terms above, then that alternative license * + * agreement takes precedence over this comment. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes (none * + * have been found so far). * + * * + * Source code also allows you to port Nmap to new platforms, fix bugs, * + * and add new features. You are highly encouraged to send your changes * + * to nmap-dev@insecure.org for possible incorporation into the main * + * distribution. By sending these changes to Fyodor or one of the * + * Insecure.Org development mailing lists, it is assumed that you are * + * offering the Nmap Project (Insecure.Com LLC) the unlimited, * + * non-exclusive right to reuse, modify, and relicense the code. Nmap * + * will always be available Open Source, but this is important because the * + * inability to relicense code has caused devastating problems for other * + * Free Software projects (such as KDE and NASM). We also occasionally * + * relicense the code to third parties as discussed above. If you wish to * + * specify special license conditions of your contributions, just say so * + * when you send them. * + * * + * 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 v2.0 for more details * + * (http://www.gnu.org/licenses/gpl-2.0.html). * + * * + ***************************************************************************/ + +/* $Id: $ */ + +#ifndef NSOCK_PROXY_H +#define NSOCK_PROXY_H + +#include + +/* ------------------- CONSTANTS ------------------- */ +enum nsock_proxy_type { + PROXY_TYPE_HTTP, +}; + +enum nsock_proxy_state { + /* Common initial state for all proxy types. */ + PROXY_STATE_INITIAL, + + /* HTTP proxy states. */ + PROXY_STATE_HTTP_TCP_CONNECTED, + PROXY_STATE_HTTP_TUNNEL_ESTABLISHED +}; + + +/* ------------------- STRUCTURES ------------------- */ +struct proxy_node { + enum nsock_proxy_type px_type; + + struct sockaddr_storage ss; + size_t sslen; + unsigned short port; + + /* Use a 'next' pointer instead of a gh_list. This is lighter and doesn't + * affect performances since the proxy list isn't dynamic. */ + struct proxy_node *next; +}; + +struct proxy_chain_context { + enum nsock_proxy_state px_state; + struct proxy_node *px_current; + + struct sockaddr_storage target_ss; + size_t target_sslen; + unsigned short target_port; + nsock_ev_handler target_handler; +}; + + +/* ------------------- PROTOTYPES ------------------- */ +void nsock_proxy_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata); + +struct proxy_chain_context *proxy_chain_context_new(nsock_pool nspool); + + +#endif /* NSOCK_PROXY_H */ +