/*
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
#include
int numcharsinstr(char *str, char ch) {
int i, count = 0;
for (i = 0; str[i] != '\0'; i++) {
if (str[i] == ch)
count++;
}
return count;
}
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;
/* For for loops */
int i, j;
/* Destination addresses passed in */
unsigned long addrcount = 0;
int addressessize = 1024;
int* addresses = malloc(addressessize * sizeof(int));
/* argc offset to take into account -b or not */
int argo = 4;
/* Do we want to enable sending to a broadcast address? */
short broadcast = 0;
if (argc > 1 && !strcmp(argv[1], "-b")) {
broadcast = 1;
argo++;
}
/* See if we're dealing with a destination address range */
for (i = 0; i < argc - argo; i++) {
/* Make sure there are zero or one range separators */
if (numcharsinstr(argv[i + argo], '-') > 1) {
fprintf(stderr, "Too many range separators (-): %s\n", argv[i + argo]);
return 1;
/* We're using a range */
} else if (numcharsinstr(argv[i + argo], '-') == 1) {
int ip1, ip2, rangelen;
char *tok = NULL;
char *ptr;
tok = strtok(argv[i + argo], "-");
if ((inet_pton(AF_INET, tok, &ip1)) != 1) {
fprintf(stderr, "First IP in range is invalid\n");
return 1;
}
ip1 = htonl(ip1);
tok = strtok(NULL, "-");
if ((inet_pton(AF_INET, tok, &ip2)) != 1) {
fprintf(stderr, "Second IP in range is invalid\n");
return 1;
}
ip2 = htonl(ip2);
/* Length of range */
rangelen = ip2 - ip1 + 1;
if (rangelen < 1) {
fprintf(stderr, "Range is less than one address long "
"(higher address before lower address?)\n");
return 1;
}
// Debugging:
// printf("IP range is %d addresses long\n", rangelen);
if (addrcount + rangelen > addressessize) {
while (addrcount + rangelen > addressessize) {
addressessize *= 2;
}
addresses = realloc(addresses, addressessize * sizeof(int));
}
for (j = 0; j < rangelen; j++) {
addresses[addrcount + j] = ip1 + j;
}
addrcount += rangelen;
/* Limit to around 2^18 addresses because doubling
again leads to struct size problems
*/
if (addrcount > 262144) {
fprintf(stderr, "Range of addresses is too great\n");
return 1;
}
/* It's a single address */
} else {
int ip;
if ((inet_pton(AF_INET, argv[i + argo], &ip)) != 1) {
fprintf(stderr, "IP address is invalid\n");
return 1;
}
addresses[addrcount] = htonl(ip);
addrcount++;
}
}
/* Destination addresses */
struct {
struct sockaddr_in dstaddr;
} dsts[addrcount];
/* 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 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 [-b] LST-ADDR LST-PORT DST-PORT DST-ADDR [DST-ADDR]...\n"
"\n"
"-b Allow sending to a broadcast address. Beware, this can\n"
" create a broadcast storm if retransmitting to a broadcast\n"
" address on the same port a packet came in on.\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"
"DST-ADDR can be a range of IP addresses in the form:\n"
"a.b.c.d-w.x.y.z e.g. 10.0.0.1-10.0.1.254\n"
"Note that this may include a broadcast address (see above).\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[argo - 2])) == 0) || ((dstport = atoi(argv[argo - 1])) == 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 < addrcount; i++) {
dsts[i].dstaddr.sin_family = AF_INET;
dsts[i].dstaddr.sin_port = htons(dstport);
/* Populate sin_addr straight from our numeric addresses */
dsts[i].dstaddr.sin_addr.s_addr = ntohl(addresses[i]);
}
/* 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[argo - 3], &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;
}
/* Allow UDP broadcasts on sending socket if requested */
if (broadcast && setsockopt(sndfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(int)) < 0) {
perror("setsockopt(sndfd, SOL_SOCKET, SO_BROADCAST, ...)");
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;
}