penguin/tinyproxy

An L4 proxy designed to act as a tiny transparent shim

commit ecbb3a9441fb80805da2bd1d26fed3c86da3e1a9

author斟酌 鵬兄 <tgckpg@gmail.com>
date2026-05-30T22:15:13Z
subjectAdded minecraft support
commit ecbb3a9441fb80805da2bd1d26fed3c86da3e1a9
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2026-05-30T22:15:13Z

    Added minecraft support
---
 README.md                           | 11 ++++++++++-
 examples/minecraft-bedrock-lan.conf | 13 +++++++++++++
 src/datagram_listener.c             | 18 ++++++++++++++++++
 src/file_conf.c                     |  1 +
 src/route.h                         |  1 +
 5 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index f3068f7..faa9466 100644
--- a/README.md
+++ b/README.md
@@ -19,12 +19,21 @@ listen <listen-proto> <listen-endpoint> <backend-proto> <backend-endpoint> [opti
 
 Example routes:
 
-```text
+```sh
 tinyproxy \
     -L "tcp :31232 tcp 127.0.0.1:41232" \
     -L "udp :31232 udp 127.0.0.1:41232"
 ```
 
+Minecraft Bedrock public-to-LAN proxy
+
+Makes a remote Minecraft Bedrock server appear as a LAN server.
+
+```sh
+tinyproxy \
+    -L "udp :19132 udp 123.123.123.123:19132 broadcast_reply"
+```
+
 Run with config file:
 
 ```sh
diff --git a/examples/minecraft-bedrock-lan.conf b/examples/minecraft-bedrock-lan.conf
new file mode 100644
index 0000000..1067f51
--- /dev/null
+++ b/examples/minecraft-bedrock-lan.conf
@@ -0,0 +1,13 @@
+#
+# Expose a remote Minecraft Bedrock server as a local LAN server.
+#
+# Clients on the local network can discover the remote server from the
+# Minecraft "Friends" -> "LAN Games" tab without manually entering the
+# server address.
+#
+# tinyproxy listens on UDP port 19132, forwards discovery requests to
+# the remote server, and sends discovery replies back to local clients.
+#
+# Replace 123.123.123.123:19132 with your server address.
+#
+udp :19132 udp 123.123.123.123:19132 broadcast_reply
diff --git a/src/datagram_listener.c b/src/datagram_listener.c
index 40d9603..a9c5c31 100644
--- a/src/datagram_listener.c
+++ b/src/datagram_listener.c
@@ -170,6 +170,12 @@ static int bind_unix_datagram_listener(struct datagram_route_ctx *ctx)
 		return -err;
 	}
 
+	if (r->opts.broadcast_reply) {
+		LOG_ERROR("broadcast_reply is only valid for udp inet listeners",
+				"line", _LOGV(r->line_no));
+		return -EINVAL;
+	}
+
 	evutil_make_socket_closeonexec(ctx->listen_fd);
 
 	if (evutil_make_socket_nonblocking(ctx->listen_fd) < 0) {
@@ -272,6 +278,18 @@ static int bind_udp_datagram_listener(struct datagram_route_ctx *ctx)
 		return -err;
 	}
 
+	if (r->opts.broadcast_reply) {
+		int yes = 1;
+		if (setsockopt(ctx->listen_fd, SOL_SOCKET, SO_BROADCAST,
+					(const char *)&yes, sizeof(yes)) < 0) {
+			int err = EVUTIL_SOCKET_ERROR();
+			LOG_ERROR("failed to enable broadcast",
+					"line", _LOGV(r->line_no),
+					"err", _LOGV(evutil_socket_error_to_string(err)));
+			return -err;
+		}
+	}
+
 	evutil_make_socket_closeonexec(ctx->listen_fd);
 
 	if (evutil_make_socket_nonblocking(ctx->listen_fd) < 0) {
diff --git a/src/file_conf.c b/src/file_conf.c
index e5943fd..bd2bed9 100644
--- a/src/file_conf.c
+++ b/src/file_conf.c
@@ -231,6 +231,7 @@ static int parse_route_options(char *s, struct route_options *opts)
 
 		PARSE_BOOL_OPT("proxy_v2", proxy_v2);
 		PARSE_BOOL_OPT("keep_alive", keep_alive);
+		PARSE_BOOL_OPT("broadcast_reply", broadcast_reply);
 
 		return -EINVAL;
 
diff --git a/src/route.h b/src/route.h
index 12f8d6e..d95138e 100644
--- a/src/route.h
+++ b/src/route.h
@@ -19,6 +19,7 @@
 struct route_options {
 	bool proxy_v2;
 	bool keep_alive;
+	bool broadcast_reply;
 
 	int idle_timeout_sec;
 	int connect_timeout_sec;