Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple TCP server to show how to retrieve original dest IP:port after an iptables redirect #38

Merged
merged 3 commits into from
Jan 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions contrib/tools/server/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
server:
$(CC) -g -o server server.c

clean:
rm server

25 changes: 25 additions & 0 deletions contrib/tools/server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Sample TCP Server

This is a simple TCP server that listens on the specified port (3490 by default) and replies to incoming connections by providing info about src/destination ip/port.

It demonstrates the use of the getsockopt() system call with the ORIGINAL_DST option to retrieve the original destination ip/port after an iptables redirect.

So, for example, if you have the server listening on port 3490 on the local machine and an iptables rule like:

```
iptables -t nat -I OUTPUT 1 -p tcp --dport 4000:5000 -j REDIRECT --to-port 3490
```
your will see:

```
$ telnet localhost 3490
FROM 127.0.0.1:44978, TO 127.0.0.1:3490, ORIG DEST 127.0.0.1:3490

$ telnet localhost 4100
FROM 127.0.0.1:35476, TO 127.0.0.1:3490, ORIG DEST 127.0.0.1:4100

$ telnet 1.1.1.1 5000
FROM 100.100.100.100:60275, TO 127.0.0.1:3490, ORIG DEST 1.1.1.1:5000
```


169 changes: 169 additions & 0 deletions contrib/tools/server/server.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
** server.c
* Demo server to check that we can extract all parameters from an incoming
*request
* and respond to it
*/

#include <arpa/inet.h>
#include <errno.h>
#include <linux/netfilter_ipv4.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define DEFAULT_PORT \
"3490" // the port users will be connecting to, if not specified on command
// line

#define QUEUE_SIZE 10 // pending connections queue size

void sigchld_handler(int s) {
int saved_errno = errno;

while (waitpid(-1, NULL, WNOHANG) > 0)
;

errno = saved_errno;
}

int main(int argc, char *argv[]) {
struct sigaction sa;
const int yes = 1;
char *port = DEFAULT_PORT;
int rv;

if (argc > 1) {
port = argv[1];
}

struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP

struct addrinfo *server_info = NULL;
if ((rv = getaddrinfo(NULL, port, &hints, &server_info)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}

struct addrinfo *p;
int sockfd; // listen on sock_fd
for (p = server_info; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}

if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}

break;
}

freeaddrinfo(server_info);

if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, QUEUE_SIZE) == -1) {
perror("listen");
exit(1);
}

sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}

char bind_addr_str[INET6_ADDRSTRLEN] = {0};
struct sockaddr_in *bind_sock_addr = (struct sockaddr_in *)p->ai_addr;
inet_ntop(p->ai_family, &bind_sock_addr->sin_addr, bind_addr_str,
p->ai_addrlen);
printf("server %s: waiting for connections on port %s:%u...\n", port,
bind_addr_str, ntohs(bind_sock_addr->sin_port));

while (1) {
socklen_t addr_len = sizeof(struct sockaddr_storage);
int addr_str_len = INET6_ADDRSTRLEN;

struct sockaddr_storage their_addr = {
0}; // connector's address information
int accepted_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_len);
if (accepted_fd == -1) {
perror("accept");
continue;
}

struct sockaddr_in *src_sock_addr = (struct sockaddr_in *)&their_addr;
char their_addr_str[INET6_ADDRSTRLEN] = {0};
inet_ntop(their_addr.ss_family, &src_sock_addr->sin_addr, their_addr_str,
addr_str_len);
printf("server %s: got connection FROM %s:%u\n", port, their_addr_str,
ntohs(src_sock_addr->sin_port));

struct sockaddr_storage my_addr = {0}; // my address information
struct sockaddr_in *dst_sock_addr = (struct sockaddr_in *)&my_addr;
char my_addr_str[INET6_ADDRSTRLEN] = {0};
getsockname(accepted_fd, (struct sockaddr *)dst_sock_addr, &addr_len);
inet_ntop(my_addr.ss_family, &dst_sock_addr->sin_addr, my_addr_str,
addr_str_len);
printf("server %s: got connection TO %s:%u\n", port, my_addr_str,
ntohs(dst_sock_addr->sin_port));

struct sockaddr_storage orig_addr = {0}; // orig address information
struct sockaddr_in *orig_sock_addr = (struct sockaddr_in *)&orig_addr;
char orig_addr_str[INET6_ADDRSTRLEN] = {0};
int status = getsockopt(accepted_fd, SOL_IP, SO_ORIGINAL_DST,
orig_sock_addr, &addr_len);

if (status == 0) {
inet_ntop(orig_addr.ss_family, &orig_sock_addr->sin_addr, orig_addr_str,
addr_str_len);
printf("server %s: ORIG DEST %s:%u\n", port, orig_addr_str,
ntohs(orig_sock_addr->sin_port));
} else {
printf("Could not get orig destination from accepted socket.\n");
}

if (!fork()) { // this is the child process

close(sockfd);
char msg[256] = {0};
snprintf(msg, 256, "FROM %s:%u, TO %s:%u, ORIG DEST %s:%u\n",
their_addr_str, ntohs(src_sock_addr->sin_port), my_addr_str,
ntohs(dst_sock_addr->sin_port), orig_addr_str,
ntohs(orig_sock_addr->sin_port));

if (send(accepted_fd, msg, strlen(msg), 0) == -1) {
perror("send");
}
close(accepted_fd);
exit(0);
}
close(accepted_fd);
}

return 0;
}