From 729024275663c88946d71f8c8d0faf8614627895 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Thu, 28 Jun 2012 14:20:21 +0100 Subject: Initial commit --- Makefile | 7 +++ README | 19 ++++++ udprelay.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 udprelay.c 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 . +*/ + +/* + Copyright 2010 Luke Bratch + + 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 +#include +#include +#include + +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; +} -- cgit v1.2.3