/* Wait for an incoming TCP connection. Once it arrives, listen for UDP on
* the specified port, then send the UDP packets (with a length header) over
* the TCP connection */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "host2ip.h"
#define UDPBUFFERSIZE 65536
#define TCPBUFFERSIZE (UDPBUFFERSIZE + 2) /* UDP packet + 2 (length field) */
#define SET_MAX(fd) do { if (max < (fd) + 1) { max = (fd) + 1; } } while (0)
#if (SIZEOF_SHORT == 2)
typedef unsigned short u_int16;
#else
#error Need a typedef for a 16-bit type
#endif
typedef unsigned char u_int8;
struct out_packet {
u_int16 length;
char buf[UDPBUFFERSIZE];
};
struct relay {
struct sockaddr_in udpaddr;
struct sockaddr_in tcpaddr;
u_int8 udp_ttl;
int multicast_udp;
int udp_send_sock;
int udp_recv_sock;
int tcp_listen_sock;
int tcp_sock;
char buf[TCPBUFFERSIZE];
char *buf_ptr, *packet_start;
int packet_length;
enum {uninitialized = 0, reading_length, reading_packet} state;
};
static int debug = 0;
/*
* usage()
* Print the program usage info, and exit.
*/
static void usage(char *progname) {
fprintf(stderr, "Usage: %s -s TCP-port [-r] [-v] UDP-addr/UDP-port[/ttl]\n",
progname);
fprintf(stderr, " or %s -c TCP-addr[/TCP-port] [-r] [-v] UDP-addr/UDP-port[/ttl]\n",
progname);
fprintf(stderr, " -s: Server mode. Wait for TCP connections on the port.\n");
fprintf(stderr, " -c: Client mode. Connect to the given address.\n");
fprintf(stderr, " -r: RTP mode. Connect/listen on ports N and N+1 for both UDP and TCP.\n");
fprintf(stderr, " Port numbers must be even.\n");
fprintf(stderr, " -v: Verbose mode. Specify -v multiple times for increased verbosity.\n");
exit(2);
} /* usage */
/*
* parse_args()
* Parse argv, and return parsed info in **relays, *relay_count, and
* *is_server. On failure, exit.
*/
static void parse_args(int argc, char *argv[], struct relay **relays,
int *relay_count, int *is_server)
{
int c;
char *tcphostname, *tcpportstr, *udphostname, *udpportstr, *udpttlstr;
struct in_addr tcpaddr, udpaddr;
int tcpport, udpport, udpttl;
int i;
*is_server = -1;
*relay_count = 1;
debug = 0;
tcphostname = NULL;
tcpportstr = NULL;
while ((c = getopt(argc, argv, "s:c:rvh")) != EOF) {
switch (c) {
case 's':
if (*is_server != -1) {
fprintf(stderr, "%s: Only one of -s and -c may be specified.\n",
argv[0]);
exit(2);
}
*is_server = 1;
tcpportstr = optarg;
break;
case 'c':
if (*is_server != -1) {
fprintf(stderr, "%s: Only one of -s and -c may be specified.\n",
argv[0]);
exit(2);
}
*is_server = 0;
tcphostname = optarg;
break;
case 'r':
*relay_count = 2;
break;
case 'v':
debug++;
break;
case 'h':
case '?':
default:
usage(argv[0]);
break;
}
}
if (*is_server == -1) {
fprintf(stderr, "%s: You must specify one of -s and -c.\n",
argv[0]);
exit(2);
}
if (argc <= optind) {
usage(argv[0]);
}
udphostname = strtok(argv[optind], ":/ ");
udpportstr = strtok(NULL, ":/ ");
if (udpportstr == NULL) {
usage(argv[0]);
}
udpttlstr = strtok(NULL, ":/ ");
if (!*is_server) {
tcphostname = strtok(tcphostname, ":/ ");
tcpportstr = strtok(NULL, ":/ ");
}
else {
tcphostname = NULL;
}
errno = 0;
udpport = strtol(udpportstr, NULL, 0);
if (errno || udpport <= 0 || udpport >= 65536) {
fprintf(stderr, "%s: invalid port number\n", udpportstr);
exit(2);
}
if (udpttlstr != NULL) {
errno = 0;
udpttl = strtol(udpttlstr, NULL, 0);
if (errno || udpttl < 0 || udpttl >= 256) {
fprintf(stderr, "%s: invalid TTL\n", udpttlstr);
exit(2);
}
}
else {
udpttl = 1;
}
if (tcpportstr != NULL) {
errno = 0;
tcpport = strtol(tcpportstr, NULL, 0);
if (errno || tcpport <= 0 || tcpport >= 65536) {
fprintf(stderr, "%s: invalid port number\n", tcpportstr);
exit(2);
}
}
else {
tcpport = udpport;
}
if (*relay_count == 2 && (tcpport % 2 != 0 || udpport % 2 != 0)) {
fprintf(stderr, "Port numbers must be even when using RTP mode.\n");
exit(2);
}
udpaddr = host2ip(udphostname);
if (udpaddr.s_addr == INADDR_ANY) {
fprintf(stderr, "%s: UDP host unknown\n", udphostname);
exit(2);
}
if (*is_server) {
tcpaddr.s_addr = INADDR_ANY;
}
else {
tcpaddr = host2ip(tcphostname);
if (tcpaddr.s_addr == INADDR_ANY) {
fprintf(stderr, "%s: TCP host unknown\n", tcphostname);
exit(2);
}
}
*relays = (struct relay *) calloc(*relay_count, sizeof(struct relay));
if (relays == NULL) {
perror("Error allocating relay structure");
exit(1);
}
for (i = 0; i < *relay_count; i++) {
(*relays)[i].udpaddr.sin_addr = udpaddr;
(*relays)[i].udpaddr.sin_port = htons(udpport + i);
(*relays)[i].udpaddr.sin_family = AF_INET;
(*relays)[i].udp_ttl = udpttl;
(*relays)[i].multicast_udp = IN_MULTICAST(htons(udpaddr.s_addr));
(*relays)[i].tcpaddr.sin_addr = tcpaddr;
(*relays)[i].tcpaddr.sin_port = htons(tcpport + i);
(*relays)[i].tcpaddr.sin_family = AF_INET;
}
} /* parse_args */
/* setup_udp_recv()
* Set up the UDP receiving socket for the specified relay.
* Exit if anything goes wrong.
*/
static void setup_udp_recv(struct relay *relay)
{
int opt;
struct sockaddr_in udp_recv_addr;
if ((relay->udp_recv_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("setup_udp_recv: socket");
exit(1);
}
/* Set "reuseaddr" (and "reuseport", if it exists) */
opt = 1;
if (setsockopt(relay->udp_recv_sock, SOL_SOCKET, SO_REUSEADDR,
(void *)&opt, sizeof(opt)) < 0) {
perror("setup_udp_recv: setsockopt(SO_REUSEADDR)");
exit(1);
}
#ifdef SO_REUSEPORT
opt = 1;
if (setsockopt(relay->udp_recv_sock, SOL_SOCKET, SO_REUSEPORT,
(void *)&opt, sizeof(opt)) < 0) {
perror("setup_udp_recv: setsockopt(SO_REUSEPORT)");
exit(1);
}
#endif
if (relay->multicast_udp) {
#ifdef IP_ADD_MEMBERSHIP
struct ip_mreq mreq; /* multicast group */
mreq.imr_multiaddr = relay->udpaddr.sin_addr;
mreq.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(relay->udp_recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(void *)&mreq, sizeof(mreq)) < 0) {
perror("setup_udp_recv: setsockopt(IP_ADD_MEMBERSHIP)");
exit(1);
}
#else
fprintf(stderr, "Multicast addresses not supported\n");
exit(1);
#endif
}
memcpy(&udp_recv_addr, &(relay->udpaddr), sizeof(struct sockaddr_in));
if (!(relay->multicast_udp)) {
/* XXX: some platforms don't allow you to bind to a multicast addr;
these need to bind recv_addr to INADDR_ANY regardless? */
udp_recv_addr.sin_addr.s_addr = INADDR_ANY;
}
if (bind(relay->udp_recv_sock, (struct sockaddr *)&udp_recv_addr,
sizeof(udp_recv_addr)) < 0) {
perror("setup_udp_recv: bind");
exit(1);
}
return;
} /* setup_udp_recv */
/* setup_udp_send()
* Set up the UDP sending socket for the specified relay.
* Exit if anything goes wrong.
*/
static void setup_udp_send(struct relay *relay)
{
/* Create UDP socket. */
if ((relay->udp_send_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("setup_udp_send: socket");
exit(1);
}
if (connect(relay->udp_send_sock, (struct sockaddr *) &(relay->udpaddr),
sizeof(relay->udpaddr)) < 0) {
perror("setup_udp_send: connect");
exit(1);
}
if (IN_MULTICAST(htonl(relay->udpaddr.sin_addr.s_addr))) {
#ifdef IP_MULTICAST_LOOP
u_int8 loop = 0;
if (setsockopt