summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--README19
-rw-r--r--udprelay.c209
3 files changed, 235 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3879b39
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+CC = gcc
+
+codegen: udprelay.c
+ $(CC) udprelay.c -o udprelay
+
+clean: codegen
+ rm -f udprelay
diff --git a/README b/README
new file mode 100644
index 0000000..450a49f
--- /dev/null
+++ b/README
@@ -0,0 +1,19 @@
+bind()s to address LST-ADDR on port LST-PORT and retransmits UDP
+packets received to addresses(s) DST-ADDR(s) on port DST-PORT,
+with modified headers to reflect the new target.
+
+To receieve broadcast traffic, use a LST-ADDR of 0.0.0.0. If
+a program is already listening on LST-PORT on LST-ADDR, you may
+listen on a broadcast address such as 255.255.255.255.
+
+Steam broadcasts to a number of different ports, so it is
+sufficient to listen on a different port to your game server.
+e.g. a LST-PORT of 27016 and a DST-PORT of 27015 will let you
+listen on a different port to a game server, but for it to still
+directly receive packets that were broadcast.
+
+Revision history:
+
+29th September 2010
+- udprelay-1.0
+-- Initial release
diff --git a/udprelay.c b/udprelay.c
new file mode 100644
index 0000000..672b6d1
--- /dev/null
+++ b/udprelay.c
@@ -0,0 +1,209 @@
+/*
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Copyright 2010 Luke Bratch <l_bratch@yahoo.co.uk>
+
+ UDP header struct and modification method from udp-broadcast-relay [1].
+ I believe the struct was originally by cs6171@scitsc.wlv.ac.uk.
+
+ [1] http://www.joachim-breitner.de/udp-broadcast-relay/
+*/
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <linux/if.h>
+
+int main(int argc, char *argv[]) {
+
+ /* Listening and destination ports */
+ unsigned short int lstport;
+ unsigned short int dstport;
+
+ /* Receiving and sending sockets */
+ int rcvfd;
+ int sndfd;
+
+ /* Destination addresses */
+ struct {
+ struct sockaddr_in dstaddr;
+ } dsts[argc - 4]; /* Anything after 4th arg is a destination address */
+ /* Number of destination addresses */
+ int dstcount;
+
+ /* Source packet */
+ struct msghdr rcvmsg;
+ struct iovec iov;
+ u_char pkt_info[16384];
+ int rcvlen;
+
+ /* Address of source packet */
+ struct sockaddr_in srcaddr;
+
+ /* For for loops */
+ int i;
+ /* For setsockopt() values */
+ int optval;
+
+ /* Outgoing UDP packet structure */
+ u_char packet[4096] = {
+ 0x45, 0x00, 0x00, 0x26,
+ 0x12, 0x34, 0x00, 0x00,
+ 0xFF, 0x11, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0x00, 0x12, 0x00, 0x00,
+ '1','2','3','4','5','6','7','8','9','0'
+ };
+
+ /* Print some usage instructions */
+ if (argc < 5) {
+ fprintf(stderr, "Usage: %s LST-ADDR LST-PORT DST-PORT DST-ADDR [DST-ADDR]...\n"
+ "\n"
+ "bind()s to address LST-ADDR on port LST-PORT and retransmits UDP\n"
+ "packets received to addresses(s) DST-ADDR(s) on port DST-PORT,\n"
+ "with modified headers to reflect the new target.\n"
+ "\n"
+ "To receieve broadcast traffic, use a LST-ADDR of 0.0.0.0. If\n"
+ "a program is already listening on LST-PORT on LST-ADDR, you may\n"
+ "listen on a broadcast address such as 255.255.255.255.\n"
+ "\n"
+ "Steam broadcasts to a number of different ports, so it is\n"
+ "sufficient to listen on a different port to your game server.\n"
+ "e.g. a LST-PORT of 27016 and a DST-PORT of 27015 will let you\n"
+ "listen on a different port to a game server, but for it to still\n"
+ "directly receive packets that were broadcast.\n",
+ argv[0]);
+ return 1;
+ }
+
+ if (((lstport = atoi(argv[2])) == 0) || ((dstport = atoi(argv[3])) == 0)) {
+ fprintf(stderr, "lst-port or dst-port is invalid\n");
+ return 1;
+ }
+
+ /* Populate iovec */
+ iov.iov_base = packet + 28; /* Header length is 28 */
+ iov.iov_len = 3977; /* 4006 - header length - 1 */
+
+ /* Populate msghdr */
+ rcvmsg.msg_name = &srcaddr;
+ rcvmsg.msg_namelen = sizeof(srcaddr);
+ rcvmsg.msg_iov = &iov;
+ rcvmsg.msg_iovlen = 1;
+ rcvmsg.msg_control = pkt_info;
+ rcvmsg.msg_controllen = sizeof(pkt_info);
+
+ /* Enumerate destination addresses */
+ for (i = 0; i < argc - 4; i++) {
+ dsts[i].dstaddr.sin_family = AF_INET;
+ dsts[i].dstaddr.sin_port = htons(dstport);
+ if ((inet_pton(AF_INET, argv[i + 4], &dsts[i].dstaddr.sin_addr)) != 1) {
+ fprintf(stderr, "dst-addr%d is invalid\n", i + 1);
+ return 1;
+ }
+ }
+ /* Set number of destinations */
+ dstcount = i;
+
+ /* Create receiving socket */
+ if ((rcvfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ /* Allow UDP broadcasts on receiving socket */
+ optval = 1;
+ if (setsockopt(rcvfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(int)) < 0) {
+ perror("setsockopt(rcvfd, SOL_SOCKET, SO_BROADCAST, ...)");
+ return 1;
+ };
+
+ /* Set properties of source address */
+ srcaddr.sin_family = AF_INET;
+ srcaddr.sin_port = htons(lstport);
+ if ((inet_pton(AF_INET, argv[1], &srcaddr.sin_addr.s_addr)) != 1) {
+ fprintf(stderr, "lst-addr is invalid\n");
+ return 1;
+ }
+
+ /* Bind the receiving socket */
+ if (bind(rcvfd, (struct sockaddr*)&srcaddr, sizeof(struct sockaddr_in)) < 0) {
+ perror("bind");
+ return 1;
+ }
+
+ /* Create sending socket */
+ if ((sndfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ /* Set destination port in outgoing packet */
+ *(u_short *)(packet + 22) = (u_short)htons(dstport);
+
+ /* Allow setting outgoing header manually */
+ optval = 1;
+ if (setsockopt(sndfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int)) < 0) {
+ perror("setsockopt(sndfd, IPPROTO_IP, IP_HDRINCL, ...)");
+ return 1;
+ }
+
+ /* Main loop */
+ for ( ; ; ) {
+ /* Wait for a packet */
+ rcvlen = recvmsg(rcvfd, &rcvmsg, 0);
+
+ if (rcvlen < 1) {
+ /* Packet must not be broken */
+ continue;
+ }
+
+ // Debugging:
+ // printf("Recv: %s:%d\n",inet_ntoa(srcaddr.sin_addr),srcaddr.sin_port);
+
+ /* Grow packet */
+ packet[28 + rcvlen] = 0;
+
+ /* Copy source packet source address/port into outgoing packet */
+ bcopy(&(srcaddr.sin_addr.s_addr), (packet + 12), 4);
+ *(u_short *)(packet + 20) = (u_short)srcaddr.sin_port;
+
+ /* Set length of outgoing packet */
+ *(u_short *)(packet + 24) = htons(8 + rcvlen);
+ *(u_short *)(packet + 2) = htons(28 + rcvlen);
+
+ for (i = 0; i < dstcount; i++) {
+ /* Set destination of outgoing packet */
+ bcopy(&(dsts[i].dstaddr.sin_addr.s_addr), (packet + 16), 4);
+
+ /* Send outgoing packet */
+ if (sendto(sndfd, &packet, 28 + rcvlen, 0,
+ (struct sockaddr *)&dsts[i].dstaddr,
+ sizeof(struct sockaddr))
+ < 0) {
+ perror("sendto");
+ }
+
+ // Debugging:
+ // printf("Sent: %s:%d\n", inet_ntoa(dsts[i].dstaddr.sin_addr),
+ // ntohs(*(u_short *)(packet+22)));
+ }
+ }
+
+ return 0;
+}