diff --git a/nsock/include/nsock.h b/nsock/include/nsock.h index 26d2e7a9c..90b778ee3 100644 --- a/nsock/include/nsock.h +++ b/nsock/include/nsock.h @@ -131,6 +131,8 @@ typedef void *nsock_ssl_session; typedef void *nsock_ssl_ctx; typedef void *nsock_ssl; +typedef void *nsock_proxychain; + /* Logging-related data structures */ @@ -187,8 +189,6 @@ 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); /* Calling this function will cause nsock_loop to quit on its next iteration @@ -270,6 +270,21 @@ void nsock_set_log_function(nsock_pool nsp, nsock_logger_t logger); nsock_loglevel_t nsock_get_loglevel(nsock_pool nsp); void nsock_set_loglevel(nsock_pool nsp, nsock_loglevel_t loglevel); +/* Parse a proxy chain description string and build a nsock_proxychain object + * accordingly. If the optional nsock_pool parameter is passed in, it gets + * associated to the chain object. The alternative is to pass nsp=NULL and call + * nsp_set_proxychain() manually. Whatever is done, the chain object has to be + * deleted by the caller, using proxychain_delete(). */ +int nsock_proxychain_new(const char *proxystr, nsock_proxychain *chain, nsock_pool nspool); + +/* If nsock_proxychain_new() returned success, caller has to free the chain + * object using this function. */ +void nsock_proxychain_delete(nsock_proxychain chain); + +/* Assign a previously created proxychain object to a nsock pool. After this, + * new connections requests will be issued through the chain of proxies (if + * possible). */ +int nsp_set_proxychain(nsock_pool nspool, nsock_proxychain chain); /* nsock_event handles a single event. Its ID is generally returned when the * event is created, and the event itself is included in callbacks diff --git a/nsock/src/nsock_connect.c b/nsock/src/nsock_connect.c index 72a690b26..9ea3a2d28 100644 --- a/nsock/src/nsock_connect.c +++ b/nsock/src/nsock_connect.c @@ -66,6 +66,9 @@ #include +extern struct proxy_actions *ProxyActions; + + /* 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 @@ -266,15 +269,7 @@ nsock_event_id nsock_connect_tcp(nsock_pool nsp, nsock_iod ms_iod, nsock_ev_hand 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 IOD_PX_TCP_CONNECT(nsi)(nsp, ms_iod, handler, timeout_msecs, userdata, saddr, sslen, port); } return nsock_connect_tcp_direct(nsp, ms_iod, handler, timeout_msecs, userdata, saddr, sslen, port); diff --git a/nsock/src/nsock_internal.h b/nsock/src/nsock_internal.h index cdb7d4160..f4a8c4c8a 100644 --- a/nsock/src/nsock_internal.h +++ b/nsock/src/nsock_internal.h @@ -219,8 +219,9 @@ typedef struct { SSL_CTX *sslctx; #endif - /* Proxy chain (or NULL) */ - struct proxy_node *px_chain; + /* Optional proxy chain (NULL is not set). Can only be set once per NSP (using + * nsock_proxychain_new() or nsp_set_proxychain(). */ + struct proxy_chain *px_chain; } mspool; diff --git a/nsock/src/nsock_proxy.c b/nsock/src/nsock_proxy.c index 074766ee8..77c5eac76 100644 --- a/nsock/src/nsock_proxy.c +++ b/nsock/src/nsock_proxy.c @@ -84,52 +84,94 @@ 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_node_init(struct proxy_node *proxy, char *proxystr); static void proxy_http_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata); +static nsock_event_id proxy_http_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); +static char *proxy_http_data_encode(const char *src, size_t len, size_t *dlen); +static char *proxy_http_data_decode(const char *src, size_t len, size_t *dlen); -/* 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; +const struct proxy_actions ProxyActions[PROXY_TYPE_COUNT] = { + [PROXY_TYPE_HTTP] = { + .connect_tcp = proxy_http_connect_tcp, + .data_encode = proxy_http_data_encode, + .data_decode = proxy_http_data_decode } +}; + + +/* A proxy chain is a comma-separated list of proxy specification strings: + * proto://[user:pass@]host[:port] */ +int nsock_proxychain_new(const char *proxystr, nsock_proxychain *chain, nsock_pool nspool) { + mspool *nsp = (mspool *)nspool; + struct proxy_chain **pchain = (struct proxy_chain **)chain; + + *pchain = (struct proxy_chain *)safe_malloc(sizeof(struct proxy_chain)); + (*pchain)->specstr = strdup(proxystr); + gh_list_init(&(*pchain)->nodes); - /* 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; + gh_list_append(&(*pchain)->nodes, parser->value); } proxy_parser_delete(parser); } + + if (nsp) { + if (nsp_set_proxychain(nspool, *pchain) < 0) { + nsock_proxychain_delete(*pchain); + return -1; + } + } + return 0; } +void nsock_proxychain_delete(nsock_proxychain chain) { + struct proxy_chain *pchain = (struct proxy_chain *)chain; + + if (pchain) { + struct proxy_node *node; + + free(pchain->specstr); + while ((node = (struct proxy_node *)gh_list_pop(&pchain->nodes)) != NULL) { + proxy_node_delete(node); + } + + gh_list_free(&pchain->nodes); + free(pchain); + } +} + +int nsp_set_proxychain(nsock_pool nspool, nsock_proxychain chain) { + mspool *nsp = (mspool *)nspool; + + if (nsp && nsp->px_chain) { + nsock_trace(nsp, "Invalid call to %s. Existing proxychain on this nsock_pool", __func__); + return -1; + } + + nsp->px_chain = (struct proxy_chain *)chain; + 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; + ctx->px_current = GH_LIST_FIRST_ELEM(&nsp->px_chain->nodes); return ctx; } @@ -188,7 +230,6 @@ static struct proxy_node *proxy_node_new(char *proxystr) { } proxy->px_type = PROXY_TYPE_HTTP; - proxy->next = NULL; proxy_http_node_init(proxy, proxystr); return proxy; @@ -199,15 +240,6 @@ static void proxy_node_delete(struct proxy_node *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; @@ -230,17 +262,19 @@ void forward_event(mspool *nsp, msevent *nse, void *udata) { void nsock_proxy_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata) { msevent *nse = (msevent *)nsevent; + struct proxy_node *current; if (nse->status != NSE_STATUS_SUCCESS) fatal("Error, but this is debug only!"); - switch (nse->iod->px_ctx->px_current->px_type) { + current = PROXY_CTX_CURRENT(nse->iod->px_ctx); + switch (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); + fatal("Invalid proxy type (%d)", current->px_type); } } @@ -274,16 +308,17 @@ void proxy_http_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata) case PROXY_STATE_INITIAL: nse->iod->px_ctx->px_state = PROXY_STATE_HTTP_TCP_CONNECTED; - if (nse->iod->px_ctx->px_current->next == NULL) { + if (PROXY_CTX_NEXT(nse->iod->px_ctx)) { + struct proxy_node *next; + + next = PROXY_CTX_NEXT(nse->iod->px_ctx); + ss = &next->ss; + sslen = next->sslen; + port = next->port; + } else { 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", @@ -323,3 +358,33 @@ void proxy_http_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata) } } +nsock_event_id proxy_http_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; + struct proxy_node *current; + + 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; + + current = PROXY_CTX_CURRENT(nsi->px_ctx); + saddr = (struct sockaddr *)¤t->ss; + sslen = current->sslen; + port = current->port; + handler = nsock_proxy_ev_handler; + + return nsock_connect_tcp_direct(nsp, ms_iod, handler, timeout_msecs, userdata, saddr, sslen, port); +} + +char *proxy_http_data_encode(const char *src, size_t len, size_t *dlen) { + // TODO + return NULL; +} + +char *proxy_http_data_decode(const char *src, size_t len, size_t *dlen) { + // TODO + return NULL; +} + diff --git a/nsock/src/nsock_proxy.h b/nsock/src/nsock_proxy.h index 15ca40d3a..17540a20a 100644 --- a/nsock/src/nsock_proxy.h +++ b/nsock/src/nsock_proxy.h @@ -57,11 +57,24 @@ #ifndef NSOCK_PROXY_H #define NSOCK_PROXY_H +#include "gh_list.h" #include + +/* ------------------ UTIL MACROS ------------------ */ +#define PROXY_CTX_CURRENT(ctx) ((struct proxy_node *)(GH_LIST_ELEM_DATA((ctx)->px_current))) +#define PROXY_CTX_NEXT(ctx) ((struct proxy_node *)((GH_LIST_ELEM_NEXT((ctx)->px_current)) ? GH_LIST_ELEM_DATA(GH_LIST_ELEM_NEXT((ctx)->px_current)) : NULL)) +#define PROXY_CTX_NODES(ctx) ((ctx)->px_chain->nodes) + +#define IOD_PX_TCP_CONNECT(iod) (ProxyActions[PROXY_CTX_CURRENT((iod)->px_ctx)->px_type].connect_tcp) +#define IOD_PX_DATA_ENCODE(iod) (ProxyActions[PROXY_CTX_CURRENT((iod)->px_ctx)->px_type].data_encode) +#define IOD_PX_DATA_DECODE(iod) (ProxyActions[PROXY_CTX_CURRENT((iod)->px_ctx)->px_type].data_decode) + + /* ------------------- CONSTANTS ------------------- */ enum nsock_proxy_type { - PROXY_TYPE_HTTP, + PROXY_TYPE_HTTP = 0, + PROXY_TYPE_COUNT, }; enum nsock_proxy_state { @@ -81,15 +94,20 @@ struct proxy_node { 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 { + char *specstr; + gh_list nodes; }; struct proxy_chain_context { + struct proxy_chain *px_chain; + + /* Those fields are used to store current state during the tunnel + * establishment phase. */ + gh_list_elem *px_current; enum nsock_proxy_state px_state; - struct proxy_node *px_current; struct sockaddr_storage target_ss; size_t target_sslen; @@ -97,6 +115,13 @@ struct proxy_chain_context { nsock_ev_handler target_handler; }; +struct proxy_actions { + nsock_event_id (*connect_tcp)(nsock_pool nsp, nsock_iod ms_iod, nsock_ev_handler handler, int mstimeout, + void *userdata, struct sockaddr *saddr, size_t sslen, unsigned short port); + char *(*data_encode)(const char *src, size_t len, size_t *dlen); + char *(*data_decode)(const char *src, size_t len, size_t *dlen); +}; + /* ------------------- PROTOTYPES ------------------- */ void nsock_proxy_ev_handler(nsock_pool nspool, nsock_event nsevent, void *udata);