commit a85d9c71f0552d99ddf63550d8d39c8986097121
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-05-27T13:44:16Z |
| subject | tcp upstream support unix socket |
commit a85d9c71f0552d99ddf63550d8d39c8986097121
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-05-27T13:44:16Z
tcp upstream support unix socket
---
file_conf.c | 39 +++++++++++++++---
klog.c | 27 +++++++++++++
klog.h | 11 +++++
route.h | 21 +++++++---
tcp_route.c | 126 +++++++++++++++++++++++++++++++++++++++------------------
tinyproxy.c | 7 ++--
tinyproxy.conf | 11 ++---
udp_route.c | 18 ++++-----
8 files changed, 190 insertions(+), 70 deletions(-)
diff --git a/file_conf.c b/file_conf.c
index 08dc0cd..3a867e9 100644
--- a/file_conf.c
+++ b/file_conf.c
@@ -193,6 +193,37 @@ static inline void route_options_set_defaults(struct route_options *opts)
opts->connect_timeout_sec = ROUTE_DEFAULT_CONNECT_TIMEOUT_SEC;
}
+static int parse_endpoint(const char *s, struct endpoint *ep)
+{
+ const char *path;
+
+ memset(ep, 0, sizeof(*ep));
+
+ if (strncmp(s, "unix:", 5) == 0) {
+ path = s + 5;
+
+ if (path[0] == '\0') {
+ return -1;
+ }
+
+ if (strlen(path) >= sizeof(ep->path)) {
+ return -1;
+ }
+
+ ep->kind = ENDPOINT_UNIX;
+ strcpy(ep->path, path);
+ return 0;
+ }
+
+ ep->kind = ENDPOINT_TCP;
+
+ if (split_host_port(s, ep->host, sizeof(ep->host), &ep->port) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
static int parse_route_line(char *line, struct route *route)
{
char *fields[4] = {0};
@@ -217,15 +248,11 @@ static int parse_route_line(char *line, struct route *route)
memset(route, 0, sizeof(*route));
route_options_set_defaults(&route->opts);
- if (split_host_port(fields[0],
- route->listen_host, sizeof(route->listen_host),
- &route->listen_port) != 0) {
+ if (parse_endpoint(fields[0], &route->listen) != 0) {
return -1;
}
- if (split_host_port(fields[1],
- route->upstream_host, sizeof(route->upstream_host),
- &route->upstream_port) != 0) {
+ if (parse_endpoint(fields[1], &route->upstream) != 0) {
return -1;
}
diff --git a/klog.c b/klog.c
index 28b6b55..e44bd2e 100644
--- a/klog.c
+++ b/klog.c
@@ -11,6 +11,8 @@
#include <time.h>
#endif
+#include "route.h"
+
static int localtime_compat(const time_t *t, struct tm *out)
{
#ifdef _WIN32
@@ -55,6 +57,28 @@ static void print_quoted_value(const char *s)
fputc('"', stderr);
}
+static void log_print_endpoint(FILE *out, const struct endpoint *ep)
+{
+ if (ep == NULL) {
+ fputs("<nil>", out);
+ return;
+ }
+
+ switch (ep->kind) {
+ case ENDPOINT_TCP:
+ fprintf(out, "%s:%u", ep->host, ep->port);
+ return;
+
+ case ENDPOINT_UNIX:
+ fprintf(out, "unix:%s", ep->path);
+ return;
+
+ default:
+ fputs("<unknown>", out);
+ return;
+ }
+}
+
static void print_log_value(struct log_value value)
{
switch (value.type) {
@@ -79,6 +103,9 @@ static void print_log_value(struct log_value value)
case LOG_VALUE_ULLONG:
fprintf(stderr, "%llu", value.v.ull);
break;
+ case LOG_VALUE_ENDPOINT:
+ log_print_endpoint(stderr, value.v.endpoint);
+ break;
default:
fputs("<invalid>", stderr);
break;
diff --git a/klog.h b/klog.h
index 92cdf93..5a74183 100644
--- a/klog.h
+++ b/klog.h
@@ -3,6 +3,8 @@
#include <stdint.h>
+struct endpoint;
+
enum log_value_type {
LOG_VALUE_STR = 1,
LOG_VALUE_INT,
@@ -11,6 +13,7 @@ enum log_value_type {
LOG_VALUE_ULONG,
LOG_VALUE_LLONG,
LOG_VALUE_ULLONG,
+ LOG_VALUE_ENDPOINT,
};
struct log_value {
@@ -23,6 +26,7 @@ struct log_value {
unsigned long ul;
long long ll;
unsigned long long ull;
+ const struct endpoint *endpoint;
} v;
};
@@ -63,6 +67,11 @@ static inline struct log_value log_value_ullong(unsigned long long v)
return (struct log_value){ LOG_VALUE_ULLONG, { .ull = v } };
}
+static inline struct log_value log_value_endpoint(const struct endpoint *v)
+{
+ return (struct log_value){ LOG_VALUE_ENDPOINT, { .endpoint = v } };
+}
+
#define _LOGV(v) \
_Generic((v), \
char *: log_value_str, \
@@ -79,6 +88,8 @@ static inline struct log_value log_value_ullong(unsigned long long v)
unsigned long long: log_value_ullong \
)(v)
+#define _LOGV_ENDPOINT(v) log_value_endpoint(v)
+
/*
* Structured fields must be passed as:
*
diff --git a/route.h b/route.h
index 500dee9..c755bb6 100644
--- a/route.h
+++ b/route.h
@@ -24,12 +24,23 @@ struct route_options {
int connect_timeout_sec;
};
-struct route {
- char listen_host[ROUTE_HOST_MAX];
- uint16_t listen_port;
+enum endpoint_kind {
+ ENDPOINT_TCP,
+ ENDPOINT_UNIX,
+};
+
+struct endpoint {
+ enum endpoint_kind kind;
+
+ char host[256];
+ uint16_t port;
- char upstream_host[ROUTE_HOST_MAX];
- uint16_t upstream_port;
+ char path[108]; /* sockaddr_un sun_path limit on Linux */
+};
+
+struct route {
+ struct endpoint listen;
+ struct endpoint upstream;
enum proto proto;
struct route_options opts;
diff --git a/tcp_route.c b/tcp_route.c
index bf89054..aea2bab 100644
--- a/tcp_route.c
+++ b/tcp_route.c
@@ -9,6 +9,10 @@
#include <stdlib.h>
#include <string.h>
+#ifndef _WIN32
+#include <sys/un.h>
+#endif
+
#include "klog.h"
#include "file_conf.h"
#include "compat_socket.h"
@@ -164,6 +168,70 @@ static void event_cb(struct bufferevent *bev, short events, void *arg) {
(void)bev;
}
+static int connect_upstream(struct bufferevent *bev, const struct endpoint *ep)
+{
+ if (ep == NULL) {
+ return -EINVAL;
+ }
+
+ switch (ep->kind) {
+ case ENDPOINT_TCP: {
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(ep->port);
+
+ if (inet_pton(AF_INET, ep->host, &addr.sin_addr) != 1) {
+ return -EINVAL;
+ }
+
+ if (bufferevent_socket_connect(
+ bev,
+ (struct sockaddr *)&addr,
+ sizeof(addr)) < 0) {
+ return -errno;
+ }
+
+ return 0;
+ }
+
+ case ENDPOINT_UNIX:
+#ifdef _WIN32
+ return -ENOTSUP;
+#else
+ {
+ struct sockaddr_un addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ if (ep->path[0] == '\0') {
+ return -EINVAL;
+ }
+
+ if (strlen(ep->path) >= sizeof(addr.sun_path)) {
+ return -ENAMETOOLONG;
+ }
+
+ strcpy(addr.sun_path, ep->path);
+
+ if (bufferevent_socket_connect(
+ bev,
+ (struct sockaddr *)&addr,
+ sizeof(addr)) < 0) {
+ return -errno;
+ }
+
+ return 0;
+ }
+#endif
+
+ default:
+ return -ENOTSUP;
+ }
+}
+
static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac) {
conn_t *conn = calloc(1, sizeof(*conn));
if (conn == NULL) {
@@ -197,32 +265,8 @@ static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac)
bufferevent_setwatermark(conn->client, EV_READ, 0, BEV_READ_HIGH_WATER);
bufferevent_setwatermark(conn->upstream, EV_READ, 0, BEV_READ_HIGH_WATER);
- bufferevent_setcb(
- conn->client,
- pipe_read_cb,
- pipe_write_cb,
- event_cb,
- conn
- );
-
- bufferevent_setcb(
- conn->upstream,
- pipe_read_cb,
- pipe_write_cb,
- event_cb,
- conn
- );
-
- struct sockaddr_in upstream_addr;
- memset(&upstream_addr, 0, sizeof(upstream_addr));
- upstream_addr.sin_family = AF_INET;
- upstream_addr.sin_port = htons(r->upstream_port);
-
- if (inet_pton(AF_INET, r->upstream_host, &upstream_addr.sin_addr) != 1) {
- LOG_ERROR("invalid upstream address");
- free_conn(conn);
- return;
- }
+ bufferevent_setcb(conn->client, pipe_read_cb, pipe_write_cb, event_cb, conn);
+ bufferevent_setcb(conn->upstream, pipe_read_cb, pipe_write_cb, event_cb, conn);
/*
* Do not read from the client yet.
@@ -233,23 +277,25 @@ static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac)
bufferevent_disable(conn->client, EV_READ);
set_connect_timeout(conn, r);
- if (bufferevent_socket_connect(
- conn->upstream,
- (struct sockaddr *)&upstream_addr,
- sizeof(upstream_addr)
- ) < 0) {
- LOG_ERROR("upstream connect failed");
+
+ rc = connect_upstream(conn->upstream, &r->upstream);
+ if (rc < 0) {
+ LOG_ERROR("upstream connect failed",
+ "upstream", _LOGV_ENDPOINT(&r->upstream),
+ "err", _LOGV(strerror(-rc))
+ );
free_conn(conn);
return;
}
- if (r->opts.keep_alive) {
+ if (r->opts.keep_alive && r->upstream.kind == ENDPOINT_TCP) {
evutil_socket_t upstream_fd = bufferevent_getfd(conn->upstream);
if (upstream_fd >= 0) {
int rc = set_socket_keepalive(upstream_fd, r);
if (rc < 0) {
LOG_WARN("failed to enable upstream TCP keepalive",
+ "upstream", _LOGV_ENDPOINT(&r->upstream),
"err", _LOGV(strerror(-rc))
);
}
@@ -351,10 +397,12 @@ int start_tcp_route(
struct sockaddr_in listen_addr;
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
- listen_addr.sin_port = htons(r->listen_port);
+ listen_addr.sin_port = htons(r->listen.port);
- if (inet_pton(AF_INET, r->listen_host, &listen_addr.sin_addr) != 1) {
- LOG_ERROR("invalid listen address", "listen_host", _LOGV(r->listen_host));
+ if (inet_pton(AF_INET, r->listen.host, &listen_addr.sin_addr) != 1) {
+ LOG_ERROR("invalid listen address",
+ "listen", _LOGV_ENDPOINT(&r->listen)
+ );
return -EINVAL;
}
@@ -391,10 +439,8 @@ int start_tcp_route(
LOG_INFO("route started",
"line", _LOGV(r->line_no),
- "listen_host", _LOGV(r->listen_host),
- "listen_port", _LOGV(r->listen_port),
- "upstream_host", _LOGV(r->upstream_host),
- "upstream_port", _LOGV(r->upstream_port),
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream),
"options", _LOGV(opts[0] ? opts : "")
);
diff --git a/tinyproxy.c b/tinyproxy.c
index dfc8381..1d2db66 100644
--- a/tinyproxy.c
+++ b/tinyproxy.c
@@ -156,10 +156,9 @@ int main(int argc, char **argv)
if (rc != 0) {
LOG_ERROR("failed to start route",
"line", _LOGV(r->line_no),
- "listen_host", _LOGV(r->listen_host),
- "listen_port", _LOGV(r->listen_port),
- "upstream_host", _LOGV(r->upstream_host),
- "upstream_port", _LOGV(r->upstream_port));
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
goto out;
}
}
diff --git a/tinyproxy.conf b/tinyproxy.conf
index f7a3620..ff974fe 100644
--- a/tinyproxy.conf
+++ b/tinyproxy.conf
@@ -1,5 +1,6 @@
-# listen upstream proto option
-:80 10.0.1.1:80 tcp
-:443 10.0.1.1:443 tcp proxy_v2,keep_alive
-:12345 10.0.1.1:12345 udp idle_timeout=10,connect_timeout=17
-:19132 10.0.1.1:19132 udp proxy_v2
+# listen upstream proto option
+:80 10.0.1.1:80 tcp
+:443 10.0.1.1:443 tcp proxy_v2,keep_alive
+:12345 10.0.1.1:12345 udp idle_timeout=10,connect_timeout=17
+:19132 10.0.1.1:19132 udp proxy_v2
+:12990 unix:/tmp/test.sock tcp
diff --git a/udp_route.c b/udp_route.c
index c3e6af6..f6d777b 100644
--- a/udp_route.c
+++ b/udp_route.c
@@ -206,11 +206,11 @@ static struct udp_client *create_udp_client(
memset(&upstream_addr, 0, sizeof(upstream_addr));
upstream_addr.sin_family = AF_INET;
- upstream_addr.sin_port = htons(r->upstream_port);
+ upstream_addr.sin_port = htons(r->upstream.port);
- if (inet_pton(AF_INET, r->upstream_host, &upstream_addr.sin_addr) != 1) {
+ if (inet_pton(AF_INET, r->upstream.host, &upstream_addr.sin_addr) != 1) {
LOG_ERROR("invalid udp upstream address",
- "upstream_host", _LOGV(r->upstream_host)
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
);
free_udp_client(c);
return NULL;
@@ -385,11 +385,11 @@ int start_udp_route(
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
- listen_addr.sin_port = htons(r->listen_port);
+ listen_addr.sin_port = htons(r->listen.port);
- if (inet_pton(AF_INET, r->listen_host, &listen_addr.sin_addr) != 1) {
+ if (inet_pton(AF_INET, r->listen.host, &listen_addr.sin_addr) != 1) {
LOG_ERROR("invalid udp listen address",
- "listen_host", _LOGV(r->listen_host)
+ "listen", _LOGV_ENDPOINT(&r->listen)
);
return -EINVAL;
}
@@ -494,10 +494,8 @@ int start_udp_route(
LOG_INFO("udp route started",
"line", _LOGV(r->line_no),
- "listen_host", _LOGV(r->listen_host),
- "listen_port", _LOGV(r->listen_port),
- "upstream_host", _LOGV(r->upstream_host),
- "upstream_port", _LOGV(r->upstream_port),
+ "listen", _LOGV_ENDPOINT(&r->listen),
+ "upstream", _LOGV_ENDPOINT(&r->upstream),
"options", _LOGV(opts[0] ? opts : "")
);