This commit is contained in:
git@daemon.de
2015-04-24 22:03:50 +02:00
parent 0d22e6fc6f
commit e56848c4d1
7 changed files with 252 additions and 148 deletions

View File

@@ -21,7 +21,7 @@
# warning: do not set -O to 2, see TODO # warning: do not set -O to 2, see TODO
CFLAGS = -Wall -Wextra -Werror -O1 -g CFLAGS = -Wall -Wextra -Werror -O1 -g
LDFLAGS= LDFLAGS=
OBJS = client.o net.o udpxd.o OBJS = host.o client.o net.o udpxd.o
DST = udpxd DST = udpxd
PREFIX = /usr/local PREFIX = /usr/local
UID = root UID = root

View File

@@ -35,10 +35,10 @@ client_t *client_find_fd(int fd) {
return client; /* maybe NULL! */ return client; /* maybe NULL! */
} }
client_t *client_find_src(struct sockaddr_in *src) { client_t *client_find_src(host_t *src) {
client_t *current = NULL; client_t *current = NULL;
client_iter(clients, current) { client_iter(clients, current) {
if (current->src == src) if(strcmp(current->src->ip, src->ip) == 0 && current->src->port == src->port)
return current; return current;
} }
return NULL; return NULL;
@@ -48,7 +48,7 @@ void client_seen(client_t *client) {
client->lastseen = (long)time(0); client->lastseen = (long)time(0);
} }
client_t *client_new(int fd, struct sockaddr_in *src, struct sockaddr_in *dst) { client_t *client_new(int fd, host_t *src, host_t *dst) {
client_t *client = malloc(sizeof(client_t)); client_t *client = malloc(sizeof(client_t));
client->socket = fd; client->socket = fd;
client->src = src; client->src = src;
@@ -73,10 +73,8 @@ void client_clean() {
diff = now - current->lastseen; diff = now - current->lastseen;
if(diff >= MAXAGE) { if(diff >= MAXAGE) {
if(VERBOSE) { if(VERBOSE) {
char *srcip = inet_ntoa(current->src->sin_addr);
char *bindip = inet_ntoa(current->dst->sin_addr);
fprintf(stderr, "closing socket %s:%d for client %s:%d (aged out after %d seconds)\n", fprintf(stderr, "closing socket %s:%d for client %s:%d (aged out after %d seconds)\n",
srcip, ntohs(current->src->sin_port), bindip, ntohs(current->dst->sin_port), MAXAGE); current->src->ip, current->src->port, current->dst->ip, current->dst->port, MAXAGE);
} }
client_close(current); client_close(current);
} }

View File

@@ -39,14 +39,16 @@ typedef uint8_t byte;
#endif #endif
#include "uthash.h" #include "uthash.h"
#include "host.h"
#define MAXAGE 3600 /* seconds after which to close outgoing sockets and forget client src */ #define MAXAGE 3600 /* seconds after which to close outgoing sockets and forget client src */
struct _client_t { struct _client_t {
int socket; /* bind socket for outgoing traffic */ int socket; /* bind socket for outgoing traffic */
struct sockaddr_in *src; /* client src (ip+port) from incoming socket */ host_t *src; /* client src (ip+port) from incoming socket */
struct sockaddr_in *dst; /* client dst (ip+port) to outgoing socket */ host_t *dst; /* client dst (ip+port) to outgoing socket */
uint64_t lastseen; /* when did we recv last time from it */ uint64_t lastseen; /* when did we recv last time from it */
size_t size; /* sockaddr size */
UT_hash_handle hh; UT_hash_handle hh;
}; };
typedef struct _client_t client_t; typedef struct _client_t client_t;
@@ -78,8 +80,8 @@ void client_close(client_t *client);
void client_clean(); void client_clean();
client_t *client_find_fd(int fd); client_t *client_find_fd(int fd);
client_t *client_find_src(struct sockaddr_in *src); client_t *client_find_src(host_t *src);
client_t *client_new(int fd, struct sockaddr_in *src, struct sockaddr_in *dst); client_t *client_new(int fd, host_t *src, host_t *dst);
#endif #endif

188
net.c
View File

@@ -21,6 +21,8 @@
#include "net.h" #include "net.h"
#include "client.h" #include "client.h"
#include "host.h"
char *ntoa(struct sockaddr_in *src) { char *ntoa(struct sockaddr_in *src) {
@@ -29,6 +31,7 @@ char *ntoa(struct sockaddr_in *src) {
return ip; return ip;
} }
/* called each time when the loop restarts to feed select() correctly */ /* called each time when the loop restarts to feed select() correctly */
int fill_set(fd_set *fds) { int fill_set(fd_set *fds) {
int max = 0; int max = 0;
@@ -58,57 +61,106 @@ int get_sender(fd_set *fds) {
return i; return i;
} }
/* bind to a socket, either for listen() or for outgoing src ip binding */ /* bind to a socket, either for listen() or for outgoing src ip binding */
int bindsocket( char* ip, int port ) { int bindsocket( host_t *sock_h) {
int fd; int fd;
struct sockaddr_in addr; int err = 0;
addr.sin_family = AF_INET; if(sock_h->is_v6) {
addr.sin_addr.s_addr = inet_addr( ip ); fd = socket( PF_INET6, SOCK_DGRAM, IPPROTO_UDP );
addr.sin_port = htons( port ); }
else {
fd = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
}
fd = socket( PF_INET, SOCK_DGRAM, IPPROTO_IP ); if( -1 == bind( fd, (struct sockaddr*)sock_h->sock, sock_h->size ) ) {
if( -1 == bind( fd, (struct sockaddr*)&addr, sizeof( addr ) ) ) { err = 1;
fprintf( stderr, "Cannot bind address (%s:%d)\n", ip, port ); }
exit( 1 );
if(err) {
fprintf( stderr, "Cannot bind address ([%s]:%d)\n", sock_h->ip, sock_h->port );
perror(NULL);
return -1;
} }
return fd; return fd;
} }
/* handle new or known incoming requests */ int start_listener (char *inip, char *inpt, char *srcip, char *dstip, char *dstpt) {
void handle_inside(int inside, char *bindip, struct sockaddr_in *dst) { host_t *listen_h = get_host(inip, atoi(inpt), NULL, NULL);
int len; host_t *dst_h = get_host(dstip, atoi(dstpt), NULL, NULL);
unsigned char buffer[MAX_BUFFER_SIZE]; host_t *bind_h = NULL;
struct sockaddr_in *src;
client_t *client; if(srcip != NULL) {
int output; bind_h = get_host(srcip, 0, NULL, NULL);
char *srcip; }
char *dstip = ntoa(dst); else {
size_t size = sizeof(struct sockaddr_in); if(dst_h->is_v6)
src = malloc(size); bind_h = get_host("::0", 0, NULL, NULL);
else
len = recvfrom( inside, buffer, sizeof( buffer ), 0, (struct sockaddr*)src, (socklen_t *)&size ); bind_h = get_host("0.0.0.0", 0, NULL, NULL);
srcip = ntoa(src); }
int listen = bindsocket(listen_h);
if(listen == -1)
return 1;
if(VERBOSE) {
fprintf(stderr, "Listening on %s:%s, forwarding to %s:%s",
inip, inpt, dstip, dstpt);
if(srcip != NULL)
fprintf(stderr, ", binding to %s\n", srcip);
else
fprintf(stderr, "\n");
}
main_loop(listen, listen_h, bind_h, dst_h);
return 0;
}
/* handle new or known incoming requests
FIXME: check client handling:
http://long.ccaba.upc.es/long/045Guidelines/eva/ipv6.html#daytimeServer6
*/
void handle_inside(int inside, host_t *listen_h, host_t *bind_h, host_t *dst_h) {
int len;
unsigned char buffer[MAX_BUFFER_SIZE];
void *src;
client_t *client;
host_t *src_h;
int output;
size_t size = listen_h->size;
src = malloc(size);
len = recvfrom( inside, buffer, sizeof( buffer ), 0,
(struct sockaddr*)src, (socklen_t *)&size );
if(listen_h->is_v6)
src_h = get_host(NULL, 0, NULL, (struct sockaddr_in6 *)src);
else
src_h = get_host(NULL, 0, (struct sockaddr_in *)src, NULL);
if(VERBOSE) { if(VERBOSE) {
char *srcip = ntoa(src);
fprintf(stderr, "New incomming request from %s:%d with %d bytes\n", fprintf(stderr, "New incomming request from %s:%d with %d bytes\n",
srcip, ntohs(src->sin_port), len); src_h->ip, src_h->port, len);
} }
if(len > 0) { if(len > 0) {
/* do we know it ? */ /* do we know it ? */
client = client_find_src(src); client = client_find_src(src_h);
if(client != NULL) { if(client != NULL) {
/* yes, we know it, send req out via existing bind socket */ /* yes, we know it, send req out via existing bind socket */
if(VERBOSE) { if(VERBOSE) {
fprintf(stderr, "Client %s:%d is known, forwarding data to %s:%d\n", fprintf(stderr, "Client %s:%d is known, forwarding data to %s:%d\n",
srcip, ntohs(src->sin_port), dstip, ntohs(dst->sin_port)); src_h->ip, src_h->port, dst_h->ip, dst_h->port);
} }
if(sendto(client->socket, buffer, len, 0, (struct sockaddr*)dst, size) < 0) { if(sendto(client->socket, buffer, len, 0, (struct sockaddr*)dst_h->sock, dst_h->size) < 0) {
fprintf(stderr, "unable to forward to %s:%d\n", dstip, ntohs(dst->sin_port)); fprintf(stderr, "unable to forward to %s:%d\n", dst_h->ip, dst_h->port);
perror(NULL); perror(NULL);
} }
else { else {
@@ -119,29 +171,38 @@ void handle_inside(int inside, char *bindip, struct sockaddr_in *dst) {
/* unknown client, open new out socket */ /* unknown client, open new out socket */
if(VERBOSE) { if(VERBOSE) {
fprintf(stderr, "Client %s:%d is unknown, forwarding data to %s:%d ", fprintf(stderr, "Client %s:%d is unknown, forwarding data to %s:%d ",
srcip, ntohs(src->sin_port), dstip, ntohs(dst->sin_port)); src_h->ip, src_h->port, dst_h->ip, dst_h->port);
} }
if(bindip == NULL) output = bindsocket(bind_h);
output = bindsocket("0.0.0.0", 0);
else
output = bindsocket(bindip, 0);
/* send req out */ /* send req out */
if(sendto(output, buffer, len, 0, (struct sockaddr*)dst, size) < 0) { if(sendto(output, buffer, len, 0, (struct sockaddr*)dst_h->sock, dst_h->size) < 0) {
fprintf(stderr, "unable to forward to %s:%d\n", dstip, ntohs(dst->sin_port)); fprintf(stderr, "unable to forward to %s:%d\n", dst_h->ip, dst_h->port);
perror(NULL); perror(NULL);
} }
else { else {
struct sockaddr_in *ret = malloc(size); size = listen_h->size;
getsockname(output, (struct sockaddr*)ret, (socklen_t *)&size); host_t *ret_h;
client = client_new(output, src, ret); if(listen_h->is_v6) {
struct sockaddr_in6 *ret = malloc(size);
getsockname(output, (struct sockaddr*)ret, (socklen_t *)&size);
ret_h = get_host(NULL, 0, NULL, ret);
client = client_new(output, src_h, ret_h);
}
else {
struct sockaddr_in *ret = malloc(size);
getsockname(output, (struct sockaddr*)ret, (socklen_t *)&size);
ret_h = get_host(NULL, 0, ret, NULL);
client = client_new(output, src_h, ret_h);
}
client_add(client); client_add(client);
if(VERBOSE) { if(VERBOSE) {
if(bindip != NULL) { if(strcmp(bind_h->ip, "0.0.0.0") != 0 || strcmp(bind_h->ip, "::0") != 0) {
char *bindip = ntoa(ret); fprintf(stderr, "from %s:%d\n", ret_h->ip, ret_h->port);
fprintf(stderr, "from %s:%d\n", bindip, ntohs(ret->sin_port));
} }
else { else {
fprintf(stderr, "\n"); fprintf(stderr, "\n");
@@ -151,18 +212,17 @@ void handle_inside(int inside, char *bindip, struct sockaddr_in *dst) {
} }
} }
free(dstip); /* FIXME: free? */
free(srcip);
} }
/* handle answer from the outside */ /* handle answer from the outside */
void handle_outside(int inside, int outside) { void handle_outside(int inside, int outside, host_t *outside_h) {
int len; int len;
unsigned char buffer[MAX_BUFFER_SIZE]; unsigned char buffer[MAX_BUFFER_SIZE];
struct sockaddr_in *src; void *src;
client_t *client; client_t *client;
size_t size = sizeof(struct sockaddr_in); size_t size = outside_h->size;
src = malloc(size); src = malloc(size);
len = recvfrom( outside, buffer, sizeof( buffer ), 0, (struct sockaddr*)src, (socklen_t *)&size ); len = recvfrom( outside, buffer, sizeof( buffer ), 0, (struct sockaddr*)src, (socklen_t *)&size );
@@ -172,7 +232,7 @@ void handle_outside(int inside, int outside) {
client = client_find_fd(outside); client = client_find_fd(outside);
if(client != NULL) { if(client != NULL) {
/* yes, we know it */ /* yes, we know it */
if(sendto(inside, buffer, len, 0, (struct sockaddr*)client->src, size) < 0) { if(sendto(inside, buffer, len, 0, (struct sockaddr*)client->src, client->size) < 0) {
perror("unable to send back to client"); /* FIXME: add src+port */ perror("unable to send back to client"); /* FIXME: add src+port */
client_close(client); client_close(client);
} }
@@ -180,3 +240,33 @@ void handle_outside(int inside, int outside) {
} }
} }
/* runs forever, handles incoming requests on the inside and answers on the outside */
int main_loop(int listensocket, host_t *listen_h, host_t *bind_h, host_t *dst_h) {
int max, sender;
fd_set fds;
for(;;) {
FD_ZERO(&fds);
max = fill_set(&fds);
FD_SET(listensocket, &fds);
if (listensocket > max)
max = listensocket;
select(max + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(listensocket, &fds)) {
/* incoming client on the inside, get src, bind output fd, add to list
if known, otherwise just handle it */
handle_inside(listensocket, listen_h, bind_h, dst_h);
}
else {
/* remote answer came in on an output fd, proxy back to the inside */
sender = get_sender(&fds);
handle_outside(listensocket, sender, dst_h);
}
/* close old outputs, if any */
client_clean();
}
}

17
net.h
View File

@@ -35,6 +35,7 @@
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "client.h" #include "client.h"
#define MAX_BUFFER_SIZE 65535 #define MAX_BUFFER_SIZE 65535
@@ -42,10 +43,20 @@
extern client_t *clients; extern client_t *clients;
extern int VERBOSE; extern int VERBOSE;
void handle_inside(int inside, host_t *listen_h, host_t *bind_h, host_t *dst_h);
void handle_outside(int inside, int outside, host_t *outside_h);
int main_loop(int listensocket, host_t *listen_h, host_t *bind_h, host_t *dst_h);
int start_listener (char *inip, char *inpt, char *srcip, char *dstip, char *dstpt);
int fill_set(fd_set *fds); int fill_set(fd_set *fds);
int get_sender(fd_set *fds); int get_sender(fd_set *fds);
int bindsocket( char* ip, int port ); int bindsocket( host_t *sock_h);
void handle_inside(int inside, char *bindip, struct sockaddr_in *dst);
void handle_outside(int inside, int outside);
#define _IS_LINK_LOCAL(a) do { IN6_IS_ADDR_LINKLOCAL(a); } while(0)
#endif #endif

172
udpxd.c
View File

@@ -27,43 +27,70 @@
client_t *clients = NULL; client_t *clients = NULL;
int VERBOSE = 0; int VERBOSE = 0;
/* runs forever, handles incoming requests on the inside and answers on the outside */ /* parse ip:port */
int main_loop(int listensocket, char *bindip, struct sockaddr_in *dst) { int parse_ip(char *src, char *ip, char *pt) {
int max, sender; char *ptr = NULL;
fd_set fds;
for(;;) { if (strchr(optarg, '[')) {
FD_ZERO(&fds); /* v6 */
max = fill_set(&fds); ptr = strtok(&src[1], "]");
FD_SET(listensocket, &fds); if(strlen(ptr) > INET6_ADDRSTRLEN) {
if (listensocket > max) fprintf(stderr, "ip v6 address is too long!\n");
max = listensocket; return 1;
select(max + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(listensocket, &fds)) {
/* incoming client on the inside, get src, bind output fd, add to list
if known, otherwise just handle it */
handle_inside(listensocket, bindip, dst);
}
else {
/* remote answer came in on an output fd, proxy back to the inside */
sender = get_sender(&fds);
handle_outside(listensocket, sender);
}
/* close old outputs, if any */
client_clean();
} }
strncpy(ip, ptr, strlen(ptr)+1);
ptr = strtok(NULL, "]");
if(ptr)
ptr = &ptr[1]; /* remove : */
}
else if(strchr(optarg, ':')) {
/* v4 */
ptr = strtok(src, ":");
if(strlen(ptr) > INET_ADDRSTRLEN) {
fprintf(stderr, "ip v4 address is too long!\n");
return 1;
}
strncpy(ip, ptr, strlen(ptr)+1);
ptr = strtok(NULL, ":");
}
else {
fprintf(stderr, "Invalid ip/port specification!\n");
return 1;
}
if(ptr != NULL) {
/* got a port */
if(strlen(ptr) > 5) {
fprintf(stderr, "port is too long!\n");
return 1;
}
else {
if(atoi(ptr) > 65535) {
fprintf(stderr, "maximum port number possible: 65535!\n");
return 1;
}
strncpy(pt, ptr, strlen(ptr)+1);
}
}
else {
fprintf(stderr, "Port is missing!\n");
return 1;
}
return 0;
} }
int main ( int argc, char* argv[] ) { int main ( int argc, char* argv[] ) {
int listen, opt; int opt, err;
char *inip, *inpt, *srcip, *dstip, *dstpt; char *inip, *inpt, *srcip, *dstip, *dstpt;
struct sockaddr_in *dst; err = 0;
char colon[] = ":";
static struct option longopts[] = { static struct option longopts[] = {
{ "listen", required_argument, NULL, 'l' }, { "listen", required_argument, NULL, 'l' },
@@ -80,7 +107,7 @@ int main ( int argc, char* argv[] ) {
} }
srcip = dstip = inip = dstpt = inpt = NULL; srcip = dstip = inip = dstpt = inpt = NULL;
while ((opt = getopt_long(argc, argv, "l:b:d:vVh?", longopts, NULL)) != -1) { while ((opt = getopt_long(argc, argv, "l:b:d:vVh?", longopts, NULL)) != -1) {
switch (opt) { switch (opt) {
case 'v': case 'v':
@@ -96,49 +123,27 @@ int main ( int argc, char* argv[] ) {
VERBOSE = 1; VERBOSE = 1;
break; break;
case 'l': case 'l':
if(strchr(optarg, ':')) { inip = malloc(INET6_ADDRSTRLEN+1);
char *ptr = NULL; inpt = malloc(6);
ptr = strtok(optarg, colon); if (parse_ip(optarg, inip, inpt) != 0) {
inip = malloc( strlen(ptr)+1);
strncpy(inip, ptr, strlen(ptr)+1);
ptr = strtok(NULL, colon);
if(ptr != NULL) {
inpt = malloc( strlen(ptr)+1);
strncpy(inpt, ptr, strlen(ptr)+1);
}
else {
fprintf(stderr, "Listen port for parameter -l is missing!\n");
return 0;
}
}
else {
fprintf(stderr, "Parameter -l has the format <ip-address:port>!\n"); fprintf(stderr, "Parameter -l has the format <ip-address:port>!\n");
return 0; err = 1;
} }
break; break;
case 'd': case 'd':
if(strchr(optarg, ':')) { dstip = malloc(INET6_ADDRSTRLEN+1);
char *ptr = NULL; dstpt = malloc(6);
ptr = strtok(optarg, colon); if (parse_ip(optarg, dstip, dstpt) != 0) {
dstip = malloc( strlen(ptr)+1);
strncpy(dstip, ptr, strlen(ptr)+1);
ptr = strtok(NULL, colon);
if(ptr != NULL) {
dstpt = malloc( strlen(ptr)+1);
strncpy(dstpt, ptr, strlen(ptr)+1);
}
else {
fprintf(stderr, "Destination port for parameter -d is missing!\n");
return 0;
}
}
else {
fprintf(stderr, "Parameter -d has the format <ip-address:port>!\n"); fprintf(stderr, "Parameter -d has the format <ip-address:port>!\n");
return 0; err = 1;
} }
break; break;
case 'b': case 'b':
srcip = malloc(strlen(optarg)); if(strlen(optarg) > INET6_ADDRSTRLEN) {
fprintf(stderr, "Bind ip address is too long!\n");
err = 1;
}
srcip = malloc(INET6_ADDRSTRLEN+1);
strncpy(srcip, optarg, strlen(optarg)); strncpy(srcip, optarg, strlen(optarg));
break; break;
default: default:
@@ -151,36 +156,33 @@ int main ( int argc, char* argv[] ) {
if(inip == NULL) { if(inip == NULL) {
fprintf(stderr, "-l parameter is required!\n"); fprintf(stderr, "-l parameter is required!\n");
usage(); usage();
return 1; err = 1;
} }
if(dstip == NULL) { if(dstip == NULL) {
fprintf(stderr, "-d parameter is required!\n"); fprintf(stderr, "-d parameter is required!\n");
usage(); usage();
return 1; err = 1;
} }
listen = bindsocket(inip, atoi(inpt)); if(! err) {
err = start_listener (inip, inpt, srcip, dstip, dstpt);
dst = malloc(sizeof(struct sockaddr_in));
dst->sin_family = AF_INET;
dst->sin_addr.s_addr = inet_addr( dstip );
dst->sin_port = htons( atoi( dstpt ) );
if(VERBOSE) {
fprintf(stderr, "Listening on %s:%s, forwarding to %s:%s",
inip, inpt, dstip, dstpt);
if(srcip != NULL)
fprintf(stderr, ", binding to %s\n", srcip);
else
fprintf(stderr, "\n");
} }
main_loop(listen, srcip, dst); /* FIXME: add sighandler */
/* FIXME: add sighandler, clean up mem */ if(srcip != NULL)
free(srcip);
return 0; if(dstip != NULL)
free(dstip);
if(inip != NULL)
free(inip);
if(inpt != NULL)
free(inpt);
if(dstpt != NULL)
free(dstpt);
return err;
} }
void usage() { void usage() {

View File

@@ -41,5 +41,6 @@
int main_loop(int listensocket, char *bindip, struct sockaddr_in *dst); int main_loop(int listensocket, char *bindip, struct sockaddr_in *dst);
void usage(); void usage();
int parse_ip(char *src, char *ip, char *pt);
#endif #endif