TCP port mapping
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
============================================================================ | |
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