aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2025-06-25 19:49:01 +0000
committerLaurent Bercot <ska@appnovation.com>2025-06-25 19:49:01 +0000
commit9acee22dd82472bf408204a066476334c3568e87 (patch)
tree2ba82c4cba39e633dcbf422f67cc3fe27f87a8df
parent592c13a20f5ece4a596635c75bf096ccd3dfa6b8 (diff)
downloadtipidee-9acee22dd82472bf408204a066476334c3568e87.tar.gz
Add rproxy
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--doc/tipidee.conf.html37
-rw-r--r--package/deps.mak3
-rw-r--r--src/config/lexparse.c65
-rw-r--r--src/include/tipidee/conf.h6
-rw-r--r--src/libtipidee/tipidee_conf_get_redirection.c5
-rw-r--r--src/tipideed/deps-exe/tipideed1
-rw-r--r--src/tipideed/redirection.c7
-rw-r--r--src/tipideed/rproxy.c97
-rw-r--r--src/tipideed/tipideed-internal.h5
-rw-r--r--src/tipideed/tipideed.c5
10 files changed, 218 insertions, 13 deletions
diff --git a/doc/tipidee.conf.html b/doc/tipidee.conf.html
index a9b5063..f7753da 100644
--- a/doc/tipidee.conf.html
+++ b/doc/tipidee.conf.html
@@ -847,6 +847,41 @@ requests received on port 443). But if you declare a redirection under the
<tt>example.com</tt> domain, it will apply to requests received on <em>any</em> port. </li>
</ul>
+<div id="rproxy">
+<h4> <tt>rproxy</tt> </h4>
+</div>
+
+<p>
+ <code> rproxy <em>resource</em> unix <em>socketpath</em> </code> <br>
+ <code> rproxy <em>resource</em> tcp <em>ip</em> <em>port</em> </code>
+</p>
+
+<ul>
+ <li> The <em>rproxy</em> directive tells tipidee that when a client hits a
+given URL, the HTTP exchange should be passed as is to another server. For this
+exchange, tipideed will act as a proxy, transmitting client data to the real
+server and vice-versa. It is handled by the same mechanism as redirection, but
+unlike a redirection, it will be transparent to the client. </li>
+ <li> <em>resource</em> is the URI to redirect, relative to the current domain.
+For instance, if the current domain is <tt>example.com</tt> and <em>resource</em>
+is <tt>foobar.html</tt>, then a request for <tt>http://example.com/foobar.html</tt>
+will be reverse-proxied to <em>socketpath</em> or <em>ip</em>:<em>port</em>. </li>
+ <li> The second argument must be <code>unix</code> or <code>tcp</code>.
+ <ul>
+ <li> <code>unix</code> means that the real server is listening on a Unix domain
+socket reachable in the filesystem at <em>socketpath</em>. Note that
+<em>socketpath</em> will be understood relative to tipideed's current filesystem
+root, if it runs chrooted; this is generally not what you want, and <code>unix</code>
+should generally only be used when tipideed is <em>not</em> chrooted. </li>
+ <li> <code>tcp</code> means that the real server is listening on an Internet
+domain socket at address <em>ip</em>, port <em>port</em>. <em>ip</em> can be IPv4
+or IPv6. </li>
+ </ul> </li>
+ <li> Just like with the <code>redirection</code> directive,
+<em>resource</em> does not need to exist in the filesystem. The same caveats apply. </li>
+</ul>
+
+
<div id="noredirect">
<h4> <tt>noredirect</tt> </h4>
</div>
@@ -860,6 +895,8 @@ requests received on port 443). But if you declare a redirection under the
useful to carve exceptions to a generic redirection policy: if you have a <code>redirect</code>
directive for directory A and a <code>noredirect</code> directive for resource B, and A is
a prefix of B, then B will not be redirected, but everything else under A will. </li>
+ <li> You can also use <code>noredirect</code> to prevent <em>resource</em> from being
+proxied by a more generic <code>rproxy</code> directive.
</ul>
<div id="custom-response">
diff --git a/package/deps.mak b/package/deps.mak
index 2733f38..5c91a0c 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -71,6 +71,7 @@ src/tipideed/harden.o src/tipideed/harden.lo: src/tipideed/harden.c src/tipideed
src/tipideed/options.o src/tipideed/options.lo: src/tipideed/options.c src/include/tipidee/log.h src/include/tipidee/response.h src/tipideed/tipideed-internal.h
src/tipideed/redirection.o src/tipideed/redirection.lo: src/tipideed/redirection.c src/include/tipidee/log.h src/include/tipidee/response.h src/include/tipidee/util.h src/tipideed/tipideed-internal.h
src/tipideed/regular.o src/tipideed/regular.lo: src/tipideed/regular.c src/include/tipidee/log.h src/include/tipidee/method.h src/include/tipidee/response.h src/tipideed/tipideed-internal.h
+src/tipideed/rproxy.o src/tipideed/rproxy.lo: src/tipideed/rproxy.c src/include/tipidee/tipidee.h src/tipideed/tipideed-internal.h
src/tipideed/send_file.o src/tipideed/send_file.lo: src/tipideed/send_file.c src/tipideed/tipideed-internal.h
src/tipideed/stream.o src/tipideed/stream.lo: src/tipideed/stream.c src/tipideed/tipideed-internal.h
src/tipideed/tipideed.o src/tipideed/tipideed.lo: src/tipideed/tipideed.c src/include/tipidee/tipidee.h src/tipideed/tipideed-internal.h
@@ -94,5 +95,5 @@ libtipidee.dylib.xyzzy:src/libtipidee/tipidee_conf_free.lo src/libtipidee/tipide
ls.cgi: EXTRA_LIBS :=
ls.cgi: src/misc/ls.cgi.o libtipidee.a.xyzzy -lskarnet
tipideed: EXTRA_LIBS :=
-tipideed: src/tipideed/tipideed.o src/tipideed/cgi.o src/tipideed/errors.o src/tipideed/harden.o src/tipideed/options.o src/tipideed/regular.o src/tipideed/redirection.o src/tipideed/send_file.o src/tipideed/stream.o src/tipideed/trace.o src/tipideed/util.o libtipidee.a.xyzzy -lskarnet
+tipideed: src/tipideed/tipideed.o src/tipideed/cgi.o src/tipideed/errors.o src/tipideed/harden.o src/tipideed/options.o src/tipideed/regular.o src/tipideed/redirection.o src/tipideed/rproxy.o src/tipideed/send_file.o src/tipideed/stream.o src/tipideed/trace.o src/tipideed/util.o libtipidee.a.xyzzy -lskarnet
INTERNAL_LIBS :=
diff --git a/src/config/lexparse.c b/src/config/lexparse.c
index ad22762..e87c501 100644
--- a/src/config/lexparse.c
+++ b/src/config/lexparse.c
@@ -6,6 +6,7 @@
#include <errno.h>
#include <skalibs/uint32.h>
+#include <skalibs/fmtscan.h>
#include <skalibs/buffer.h>
#include <skalibs/strerr.h>
#include <skalibs/stralloc.h>
@@ -45,6 +46,7 @@ enum directivevalue_e
T_NPHPREFIX,
T_REDIRECT,
T_NOREDIRECT,
+ T_RPROXY,
T_CGI,
T_NONCGI,
T_NPH,
@@ -262,7 +264,7 @@ static inline void parse_noredirect (char const *s, size_t const *word, size_t n
memcpy(key + 2, domain, domainlen) ;
memcpy(key + 2 + domainlen, s + *word, urlen) ;
key[2 + domainlen + urlen] = 0 ;
- add_unique(key, " ", 2, md) ;
+ add_unique(key, "\200", 2, md) ;
}
}
@@ -304,6 +306,63 @@ static inline void parse_redirect (char const *s, size_t const *word, size_t n,
}
}
+static inline void parse_rproxy (char const *s, size_t const *word, size_t n, char const *domain, size_t domainlen, mdt const *md)
+{
+ uint8_t type ;
+ if (n < 3 || n > 4)
+ strerr_dief8x(1, "too ", n > 4 ? "many" : "few", " arguments to directive ", "rproxy", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
+ if (!domain)
+ strerr_dief6x(1, "rproxy redirection", " without a domain directive", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
+ if (s[word[0]] != '/')
+ strerr_dief6x(1, "redirected resource", " must start with /", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
+ if (!strcmp(s + word[1], "unix"))
+ type = 48 ;
+ else if (!strcmp(s + word[1], "tcp"))
+ type = 32 ;
+ else
+ strerr_dief7x(1, "rproxy", " directive", " needs to specify unix or tcp", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
+
+ {
+ node node ;
+ size_t urlen = strlen(s + word[0]) ;
+ char key[3 + domainlen + urlen] ;
+ if (s[word[0] + urlen - 1] == '/') { key[0] = 'r' ; urlen-- ; } else key[0] = 'R' ;
+ key[1] = ':' ;
+ memcpy(key + 2, domain, domainlen) ;
+ memcpy(key + 2 + domainlen, s + word[0], urlen) ;
+ key[2 + domainlen + urlen] = 0 ;
+ conftree_checkunique(key, md) ;
+ confnode_start(&node, key, md->filepos, md->line) ;
+
+ if (type == 48)
+ {
+ if (n != 3)
+ strerr_dief8x(1, "too ", "many" , " arguments to directive ", "rproxy", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
+ key[0] = '@' | type ;
+ confnode_add(&node, &key[0], 1) ;
+ confnode_add(&node, s + word[2], strlen(s + word[2]) + 1) ;
+ }
+ else
+ {
+ char ip[16] ;
+ uint16_t port ;
+ if (n != 4)
+ strerr_dief8x(1, "too ", "few" , " arguments to directive ", "rproxy", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
+ if (ip6_scan(s + word[2], ip)) type |= 8 ;
+ else if (!ip4_scan(s + word[2], ip))
+ strerr_dief8x(1, "invalid ip address ", "in directive ", "rproxy", " tcp", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
+ if (!uint160_scan(s + word[3], &port))
+ strerr_dief8x(1, "invalid port ", "in directive ", "rproxy", " tcp", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
+ key[0] = '@' | type ;
+ uint16_pack_big(key+1, port) ;
+ confnode_add(&node, key, 3) ;
+ confnode_add(&node, ip, type & 8 ? 16 : 4) ;
+ }
+ confnode_add(&node, "", 1) ;
+ conftree_add(&node) ;
+ }
+}
+
static void parse_bitattr (char const *s, size_t const *word, size_t n, char const *domain, size_t domainlen, mdt const *md, uint8_t bit, int h)
{
static char const *const attr[3][2] = { { "noncgi", "cgi" }, { "nonnph", "nph", }, { "noauth", "basic-auth" } } ;
@@ -450,6 +509,7 @@ static inline void process_line (char const *s, size_t const *word, size_t n, st
{ .name = "nph", .value = T_NPH },
{ .name = "nph-prefix", .value = T_NPHPREFIX },
{ .name = "redirect", .value = T_REDIRECT },
+ { .name = "rproxy", .value = T_RPROXY },
} ;
struct namevalue_s const *directive ;
char const *word0 ;
@@ -514,6 +574,9 @@ static inline void process_line (char const *s, size_t const *word, size_t n, st
case T_REDIRECT :
parse_redirect(s, word, n, domain->s, domain->len, md) ;
break ;
+ case T_RPROXY :
+ parse_rproxy(s, word, n, domain->s, domain->len, md) ;
+ break ;
case T_NOREDIRECT :
parse_noredirect(s, word, n, domain->s, domain->len, md) ;
break ;
diff --git a/src/include/tipidee/conf.h b/src/include/tipidee/conf.h
index aa8cb45..a173d6a 100644
--- a/src/include/tipidee/conf.h
+++ b/src/include/tipidee/conf.h
@@ -25,11 +25,11 @@ struct tipidee_conf_s
typedef struct tipidee_redirection_s tipidee_redirection, *tipidee_redirection_ref ;
struct tipidee_redirection_s
{
- char const *location ;
char const *sub ;
- uint32_t type : 2 ;
+ char const *location ;
+ uint8_t type ;
} ;
-#define TIPIDEE_REDIRECTION_ZERO { .location = 0, .sub = 0, .type = 0 }
+#define TIPIDEE_REDIRECTION_ZERO { .sub = 0, .location = 0, .type = 0 }
extern void tipidee_conf_free (tipidee_conf *) ;
extern int tipidee_conf_init (tipidee_conf *, char const *) ;
diff --git a/src/libtipidee/tipidee_conf_get_redirection.c b/src/libtipidee/tipidee_conf_get_redirection.c
index 9554227..a8397ec 100644
--- a/src/libtipidee/tipidee_conf_get_redirection.c
+++ b/src/libtipidee/tipidee_conf_get_redirection.c
@@ -22,9 +22,8 @@ static int get_redir (tipidee_conf const *conf, size_t minl, char *key, size_t l
v = tipidee_conf_get_string(conf, key) ;
key[0] = 'r' ;
}
- if (!v || v[0] == ' ') return 0 ;
- if (v[0] < '@' || v[0] > 'C') return (errno = EPROTO, -1) ;
- r->type = v[0] & ~'@' ;
+ if (!v || ((unsigned char)v[0] & 128)) return 0 ;
+ r->type = v[0] ;
r->location = v+1 ;
r->sub = path + (l - minl + 1) ;
return 1 ;
diff --git a/src/tipideed/deps-exe/tipideed b/src/tipideed/deps-exe/tipideed
index a47d715..67d619a 100644
--- a/src/tipideed/deps-exe/tipideed
+++ b/src/tipideed/deps-exe/tipideed
@@ -4,6 +4,7 @@ harden.o
options.o
regular.o
redirection.o
+rproxy.o
send_file.o
stream.o
trace.o
diff --git a/src/tipideed/redirection.c b/src/tipideed/redirection.c
index e525e66..ce32e44 100644
--- a/src/tipideed/redirection.c
+++ b/src/tipideed/redirection.c
@@ -15,14 +15,15 @@ void respond_30x (tipidee_rql const *rql, tipidee_redirection const *rd)
static unsigned int const status[4] = { 307, 308, 302, 301 } ;
tipidee_defaulttext dt ;
tain deadline ;
- tipidee_util_defaulttext(status[rd->type], &dt) ;
- tipidee_response_status(buffer_1, rql, status[rd->type], dt.reason) ;
+ unsigned int type = rd->type & 3 ;
+ tipidee_util_defaulttext(status[type], &dt) ;
+ tipidee_response_status(buffer_1, rql, status[type], dt.reason) ;
tipidee_response_header_writeall_G(buffer_1, g.rhdr, g.rhdrn, 0) ;
buffer_putsnoflush(buffer_1, "Content-Length: 0\r\nLocation: ") ;
buffer_putsnoflush(buffer_1, rd->location) ;
if (rd->sub) buffer_putsnoflush(buffer_1, rd->sub) ;
buffer_putnoflush(buffer_1, "\r\n\r\n", 4) ;
- tipidee_log_answer(g.logv, rql, status[rd->type], 0) ;
+ tipidee_log_answer(g.logv, rql, status[type], 0) ;
tain_add_g(&deadline, &g.writetto) ;
if (!buffer_timed_flush_g(buffer_1, &deadline))
strerr_diefu1sys(111, "write to stdout") ;
diff --git a/src/tipideed/rproxy.c b/src/tipideed/rproxy.c
new file mode 100644
index 0000000..5b0ec29
--- /dev/null
+++ b/src/tipideed/rproxy.c
@@ -0,0 +1,97 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <strings.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/tai.h>
+#include <skalibs/buffer.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/socket.h>
+#include <skalibs/ip46.h>
+#include <skalibs/unix-timed.h>
+
+#include <tipidee/tipidee.h>
+#include "tipideed-internal.h"
+
+#define putit(b, s, deadline) if (buffer_timed_puts_g(b, s, deadline) == -1) die500sys(rql, 111, docroot, "write to ", fn)
+
+static void do_rproxy (tipidee_rql const *rql, int fd, char const *sub, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen, tain const *deadline, char const *fn)
+{
+ char buf[4096] ;
+ buffer b = BUFFER_INIT(&buffer_write, fd, buf, 4096) ;
+ putit(&b, tipidee_method_tostr(rql->m), deadline) ;
+ putit(&b, " ", deadline) ;
+ putit(&b, rql->uri.path, deadline) ;
+ if (rql->uri.query)
+ {
+ putit(&b, "?", deadline) ;
+ putit(&b, rql->uri.query, deadline) ;
+ }
+ putit(&b, " HTTP/1.", deadline) ;
+ putit(&b, rql->http_minor == 1 ? "1" : "0", deadline) ;
+ putit(&b, "\r\nConnection: close\r\n", deadline) ;
+ for (uint32_t i = 0 ; i < hdr->n ; i++)
+ {
+ if (!strcasecmp(hdr->buf + hdr->list[i].left, "Connection")) continue ;
+ putit(&b, hdr->buf + hdr->list[i].left, deadline) ;
+ putit(&b, ": ", deadline) ;
+ putit(&b, hdr->buf + hdr->list[i].right, deadline) ;
+ putit(&b, "\r\n", deadline) ;
+ }
+ putit(&b, "\r\n", deadline) ;
+ if (!buffer_timed_flush_g(&b, deadline)) die500sys(rql, 111, docroot, "write to ", fn) ;
+ if (bodylen)
+ {
+ if (timed_write_g(fd, body, bodylen, deadline) < bodylen) die500sys(rql, 111, docroot, "write to ", fn) ;
+ }
+ fd_shutdown(fd, 1) ;
+ if (ndelay_off(fd) == -1) die500sys(rql, 111, docroot, "set socket parameters for ", fn) ;
+ fd_cat(fd, 1) ;
+ _exit(0) ; /* expensive but the only way to avoid implementing a full proxy */
+}
+
+static void rproxy_unix (tipidee_rql const *rql, char const *socketpath, char const *sub, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen)
+{
+ tain deadline ;
+ int fd = ipc_stream_nbcoe() ;
+ if (fd == -1) die500sys(rql, 111, docroot, "create socket") ;
+ tain_add_g(&deadline, &g.readtto) ;
+ if (!ipc_timed_connect_g(fd, socketpath, &deadline)) die500sys(rql, 111, docroot, "connect to ", socketpath) ;
+ do_rproxy(rql, fd, sub, docroot, hdr, body, bodylen, &deadline, socketpath) ;
+ fd_close(fd) ;
+}
+
+static void rproxy_tcp (tipidee_rql const *rql, char const *ip, uint16_t port, int is6, char const *sub, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen)
+{
+ tain deadline ;
+ size_t m = 0 ;
+ int fd = socket_tcp46_nbcoe(is6) ;
+ char fmt[IP6_FMT + UINT16_FMT + 2] ;
+ if (fd == -1) die500sys(rql, 111, docroot, "create socket") ;
+ tain_add_g(&deadline, &g.readtto) ;
+ if (is6) fmt[m++] = '[' ;
+ m += is6 ? ip6_fmt(fmt + m, ip) : ip4_fmt(fmt + m, ip) ;
+ if (is6) fmt[m++] = ']' ;
+ fmt[m++] = ':' ;
+ m += uint16_fmt(fmt + m, port) ;
+ fmt[m++] = 0 ;
+
+ if (!(is6 ? socket_deadlineconnstamp6_g(fd, ip, port, &deadline) : socket_deadlineconnstamp4_g(fd, ip, port, &deadline)))
+ die500sys(rql, 111, docroot, "connect to ", fmt) ;
+ do_rproxy(rql, fd, sub, docroot, hdr, body, bodylen, &deadline, fmt) ;
+ fd_close(fd) ;
+}
+
+void rproxy (tipidee_rql const *rql, tipidee_redirection const *rd, char const *docroot, tipidee_headers const *hdr, char const *body, size_t bodylen)
+{
+ if (rd->type & 16) rproxy_unix(rql, rd->location, rd->sub, docroot, hdr, body, bodylen) ;
+ else
+ {
+ uint16_t port ;
+ uint16_unpack_big(rd->location, &port) ;
+ rproxy_tcp(rql, rd->location + 2, port, !!(rd->type & 8), rd->sub, docroot, hdr, body, bodylen) ;
+ }
+}
diff --git a/src/tipideed/tipideed-internal.h b/src/tipideed/tipideed-internal.h
index e8cf102..d3a66f6 100644
--- a/src/tipideed/tipideed-internal.h
+++ b/src/tipideed/tipideed-internal.h
@@ -124,6 +124,11 @@ extern void respond_416 (tipidee_rql const *, char const *, uint64_t) ;
extern void respond_30x (tipidee_rql const *, tipidee_redirection const *) ;
+ /* rproxy */
+
+extern void rproxy (tipidee_rql const *, tipidee_redirection const *, char const *, tipidee_headers const *, char const *, size_t) ;
+
+
/* trace */
extern int respond_trace (tipidee_rql const *, tipidee_headers const *) ;
diff --git a/src/tipideed/tipideed.c b/src/tipideed/tipideed.c
index c32cd2c..6cfd4ec 100644
--- a/src/tipideed/tipideed.c
+++ b/src/tipideed/tipideed.c
@@ -233,7 +233,7 @@ static inline int serve (tipidee_rql *rql, char const *docroot, char *uribuf, ti
memcpy(fn + docrootlen, rql->uri.path, pathlen) ;
fn[docrootlen + pathlen] = 0 ;
- /* Redirection */
+ /* Redirection or reverse proxy */
if (rql->m != TIPIDEE_METHOD_OPTIONS)
{
@@ -242,7 +242,8 @@ static inline int serve (tipidee_rql *rql, char const *docroot, char *uribuf, ti
if (e == -1) die500sys(rql, 111, docroot, "get redirection data for ", fn) ;
if (e)
{
- respond_30x(rql, &rd) ;
+ if (rd.type & 32) rproxy(rql, &rd, docroot, hdr, body, bodylen) ;
+ else respond_30x(rql, &rd) ;
return 0 ;
}
}