commit f2f1d2f7907b5fe0c49c283a0907d74cb2b328cb
| author | 斟酌 鵬兄 <tgckpg@gmail.com> |
| date | 2026-05-27T14:38:40Z |
| subject | udp upstream support unix-dgram socket |
commit f2f1d2f7907b5fe0c49c283a0907d74cb2b328cb
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date: 2026-05-27T14:38:40Z
udp upstream support unix-dgram socket
---
Makefile | 1 +
compat_socket.h | 1 +
env.c | 16 +++++
env.h | 6 ++
file_conf.c | 16 ++++-
klog.c | 6 +-
route.h | 3 +-
tcp_route.c | 8 +--
tinyproxy.conf | 13 ++--
udp_route.c | 186 +++++++++++++++++++++++++++++++++++++++++++++-----------
10 files changed, 206 insertions(+), 50 deletions(-)
diff --git a/Makefile b/Makefile
index fe9ef1f..d037068 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@ SRC := klog.c \
route.c \
tcp_route.c \
udp_route.c \
+ env.c \
file_conf.c \
tinyproxy.c
diff --git a/compat_socket.h b/compat_socket.h
index b981691..4c39529 100644
--- a/compat_socket.h
+++ b/compat_socket.h
@@ -14,6 +14,7 @@
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
diff --git a/env.c b/env.c
new file mode 100644
index 0000000..08b9381
--- /dev/null
+++ b/env.c
@@ -0,0 +1,16 @@
+#include "env.h"
+
+#include <stdlib.h>
+
+#define TINYPROXY_DEFAULT_RUNTIME_DIR "/tmp/tinyproxy"
+
+const char *tinyproxy_runtime_dir(void)
+{
+ const char *v = getenv("TINYPROXY_RUNTIME_DIR");
+
+ if (v != NULL && v[0] != '\0') {
+ return v;
+ }
+
+ return TINYPROXY_DEFAULT_RUNTIME_DIR;
+}
diff --git a/env.h b/env.h
new file mode 100644
index 0000000..b6c2632
--- /dev/null
+++ b/env.h
@@ -0,0 +1,6 @@
+#ifndef ENV_H
+#define ENV_H
+
+const char *tinyproxy_runtime_dir(void);
+
+#endif
diff --git a/file_conf.c b/file_conf.c
index 3a867e9..62a5e20 100644
--- a/file_conf.c
+++ b/file_conf.c
@@ -213,9 +213,23 @@ static int parse_endpoint(const char *s, struct endpoint *ep)
ep->kind = ENDPOINT_UNIX;
strcpy(ep->path, path);
return 0;
+ } else if (strncmp(s, "unix-dgram:", 11) == 0) {
+ path = s + 11;
+
+ if (path[0] == '\0') {
+ return -1;
+ }
+
+ if (strlen(path) >= sizeof(ep->path)) {
+ return -1;
+ }
+
+ ep->kind = ENDPOINT_UNIX_DGRAM;
+ strcpy(ep->path, path);
+ return 0;
}
- ep->kind = ENDPOINT_TCP;
+ ep->kind = ENDPOINT_INET;
if (split_host_port(s, ep->host, sizeof(ep->host), &ep->port) != 0) {
return -1;
diff --git a/klog.c b/klog.c
index e44bd2e..d0f1c18 100644
--- a/klog.c
+++ b/klog.c
@@ -65,7 +65,7 @@ static void log_print_endpoint(FILE *out, const struct endpoint *ep)
}
switch (ep->kind) {
- case ENDPOINT_TCP:
+ case ENDPOINT_INET:
fprintf(out, "%s:%u", ep->host, ep->port);
return;
@@ -73,6 +73,10 @@ static void log_print_endpoint(FILE *out, const struct endpoint *ep)
fprintf(out, "unix:%s", ep->path);
return;
+ case ENDPOINT_UNIX_DGRAM:
+ fprintf(out, "unix-dgram:%s", ep->path);
+ return;
+
default:
fputs("<unknown>", out);
return;
diff --git a/route.h b/route.h
index c755bb6..586f0b2 100644
--- a/route.h
+++ b/route.h
@@ -25,8 +25,9 @@ struct route_options {
};
enum endpoint_kind {
- ENDPOINT_TCP,
+ ENDPOINT_INET,
ENDPOINT_UNIX,
+ ENDPOINT_UNIX_DGRAM,
};
struct endpoint {
diff --git a/tcp_route.c b/tcp_route.c
index aea2bab..5394c46 100644
--- a/tcp_route.c
+++ b/tcp_route.c
@@ -9,10 +9,6 @@
#include <stdlib.h>
#include <string.h>
-#ifndef _WIN32
-#include <sys/un.h>
-#endif
-
#include "klog.h"
#include "file_conf.h"
#include "compat_socket.h"
@@ -175,7 +171,7 @@ static int connect_upstream(struct bufferevent *bev, const struct endpoint *ep)
}
switch (ep->kind) {
- case ENDPOINT_TCP: {
+ case ENDPOINT_INET: {
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
@@ -288,7 +284,7 @@ static void worker_adopt_client_fd(struct worker *w, struct accepted_client *ac)
return;
}
- if (r->opts.keep_alive && r->upstream.kind == ENDPOINT_TCP) {
+ if (r->opts.keep_alive && r->upstream.kind == ENDPOINT_INET) {
evutil_socket_t upstream_fd = bufferevent_getfd(conn->upstream);
if (upstream_fd >= 0) {
diff --git a/tinyproxy.conf b/tinyproxy.conf
index ff974fe..67fa8f9 100644
--- a/tinyproxy.conf
+++ b/tinyproxy.conf
@@ -1,6 +1,7 @@
-# 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
+# listen upstream proto option
+:80 10.0.1.1:80 tcp
+:443 10.0.1.1:443 tcp proxy_v2,keep_alive
+:12990 unix:/tmp/test.sock tcp
+:12345 10.0.1.1:12345 udp idle_timeout=10,connect_timeout=17
+:19132 10.0.1.1:19132 udp proxy_v2
+:12998 unix-dgram:/tmp/testd.sock udp
diff --git a/udp_route.c b/udp_route.c
index f6d777b..5bc9690 100644
--- a/udp_route.c
+++ b/udp_route.c
@@ -6,8 +6,10 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <sys/stat.h>
#include "klog.h"
+#include "env.h"
#include "file_conf.h"
#include "compat_socket.h"
#include "proxy_proto_v2.h"
@@ -24,6 +26,8 @@ struct udp_client {
evutil_socket_t fd;
struct event *ev;
+ char unix_local_path[108];
+
struct sockaddr_storage client_addr;
socklen_t client_addr_len;
@@ -77,6 +81,10 @@ static void free_udp_client(struct udp_client *c)
evutil_closesocket(c->fd);
}
+ if (c->unix_local_path[0] != '\0') {
+ unlink(c->unix_local_path);
+ }
+
free(c);
}
@@ -167,6 +175,116 @@ static void upstream_read_cb(evutil_socket_t fd, short events, void *arg)
}
}
+static int connect_udp_upstream(struct udp_client *c, const struct endpoint *upstream)
+{
+ if (upstream == NULL) {
+ return -EINVAL;
+ }
+
+ switch (upstream->kind) {
+ case ENDPOINT_INET: {
+ struct sockaddr_in upstream_addr;
+
+ memset(&upstream_addr, 0, sizeof(upstream_addr));
+ upstream_addr.sin_family = AF_INET;
+ upstream_addr.sin_port = htons(upstream->port);
+
+ if (inet_pton(AF_INET, upstream->host, &upstream_addr.sin_addr) != 1) {
+ return -EINVAL;
+ }
+
+ c->fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (c->fd < 0) {
+ return -EVUTIL_SOCKET_ERROR();
+ }
+
+ if (connect(
+ c->fd,
+ (const struct sockaddr *)&upstream_addr,
+ sizeof(upstream_addr)
+ ) < 0) {
+ return -EVUTIL_SOCKET_ERROR();
+ }
+
+ return 0;
+ }
+
+ case ENDPOINT_UNIX_DGRAM:
+#ifdef _WIN32
+ return -ENOTSUP;
+#else
+ {
+ struct sockaddr_un local_addr;
+ struct sockaddr_un upstream_addr;
+
+ if (upstream->path[0] == '\0') {
+ return -EINVAL;
+ }
+
+ if (strlen(upstream->path) >= sizeof(upstream_addr.sun_path)) {
+ return -ENAMETOOLONG;
+ }
+
+ c->fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (c->fd < 0) {
+ return -EVUTIL_SOCKET_ERROR();
+ }
+
+ memset(&local_addr, 0, sizeof(local_addr));
+ local_addr.sun_family = AF_UNIX;
+
+ // UNIX_DGRAM sock for reply
+ const char *runtime_dir = tinyproxy_runtime_dir();
+
+ int n = snprintf(
+ c->unix_local_path,
+ sizeof(c->unix_local_path),
+ "%s/udp-%ld-%p.sock",
+ runtime_dir,
+ (long)getpid(),
+ (void *)c
+ );
+
+ if (n < 0 || (size_t)n >= sizeof(c->unix_local_path)) {
+ return -ENAMETOOLONG;
+ }
+
+ if (strlen(c->unix_local_path) >= sizeof(local_addr.sun_path)) {
+ return -ENAMETOOLONG;
+ }
+
+ unlink(c->unix_local_path);
+ strcpy(local_addr.sun_path, c->unix_local_path);
+
+ if (bind(
+ c->fd,
+ (const struct sockaddr *)&local_addr,
+ sizeof(local_addr)
+ ) < 0) {
+ return -EVUTIL_SOCKET_ERROR();
+ }
+
+ memset(&upstream_addr, 0, sizeof(upstream_addr));
+ upstream_addr.sun_family = AF_UNIX;
+ strcpy(upstream_addr.sun_path, upstream->path);
+
+ if (connect(
+ c->fd,
+ (const struct sockaddr *)&upstream_addr,
+ sizeof(upstream_addr)
+ ) < 0) {
+ return -EVUTIL_SOCKET_ERROR();
+ }
+
+ return 0;
+ }
+#endif
+
+ default:
+ return -ENOTSUP;
+ }
+}
+
static struct udp_client *create_udp_client(
struct udp_route_ctx *ctx,
const struct sockaddr_storage *client_addr,
@@ -185,12 +303,11 @@ static struct udp_client *create_udp_client(
c->client_addr_len = client_addr_len;
c->last_seen = time(NULL);
- c->fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (c->fd < 0) {
- int err = EVUTIL_SOCKET_ERROR();
-
- LOG_ERROR("udp upstream socket failed",
- "err", _LOGV(evutil_socket_error_to_string(err))
+ int rc = connect_udp_upstream(c, &r->upstream);
+ if (rc < 0) {
+ LOG_ERROR("udp upstream connect failed",
+ "upstream", _LOGV_ENDPOINT(&r->upstream),
+ "err", _LOGV(evutil_socket_error_to_string(-rc))
);
free_udp_client(c);
return NULL;
@@ -202,34 +319,6 @@ static struct udp_client *create_udp_client(
return NULL;
}
- 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 udp upstream address",
- "upstream", _LOGV_ENDPOINT(&r->upstream)
- );
- free_udp_client(c);
- return NULL;
- }
-
- if (connect(
- c->fd,
- (const struct sockaddr *)&upstream_addr,
- sizeof(upstream_addr)
- ) < 0) {
- int err = EVUTIL_SOCKET_ERROR();
-
- LOG_ERROR("udp upstream connect failed",
- "err", _LOGV(evutil_socket_error_to_string(err))
- );
- free_udp_client(c);
- return NULL;
- }
-
c->ev = event_new(ctx->base, c->fd, EV_READ | EV_PERSIST, upstream_read_cb, c);
if (c->ev == NULL) {
LOG_ERROR("event_new failed for udp upstream");
@@ -346,7 +435,7 @@ static void listen_read_cb(evutil_socket_t fd, short events, void *arg)
return;
}
- LOG_ERROR("udp upstream recv failed",
+ LOG_ERROR("udp listen recv failed",
"err", _LOGV(evutil_socket_error_to_string(err))
);
return;
@@ -381,6 +470,33 @@ int start_udp_route(
const struct route *r,
struct udp_route_ctx **out
) {
+ if (r->listen.kind != ENDPOINT_INET) {
+ LOG_ERROR("unsupported udp listen endpoint",
+ "listen", _LOGV_ENDPOINT(&r->listen)
+ );
+ return -ENOTSUP;
+ }
+
+ if (r->upstream.kind != ENDPOINT_INET &&
+ r->upstream.kind != ENDPOINT_UNIX_DGRAM) {
+ LOG_ERROR("unsupported udp upstream endpoint",
+ "upstream", _LOGV_ENDPOINT(&r->upstream)
+ );
+ return -ENOTSUP;
+ }
+
+ if (r->upstream.kind == ENDPOINT_UNIX_DGRAM) {
+ const char *runtime_dir = tinyproxy_runtime_dir();
+
+ if (mkdir(runtime_dir, 0755) < 0 && errno != EEXIST) {
+ LOG_ERROR("failed to create unix-dgram runtime dir",
+ "dir", _LOGV(runtime_dir),
+ "err", _LOGV(strerror(errno))
+ );
+ return -errno;
+ }
+ }
+
struct sockaddr_in listen_addr;
memset(&listen_addr, 0, sizeof(listen_addr));