/*
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;
}