From c2703b9e320d0c1903c0d299f259463716d5ce83 Mon Sep 17 00:00:00 2001 From: dnomd343 Date: Fri, 4 Feb 2022 16:53:22 +0800 Subject: [PATCH] feat: support udp proxy --- include/network.h | 3 ++ include/process.h | 2 +- src/CMakeLists.txt | 4 +- src/common.c | 2 +- src/local.c | 2 +- src/network.c | 121 +++++++++++++++++++++++++++++++++++++++++++++ src/process.c | 12 ++++- src/server.c | 4 +- 8 files changed, 143 insertions(+), 7 deletions(-) diff --git a/include/network.h b/include/network.h index 731bd1b..c162e5a 100644 --- a/include/network.h +++ b/include/network.h @@ -1,6 +1,9 @@ #ifndef _NETWORK_H_ #define _NETWORK_H_ +extern int proxy_exit; + int get_available_port(unsigned short range_start, unsigned short range_end); +void proxy(char *server_ip, int server_port, char *listen_ip, int listen_port); #endif diff --git a/include/process.h b/include/process.h index 169b83a..e6cf7fb 100644 --- a/include/process.h +++ b/include/process.h @@ -9,6 +9,6 @@ extern char *SS_LOCAL_HOST; extern char *SS_LOCAL_PORT; extern char *SS_PLUGIN_OPTIONS; -void start_bootstrap(); +void start_bootstrap(char *ss_type); #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 11db067..fb87acc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,7 @@ list(REMOVE_ITEM SRC ${PROJECT_SOURCE_DIR}/src/local.c) list(REMOVE_ITEM SRC ${PROJECT_SOURCE_DIR}/src/server.c) add_executable(ss-bootstrap-local local.c ${SRC}) -target_link_libraries(ss-bootstrap-local glib-2.0) +target_link_libraries(ss-bootstrap-local glib-2.0 pthread) add_executable(ss-bootstrap-server server.c ${SRC}) -target_link_libraries(ss-bootstrap-server glib-2.0) +target_link_libraries(ss-bootstrap-server glib-2.0 pthread) diff --git a/src/common.c b/src/common.c index 4bdce0b..8a6ff18 100644 --- a/src/common.c +++ b/src/common.c @@ -39,7 +39,7 @@ char* int_to_string(int num) { // int -> string } int count = 0; int temp = num; - while(temp != 0) { // check the number of digits + while (temp != 0) { // check the number of digits temp /= 10; ++count; } diff --git a/src/local.c b/src/local.c index 427e176..6e309a7 100644 --- a/src/local.c +++ b/src/local.c @@ -39,6 +39,6 @@ int main(int argc, char *argv[]) { } args_decode(argc, argv); params_load(SHADOWSOCKS_DEFAULT); // default file name - start_bootstrap(); + start_bootstrap(SHADOWSOCKS_DEFAULT); // local or server mode return 0; } diff --git a/src/network.c b/src/network.c index 2df05b0..49d8cc5 100644 --- a/src/network.c +++ b/src/network.c @@ -1,10 +1,27 @@ +#include #include #include #include +#include #include +#include #include #include "network.h" +#define TIMEOUT 15 +#define BUFFER_SIZE 4096 + +int proxy_exit; + +typedef struct ipv4_proxy_info { + char *server_ip; + int server_port; + struct sockaddr_in ipv4_client_addr; + int ipv4_client_fd; + char *buffer; + long len; +} ipv4_proxy_info; + int get_random_num(int range_start, int range_end) { // create a random number in range struct timeval tp; gettimeofday(&tp, NULL); @@ -78,3 +95,107 @@ int get_available_port(unsigned short range_start, unsigned short range_end) { / } } } + +int create_ipv4_udp_sock(char *address, int port) { // 创建并绑定IPv4 UDP端口 + struct sockaddr_in ipv4_udp_addr; + int ipv4_udp_sock = socket(AF_INET, SOCK_DGRAM, 0); // IPv4 UDP模式 + bzero(&ipv4_udp_addr, sizeof(ipv4_udp_addr)); // 清空为0x00 + ipv4_udp_addr.sin_family = AF_INET; + ipv4_udp_addr.sin_port = htons(port); // 监听端口 + if (address == NULL) { + ipv4_udp_addr.sin_addr.s_addr = INADDR_ANY; // 监听0.0.0.0 + } else { + ipv4_udp_addr.sin_addr.s_addr = inet_addr(address); // 监听地址 + } + if (bind(ipv4_udp_sock, (struct sockaddr*)&ipv4_udp_addr, sizeof(ipv4_udp_addr)) < 0) { // 绑定接口 + perror("[Shadowsocks Bootstrap] IPv4 UDP Sock bind error"); + return -1; // 端口被占用 + } + return ipv4_udp_sock; +} + +long ipv4_receive(int fd, char *buffer, int buffer_size, int timeout, struct sockaddr_in sa) { // IPv4接收 超时处理 + socklen_t sa_len = sizeof(sa); + if (timeout == 0) { // 永久等待 + return recvfrom(fd, buffer, buffer_size, 0, (struct sockaddr*)&sa, &sa_len); + } + fd_set rfds; + struct timeval tv; + tv.tv_sec = timeout; // 超时时间 单位s + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + select(fd + 1, &rfds, (fd_set*)0, (fd_set*)0, &tv); + if (FD_ISSET(fd, &rfds)) { + return recvfrom(fd, buffer, buffer_size, 0, (struct sockaddr*)&sa, &sa_len); + } + return -1; // 接收超时 +} + +long ipv4_send_and_receive(char *ipv4_server_ip, int ipv4_server_port, char *send_buffer, long send_len, char *recv_buffer) { // IPv4下发送并接收 + struct sockaddr_in ipv4_server_addr; + int ipv4_server_fd = socket(AF_INET, SOCK_DGRAM, 0); // 通讯描述符 + bzero(&ipv4_server_addr, sizeof(ipv4_server_addr)); // 清空为0x00 + ipv4_server_addr.sin_family = AF_INET; + ipv4_server_addr.sin_port = htons(ipv4_server_port); // 目标IP + ipv4_server_addr.sin_addr.s_addr = inet_addr(ipv4_server_ip); // 目标端口 + if (sendto(ipv4_server_fd, send_buffer, send_len, 0, (struct sockaddr*)&ipv4_server_addr, sizeof(ipv4_server_addr)) < 0) { // 发送缓冲区数据 + perror("[Shadowsocks Bootstrap] IPv4 UDP send failed"); + } + long recv_len = ipv4_receive(ipv4_server_fd, recv_buffer, BUFFER_SIZE, TIMEOUT, ipv4_server_addr); // 接收数据到缓冲区 + close(ipv4_server_fd); // 关闭描述符 + return recv_len; // 返回接收长度 +} + +void ipv4_proxy(void *ipv4_info) { // 代理IPv4客户端 + ipv4_proxy_info *info = (ipv4_proxy_info*)ipv4_info; + char *recv_buffer = (char*)malloc(BUFFER_SIZE); // 申请接收缓冲区内存 + long recv_len = ipv4_send_and_receive(info->server_ip, info->server_port, info->buffer, info->len, recv_buffer); // 服务端交互 + if (recv_len < 0) { // 服务端超时 + printf("[Shadowsocks Bootstrap] Server return timeout\n"); + } else { + if (sendto(info->ipv4_client_fd, recv_buffer, recv_len, 0, (struct sockaddr*)&(info->ipv4_client_addr), sizeof(info->ipv4_client_addr)) < 0) { // 服务端数据返回给客户端 + perror("[Shadowsocks Bootstrap] IPv4 UDP return failed"); + } else { + printf("[Shadowsocks Bootstrap] UDP Proxy: ↑ %ld bytes ↓ %ld bytes\n", info->len, recv_len); + } + } + free(recv_buffer); // 释放接收缓冲区内存 + free(info->buffer); // 释放发送缓冲区内存 + free(ipv4_info); // 释放线程传参结构体 +} + +void proxy(char *server_ip, int server_port, char *listen_ip, int listen_port) { // 代理UDP请求 + pthread_t tid; + long recv_len; + char recv_buffer[BUFFER_SIZE]; // 接收缓冲区 + struct sockaddr_in ipv4_client_addr; + socklen_t ipv4_client_addr_len = sizeof(ipv4_client_addr); + int ipv4_client_fd = create_ipv4_udp_sock(listen_ip, listen_port); // 监听端口描述符 + if (ipv4_client_fd == -1) { // 端口被占用 + printf("[Shadowsocks Bootstrap] The UDP port seems to be occupied by the SIP003 plugin\n"); + printf("[Shadowsocks Bootstrap] WARNING: UDP communication of the agent will not work properly\n"); + return; + } + proxy_exit = 0; // 重置退出标识 + printf("[Shadowsocks Bootstrap] UDP Proxy: %s:%d -> %s:%d\n", listen_ip, listen_port, server_ip, server_port); + for (;;) { + recv_len = recvfrom(ipv4_client_fd, recv_buffer, BUFFER_SIZE, 0, (struct sockaddr*)&ipv4_client_addr, &ipv4_client_addr_len); + char *proxy_buffer = (char*)malloc(recv_len); + memcpy(proxy_buffer, recv_buffer, recv_len); // 复制缓冲区数据 + ipv4_proxy_info *info = (ipv4_proxy_info*)malloc(sizeof(ipv4_proxy_info)); + info->server_ip = server_ip; + info->server_port = server_port; + info->ipv4_client_addr = ipv4_client_addr; + info->ipv4_client_fd = ipv4_client_fd; + info->buffer = proxy_buffer; + info->len = recv_len; + pthread_create(&tid, NULL, (void*)ipv4_proxy, (void*)info); // 新线程代理请求 + if (proxy_exit) { + break; // 退出代理 + } + } + sleep(TIMEOUT); // 等待线程结束 + close(ipv4_client_fd); // 关闭监听 +} + diff --git a/src/process.c b/src/process.c index 9f2f853..a50df21 100644 --- a/src/process.c +++ b/src/process.c @@ -4,6 +4,7 @@ #include #include #include +#include "network.h" #include "process.h" char **shadowsocks_args; @@ -156,6 +157,7 @@ void exit_with_child() { // exit and kill his child process sleep(1); // block } exiting = 1; + proxy_exit = 1; if (ss_pid != 0) { kill(ss_pid, SIGKILL); printf("[Shadowsocks Bootstrap] kill shadowsocks process.\n"); @@ -190,13 +192,21 @@ void show_params() { // show shadowsocks and plugin params printf("[Shadowsocks Bootstrap] SS_PLUGIN_OPTIONS -> %s\n", SS_PLUGIN_OPTIONS); } -void start_bootstrap() { // start shadowsocks and plugin (optional) +void start_bootstrap(char *ss_type) { // start shadowsocks and plugin (optional) show_params(); main_loop = g_main_loop_new(NULL, FALSE); signal(SIGINT, exit_with_child); // catch Ctrl + C (2) signal(SIGTERM, exit_with_child); // catch exit signal (15) signal(SIGCHLD, get_sub_exit); // callback when child process die process_exec(); // exec child process + usleep(500 * 1000); // wait 500ms for plugin start + if (plugin_file != NULL) { // start udp proxy when using plugin + if (!strcmp(ss_type, "sslocal")) { // local mode + proxy(SS_REMOTE_HOST, atoi(SS_REMOTE_PORT), SS_LOCAL_HOST, atoi(SS_LOCAL_PORT)); + } else { // server mode + proxy(SS_LOCAL_HOST, atoi(SS_LOCAL_PORT), SS_REMOTE_HOST, atoi(SS_REMOTE_PORT)); + } + } g_main_loop_run(main_loop); // into main loop for wait exit_with_child(); } diff --git a/src/server.c b/src/server.c index 7e68977..35ea722 100644 --- a/src/server.c +++ b/src/server.c @@ -3,6 +3,8 @@ #include "common.h" #include "process.h" +#include "network.h" + #define SHADOWSOCKS_DEFAULT "ssserver" char *help_msg = @@ -39,6 +41,6 @@ int main(int argc, char *argv[]) { } args_decode(argc, argv); params_load(SHADOWSOCKS_DEFAULT); // default file name - start_bootstrap(); + start_bootstrap(SHADOWSOCKS_DEFAULT); // local or server mode return 0; }