This commit is contained in:
Mikko Ahlroth 2024-07-18 19:27:15 +03:00
parent 936d83a89c
commit bb86fe1ccf
2 changed files with 41 additions and 3 deletions

View file

@ -1,3 +1,9 @@
# valproxy
UDP proxy for Valheim dedicated server Systemd socket activation.
This is theoretically a non-Valheim specific UDP proxy that can proxy any UDP traffic from a Systemd socket
into a specified local or remote target (bi-directionally), but the feature set and configuration is focused
on running the Valheim dedicated server.
The `systemd` folder contains example socket and server files detailing how it can be set up.

View file

@ -34,6 +34,7 @@ const SOCKET_TYPE = "udp4";
*/
const SYSTEMD_READ_SOCKET_FD = 3;
/** A network target. */
class Target {
/** @type {string} */
address;
@ -51,7 +52,11 @@ class Target {
}
}
/** @template T */
/**
* A convenience over a 2-dimensional map.
*
* @template T
*/
class TargetMap extends Map {
/**
* @param {Target} target
@ -89,11 +94,20 @@ class TargetMap extends Map {
}
}
/**
* A connection from a remote to our target.
*/
class Connection {
/** @type {Target} */
/**
* The remote client address information.
* @type {Target}
*/
source;
/** @type {Socket | null} */
/**
* The socket used to connect to the target, and to receive messages from it.
* @type {Socket | null}
*/
writeSocket;
/** @type {NodeJS.Timer | null} */
@ -123,6 +137,9 @@ class Connection {
this.onMessage = onMessage;
}
/**
* Initialise {@link writeSocket} by setting up the event handlers and connecting it to target.
*/
initTargetSocket() {
this.connected = false;
clearTimeout(this.reconnectTimeout);
@ -135,6 +152,7 @@ class Connection {
this.source.port
);
});
this.writeSocket.on("message", (fromMsg) => {
this.#resetIdleTimeout();
if (DEBUG) {
@ -149,9 +167,11 @@ class Connection {
this.onMessage(fromMsg);
});
this.writeSocket.on("error", (err) => {
this.#writeError(err);
});
this.writeSocket.on("close", () => {
console.log(
"Socket for",
@ -169,6 +189,10 @@ class Connection {
}
/**
* Send a message to target using our socket.
*
* If the socket is down, the message is discarded.
*
* @param {Buffer} msg
*/
send(msg) {
@ -200,6 +224,7 @@ class Connection {
this.source.port
);
console.error(err);
this.writeSocket = null;
this.reconnectTimeout = setTimeout(() => {
this.writeSocket = createSocket(SOCKET_TYPE);
@ -255,6 +280,8 @@ READ_SOCKET.on("listening", () => {
});
READ_SOCKET.on("message", (toMsg, rinfo) => {
// We need to strip off the dual stack mapped address prefix, if one exists. Otherwise our replies will not be
// delivered to the origin.
let raddress = rinfo.address;
if (raddress.indexOf("::ffff:") === 0) {
raddress = raddress.substring(7);
@ -267,6 +294,9 @@ READ_SOCKET.on("message", (toMsg, rinfo) => {
const source = new Target(raddress, rinfo.port);
let connection = CONNECTIONS.get(source);
// Create a new connection for each new client address/port pair. This allows us to route the replies from the target
// back to the correct client address/port and thus the correct remote client.
if (!connection) {
clearTimeout(TOTAL_IDLE_TIMER);
@ -305,6 +335,8 @@ READ_SOCKET.on("close", () => {
console.log("Systemd read socket closed.");
});
// Bind the read socket to the file descriptor given by Systemd. Systemd is already listening to packets for us, so we
// don't open our own socket.
READ_SOCKET.bind({
fd: SYSTEMD_READ_SOCKET_FD,
});