Skip to content

Instantly share code, notes, and snippets.

@heiher
Created September 24, 2021 15:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save heiher/dab15c5ee91c60e06af33721ca19b0e5 to your computer and use it in GitHub Desktop.
Save heiher/dab15c5ee91c60e06af33721ca19b0e5 to your computer and use it in GitHub Desktop.
TCP port mapping
/*
============================================================================
Name : portmap.c
Author : hev <r@hev.cc>
Copyright : Copyright (c) 2021 xyz
Description : TCP port mapping
============================================================================
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <hev-task.h>
#include <hev-task-dns.h>
#include <hev-task-system.h>
#include <hev-task-io.h>
#include <hev-task-io-socket.h>
#define TIMEOUT 300000
static int sport;
static const char *dport;
static const char *daddr;
static const char *iface;
static int
io_yielder (HevTaskYieldType type, void *data)
{
if (type == HEV_TASK_YIELD) {
hev_task_yield (HEV_TASK_YIELD);
return 0;
}
if (TIMEOUT < 0) {
hev_task_yield (HEV_TASK_WAITIO);
} else {
if (hev_task_sleep (TIMEOUT) == 0)
return -1;
}
return 0;
}
static void
task_client_entry (void *data)
{
struct addrinfo *result = NULL;
struct addrinfo hints = { 0 };
struct ifreq ifr = { 0 };
int cfd = (intptr_t)data;
int rfd;
int res;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
res = hev_task_dns_getaddrinfo (daddr, dport, &hints, &result);
if (res < 0)
goto exit1;
rfd = hev_task_io_socket_socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (rfd < 0)
goto exit2;
hev_task_add_fd (hev_task_self (), rfd, POLLIN | POLLOUT);
snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), iface);
res = setsockopt (rfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr));
if (res < 0)
goto exit3;
res = hev_task_io_socket_connect (rfd, result->ai_addr, result->ai_addrlen,
io_yielder, NULL);
if (res < 0)
goto exit3;
freeaddrinfo (result);
result = NULL;
hev_task_io_splice (cfd, cfd, rfd, rfd, 8192, io_yielder, NULL);
exit3:
close (rfd);
exit2:
if (result)
freeaddrinfo (result);
exit1:
close (cfd);
}
static void
task_listener_entry (void *data)
{
struct sockaddr_in addr = { 0 };
int reuse = 1;
int res;
int fd;
fd = hev_task_io_socket_socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd == -1) {
fprintf (stderr, "socket\n");
return;
}
res = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
if (res < 0) {
fprintf (stderr, "reuse\n");
close (fd);
return;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons (sport);
res = bind (fd, (struct sockaddr *)&addr, sizeof (addr));
if (res < 0) {
fprintf (stderr, "bind\n");
close (fd);
return;
}
res = listen (fd, 10);
if (res < 0) {
fprintf (stderr, "listen\n");
close (fd);
return;
}
hev_task_add_fd (hev_task_self (), fd, POLLIN);
while (1) {
HevTask *task;
int cfd;
cfd = hev_task_io_socket_accept (fd, NULL, NULL, NULL, NULL);
if (cfd < 0) {
hev_task_sleep (500);
continue;
}
task = hev_task_new (8192);
hev_task_add_fd (task, cfd, POLLIN | POLLOUT);
hev_task_run (task, task_client_entry, (void *)(intptr_t)cfd);
}
close (fd);
}
int
main (int argc, char *argv[])
{
struct rlimit limit = {
.rlim_cur = 65536,
.rlim_max = 65536,
};
HevTask *task;
if (argc != 5) {
fprintf (stderr, "%s SRCPORT DSTADDR DSTPORT IFACE\n", argv[0]);
return 0;
}
sport = atoi (argv[1]);
daddr = argv[2];
dport = argv[3];
iface = argv[4];
signal (SIGPIPE, SIG_IGN);
setrlimit (RLIMIT_NOFILE, &limit);
hev_task_system_init ();
task = hev_task_new (-1);
hev_task_run (task, task_listener_entry, NULL);
hev_task_system_run ();
hev_task_system_fini ();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment