From 699e8b5680fc35ab57f74f22f4fe9e601d32662b Mon Sep 17 00:00:00 2001 From: zfl9 Date: Sat, 7 Mar 2020 14:57:46 +0800 Subject: use libev instead of libuv --- Makefile | 23 ++- dns2tcp.c | 569 +++++++++++++++++++++++++++++++++++++---------------------- dnsudp2tcp.c | 516 ----------------------------------------------------- logutils.h | 27 --- netutils.c | 43 ----- netutils.h | 42 ----- test | Bin 77424 -> 0 bytes 7 files changed, 375 insertions(+), 845 deletions(-) delete mode 100644 dnsudp2tcp.c delete mode 100644 logutils.h delete mode 100644 netutils.c delete mode 100644 netutils.h delete mode 100755 test diff --git a/Makefile b/Makefile index fee73ad..7267c36 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,15 @@ CC = gcc -CFLAGS = -std=c99 -Wall -Wextra -O3 -pthread -INCLUDES = -LDFLAGS = -LIBS = -luv -SRCS = netutils.c dns2tcp.c +CFLAGS = -std=c99 -Wall -Wextra -O2 -no-pie +LIBS = -lm +SRCS = dns2tcp.c OBJS = $(SRCS:.c=.o) MAIN = dns2tcp DESTDIR = /usr/local/bin +EVCFLAGS = -w -O2 -no-pie +EVSRCFILE = libev/ev.c +EVOBJFILE = ev.o + .PHONY: all install clean all: $(MAIN) @@ -19,8 +21,11 @@ install: $(MAIN) clean: $(RM) *.o $(MAIN) -$(MAIN): $(OBJS) - $(CC) $(CFLAGS) $(INCLUDES) -s -o $(MAIN) $(OBJS) $(LDFLAGS) $(LIBS) +$(MAIN): $(EVOBJFILE) $(OBJS) + $(CC) $(CFLAGS) -s -o $(MAIN) $(OBJS) $(EVOBJFILE) $(LIBS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ -.c.o: - $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ +$(EVOBJFILE): $(EVSRCFILE) + $(CC) $(EVCFLAGS) -c $(EVSRCFILE) -o $(EVOBJFILE) diff --git a/dns2tcp.c b/dns2tcp.c index 79b5d41..be45d83 100644 --- a/dns2tcp.c +++ b/dns2tcp.c @@ -1,50 +1,204 @@ #define _GNU_SOURCE -#include "logutils.h" -#include "netutils.h" -#include #include #include +#include #include #include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include "libev/ev.h" #undef _GNU_SOURCE +#define DNS2TCP_VER "dns2tcp v1.1.0" + +#ifndef IPV6_V6ONLY + #define IPV6_V6ONLY 26 +#endif +#ifndef SO_REUSEPORT + #define SO_REUSEPORT 15 +#endif +#ifndef TCP_QUICKACK + #define TCP_QUICKACK 12 +#endif +#ifndef TCP_SYNCNT + #define TCP_SYNCNT 7 +#endif +#ifndef MSG_FASTOPEN + #define MSG_FASTOPEN 0x20000000 +#endif + +#define IP4STRLEN INET_ADDRSTRLEN /* ipv4addr max strlen */ +#define IP6STRLEN INET6_ADDRSTRLEN /* ipv6addr max strlen */ +#define PORTSTRLEN 6 /* "65535", include the null character */ +#define UDPDGRAM_MAXSIZ 1472 /* mtu:1500 - iphdr:20 - udphdr:8 */ + +typedef uint16_t portno_t; /* 16bit */ +typedef struct sockaddr_in skaddr4_t; +typedef struct sockaddr_in6 skaddr6_t; + #define IF_VERBOSE if (g_verbose) -#define DNS2TCP_VERSION "dns2tcp v1.0" + +#define LOGINF(fmt, ...) \ + do { \ + struct tm *tm = localtime(&(time_t){time(NULL)}); \ + printf("\e[1;32m%04d-%02d-%02d %02d:%02d:%02d INF:\e[0m " fmt "\n", \ + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, \ + tm->tm_hour, tm->tm_min, tm->tm_sec, \ + ##__VA_ARGS__); \ + } while (0) + +#define LOGERR(fmt, ...) \ + do { \ + struct tm *tm = localtime(&(time_t){time(NULL)}); \ + printf("\e[1;35m%04d-%02d-%02d %02d:%02d:%02d ERR:\e[0m " fmt "\n", \ + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, \ + tm->tm_hour, tm->tm_min, tm->tm_sec, \ + ##__VA_ARGS__); \ + } while (0) typedef struct { - void *buffer; - uint16_t nread; + evio_t watcher; + uint8_t buffer[2 + UDPDGRAM_MAXSIZ]; /* msglen(16bit) + msgbuf */ + uint16_t nrcvsnd; skaddr6_t srcaddr; -} tcp_data_t; +} tcpwatcher_t; + +enum { + OPT_IPV6_V6ONLY = 1 << 0, + OPT_REUSE_PORT = 1 << 1, + OPT_QUICK_ACK = 1 << 2, + OPT_FAST_OPEN = 1 << 3, +}; static bool g_verbose = false; -static uv_loop_t *g_evloop = NULL; -static uv_udp_t *g_udp_server = NULL; +static uint8_t g_options = 0; +static uint8_t g_syn_maxcnt = 0; +static int g_udp_sockfd = -1; static char g_listen_ipstr[IP6STRLEN] = {0}; static portno_t g_listen_portno = 0; static skaddr6_t g_listen_skaddr = {0}; static char g_remote_ipstr[IP6STRLEN] = {0}; static portno_t g_remote_portno = 0; static skaddr6_t g_remote_skaddr = {0}; +static char g_ipstr_buf[IP6STRLEN] = {0}; + +static void udp_recvmsg_cb(evloop_t *evloop, evio_t *watcher, int events); +static void tcp_connect_cb(evloop_t *evloop, evio_t *watcher, int events); +static void tcp_sendmsg_cb(evloop_t *evloop, evio_t *watcher, int events); +static void tcp_recvmsg_cb(evloop_t *evloop, evio_t *watcher, int events); + +static void set_nonblock(int sockfd) { + int flags = fcntl(sockfd, F_GETFL, 0); + if (flags < 0) { + LOGERR("[set_nonblock] fcntl(%d, F_GETFL): (%d) %s", sockfd, errno, strerror(errno)); + exit(errno); + } + if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { + LOGERR("[set_nonblock] fcntl(%d, F_SETFL): (%d) %s", sockfd, errno, strerror(errno)); + exit(errno); + } +} + +static void set_ipv6only(int sockfd) { + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){1}, sizeof(int)) < 0) { + LOGERR("[set_ipv6only] setsockopt(%d, IPV6_V6ONLY): (%d) %s", sockfd, errno, strerror(errno)); + exit(errno); + } +} + +static void set_reuseaddr(int sockfd) { + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) { + LOGERR("[set_reuseaddr] setsockopt(%d, SO_REUSEADDR): (%d) %s", sockfd, errno, strerror(errno)); + exit(errno); + } +} + +static void set_reuseport(int sockfd) { + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)) < 0) { + LOGERR("[set_reuseport] setsockopt(%d, SO_REUSEPORT): (%d) %s", sockfd, errno, strerror(errno)); + exit(errno); + } +} + +static void set_nodelay(int sockfd) { + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &(int){1}, sizeof(int)) < 0) { + LOGERR("[set_nodelay] setsockopt(%d, TCP_NODELAY): (%d) %s", sockfd, errno, strerror(errno)); + exit(errno); + } +} + +static void set_quickack(int sockfd) { + if (setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &(int){1}, sizeof(int)) < 0) { + LOGERR("[set_quickack] setsockopt(%d, TCP_QUICKACK): (%d) %s", sockfd, errno, strerror(errno)); + exit(errno); + } +} + +static void set_syncnt(int sockfd, int syncnt) { + if (setsockopt(sockfd, IPPROTO_TCP, TCP_SYNCNT, &syncnt, sizeof(syncnt)) < 0) { + LOGERR("[set_syncnt] setsockopt(%d, TCP_SYNCNT): (%d) %s", sockfd, errno, strerror(errno)); + exit(errno); + } +} + +static int get_ipstr_family(const char *ipstr) { + if (!ipstr) return -1; /* invalid */ + uint8_t ipaddr[16]; /* 16-bytes */ + if (inet_pton(AF_INET, ipstr, &ipaddr) == 1) { + return AF_INET; + } else if (inet_pton(AF_INET6, ipstr, &ipaddr) == 1) { + return AF_INET6; + } else { + return -1; /* invalid */ + } +} -static void udp_alloc_cb(uv_handle_t *udp_server, size_t sugsize, uv_buf_t *uvbuf); -static void udp_recv_cb(uv_udp_t *udp_server, ssize_t nread, const uv_buf_t *uvbuf, const skaddr_t *skaddr, unsigned flags); +static void build_socket_addr(int ipfamily, void *skaddr, const char *ipstr, portno_t portno) { + if (ipfamily == AF_INET) { + skaddr4_t *addr = skaddr; + addr->sin_family = AF_INET; + inet_pton(AF_INET, ipstr, &addr->sin_addr); + addr->sin_port = htons(portno); + } else { + skaddr6_t *addr = skaddr; + addr->sin6_family = AF_INET6; + inet_pton(AF_INET6, ipstr, &addr->sin6_addr); + addr->sin6_port = htons(portno); + } +} -static void tcp_connect_cb(uv_connect_t *connreq, int status); -static void tcp_write_cb(uv_write_t *writereq, int status); -static void tcp_alloc_cb(uv_handle_t *tcp_client, size_t sugsize, uv_buf_t *uvbuf); -static void tcp_read_cb(uv_stream_t *tcp_client, ssize_t nread, const uv_buf_t *uvbuf); -static void tcp_close_cb(uv_handle_t *tcp_client); +static void parse_socket_addr(const void *skaddr, char *ipstr, portno_t *portno) { + if (((skaddr4_t *)skaddr)->sin_family == AF_INET) { + const skaddr4_t *addr = skaddr; + inet_ntop(AF_INET, &addr->sin_addr, ipstr, IP4STRLEN); + *portno = ntohs(addr->sin_port); + } else { + const skaddr6_t *addr = skaddr; + inet_ntop(AF_INET6, &addr->sin6_addr, ipstr, IP6STRLEN); + *portno = ntohs(addr->sin6_port); + } +} static void print_command_help(void) { - printf("usage: dns2tcp <-L LISTEN_ADDR> <-R REMOTE_ADDR> [-vVh]\n" - " -L udp listen address, it is required\n" - " -R tcp remote address, it is required\n" - " -v print verbose log, default: \n" - " -V print version number of dns2tcp and exit\n" - " -h print help information of dns2tcp and exit\n" + printf("usage: dns2tcp <-L listen> <-R remote> [-s syncnt] [-6rafvVh]\n" + " -L udp listen address, this is required\n" + " -R tcp remote address, this is required\n" + " -s set TCP_SYNCNT(max) for remote socket\n" + " -6 enable IPV6_V6ONLY for listen socket\n" + " -r enable SO_REUSEPORT for listen socket\n" + " -a enable TCP_QUICKACK for remote socket\n" + " -f enable TCP_FASTOPEN for remote socket\n" + " -v print verbose log, default: \n" + " -V print version number of dns2tcp and exit\n" + " -h print help information of dns2tcp and exit\n" "bug report: https://github.com/zfl9/dns2tcp. email: zfl9.com@gmail.com\n" ); } @@ -54,11 +208,15 @@ static void parse_address_opt(char *ip_port_str, bool is_listen_addr) { char *portstr = strchr(ip_port_str, '#'); if (!portstr) { - printf("[parse_address_opt] %s port is not specified\n", opt_name); + printf("[parse_address_opt] %s port not provided\n", opt_name); goto PRINT_HELP_AND_EXIT; } if (portstr == ip_port_str) { - printf("[parse_address_opt] %s addr is not specified\n", opt_name); + printf("[parse_address_opt] %s addr not provided\n", opt_name); + goto PRINT_HELP_AND_EXIT; + } + if (portstr == ip_port_str + strlen(ip_port_str) - 1) { + printf("[parse_address_opt] %s port not provided\n", opt_name); goto PRINT_HELP_AND_EXIT; } @@ -67,13 +225,13 @@ static void parse_address_opt(char *ip_port_str, bool is_listen_addr) { printf("[parse_address_opt] %s port is invalid: %s\n", opt_name, portstr); goto PRINT_HELP_AND_EXIT; } - portno_t portno = strtol(portstr, NULL, 10); + portno_t portno = strtoul(portstr, NULL, 10); if (portno == 0) { printf("[parse_address_opt] %s port is invalid: %s\n", opt_name, portstr); goto PRINT_HELP_AND_EXIT; } - char *ipstr = ip_port_str; + const char *ipstr = ip_port_str; if (strlen(ipstr) + 1 > IP6STRLEN) { printf("[parse_address_opt] %s addr is invalid: %s\n", opt_name, ipstr); goto PRINT_HELP_AND_EXIT; @@ -84,19 +242,14 @@ static void parse_address_opt(char *ip_port_str, bool is_listen_addr) { goto PRINT_HELP_AND_EXIT; } - void *skaddr_ptr = is_listen_addr ? &g_listen_skaddr : &g_remote_skaddr; - if (ipfamily == AF_INET) { - build_ipv4_addr(skaddr_ptr, ipstr, portno); - } else { - build_ipv6_addr(skaddr_ptr, ipstr, portno); - } - if (is_listen_addr) { strcpy(g_listen_ipstr, ipstr); g_listen_portno = portno; + build_socket_addr(ipfamily, &g_listen_skaddr, ipstr, portno); } else { strcpy(g_remote_ipstr, ipstr); g_remote_portno = portno; + build_socket_addr(ipfamily, &g_remote_skaddr, ipstr, portno); } return; @@ -106,25 +259,52 @@ PRINT_HELP_AND_EXIT: } static void parse_command_args(int argc, char *argv[]) { - char *opt_listen_addr = NULL; - char *opt_remote_addr = NULL; + char opt_listen_addr[IP6STRLEN + PORTSTRLEN] = {0}; + char opt_remote_addr[IP6STRLEN + PORTSTRLEN] = {0}; opterr = 0; int shortopt = -1; - const char *optstr = "L:R:vVh"; + const char *optstr = "L:R:s:6rafvVh"; while ((shortopt = getopt(argc, argv, optstr)) != -1) { switch (shortopt) { case 'L': - opt_listen_addr = optarg; + if (strlen(optarg) + 1 > IP6STRLEN + PORTSTRLEN) { + printf("[parse_command_args] invalid listen addr: %s\n", optarg); + goto PRINT_HELP_AND_EXIT; + } + strcpy(opt_listen_addr, optarg); break; case 'R': - opt_remote_addr = optarg; + if (strlen(optarg) + 1 > IP6STRLEN + PORTSTRLEN) { + printf("[parse_command_args] invalid remote addr: %s\n", optarg); + goto PRINT_HELP_AND_EXIT; + } + strcpy(opt_remote_addr, optarg); + break; + case 's': + g_syn_maxcnt = strtoul(optarg, NULL, 10); + if (g_syn_maxcnt == 0) { + printf("[parse_command_args] invalid tcp syn cnt: %s\n", optarg); + goto PRINT_HELP_AND_EXIT; + } + break; + case '6': + g_options |= OPT_IPV6_V6ONLY; + break; + case 'r': + g_options |= OPT_REUSE_PORT; + break; + case 'a': + g_options |= OPT_QUICK_ACK; + break; + case 'f': + g_options |= OPT_FAST_OPEN; break; case 'v': g_verbose = true; break; case 'V': - printf(DNS2TCP_VERSION"\n"); + printf(DNS2TCP_VER"\n"); exit(0); case 'h': print_command_help(); @@ -139,23 +319,17 @@ static void parse_command_args(int argc, char *argv[]) { } } - if (!opt_listen_addr) { + if (strlen(opt_listen_addr) == 0) { printf("[parse_command_args] missing option: '-L'\n"); goto PRINT_HELP_AND_EXIT; } - if (!opt_remote_addr) { + if (strlen(opt_remote_addr) == 0) { printf("[parse_command_args] missing option: '-R'\n"); goto PRINT_HELP_AND_EXIT; } - do { - char listenaddr_optstring[strlen(opt_listen_addr) + 1]; - char remoteaddr_optstring[strlen(opt_remote_addr) + 1]; - strcpy(listenaddr_optstring, opt_listen_addr); - strcpy(remoteaddr_optstring, opt_remote_addr); - parse_address_opt(listenaddr_optstring, true); - parse_address_opt(remoteaddr_optstring, false); - } while (0); + parse_address_opt(opt_listen_addr, true); + parse_address_opt(opt_remote_addr, false); return; PRINT_HELP_AND_EXIT: @@ -170,194 +344,173 @@ int main(int argc, char *argv[]) { LOGINF("[main] udp listen addr: %s#%hu", g_listen_ipstr, g_listen_portno); LOGINF("[main] tcp remote addr: %s#%hu", g_remote_ipstr, g_remote_portno); + if (g_syn_maxcnt) LOGINF("[main] enable TCP_SYNCNT:%hhu sockopt", g_syn_maxcnt); + if (g_options & OPT_IPV6_V6ONLY) LOGINF("[main] enable IPV6_V6ONLY sockopt"); + if (g_options & OPT_REUSE_PORT) LOGINF("[main] enable SO_REUSEPORT sockopt"); + if (g_options & OPT_QUICK_ACK) LOGINF("[main] enable TCP_QUICKACK sockopt"); + if (g_options & OPT_FAST_OPEN) LOGINF("[main] enable TCP_FASTOPEN sockopt"); IF_VERBOSE LOGINF("[main] verbose mode, affect performance"); - g_evloop = uv_default_loop(); - g_udp_server = &(uv_udp_t){0}; - uv_udp_init(g_evloop, g_udp_server); - - int retval = uv_udp_bind(g_udp_server, (void *)&g_listen_skaddr, (g_listen_skaddr.sin6_family == AF_INET) ? 0 : UV_UDP_IPV6ONLY); - if (retval < 0) { - LOGERR("[main] bind failed: (%d) %s", -retval, uv_strerror(retval)); - return -retval; + g_udp_sockfd = socket(g_listen_skaddr.sin6_family, SOCK_DGRAM, 0); + if (g_udp_sockfd < 0) { + LOGERR("[main] create udp socket: (%d) %s", errno, strerror(errno)); + return errno; } - uv_udp_recv_start(g_udp_server, udp_alloc_cb, udp_recv_cb); - uv_run(g_evloop, UV_RUN_DEFAULT); - return 0; -} + set_nonblock(g_udp_sockfd); + set_reuseaddr(g_udp_sockfd); + if (g_options & OPT_REUSE_PORT) set_reuseport(g_udp_sockfd); + if ((g_options & OPT_IPV6_V6ONLY) && g_listen_skaddr.sin6_family == AF_INET6) set_ipv6only(g_udp_sockfd); -static void udp_alloc_cb(uv_handle_t *udp_server __attribute__((unused)), size_t sugsize __attribute__((unused)), uv_buf_t *uvbuf) { - uvbuf->base = malloc(DNS_PACKET_MAXSIZE + 2) + 2; - uvbuf->len = DNS_PACKET_MAXSIZE; -} + if (bind(g_udp_sockfd, (void *)&g_listen_skaddr, g_listen_skaddr.sin6_family == AF_INET ? sizeof(skaddr4_t) : sizeof(skaddr6_t)) < 0) { + LOGERR("[main] bind udp address: (%d) %s", errno, strerror(errno)); + return errno; + } -static void udp_recv_cb(uv_udp_t *udp_server __attribute__((unused)), ssize_t nread, const uv_buf_t *uvbuf, const skaddr_t *skaddr, unsigned flags) { - if (nread == 0) goto FREE_UVBUF; + evloop_t *evloop = ev_default_loop(0); + evio_t *watcher = &(evio_t){0}; + ev_io_init(watcher, udp_recvmsg_cb, g_udp_sockfd, EV_READ); + ev_io_start(evloop, watcher); - if (nread < 0) { - LOGERR("[udp_recv_cb] recv failed: (%zd) %s", -nread, uv_strerror(nread)); - goto FREE_UVBUF; - } + ev_run(evloop, 0); + return 0; +} - if (flags & UV_UDP_PARTIAL) { - LOGERR("[udp_recv_cb] received a partial packet, discard it"); - goto FREE_UVBUF; +static void udp_recvmsg_cb(evloop_t *evloop, evio_t *watcher __attribute__((unused)), int events __attribute__((unused))) { + tcpwatcher_t *tcpw = malloc(sizeof(*tcpw)); + ssize_t nrecv = recvfrom(g_udp_sockfd, (void *)tcpw->buffer + 2, UDPDGRAM_MAXSIZ, 0, (void *)&tcpw->srcaddr, &(socklen_t){sizeof(tcpw->srcaddr)}); + if (nrecv < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + LOGERR("[udp_recvmsg_cb] recv from udp socket: (%d) %s", errno, strerror(errno)); + } + goto FREE_TCP_WATCHER; } - - bool is_ipv4 = skaddr->sa_family == AF_INET; IF_VERBOSE { - char ipstr[IP6STRLEN]; portno_t portno; - if (is_ipv4) { - parse_ipv4_addr((void *)skaddr, ipstr, &portno); + portno_t portno; + parse_socket_addr(&tcpw->srcaddr, g_ipstr_buf, &portno); + LOGINF("[udp_recvmsg_cb] recv from %s#%hu, nrecv:%zd", g_ipstr_buf, portno, nrecv); + } + *(uint16_t *)tcpw->buffer = htons(nrecv); + nrecv += 2; /* msglen + msgbuf */ + tcpw->nrcvsnd = 0; + + int sockfd = socket(g_remote_skaddr.sin6_family, SOCK_STREAM, 0); + if (sockfd < 0) { + LOGERR("[udp_recvmsg_cb] create tcp socket: (%d) %s", errno, strerror(errno)); + goto FREE_TCP_WATCHER; + } + set_nonblock(sockfd); + set_reuseaddr(sockfd); + set_nodelay(sockfd); + if (g_syn_maxcnt) set_syncnt(sockfd, g_syn_maxcnt); + if (g_options & OPT_QUICK_ACK) set_quickack(sockfd); + + bool tfo_succ = false; + if (g_options & OPT_FAST_OPEN) { + ssize_t nsend = sendto(sockfd, tcpw->buffer, nrecv, MSG_FASTOPEN, (void *)&g_remote_skaddr, g_remote_skaddr.sin6_family == AF_INET ? sizeof(skaddr4_t) : sizeof(skaddr6_t)); + if (nsend < 0) { + if (errno != EINPROGRESS) { + LOGERR("[udp_recvmsg_cb] connect to %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); + goto CLOSE_TCP_SOCKFD; + } + IF_VERBOSE LOGINF("[udp_recvmsg_cb] try to connect to %s#%hu", g_remote_ipstr, g_remote_portno); } else { - parse_ipv6_addr((void *)skaddr, ipstr, &portno); + tfo_succ = true; + tcpw->nrcvsnd = nsend; + IF_VERBOSE LOGINF("[udp_recvmsg_cb] tcp_fastopen connect, nsend:%zd", nsend); + } + } else { + if (connect(sockfd, (void *)&g_remote_skaddr, g_remote_skaddr.sin6_family == AF_INET ? sizeof(skaddr4_t) : sizeof(skaddr6_t)) < 0 && errno != EINPROGRESS) { + LOGERR("[udp_recvmsg_cb] connect to %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); + goto CLOSE_TCP_SOCKFD; } - LOGINF("[udp_recv_cb] recv %zdB data from %s#%hu", nread, ipstr, portno); + IF_VERBOSE LOGINF("[udp_recvmsg_cb] try to connect to %s#%hu", g_remote_ipstr, g_remote_portno); } - uint16_t *msglen_ptr = (void *)uvbuf->base - 2; - *msglen_ptr = htons(nread); - - uv_tcp_t *tcp_client = malloc(sizeof(uv_tcp_t)); - uv_tcp_init(g_evloop, tcp_client); - uv_tcp_nodelay(tcp_client, 1); - - tcp_data_t *tcp_data = calloc(1, sizeof(tcp_data_t)); - tcp_data->buffer = uvbuf->base - 2; - memcpy(&tcp_data->srcaddr, skaddr, is_ipv4 ? sizeof(skaddr4_t) : sizeof(skaddr6_t)); - tcp_client->data = tcp_data; - - uv_connect_t *connreq = malloc(sizeof(uv_connect_t)); - int retval = uv_tcp_connect(connreq, tcp_client, (void *)&g_remote_skaddr, tcp_connect_cb); - if (retval < 0) { - LOGERR("[udp_recv_cb] connect failed: (%d) %s", -retval, uv_strerror(retval)); - uv_close((void *)tcp_client, tcp_close_cb); - free(connreq); - return; + if (tfo_succ && tcpw->nrcvsnd >= nrecv) { + tcpw->nrcvsnd = 0; /* reset to zero for recv data */ + ev_io_init((evio_t *)tcpw, tcp_recvmsg_cb, sockfd, EV_READ); + } else { + ev_io_init((evio_t *)tcpw, tfo_succ ? tcp_sendmsg_cb : tcp_connect_cb, sockfd, EV_WRITE); } - IF_VERBOSE LOGINF("[udp_recv_cb] connecting to %s#%hu", g_remote_ipstr, g_remote_portno); + ev_io_start(evloop, (evio_t *)tcpw); return; -FREE_UVBUF: - free(uvbuf->base - 2); +CLOSE_TCP_SOCKFD: + close(sockfd); +FREE_TCP_WATCHER: + free(tcpw); } -static void tcp_connect_cb(uv_connect_t *connreq, int status) { - uv_stream_t *tcp_client = connreq->handle; - tcp_data_t *tcp_data = tcp_client->data; - free(connreq); - - if (status < 0) { - LOGERR("[tcp_connect_cb] connect failed: (%d) %s", -status, uv_strerror(status)); - goto CLOSE_TCPCLIENT; - } - IF_VERBOSE LOGINF("[tcp_connect_cb] connected to %s#%hu", g_remote_ipstr, g_remote_portno); - - uint16_t msglen = ntohs(*(uint16_t *)tcp_data->buffer); - uv_buf_t uvbufs[] = {{ - .base = tcp_data->buffer, - .len = msglen + 2, - }}; - uv_write_t *writereq = malloc(sizeof(uv_write_t)); - status = uv_write(writereq, tcp_client, uvbufs, 1, tcp_write_cb); - if (status < 0) { - LOGERR("[tcp_connect_cb] write failed: (%d) %s", -status, uv_strerror(status)); - free(writereq); - goto CLOSE_TCPCLIENT; - } - IF_VERBOSE LOGINF("[tcp_connect_cb] writing %huB data to %s#%hu", msglen, g_remote_ipstr, g_remote_portno); - return; - -CLOSE_TCPCLIENT: - uv_close((void *)tcp_client, tcp_close_cb); -} - -static void tcp_write_cb(uv_write_t *writereq, int status) { - uv_stream_t *tcp_client = writereq->handle; - free(writereq); - - if (status < 0) { - LOGERR("[tcp_write_cb] write failed: (%d) %s", -status, uv_strerror(status)); - uv_close((void *)tcp_client, tcp_close_cb); +static void tcp_connect_cb(evloop_t *evloop, evio_t *watcher, int events __attribute__((unused))) { + if (getsockopt(watcher->fd, SOL_SOCKET, SO_ERROR, &errno, &(socklen_t){sizeof(errno)}) < 0 || errno) { + LOGERR("[tcp_connect_cb] connect to %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); + ev_io_stop(evloop, watcher); + close(watcher->fd); + free(watcher); return; } - IF_VERBOSE LOGINF("[tcp_write_cb] data has been written to %s#%hu", g_remote_ipstr, g_remote_portno); - - uv_read_start(tcp_client, tcp_alloc_cb, tcp_read_cb); -} - -static void tcp_alloc_cb(uv_handle_t *tcp_client, size_t sugsize __attribute__((unused)), uv_buf_t *uvbuf) { - tcp_data_t *tcp_data = tcp_client->data; - uvbuf->base = tcp_data->buffer + tcp_data->nread; - uvbuf->len = DNS_PACKET_MAXSIZE + 2 - tcp_data->nread; + IF_VERBOSE LOGINF("[tcp_connect_cb] connect to %s#%hu succeed", g_remote_ipstr, g_remote_portno); + ev_set_cb(watcher, tcp_sendmsg_cb); + ev_invoke(evloop, watcher, EV_WRITE); } -static void tcp_read_cb(uv_stream_t *tcp_client, ssize_t nread, const uv_buf_t *uvbuf __attribute__((unused))) { - tcp_data_t *tcp_data = tcp_client->data; - - if (nread == 0) return; - if (nread < 0) { - if (nread != UV_EOF) LOGERR("[tcp_read_cb] read failed: (%zd) %s", -nread, uv_strerror(nread)); - goto CLOSE_TCPCLIENT; +static void tcp_sendmsg_cb(evloop_t *evloop, evio_t *watcher, int events __attribute__((unused))) { + tcpwatcher_t *tcpw = (void *)watcher; + uint16_t datalen = 2 + ntohs(*(uint16_t *)tcpw->buffer); + ssize_t nsend = send(watcher->fd, (void *)tcpw->buffer + tcpw->nrcvsnd, datalen - tcpw->nrcvsnd, 0); + if (nsend < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) return; + LOGERR("[tcp_sendmsg_cb] send to %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); + ev_io_stop(evloop, watcher); + close(watcher->fd); + free(watcher); + return; } - if (tcp_data->nread == 0 && nread < 2) { - LOGERR("[tcp_read_cb] message length is too small, discard it"); - goto CLOSE_TCPCLIENT; + IF_VERBOSE LOGINF("[tcp_sendmsg_cb] send to %s#%hu, nsend:%zd", g_remote_ipstr, g_remote_portno, nsend); + tcpw->nrcvsnd += nsend; + if (tcpw->nrcvsnd >= datalen) { + tcpw->nrcvsnd = 0; /* reset to zero for recv data */ + ev_io_stop(evloop, watcher); + ev_io_init(watcher, tcp_recvmsg_cb, watcher->fd, EV_READ); + ev_io_start(evloop, watcher); } - tcp_data->nread += nread; - - uint16_t msglen = ntohs(*(uint16_t *)tcp_data->buffer); - if (tcp_data->nread - nread == 0) { - if (msglen > DNS_PACKET_MAXSIZE) { - LOGERR("[tcp_read_cb] message length is too large, discard it"); - goto CLOSE_TCPCLIENT; - } - if (tcp_data->nread > msglen + 2) { - LOGERR("[tcp_read_cb] message length is incorrect, discard it"); - goto CLOSE_TCPCLIENT; - } - } - if (tcp_data->nread < msglen + 2) return; /* received partial data */ +} - uv_buf_t uvbufs[] = {{ - .base = tcp_data->buffer + 2, - .len = msglen, - }}; +static void tcp_recvmsg_cb(evloop_t *evloop, evio_t *watcher, int events __attribute__((unused))) { + tcpwatcher_t *tcpw = (void *)watcher; + void *buffer = tcpw->buffer; - IF_VERBOSE { - LOGINF("[tcp_read_cb] recv %huB data from %s#%hu", msglen, g_remote_ipstr, g_remote_portno); - char ipstr[IP6STRLEN]; portno_t portno; - if (tcp_data->srcaddr.sin6_family == AF_INET) { - parse_ipv4_addr((void *)&tcp_data->srcaddr, ipstr, &portno); - } else { - parse_ipv6_addr((void *)&tcp_data->srcaddr, ipstr, &portno); - } - LOGINF("[tcp_read_cb] send %huB data to %s#%hu", msglen, ipstr, portno); + ssize_t nrecv = recv(watcher->fd, buffer + tcpw->nrcvsnd, 2 + UDPDGRAM_MAXSIZ - tcpw->nrcvsnd, 0); + if (nrecv < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) return; + LOGERR("[tcp_recvmsg_cb] recv from %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); + goto FREE_TCP_WATCHER; } - - nread = uv_udp_try_send(g_udp_server, uvbufs, 1, (void *)&tcp_data->srcaddr); - if (nread < 0) { - LOGERR("[tcp_read_cb] send failed: (%zd) %s", -nread, uv_strerror(nread)); + if (nrecv == 0) { + LOGERR("[tcp_recvmsg_cb] recv from %s#%hu: connection is closed", g_remote_ipstr, g_remote_portno); + goto FREE_TCP_WATCHER; + } + tcpw->nrcvsnd += nrecv; + IF_VERBOSE LOGINF("[tcp_recvmsg_cb] recv from %s#%hu, nrecv:%zd", g_remote_ipstr, g_remote_portno, nrecv); + if (tcpw->nrcvsnd < 2 || tcpw->nrcvsnd < 2 + ntohs(*(uint16_t *)buffer)) return; + + const void *sendto_skaddr = &tcpw->srcaddr; + socklen_t sendto_skaddrlen = tcpw->srcaddr.sin6_family == AF_INET ? sizeof(skaddr4_t) : sizeof(skaddr6_t); + ssize_t nsend = sendto(g_udp_sockfd, buffer + 2, ntohs(*(uint16_t *)buffer), 0, sendto_skaddr, sendto_skaddrlen); + if (nsend < 0) { + portno_t portno; + parse_socket_addr(&tcpw->srcaddr, g_ipstr_buf, &portno); + LOGERR("[tcp_recvmsg_cb] send to %s#%hu: (%d) %s", g_ipstr_buf, portno, errno, strerror(errno)); } else { IF_VERBOSE { - char ipstr[IP6STRLEN]; portno_t portno; - if (tcp_data->srcaddr.sin6_family == AF_INET) { - parse_ipv4_addr((void *)&tcp_data->srcaddr, ipstr, &portno); - } else { - parse_ipv6_addr((void *)&tcp_data->srcaddr, ipstr, &portno); - } - LOGINF("[tcp_read_cb] data has been written to %s#%hu", ipstr, portno); + portno_t portno; + parse_socket_addr(&tcpw->srcaddr, g_ipstr_buf, &portno); + LOGINF("[tcp_recvmsg_cb] send to %s#%hu, nsend:%zd", g_ipstr_buf, portno, nsend); } } - -CLOSE_TCPCLIENT: - uv_close((void *)tcp_client, tcp_close_cb); -} - -static void tcp_close_cb(uv_handle_t *tcp_client) { - tcp_data_t *tcp_data = tcp_client->data; - free(tcp_data->buffer); - free(tcp_data); - free(tcp_client); +FREE_TCP_WATCHER: + ev_io_stop(evloop, watcher); + close(watcher->fd); + free(watcher); } diff --git a/dnsudp2tcp.c b/dnsudp2tcp.c deleted file mode 100644 index be45d83..0000000 --- a/dnsudp2tcp.c +++ /dev/null @@ -1,516 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "libev/ev.h" -#undef _GNU_SOURCE - -#define DNS2TCP_VER "dns2tcp v1.1.0" - -#ifndef IPV6_V6ONLY - #define IPV6_V6ONLY 26 -#endif -#ifndef SO_REUSEPORT - #define SO_REUSEPORT 15 -#endif -#ifndef TCP_QUICKACK - #define TCP_QUICKACK 12 -#endif -#ifndef TCP_SYNCNT - #define TCP_SYNCNT 7 -#endif -#ifndef MSG_FASTOPEN - #define MSG_FASTOPEN 0x20000000 -#endif - -#define IP4STRLEN INET_ADDRSTRLEN /* ipv4addr max strlen */ -#define IP6STRLEN INET6_ADDRSTRLEN /* ipv6addr max strlen */ -#define PORTSTRLEN 6 /* "65535", include the null character */ -#define UDPDGRAM_MAXSIZ 1472 /* mtu:1500 - iphdr:20 - udphdr:8 */ - -typedef uint16_t portno_t; /* 16bit */ -typedef struct sockaddr_in skaddr4_t; -typedef struct sockaddr_in6 skaddr6_t; - -#define IF_VERBOSE if (g_verbose) - -#define LOGINF(fmt, ...) \ - do { \ - struct tm *tm = localtime(&(time_t){time(NULL)}); \ - printf("\e[1;32m%04d-%02d-%02d %02d:%02d:%02d INF:\e[0m " fmt "\n", \ - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, \ - tm->tm_hour, tm->tm_min, tm->tm_sec, \ - ##__VA_ARGS__); \ - } while (0) - -#define LOGERR(fmt, ...) \ - do { \ - struct tm *tm = localtime(&(time_t){time(NULL)}); \ - printf("\e[1;35m%04d-%02d-%02d %02d:%02d:%02d ERR:\e[0m " fmt "\n", \ - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, \ - tm->tm_hour, tm->tm_min, tm->tm_sec, \ - ##__VA_ARGS__); \ - } while (0) - -typedef struct { - evio_t watcher; - uint8_t buffer[2 + UDPDGRAM_MAXSIZ]; /* msglen(16bit) + msgbuf */ - uint16_t nrcvsnd; - skaddr6_t srcaddr; -} tcpwatcher_t; - -enum { - OPT_IPV6_V6ONLY = 1 << 0, - OPT_REUSE_PORT = 1 << 1, - OPT_QUICK_ACK = 1 << 2, - OPT_FAST_OPEN = 1 << 3, -}; - -static bool g_verbose = false; -static uint8_t g_options = 0; -static uint8_t g_syn_maxcnt = 0; -static int g_udp_sockfd = -1; -static char g_listen_ipstr[IP6STRLEN] = {0}; -static portno_t g_listen_portno = 0; -static skaddr6_t g_listen_skaddr = {0}; -static char g_remote_ipstr[IP6STRLEN] = {0}; -static portno_t g_remote_portno = 0; -static skaddr6_t g_remote_skaddr = {0}; -static char g_ipstr_buf[IP6STRLEN] = {0}; - -static void udp_recvmsg_cb(evloop_t *evloop, evio_t *watcher, int events); -static void tcp_connect_cb(evloop_t *evloop, evio_t *watcher, int events); -static void tcp_sendmsg_cb(evloop_t *evloop, evio_t *watcher, int events); -static void tcp_recvmsg_cb(evloop_t *evloop, evio_t *watcher, int events); - -static void set_nonblock(int sockfd) { - int flags = fcntl(sockfd, F_GETFL, 0); - if (flags < 0) { - LOGERR("[set_nonblock] fcntl(%d, F_GETFL): (%d) %s", sockfd, errno, strerror(errno)); - exit(errno); - } - if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { - LOGERR("[set_nonblock] fcntl(%d, F_SETFL): (%d) %s", sockfd, errno, strerror(errno)); - exit(errno); - } -} - -static void set_ipv6only(int sockfd) { - if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){1}, sizeof(int)) < 0) { - LOGERR("[set_ipv6only] setsockopt(%d, IPV6_V6ONLY): (%d) %s", sockfd, errno, strerror(errno)); - exit(errno); - } -} - -static void set_reuseaddr(int sockfd) { - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) { - LOGERR("[set_reuseaddr] setsockopt(%d, SO_REUSEADDR): (%d) %s", sockfd, errno, strerror(errno)); - exit(errno); - } -} - -static void set_reuseport(int sockfd) { - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)) < 0) { - LOGERR("[set_reuseport] setsockopt(%d, SO_REUSEPORT): (%d) %s", sockfd, errno, strerror(errno)); - exit(errno); - } -} - -static void set_nodelay(int sockfd) { - if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &(int){1}, sizeof(int)) < 0) { - LOGERR("[set_nodelay] setsockopt(%d, TCP_NODELAY): (%d) %s", sockfd, errno, strerror(errno)); - exit(errno); - } -} - -static void set_quickack(int sockfd) { - if (setsockopt(sockfd, IPPROTO_TCP, TCP_QUICKACK, &(int){1}, sizeof(int)) < 0) { - LOGERR("[set_quickack] setsockopt(%d, TCP_QUICKACK): (%d) %s", sockfd, errno, strerror(errno)); - exit(errno); - } -} - -static void set_syncnt(int sockfd, int syncnt) { - if (setsockopt(sockfd, IPPROTO_TCP, TCP_SYNCNT, &syncnt, sizeof(syncnt)) < 0) { - LOGERR("[set_syncnt] setsockopt(%d, TCP_SYNCNT): (%d) %s", sockfd, errno, strerror(errno)); - exit(errno); - } -} - -static int get_ipstr_family(const char *ipstr) { - if (!ipstr) return -1; /* invalid */ - uint8_t ipaddr[16]; /* 16-bytes */ - if (inet_pton(AF_INET, ipstr, &ipaddr) == 1) { - return AF_INET; - } else if (inet_pton(AF_INET6, ipstr, &ipaddr) == 1) { - return AF_INET6; - } else { - return -1; /* invalid */ - } -} - -static void build_socket_addr(int ipfamily, void *skaddr, const char *ipstr, portno_t portno) { - if (ipfamily == AF_INET) { - skaddr4_t *addr = skaddr; - addr->sin_family = AF_INET; - inet_pton(AF_INET, ipstr, &addr->sin_addr); - addr->sin_port = htons(portno); - } else { - skaddr6_t *addr = skaddr; - addr->sin6_family = AF_INET6; - inet_pton(AF_INET6, ipstr, &addr->sin6_addr); - addr->sin6_port = htons(portno); - } -} - -static void parse_socket_addr(const void *skaddr, char *ipstr, portno_t *portno) { - if (((skaddr4_t *)skaddr)->sin_family == AF_INET) { - const skaddr4_t *addr = skaddr; - inet_ntop(AF_INET, &addr->sin_addr, ipstr, IP4STRLEN); - *portno = ntohs(addr->sin_port); - } else { - const skaddr6_t *addr = skaddr; - inet_ntop(AF_INET6, &addr->sin6_addr, ipstr, IP6STRLEN); - *portno = ntohs(addr->sin6_port); - } -} - -static void print_command_help(void) { - printf("usage: dns2tcp <-L listen> <-R remote> [-s syncnt] [-6rafvVh]\n" - " -L udp listen address, this is required\n" - " -R tcp remote address, this is required\n" - " -s set TCP_SYNCNT(max) for remote socket\n" - " -6 enable IPV6_V6ONLY for listen socket\n" - " -r enable SO_REUSEPORT for listen socket\n" - " -a enable TCP_QUICKACK for remote socket\n" - " -f enable TCP_FASTOPEN for remote socket\n" - " -v print verbose log, default: \n" - " -V print version number of dns2tcp and exit\n" - " -h print help information of dns2tcp and exit\n" - "bug report: https://github.com/zfl9/dns2tcp. email: zfl9.com@gmail.com\n" - ); -} - -static void parse_address_opt(char *ip_port_str, bool is_listen_addr) { - const char *opt_name = is_listen_addr ? "listen" : "remote"; - - char *portstr = strchr(ip_port_str, '#'); - if (!portstr) { - printf("[parse_address_opt] %s port not provided\n", opt_name); - goto PRINT_HELP_AND_EXIT; - } - if (portstr == ip_port_str) { - printf("[parse_address_opt] %s addr not provided\n", opt_name); - goto PRINT_HELP_AND_EXIT; - } - if (portstr == ip_port_str + strlen(ip_port_str) - 1) { - printf("[parse_address_opt] %s port not provided\n", opt_name); - goto PRINT_HELP_AND_EXIT; - } - - *portstr = 0; ++portstr; - if (strlen(portstr) + 1 > PORTSTRLEN) { - printf("[parse_address_opt] %s port is invalid: %s\n", opt_name, portstr); - goto PRINT_HELP_AND_EXIT; - } - portno_t portno = strtoul(portstr, NULL, 10); - if (portno == 0) { - printf("[parse_address_opt] %s port is invalid: %s\n", opt_name, portstr); - goto PRINT_HELP_AND_EXIT; - } - - const char *ipstr = ip_port_str; - if (strlen(ipstr) + 1 > IP6STRLEN) { - printf("[parse_address_opt] %s addr is invalid: %s\n", opt_name, ipstr); - goto PRINT_HELP_AND_EXIT; - } - int ipfamily = get_ipstr_family(ipstr); - if (ipfamily == -1) { - printf("[parse_address_opt] %s addr is invalid: %s\n", opt_name, ipstr); - goto PRINT_HELP_AND_EXIT; - } - - if (is_listen_addr) { - strcpy(g_listen_ipstr, ipstr); - g_listen_portno = portno; - build_socket_addr(ipfamily, &g_listen_skaddr, ipstr, portno); - } else { - strcpy(g_remote_ipstr, ipstr); - g_remote_portno = portno; - build_socket_addr(ipfamily, &g_remote_skaddr, ipstr, portno); - } - return; - -PRINT_HELP_AND_EXIT: - print_command_help(); - exit(1); -} - -static void parse_command_args(int argc, char *argv[]) { - char opt_listen_addr[IP6STRLEN + PORTSTRLEN] = {0}; - char opt_remote_addr[IP6STRLEN + PORTSTRLEN] = {0}; - - opterr = 0; - int shortopt = -1; - const char *optstr = "L:R:s:6rafvVh"; - while ((shortopt = getopt(argc, argv, optstr)) != -1) { - switch (shortopt) { - case 'L': - if (strlen(optarg) + 1 > IP6STRLEN + PORTSTRLEN) { - printf("[parse_command_args] invalid listen addr: %s\n", optarg); - goto PRINT_HELP_AND_EXIT; - } - strcpy(opt_listen_addr, optarg); - break; - case 'R': - if (strlen(optarg) + 1 > IP6STRLEN + PORTSTRLEN) { - printf("[parse_command_args] invalid remote addr: %s\n", optarg); - goto PRINT_HELP_AND_EXIT; - } - strcpy(opt_remote_addr, optarg); - break; - case 's': - g_syn_maxcnt = strtoul(optarg, NULL, 10); - if (g_syn_maxcnt == 0) { - printf("[parse_command_args] invalid tcp syn cnt: %s\n", optarg); - goto PRINT_HELP_AND_EXIT; - } - break; - case '6': - g_options |= OPT_IPV6_V6ONLY; - break; - case 'r': - g_options |= OPT_REUSE_PORT; - break; - case 'a': - g_options |= OPT_QUICK_ACK; - break; - case 'f': - g_options |= OPT_FAST_OPEN; - break; - case 'v': - g_verbose = true; - break; - case 'V': - printf(DNS2TCP_VER"\n"); - exit(0); - case 'h': - print_command_help(); - exit(0); - case '?': - if (!strchr(optstr, optopt)) { - printf("[parse_command_args] unknown option '-%c'\n", optopt); - } else { - printf("[parse_command_args] missing optval '-%c'\n", optopt); - } - goto PRINT_HELP_AND_EXIT; - } - } - - if (strlen(opt_listen_addr) == 0) { - printf("[parse_command_args] missing option: '-L'\n"); - goto PRINT_HELP_AND_EXIT; - } - if (strlen(opt_remote_addr) == 0) { - printf("[parse_command_args] missing option: '-R'\n"); - goto PRINT_HELP_AND_EXIT; - } - - parse_address_opt(opt_listen_addr, true); - parse_address_opt(opt_remote_addr, false); - return; - -PRINT_HELP_AND_EXIT: - print_command_help(); - exit(1); -} - -int main(int argc, char *argv[]) { - signal(SIGPIPE, SIG_IGN); - setvbuf(stdout, NULL, _IOLBF, 256); - parse_command_args(argc, argv); - - LOGINF("[main] udp listen addr: %s#%hu", g_listen_ipstr, g_listen_portno); - LOGINF("[main] tcp remote addr: %s#%hu", g_remote_ipstr, g_remote_portno); - if (g_syn_maxcnt) LOGINF("[main] enable TCP_SYNCNT:%hhu sockopt", g_syn_maxcnt); - if (g_options & OPT_IPV6_V6ONLY) LOGINF("[main] enable IPV6_V6ONLY sockopt"); - if (g_options & OPT_REUSE_PORT) LOGINF("[main] enable SO_REUSEPORT sockopt"); - if (g_options & OPT_QUICK_ACK) LOGINF("[main] enable TCP_QUICKACK sockopt"); - if (g_options & OPT_FAST_OPEN) LOGINF("[main] enable TCP_FASTOPEN sockopt"); - IF_VERBOSE LOGINF("[main] verbose mode, affect performance"); - - g_udp_sockfd = socket(g_listen_skaddr.sin6_family, SOCK_DGRAM, 0); - if (g_udp_sockfd < 0) { - LOGERR("[main] create udp socket: (%d) %s", errno, strerror(errno)); - return errno; - } - - set_nonblock(g_udp_sockfd); - set_reuseaddr(g_udp_sockfd); - if (g_options & OPT_REUSE_PORT) set_reuseport(g_udp_sockfd); - if ((g_options & OPT_IPV6_V6ONLY) && g_listen_skaddr.sin6_family == AF_INET6) set_ipv6only(g_udp_sockfd); - - if (bind(g_udp_sockfd, (void *)&g_listen_skaddr, g_listen_skaddr.sin6_family == AF_INET ? sizeof(skaddr4_t) : sizeof(skaddr6_t)) < 0) { - LOGERR("[main] bind udp address: (%d) %s", errno, strerror(errno)); - return errno; - } - - evloop_t *evloop = ev_default_loop(0); - evio_t *watcher = &(evio_t){0}; - ev_io_init(watcher, udp_recvmsg_cb, g_udp_sockfd, EV_READ); - ev_io_start(evloop, watcher); - - ev_run(evloop, 0); - return 0; -} - -static void udp_recvmsg_cb(evloop_t *evloop, evio_t *watcher __attribute__((unused)), int events __attribute__((unused))) { - tcpwatcher_t *tcpw = malloc(sizeof(*tcpw)); - ssize_t nrecv = recvfrom(g_udp_sockfd, (void *)tcpw->buffer + 2, UDPDGRAM_MAXSIZ, 0, (void *)&tcpw->srcaddr, &(socklen_t){sizeof(tcpw->srcaddr)}); - if (nrecv < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - LOGERR("[udp_recvmsg_cb] recv from udp socket: (%d) %s", errno, strerror(errno)); - } - goto FREE_TCP_WATCHER; - } - IF_VERBOSE { - portno_t portno; - parse_socket_addr(&tcpw->srcaddr, g_ipstr_buf, &portno); - LOGINF("[udp_recvmsg_cb] recv from %s#%hu, nrecv:%zd", g_ipstr_buf, portno, nrecv); - } - *(uint16_t *)tcpw->buffer = htons(nrecv); - nrecv += 2; /* msglen + msgbuf */ - tcpw->nrcvsnd = 0; - - int sockfd = socket(g_remote_skaddr.sin6_family, SOCK_STREAM, 0); - if (sockfd < 0) { - LOGERR("[udp_recvmsg_cb] create tcp socket: (%d) %s", errno, strerror(errno)); - goto FREE_TCP_WATCHER; - } - set_nonblock(sockfd); - set_reuseaddr(sockfd); - set_nodelay(sockfd); - if (g_syn_maxcnt) set_syncnt(sockfd, g_syn_maxcnt); - if (g_options & OPT_QUICK_ACK) set_quickack(sockfd); - - bool tfo_succ = false; - if (g_options & OPT_FAST_OPEN) { - ssize_t nsend = sendto(sockfd, tcpw->buffer, nrecv, MSG_FASTOPEN, (void *)&g_remote_skaddr, g_remote_skaddr.sin6_family == AF_INET ? sizeof(skaddr4_t) : sizeof(skaddr6_t)); - if (nsend < 0) { - if (errno != EINPROGRESS) { - LOGERR("[udp_recvmsg_cb] connect to %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); - goto CLOSE_TCP_SOCKFD; - } - IF_VERBOSE LOGINF("[udp_recvmsg_cb] try to connect to %s#%hu", g_remote_ipstr, g_remote_portno); - } else { - tfo_succ = true; - tcpw->nrcvsnd = nsend; - IF_VERBOSE LOGINF("[udp_recvmsg_cb] tcp_fastopen connect, nsend:%zd", nsend); - } - } else { - if (connect(sockfd, (void *)&g_remote_skaddr, g_remote_skaddr.sin6_family == AF_INET ? sizeof(skaddr4_t) : sizeof(skaddr6_t)) < 0 && errno != EINPROGRESS) { - LOGERR("[udp_recvmsg_cb] connect to %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); - goto CLOSE_TCP_SOCKFD; - } - IF_VERBOSE LOGINF("[udp_recvmsg_cb] try to connect to %s#%hu", g_remote_ipstr, g_remote_portno); - } - - if (tfo_succ && tcpw->nrcvsnd >= nrecv) { - tcpw->nrcvsnd = 0; /* reset to zero for recv data */ - ev_io_init((evio_t *)tcpw, tcp_recvmsg_cb, sockfd, EV_READ); - } else { - ev_io_init((evio_t *)tcpw, tfo_succ ? tcp_sendmsg_cb : tcp_connect_cb, sockfd, EV_WRITE); - } - ev_io_start(evloop, (evio_t *)tcpw); - return; - -CLOSE_TCP_SOCKFD: - close(sockfd); -FREE_TCP_WATCHER: - free(tcpw); -} - -static void tcp_connect_cb(evloop_t *evloop, evio_t *watcher, int events __attribute__((unused))) { - if (getsockopt(watcher->fd, SOL_SOCKET, SO_ERROR, &errno, &(socklen_t){sizeof(errno)}) < 0 || errno) { - LOGERR("[tcp_connect_cb] connect to %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); - ev_io_stop(evloop, watcher); - close(watcher->fd); - free(watcher); - return; - } - IF_VERBOSE LOGINF("[tcp_connect_cb] connect to %s#%hu succeed", g_remote_ipstr, g_remote_portno); - ev_set_cb(watcher, tcp_sendmsg_cb); - ev_invoke(evloop, watcher, EV_WRITE); -} - -static void tcp_sendmsg_cb(evloop_t *evloop, evio_t *watcher, int events __attribute__((unused))) { - tcpwatcher_t *tcpw = (void *)watcher; - uint16_t datalen = 2 + ntohs(*(uint16_t *)tcpw->buffer); - ssize_t nsend = send(watcher->fd, (void *)tcpw->buffer + tcpw->nrcvsnd, datalen - tcpw->nrcvsnd, 0); - if (nsend < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) return; - LOGERR("[tcp_sendmsg_cb] send to %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); - ev_io_stop(evloop, watcher); - close(watcher->fd); - free(watcher); - return; - } - IF_VERBOSE LOGINF("[tcp_sendmsg_cb] send to %s#%hu, nsend:%zd", g_remote_ipstr, g_remote_portno, nsend); - tcpw->nrcvsnd += nsend; - if (tcpw->nrcvsnd >= datalen) { - tcpw->nrcvsnd = 0; /* reset to zero for recv data */ - ev_io_stop(evloop, watcher); - ev_io_init(watcher, tcp_recvmsg_cb, watcher->fd, EV_READ); - ev_io_start(evloop, watcher); - } -} - -static void tcp_recvmsg_cb(evloop_t *evloop, evio_t *watcher, int events __attribute__((unused))) { - tcpwatcher_t *tcpw = (void *)watcher; - void *buffer = tcpw->buffer; - - ssize_t nrecv = recv(watcher->fd, buffer + tcpw->nrcvsnd, 2 + UDPDGRAM_MAXSIZ - tcpw->nrcvsnd, 0); - if (nrecv < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) return; - LOGERR("[tcp_recvmsg_cb] recv from %s#%hu: (%d) %s", g_remote_ipstr, g_remote_portno, errno, strerror(errno)); - goto FREE_TCP_WATCHER; - } - if (nrecv == 0) { - LOGERR("[tcp_recvmsg_cb] recv from %s#%hu: connection is closed", g_remote_ipstr, g_remote_portno); - goto FREE_TCP_WATCHER; - } - tcpw->nrcvsnd += nrecv; - IF_VERBOSE LOGINF("[tcp_recvmsg_cb] recv from %s#%hu, nrecv:%zd", g_remote_ipstr, g_remote_portno, nrecv); - if (tcpw->nrcvsnd < 2 || tcpw->nrcvsnd < 2 + ntohs(*(uint16_t *)buffer)) return; - - const void *sendto_skaddr = &tcpw->srcaddr; - socklen_t sendto_skaddrlen = tcpw->srcaddr.sin6_family == AF_INET ? sizeof(skaddr4_t) : sizeof(skaddr6_t); - ssize_t nsend = sendto(g_udp_sockfd, buffer + 2, ntohs(*(uint16_t *)buffer), 0, sendto_skaddr, sendto_skaddrlen); - if (nsend < 0) { - portno_t portno; - parse_socket_addr(&tcpw->srcaddr, g_ipstr_buf, &portno); - LOGERR("[tcp_recvmsg_cb] send to %s#%hu: (%d) %s", g_ipstr_buf, portno, errno, strerror(errno)); - } else { - IF_VERBOSE { - portno_t portno; - parse_socket_addr(&tcpw->srcaddr, g_ipstr_buf, &portno); - LOGINF("[tcp_recvmsg_cb] send to %s#%hu, nsend:%zd", g_ipstr_buf, portno, nsend); - } - } -FREE_TCP_WATCHER: - ev_io_stop(evloop, watcher); - close(watcher->fd); - free(watcher); -} diff --git a/logutils.h b/logutils.h deleted file mode 100644 index efc73e2..0000000 --- a/logutils.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef DNS2TCP_LOGUTILS_H -#define DNS2TCP_LOGUTILS_H - -#define _GNU_SOURCE -#include -#include -#undef _GNU_SOURCE - -#define LOGINF(fmt, ...) \ - do { \ - struct tm *tm = localtime(&(time_t){time(NULL)}); \ - printf("\e[1;32m%04d-%02d-%02d %02d:%02d:%02d INF:\e[0m " fmt "\n", \ - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, \ - tm->tm_hour, tm->tm_min, tm->tm_sec, \ - ##__VA_ARGS__); \ - } while (0) - -#define LOGERR(fmt, ...) \ - do { \ - struct tm *tm = localtime(&(time_t){time(NULL)}); \ - printf("\e[1;35m%04d-%02d-%02d %02d:%02d:%02d ERR:\e[0m " fmt "\n", \ - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, \ - tm->tm_hour, tm->tm_min, tm->tm_sec, \ - ##__VA_ARGS__); \ - } while (0) - -#endif diff --git a/netutils.c b/netutils.c deleted file mode 100644 index 03fbb44..0000000 --- a/netutils.c +++ /dev/null @@ -1,43 +0,0 @@ -#define _GNU_SOURCE -#include "netutils.h" -#include -#undef _GNU_SOURCE - -/* build ipv4 socket address from ipstr and portno */ -void build_ipv4_addr(skaddr4_t *addr, const char *ipstr, portno_t portno) { - addr->sin_family = AF_INET; - inet_pton(AF_INET, ipstr, &addr->sin_addr); - addr->sin_port = htons(portno); -} - -/* build ipv6 socket address from ipstr and portno */ -void build_ipv6_addr(skaddr6_t *addr, const char *ipstr, portno_t portno) { - addr->sin6_family = AF_INET6; - inet_pton(AF_INET6, ipstr, &addr->sin6_addr); - addr->sin6_port = htons(portno); -} - -/* parse ipstr and portno from ipv4 socket address */ -void parse_ipv4_addr(const skaddr4_t *addr, char *ipstr, portno_t *portno) { - inet_ntop(AF_INET, &addr->sin_addr, ipstr, IP4STRLEN); - *portno = ntohs(addr->sin_port); -} - -/* parse ipstr and portno from ipv6 socket address */ -void parse_ipv6_addr(const skaddr6_t *addr, char *ipstr, portno_t *portno) { - inet_ntop(AF_INET6, &addr->sin6_addr, ipstr, IP6STRLEN); - *portno = ntohs(addr->sin6_port); -} - -/* AF_INET or AF_INET6 or -1(invalid ip string) */ -int get_ipstr_family(const char *ipstr) { - if (!ipstr) return -1; - uint8_t ipaddr[16]; /* save ipv4/ipv6 addr */ - if (inet_pton(AF_INET, ipstr, &ipaddr) == 1) { - return AF_INET; - } else if (inet_pton(AF_INET6, ipstr, &ipaddr) == 1) { - return AF_INET6; - } else { - return -1; - } -} diff --git a/netutils.h b/netutils.h deleted file mode 100644 index 868b1e8..0000000 --- a/netutils.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef DNS2TCP_NETUTILS_H -#define DNS2TCP_NETUTILS_H - -#define _GNU_SOURCE -#include -#include -#undef _GNU_SOURCE - -/* mtu(1500) - iphdr(20) - udphdr(8) */ -#define DNS_PACKET_MAXSIZE 1472 /* bytes */ - -/* ipaddr string len */ -#define IP4STRLEN INET_ADDRSTRLEN -#define IP6STRLEN INET6_ADDRSTRLEN - -/* portno string len */ -#define PORTSTRLEN 6 - -/* port number typedef */ -typedef uint16_t portno_t; - -/* sockaddr type alias */ -typedef struct sockaddr skaddr_t; -typedef struct sockaddr_in skaddr4_t; -typedef struct sockaddr_in6 skaddr6_t; - -/* build ipv4 socket address from ipstr and portno */ -void build_ipv4_addr(skaddr4_t *addr, const char *ipstr, portno_t portno); - -/* build ipv6 socket address from ipstr and portno */ -void build_ipv6_addr(skaddr6_t *addr, const char *ipstr, portno_t portno); - -/* parse ipstr and portno from ipv4 socket address */ -void parse_ipv4_addr(const skaddr4_t *addr, char *ipstr, portno_t *portno); - -/* parse ipstr and portno from ipv6 socket address */ -void parse_ipv6_addr(const skaddr6_t *addr, char *ipstr, portno_t *portno); - -/* AF_INET or AF_INET6 or -1(invalid ip string) */ -int get_ipstr_family(const char *ipstr); - -#endif diff --git a/test b/test deleted file mode 100755 index 6935010..0000000 Binary files a/test and /dev/null differ -- cgit v1.2.3