From: Daniel Dickinson <open...@daniel.thecshore.com>

A first attempt at simple HTTP proxy support (to allow
URL beginning with /prefix to really be access to some
other server; this is potenitally useful with some
web applications, or if you want to have a single
entry point to multiple servers (and aren't serving
huge traffic).

Signed-off-by: Daniel Dickinson <l...@daniel.thecshore.com>
---

 NOTE: This is basically untested and is provided to get comments
 before proceeding with something which may be of no interest.

 CMakeLists.txt |  11 +-
 client.c       |  90 +++++++++++--
 listen.c       |   6 +-
 main.c         |  41 +++++-
 proc.c         |  38 ++++++
 proxy.c        | 406 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 uhttpd.h       |  53 ++++++++
 utils.c        |  52 ++++++--
 8 files changed, 667 insertions(+), 30 deletions(-)
 create mode 100644 proxy.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8514351..3bf39db 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,6 +10,7 @@ ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -Os -Wall -Werror 
-Wmissing-declarations
 OPTION(TLS_SUPPORT "TLS support" ON)
 OPTION(LUA_SUPPORT "Lua support" ON)
 OPTION(UBUS_SUPPORT "ubus support" ON)
+OPTION(PROXY_SUPPORT "proxy support" ON)
 
 IF(APPLE)
   INCLUDE_DIRECTORIES(/opt/local/include)
@@ -32,9 +33,7 @@ IF(HAVE_SHADOW)
     ADD_DEFINITIONS(-DHAVE_SHADOW)
 ENDIF()
 
-ADD_EXECUTABLE(uhttpd ${SOURCES})
 FIND_LIBRARY(libjson NAMES json-c json)
-TARGET_LINK_LIBRARIES(uhttpd ubox dl json_script blobmsg_json ${libjson} 
${LIBS})
 
 SET(PLUGINS "")
 IF(LUA_SUPPORT)
@@ -73,6 +72,14 @@ IF(UBUS_SUPPORT)
        TARGET_LINK_LIBRARIES(uhttpd_ubus ubus ubox blobmsg_json ${libjson})
 ENDIF()
 
+IF(PROXY_SUPPORT)
+       SET(SOURCES ${SOURCES} proxy.c)
+       ADD_DEFINITIONS(-DHAVE_PROXY)
+ENDIF()
+
+ADD_EXECUTABLE(uhttpd ${SOURCES})
+TARGET_LINK_LIBRARIES(uhttpd ubox dl json_script blobmsg_json ${libjson} 
${LIBS})
+
 IF(PLUGINS)
        SET_TARGET_PROPERTIES(${PLUGINS} PROPERTIES
                PREFIX ""
diff --git a/client.c b/client.c
index 73e0e49..4643a56 100644
--- a/client.c
+++ b/client.c
@@ -24,9 +24,11 @@
 #include "tls.h"
 
 static LIST_HEAD(clients);
-static bool client_done = false;
+bool client_done = false;
 
 int n_clients = 0;
+int n_connections = 0;
+
 struct config conf = {};
 
 const char * const http_versions[] = {
@@ -40,6 +42,9 @@ const char * const http_methods[] = {
        [UH_HTTP_MSG_POST] = "POST",
        [UH_HTTP_MSG_HEAD] = "HEAD",
        [UH_HTTP_MSG_OPTIONS] = "OPTIONS",
+       [UH_HTTP_MSG_DELETE] = "DELETE",
+       [UH_HTTP_MSG_PUT] = "PUT",
+       [UH_HTTP_MSG_CONNECT] = "CONNECT",
 };
 
 void uh_http_header(struct client *cl, int code, const char *summary)
@@ -73,7 +78,7 @@ static void uh_connection_close(struct client *cl)
        ustream_state_change(cl->us);
 }
 
-static void uh_dispatch_done(struct client *cl)
+void uh_dispatch_done(struct client *cl)
 {
        if (cl->dispatch.free)
                cl->dispatch.free(cl);
@@ -104,7 +109,7 @@ static void uh_keepalive_poll_cb(struct uloop_timeout 
*timeout)
        cl->us->notify_read(cl->us, 0);
 }
 
-static void uh_poll_connection(struct client *cl)
+void uh_poll_connection(struct client *cl)
 {
        cl->timeout.cb = uh_keepalive_poll_cb;
        uloop_timeout_set(&cl->timeout, 1);
@@ -160,6 +165,49 @@ static int find_idx(const char * const *list, int max, 
const char *str)
        return -1;
 }
 
+#ifdef HAVE_PROXY
+static int client_parse_response(struct client *cl, char *data)
+{
+       struct client *proxycl = cl->proxycl;
+
+       if (!proxycl)
+               return CLIENT_STATE_DONE;
+
+       struct http_request *req = &cl->request;
+       char *code, *msg, *version;
+       int h_version;
+
+       version = strtok(data, " ");
+       code = strtok(NULL, " ");
+       msg = strtok(NULL, " ");
+       if (!code || !msg || !version)
+               goto error;
+
+       memset(&cl->request, 0, sizeof(cl->request));
+       h_version = find_idx(http_versions, ARRAY_SIZE(http_versions), version);
+       if (h_version < 0) {
+               req->version = UH_HTTP_VER_1_0;
+               return CLIENT_STATE_DONE;
+       }
+
+       req->method = proxycl->request.method;
+       req->version = h_version;
+       if (req->version < UH_HTTP_VER_1_1 || req->method == UH_HTTP_MSG_POST ||
+           !conf.http_keepalive)
+               req->connection_close = true;
+       req->code = atoi(code);
+       req->msg = strdup(msg);
+       if (req->code <= 0)
+               goto error;
+
+       return CLIENT_STATE_HEADER;
+
+       error:
+               uh_client_error(cl->proxycl, 502, "Bad Gateway", "Invalid 
response from target\n");
+               return CLIENT_STATE_DONE;
+}
+#endif
+
 static int client_parse_request(struct client *cl, char *data)
 {
        struct http_request *req = &cl->request;
@@ -206,10 +254,30 @@ static bool client_init_cb(struct client *cl, char *buf, 
int len)
 
        *newline = 0;
        blob_buf_init(&cl->hdr, 0);
+#ifdef HAVE_PROXY
+       if (cl->response) {
+               cl->state = client_parse_response(cl, buf);
+       } else {
+#endif
        cl->state = client_parse_request(cl, buf);
+#ifdef HAVE_PROXY
+       }
+#endif
        ustream_consume(cl->us, newline + 2 - buf);
-       if (cl->state == CLIENT_STATE_DONE)
-               uh_header_error(cl, 400, "Bad Request");
+
+       if (cl->state == CLIENT_STATE_DONE) {
+#ifdef HAVE_PROXY
+               if (!cl->response) {
+#endif
+                       uh_header_error(cl, 400, "Bad Request");
+#ifdef HAVE_PROXY
+               } else {
+                       uh_client_error(cl->proxycl, 502, "Bad Gateway", 
"Invalid response from target\n");
+#endif
+#ifdef HAVE_PROXY
+               }
+#endif
+       }
 
        return true;
 }
@@ -303,7 +371,7 @@ static void client_header_complete(struct client *cl)
        uh_handle_request(cl);
 }
 
-static void client_parse_header(struct client *cl, char *data)
+void uh_client_parse_header(struct client *cl, char *data)
 {
        struct http_request *r = &cl->request;
        char *err;
@@ -473,7 +541,7 @@ static bool client_header_cb(struct client *cl, char *buf, 
int len)
                return false;
 
        *newline = 0;
-       client_parse_header(cl, buf);
+       uh_client_parse_header(cl, buf);
        line_len = newline + 2 - buf;
        ustream_consume(cl->us, line_len);
        if (cl->state == CLIENT_STATE_DATA)
@@ -522,6 +590,7 @@ static void client_close(struct client *cl)
 
        client_done = true;
        n_clients--;
+       n_connections--;
        uh_dispatch_done(cl);
        uloop_timeout_cancel(&cl->timeout);
        if (cl->tls)
@@ -572,7 +641,7 @@ static void client_notify_state(struct ustream *s)
        uh_client_notify_state(cl);
 }
 
-static void set_addr(struct uh_addr *addr, void *src)
+void uh_set_addr(struct uh_addr *addr, void *src)
 {
        struct sockaddr_in *sin = src;
        struct sockaddr_in6 *sin6 = src;
@@ -606,10 +675,10 @@ bool uh_accept_client(int fd, bool tls)
        if (sfd < 0)
                return false;
 
-       set_addr(&cl->peer_addr, &addr);
+       uh_set_addr(&cl->peer_addr, &addr);
        sl = sizeof(addr);
        getsockname(sfd, (struct sockaddr *) &addr, &sl);
-       set_addr(&cl->srv_addr, &addr);
+       uh_set_addr(&cl->srv_addr, &addr);
 
        cl->us = &cl->sfd.stream;
        if (tls) {
@@ -628,6 +697,7 @@ bool uh_accept_client(int fd, bool tls)
 
        next_client = NULL;
        n_clients++;
+       n_connections++;
        cl->id = client_id++;
        cl->tls = tls;
 
diff --git a/listen.c b/listen.c
index 92ca680..63d6da5 100644
--- a/listen.c
+++ b/listen.c
@@ -57,7 +57,7 @@ static void uh_poll_listeners(struct uloop_timeout *timeout)
        struct listener *l;
 
        if ((!n_blocked && conf.max_connections) ||
-           n_clients >= conf.max_connections)
+           n_connections >= conf.max_connections)
                return;
 
        list_for_each_entry(l, &listeners, list) {
@@ -65,7 +65,7 @@ static void uh_poll_listeners(struct uloop_timeout *timeout)
                        continue;
 
                l->fd.cb(&l->fd, ULOOP_READ);
-           if (n_clients >= conf.max_connections)
+           if (n_connections >= conf.max_connections)
                        break;
 
                n_blocked--;
@@ -92,7 +92,7 @@ static void listener_cb(struct uloop_fd *fd, unsigned int 
events)
                        break;
        }
 
-       if (conf.max_connections && n_clients >= conf.max_connections)
+       if (conf.max_connections && n_connections >= conf.max_connections)
                uh_block_listener(l);
 }
 
diff --git a/main.c b/main.c
index fb27665..2c6eb17 100644
--- a/main.c
+++ b/main.c
@@ -154,6 +154,10 @@ static int usage(const char *name)
                "       -a              Do not authenticate JSON-RPC requests 
against UBUS session api\n"
                "       -X              Enable CORS HTTP headers on JSON-RPC 
api\n"
 #endif
+#ifdef HAVE_PROXY
+               "       -P prefix=uri   Redirect prefix to alternate URI 
(proxy)\n"
+               "       -M count        Maximum number of proxied requests\n"
+#endif
                "       -x string       URL prefix for CGI handler, default is 
'/cgi-bin'\n"
                "       -y alias[=path] URL alias handle\n"
                "       -i .ext=path    Use interpreter at path for files with 
the given extension\n"
@@ -174,6 +178,10 @@ static void init_defaults_pre(void)
        conf.network_timeout = 30;
        conf.http_keepalive = 20;
        conf.max_script_requests = 3;
+#ifdef HAVE_PROXY
+       conf.max_proxy_requests = 10;
+       INIT_LIST_HEAD(&conf.proxies);
+#endif
        conf.max_connections = 100;
        conf.realm = "Protected Area";
        conf.cgi_prefix = "/cgi-bin";
@@ -229,10 +237,13 @@ int main(int argc, char **argv)
        BUILD_BUG_ON(sizeof(uh_buf) < PATH_MAX);
 
        uh_dispatch_add(&cgi_dispatch);
+#ifdef HAVE_PROXY
+       uh_dispatch_add(&proxy_dispatch);
+#endif
        init_defaults_pre();
        signal(SIGPIPE, SIG_IGN);
 
-       while ((ch = getopt(argc, argv, 
"A:aC:c:Dd:E:fh:H:I:i:K:k:L:l:m:N:n:p:qRr:Ss:T:t:U:u:Xx:y:")) != -1) {
+       while ((ch = getopt(argc, argv, 
"A:aC:c:Dd:E:fh:H:I:i:K:k:L:l:m:N:n:p:qRr:Ss:T:t:U:u:Xx:y:P:M:")) != -1) {
                switch(ch) {
 #ifdef HAVE_TLS
                case 'C':
@@ -447,6 +458,34 @@ int main(int argc, char **argv)
                                        "ignoring -%c\n", ch);
                        break;
 #endif
+#ifdef HAVE_PROXY
+               case 'P':
+                       optarg = strdup(optarg);
+                       port = strchr(optarg, '=');
+                       if (!port) {
+                               fprintf(stderr, "Error: Invalid proxy 
destination: %s\n",
+                                       optarg);
+                       }
+                       *port++ = 0;
+                       if ((strcmp(&optarg[0], "http://";) == 0) && port) {
+                               uh_proxy_add(optarg, &port[7]);
+                       } else {
+                               fprintf(stderr, "Error: Invalid proxy 
destination or protocol: %s\n",
+                                       optarg);
+                               exit(1);
+                       }
+
+                       break;
+
+               case 'M':
+                       conf.max_proxy_requests = atoi(optarg);
+                       break;
+#else
+               case 'P':
+               case 'M':
+                       fprintf(stderr, "uhttpd: Proxy support not compiled, "
+                                       "ignoring -%c\n", ch);
+#endif
                default:
                        return usage(argv[0]);
                }
diff --git a/proc.c b/proc.c
index 4819e08..66288fa 100644
--- a/proc.c
+++ b/proc.c
@@ -121,6 +121,44 @@ static struct env_var extra_vars[] = {
        [VAR_REMOTE_PORT] = { "REMOTE_PORT", remote_port },
 };
 
+char *uh_get_local_addr(struct client *cl)
+{
+       static char local_addr[INET6_ADDRSTRLEN];
+       inet_ntop(cl->srv_addr.family, &cl->srv_addr.in, local_addr, 
sizeof(local_addr));
+       return local_addr;
+}
+
+char *uh_get_local_port(struct client *cl)
+{
+       static char local_port[6];
+       snprintf(local_port, sizeof(local_port), "%d", cl->srv_addr.port);
+       return local_port;
+}
+
+char *uh_get_remote_addr(struct client *cl)
+{
+       static char remote_addr[INET6_ADDRSTRLEN];
+       inet_ntop(cl->peer_addr.family, &cl->peer_addr.in, remote_addr, 
sizeof(remote_addr));
+       return remote_addr;
+}
+
+char *uh_get_remote_port(struct client *cl)
+{
+       static char remote_port[6];
+       snprintf(remote_port, sizeof(remote_port), "%d", cl->peer_addr.port);
+       return remote_port;
+}
+
+char *uh_get_redirect_status(struct client *cl)
+{
+       static char redirect_status[4];
+       struct http_request *req = &cl->request;
+       snprintf(redirect_status, sizeof(redirect_status),
+                "%d", req->redirect_status);
+
+       return redirect_status;
+}
+
 struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi)
 {
        struct http_request *req = &cl->request;
diff --git a/proxy.c b/proxy.c
new file mode 100644
index 0000000..27c856b
--- /dev/null
+++ b/proxy.c
@@ -0,0 +1,406 @@
+/*
+ * uhttpd - Tiny single-threaded httpd
+ *
+ *   Copyright (C) 2010-2013 Jo-Philipp Wich <x...@subsignal.org>
+ *   Copyright (C) 2013 Felix Fietkau <n...@openwrt.org>
+ *   Copyright (C) 2015 Daniel Dickinson <open...@daniel.thecshore.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include "uhttpd.h"
+#include "plugin.h"
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <limits.h>
+
+bool proxy_done = false;
+static int n_proxies = 0;
+
+void uh_proxy_add(const char *prefix, const char *uri)
+{
+       struct proxy_uri *pu;
+       char * new_prefix;
+       char * new_port;
+       char * new_hostname;
+       char * new_path;
+
+       char *path = strchr(uri, '/');
+       *path++ = 0;
+
+       char *port = strchr(uri, ':');
+       if (!port)
+               port = "80";
+
+       pu = calloc_a(sizeof(*pu),
+               &new_prefix, strlen(prefix) + 1,
+               &new_hostname, strlen(uri) + 1,
+               &new_port, strlen(port) + 1,
+               &new_path, strlen(path) + 1);
+
+       pu->prefix = strcpy(new_prefix, prefix);
+       pu->hostname = strcpy(new_hostname, uri);
+       pu->port = strcpy(new_port, port);
+       pu->path = strcpy(new_path, path);
+
+       list_add_tail(&pu->list, &conf.proxies);
+}
+
+static int proxy_sock_init(struct proxy_uri *target, struct sockaddr_in6 
*addr, unsigned int *sl)
+{
+       int sock = -1;
+       int yes = 1;
+       int status;
+
+       struct addrinfo *addrs = NULL, *p = NULL;
+       static struct addrinfo hints = {
+               .ai_family = AF_UNSPEC,
+               .ai_socktype = SOCK_STREAM,
+               .ai_flags = AI_PASSIVE,
+       };
+
+       if ((status = getaddrinfo(target->hostname, target->port, &hints, 
&addrs)) != 0) {
+               fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(status));
+               return 0;
+       }
+
+       for (p = addrs; p; p = p->ai_next) {
+               /* get the socket */
+               sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+               if (sock < 0) {
+                       perror("socket()");
+                       goto error;
+               }
+
+               /* required to get parallel v4 + v6 working */
+               if (p->ai_family == AF_INET6 &&
+                   setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, 
sizeof(yes)) < 0) {
+                       perror("setsockopt()");
+                       goto error;
+               }
+
+               if (connect(sock, p->ai_addr, p->ai_addrlen) < 0) {
+                       continue;
+               } else {
+                       memcpy(addr, p->ai_addr, sizeof(*addr));
+                       *sl = p->ai_addrlen;
+                       fd_cloexec(sock);
+                       break;
+               }
+       }
+       freeaddrinfo(addrs);
+
+       if (sock < 0) {
+               goto error;
+       }
+
+       return sock;
+
+error:
+       if (sock > -1)
+               close(sock);
+       return -1;
+}
+
+static void proxy_close(struct client *proxy)
+{
+       proxy_done = true;
+       uh_dispatch_done(proxy);
+       uloop_timeout_cancel(&proxy->timeout);
+       ustream_free(&proxy->sfd.stream);
+       close(proxy->sfd.fd.fd);
+       n_connections--;
+       n_proxies--;
+       blob_buf_free(&proxy->hdr);
+       if (proxy->request.msg)
+               free(proxy->request.msg);
+       if (proxy->request.url)
+               free(proxy->request.url);
+       free(proxy);
+}
+
+static void proxy_client_notify_state(struct client *proxy)
+{
+       struct ustream *s = proxy->us;
+
+       if (!s->write_error && proxy->state != CLIENT_STATE_CLEANUP) {
+               if (proxy->state == CLIENT_STATE_DATA)
+                       return;
+
+               if (!s->eof || s->w.data_bytes)
+                       return;
+       }
+
+       return proxy_close(proxy);
+}
+
+static void proxy_ustream_notify_state(struct ustream *s)
+{
+       struct client *cl = container_of(s, struct client, sfd.stream);
+
+       proxy_client_notify_state(cl);
+}
+
+static int proxy_send_data_cb(struct client *cl, const char *buf, int len)
+{
+       /* For proxy cl = proxy and cl->proxycl = originator
+         * For the originator the opposite is true
+         * We just shovel data from one side to the other.
+         */
+       if (!cl->proxycl)
+               return INT_MAX;
+
+       uh_ustream_chunk_write(cl, cl->proxycl->us, buf, len);
+
+       ustream_consume(cl->us, len);
+
+       return len;
+}
+
+static void proxy_ustream_read_response_cb(struct ustream *s, int bytes)
+{
+       struct client *proxy = container_of(s, struct client, sfd.stream);
+
+       proxy->response = true;
+       uh_client_read_cb(proxy);
+}
+
+static bool proxy_write_request_init_cb(struct client *cl, char *buf, int len) 
{
+       struct client *proxy = cl->proxycl;
+
+       if (!proxy)
+               return false;
+
+       switch (proxy->request.method) {
+       case UH_HTTP_MSG_GET:
+               uh_ustream_chunk_printf(cl, proxy->us, "GET ");
+               break;
+       case UH_HTTP_MSG_POST:
+               uh_ustream_chunk_printf(cl, proxy->us, "POST ");
+               break;
+       case UH_HTTP_MSG_HEAD:
+               uh_ustream_chunk_printf(cl, proxy->us, "HEAD ");
+               break;
+       case UH_HTTP_MSG_DELETE:
+               uh_ustream_chunk_printf(cl, proxy->us, "DELETE ");
+               break;
+       case UH_HTTP_MSG_PUT:
+               uh_ustream_chunk_printf(cl, proxy->us, "PUT ");
+               break;
+       case UH_HTTP_MSG_OPTIONS:
+               uh_ustream_chunk_printf(cl, proxy->us, "OPTIONS ");
+               break;
+       case UH_HTTP_MSG_CONNECT:
+               uh_ustream_chunk_printf(cl, proxy->us, "CONNECT ");
+               break;
+       }
+
+       uh_ustream_chunk_printf(cl, proxy->us, "%s", 
proxy->request.proxy->path);
+
+       char *match = strstr(proxy->request.url, proxy->request.proxy->path) + 
strlen(proxy->request.proxy->path);
+
+       if (strlen(match) > 0) {
+               uh_ustream_chunk_printf(cl, proxy->us, "/%s ", match);
+       }
+
+       uh_ustream_chunk_printf(cl, proxy->us, "HTTP/");
+
+       switch(cl->request.version) {
+       case UH_HTTP_VER_0_9:
+               uh_ustream_chunk_printf(cl, proxy->us, "0.9");
+               break;
+       case UH_HTTP_VER_1_0:
+               uh_ustream_chunk_printf(cl, proxy->us, "1.0");
+               break;
+       case UH_HTTP_VER_1_1:
+               uh_ustream_chunk_printf(cl, proxy->us, "1.1");
+               break;
+       }
+       uh_ustream_chunk_printf(cl, proxy->us, "\r\n");
+
+       proxy->wreqstate = CLIENT_STATE_HEADER;
+
+       return true;
+}
+
+static bool proxy_write_request_header_cb(struct client *cl, char *buf, int 
len) {
+       struct client *proxy = cl->proxycl;
+       int rem;
+       struct proxy_uri *pu = proxy->request.proxy;
+
+       struct blob_attr *cur;
+
+       if (!proxy)
+               return false;
+
+       blob_for_each_attr(cur, cl->hdr.head, rem) {
+               if (strcmp(blobmsg_name(cur), "Host") == 0) {
+                       uh_ustream_chunk_printf(cl, proxy->us, "Host: 
%s:%s\r\n", pu->hostname, pu->port);
+                       uh_ustream_chunk_printf(cl, proxy->us, 
"X-Forwarded-Host: %s\r\n'", blobmsg_data(cur));
+               } else {
+                       uh_ustream_chunk_printf(cl, proxy->us, "%s: %s\r\n", 
blobmsg_name(cur), blobmsg_data(cur));
+               }
+       }
+
+       char *remote_addr = uh_get_remote_addr(cl);
+       char *remote_port = uh_get_remote_port(cl);
+
+        uh_ustream_chunk_printf(cl, proxy->us, "X-Forwarded-For: %s:%s\r\n", 
remote_addr, remote_port);
+       if (cl->tls) {
+               uh_ustream_chunk_printf(cl, proxy->us, "X-Forwarded-Proto: 
https\r\n");
+       } else {
+               uh_ustream_chunk_printf(cl, proxy->us, "X-Forwarded-Proto: 
http\r\n");
+       }
+
+       uh_ustream_chunk_printf(cl, proxy->us, "\r\n");
+
+       proxy->wreqstate = CLIENT_STATE_DATA;
+
+       return true;
+}
+
+static bool proxy_write_request_data_cb(struct client *cl, char *buf, int len)
+{
+       client_poll_post_data(cl);
+       return false;
+}
+
+typedef bool (*proxy_write_request_cb_t)(struct client *cl, char *buf, int 
len);
+static proxy_write_request_cb_t proxy_write_request_cbs[] = {
+       [CLIENT_STATE_INIT] = proxy_write_request_init_cb,
+       [CLIENT_STATE_HEADER] = proxy_write_request_header_cb,
+       [CLIENT_STATE_DATA] = proxy_write_request_data_cb
+};
+
+static void proxy_write_request_cb(struct client *cl) {
+       struct client *proxy = cl->proxycl;
+
+       if (!proxy)
+               return;
+
+       struct ustream *us = cl->us;
+       char *str;
+       int len;
+
+       proxy_done = false;
+       while (!client_done && !proxy_done) {
+               str = ustream_get_read_buf(us, &len);
+               if (!str || !len)
+                       break;
+
+               if (proxy->wreqstate >= array_size(proxy_write_request_cbs) || 
!proxy_write_request_cbs[proxy->wreqstate])
+                       break;
+
+               if (!proxy_write_request_cbs[proxy->wreqstate](cl, str, len))
+                       if (len == us->r.buffer_len &&
+                           cl->state != CLIENT_STATE_DATA) {
+                               uh_client_error(cl, 502, "Bad Gateway",
+                                                 "Connection terminated 
prematurely.");
+                       }
+       }
+}
+
+static void proxy_ustream_write_request_cb(struct ustream *s, int bytes)
+{
+       struct client *proxy = container_of(s, struct client, sfd.stream);
+
+       proxy_write_request_cb(proxy);
+}
+
+static struct client *proxy_init_client(struct client *cl, struct proxy_uri 
*target, char *url)
+{
+       static struct client *proxy;
+       unsigned int sl;
+       int sfd;
+       struct sockaddr_in6 addr;
+
+       proxy = calloc(1, sizeof(struct client));
+
+       sl = sizeof(addr);
+       sfd = proxy_sock_init(target, &addr, &sl);
+
+       if (sfd < 0)
+               return NULL;
+
+       uh_set_addr(&proxy->peer_addr, &addr);
+       sl = sizeof(addr);
+       getsockname(sfd, (struct sockaddr *) &addr, &sl);
+       uh_set_addr(&proxy->srv_addr, &addr);
+
+       proxy->sfd.fd.fd = sfd;
+       proxy->us = &proxy->sfd.stream;
+       proxy->us->notify_write = proxy_ustream_write_request_cb;
+       proxy->us->notify_read = proxy_ustream_read_response_cb;
+       proxy->us->notify_state = proxy_ustream_notify_state;
+       proxy->us->string_data = true;
+       proxy->dispatch.data_send = proxy_send_data_cb;
+       cl->dispatch.data_send = proxy_send_data_cb;
+
+       cl->proxycl = proxy;
+       proxy->proxycl = cl;
+       proxy->request.proxy = target;
+       proxy->request.url = strdup(url);
+
+       ustream_fd_init(&proxy->sfd, sfd);
+
+       uh_poll_connection(proxy);
+
+       return proxy;
+}
+
+static void proxy_init(struct client *cl, char *url, struct proxy_uri *pu) {
+       if ((conf.max_connections && (n_connections >= conf.max_connections)) 
|| (conf.max_proxy_requests && (n_proxies >= conf.max_proxy_requests)))
+               return;
+
+       struct client *proxy = proxy_init_client(cl, pu, url);
+
+       if (!proxy) {
+               uh_client_error(cl, 502, "Bad Gateway",
+                                 "Proxy %s failed to connect to any 
address\n", pu->prefix);
+               return;
+       }
+       n_connections++;
+       n_proxies++;
+
+        return;
+}
+
+static void proxy_handle_request(struct client *cl, char *url, struct 
path_info *pi)
+{
+       struct proxy_uri *p;
+
+        if (!list_empty(&conf.proxies)) list_for_each_entry(p, &conf.proxies, 
list)
+               if (uh_path_match(p->prefix, url))
+                       return proxy_init(cl, url, p);
+
+}
+
+static bool check_proxy_url(const char *url)
+{
+        struct proxy_uri *p;
+
+        if (!list_empty(&conf.proxies)) list_for_each_entry(p, &conf.proxies, 
list)
+               if (uh_path_match(p->prefix, url))
+                       return true;
+       return false;
+}
+
+struct dispatch_handler proxy_dispatch = {
+       .script = false,
+       .check_url = check_proxy_url,
+       .handle_request = proxy_handle_request,
+};
diff --git a/uhttpd.h b/uhttpd.h
index f9ea761..d47b7b4 100644
--- a/uhttpd.h
+++ b/uhttpd.h
@@ -71,6 +71,10 @@ struct config {
        int tls_redirect;
        int tcp_keepalive;
        int max_script_requests;
+#ifdef HAVE_PROXY
+       struct list_head proxies;
+       int max_proxy_requests;
+#endif
        int max_connections;
        int http_keepalive;
        int script_timeout;
@@ -92,6 +96,9 @@ enum http_method {
        UH_HTTP_MSG_POST,
        UH_HTTP_MSG_HEAD,
        UH_HTTP_MSG_OPTIONS,
+       UH_HTTP_MSG_DELETE,
+       UH_HTTP_MSG_PUT,
+       UH_HTTP_MSG_CONNECT,
 };
 
 enum http_version {
@@ -123,6 +130,12 @@ struct http_request {
        bool disable_chunked;
        uint8_t transfer_chunked;
        const struct auth_realm *realm;
+#ifdef HAVE_PROXY
+       int code;
+       char *msg;
+       struct proxy_uri *proxy;
+       char *url;
+#endif
 };
 
 enum client_state {
@@ -140,6 +153,16 @@ struct interpreter {
        const char *ext;
 };
 
+#ifdef HAVE_PROXY
+struct proxy_uri {
+       struct list_head list;
+       const char *prefix;
+       const char *hostname;
+       const char *port;
+       const char *path;
+};
+#endif
+
 struct path_info {
        const char *root;
        const char *phys;
@@ -150,6 +173,9 @@ struct path_info {
        bool redirected;
        struct stat stat;
        const struct interpreter *ip;
+#ifdef HAVE_PROXY
+       struct proxy_uri *proxy;
+#endif
 };
 
 struct env_var {
@@ -248,6 +274,13 @@ struct client {
        struct ustream_ssl ssl;
 #endif
        struct uloop_timeout timeout;
+
+#ifdef HAVE_PROXY
+       struct client *proxycl;
+       enum client_state wreqstate;
+       bool response;
+#endif
+
        int requests;
 
        enum client_state state;
@@ -263,14 +296,19 @@ struct client {
 
 extern char uh_buf[4096];
 extern int n_clients;
+extern int n_connections;
 extern struct config conf;
 extern const char * const http_versions[];
 extern const char * const http_methods[];
 extern struct dispatch_handler cgi_dispatch;
+extern struct dispatch_handler proxy_dispatch;
+extern bool client_done;
+extern bool proxy_done;
 
 void uh_index_add(const char *filename);
 
 bool uh_accept_client(int fd, bool tls);
+void uh_set_addr(struct uh_addr *addr, void *src);
 
 void uh_unblock_listeners(void);
 void uh_setup_listeners(void);
@@ -279,6 +317,9 @@ int uh_socket_bind(const char *host, const char *port, bool 
tls);
 int uh_first_tls_port(int family);
 
 bool uh_use_chunked(struct client *cl);
+void uh_ustream_chunk_write(struct client *cl, struct ustream *us, const void 
*data, int len);
+void uh_ustream_chunk_vprintf(struct client *cl, struct ustream *us, const 
char *format, va_list arg);
+void uh_ustream_chunk_printf(struct client *cl, struct ustream *us, const char 
*format, ...);
 void uh_chunk_write(struct client *cl, const void *data, int len);
 void uh_chunk_vprintf(struct client *cl, const char *format, va_list arg);
 
@@ -296,6 +337,9 @@ void uh_handle_request(struct client *cl);
 void client_poll_post_data(struct client *cl);
 void uh_client_read_cb(struct client *cl);
 void uh_client_notify_state(struct client *cl);
+void uh_client_parse_header(struct client *cl, char *data);
+void uh_dispatch_done(struct client *cl);
+void uh_poll_connection(struct client *cl);
 
 void uh_auth_add(const char *path, const char *user, const char *pass);
 bool uh_auth_check(struct client *cl, struct path_info *pi);
@@ -304,6 +348,9 @@ void uh_close_listen_fds(void);
 void uh_close_fds(void);
 
 void uh_interpreter_add(const char *ext, const char *path);
+#ifdef HAVE_PROXY
+void uh_proxy_add(const char *prefix, const char *uri);
+#endif
 void uh_dispatch_add(struct dispatch_handler *d);
 
 void uh_relay_open(struct client *cl, struct relay *r, int fd, int pid);
@@ -312,6 +359,12 @@ void uh_relay_free(struct relay *r);
 void uh_relay_kill(struct client *cl, struct relay *r);
 
 struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi);
+char *uh_get_local_addr(struct client *cl);
+char *uh_get_local_port(struct client *cl);
+char *uh_get_remote_addr(struct client *cl);
+char *uh_get_remote_port(struct client *cl);
+char *uh_get_redirect_status(struct client *cl);
+
 bool uh_create_process(struct client *cl, struct path_info *pi, char *url,
                       void (*cb)(struct client *cl, struct path_info *pi, char 
*url));
 
diff --git a/utils.c b/utils.c
index 29e03c0..bd4082f 100644
--- a/utils.c
+++ b/utils.c
@@ -35,7 +35,7 @@ bool uh_use_chunked(struct client *cl)
        return !cl->request.disable_chunked;
 }
 
-void uh_chunk_write(struct client *cl, const void *data, int len)
+void uh_ustream_chunk_write(struct client *cl, struct ustream *us, const void 
*data, int len)
 {
        bool chunked = uh_use_chunked(cl);
 
@@ -44,13 +44,13 @@ void uh_chunk_write(struct client *cl, const void *data, 
int len)
 
        uloop_timeout_set(&cl->timeout, conf.network_timeout * 1000);
        if (chunked)
-               ustream_printf(cl->us, "%X\r\n", len);
-       ustream_write(cl->us, data, len, true);
+               ustream_printf(us, "%X\r\n", len);
+       ustream_write(us, data, len, true);
        if (chunked)
-               ustream_printf(cl->us, "\r\n", len);
+               ustream_printf(us, "\r\n", len);
 }
 
-void uh_chunk_vprintf(struct client *cl, const char *format, va_list arg)
+void uh_ustream_chunk_vprintf(struct client *cl, struct ustream *us, const 
char *format, va_list arg)
 {
        char buf[256];
        va_list arg2;
@@ -61,7 +61,7 @@ void uh_chunk_vprintf(struct client *cl, const char *format, 
va_list arg)
 
        uloop_timeout_set(&cl->timeout, conf.network_timeout * 1000);
        if (!uh_use_chunked(cl)) {
-               ustream_vprintf(cl->us, format, arg);
+               ustream_vprintf(us, format, arg);
                return;
        }
 
@@ -69,24 +69,24 @@ void uh_chunk_vprintf(struct client *cl, const char 
*format, va_list arg)
        len = vsnprintf(buf, sizeof(buf), format, arg2);
        va_end(arg2);
 
-       ustream_printf(cl->us, "%X\r\n", len);
+       ustream_printf(us, "%X\r\n", len);
        if (len < sizeof(buf))
-               ustream_write(cl->us, buf, len, true);
+               ustream_write(us, buf, len, true);
        else
-               ustream_vprintf(cl->us, format, arg);
-       ustream_printf(cl->us, "\r\n", len);
+               ustream_vprintf(us, format, arg);
+       ustream_printf(us, "\r\n", len);
 }
 
-void uh_chunk_printf(struct client *cl, const char *format, ...)
+void uh_ustream_chunk_printf(struct client *cl, struct ustream *us, const char 
*format, ...)
 {
        va_list arg;
 
        va_start(arg, format);
-       uh_chunk_vprintf(cl, format, arg);
+       uh_ustream_chunk_vprintf(cl, us, format, arg);
        va_end(arg);
 }
 
-void uh_chunk_eof(struct client *cl)
+static void ustream_chunk_eof(struct client *cl, struct ustream *us)
 {
        if (!uh_use_chunked(cl))
                return;
@@ -94,7 +94,31 @@ void uh_chunk_eof(struct client *cl)
        if (cl->state == CLIENT_STATE_CLEANUP)
                return;
 
-       ustream_printf(cl->us, "0\r\n\r\n");
+       ustream_printf(us, "0\r\n\r\n");
+}
+
+void uh_chunk_write(struct client *cl, const void *data, int len)
+{
+       uh_ustream_chunk_write(cl, cl->us, data, len);
+}
+
+void uh_chunk_vprintf(struct client *cl, const char *format, va_list arg)
+{
+       uh_ustream_chunk_vprintf(cl, cl->us, format, arg);
+}
+
+void uh_chunk_printf(struct client *cl, const char *format, ...)
+{
+       va_list arg;
+
+       va_start(arg, format);
+       uh_ustream_chunk_printf(cl, cl->us, format, arg);
+       va_end(arg);
+}
+
+void uh_chunk_eof(struct client *cl)
+{
+       ustream_chunk_eof(cl, cl->us);
 }
 
 /* blen is the size of buf; slen is the length of src.  The input-string need
-- 
1.9.1


_______________________________________________
Lede-dev mailing list
Lede-dev@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/lede-dev

Reply via email to