From 3d3169397f9656c7405921088748f52c1ec697de Mon Sep 17 00:00:00 2001 From: aixiao Date: Sun, 19 Dec 2021 19:01:38 +0800 Subject: [PATCH] Initial submission --- Android.mk | 10 + Application.mk | 2 + Makefile | 26 ++ README.md | 28 +++ common.c | 99 ++++++++ common.h | 22 ++ conf.c | 197 +++++++++++++++ conf.h | 10 + httpudp.c | 434 +++++++++++++++++++++++++++++++++ httpudp.conf | 9 + httpudp.h | 33 +++ main.c | 79 ++++++ main.h | 44 ++++ udpServer/Makefile | 13 + udpServer/udpServer.c | 553 ++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 1559 insertions(+) create mode 100644 Android.mk create mode 100644 Application.mk create mode 100644 Makefile create mode 100644 README.md create mode 100644 common.c create mode 100644 common.h create mode 100644 conf.c create mode 100644 conf.h create mode 100644 httpudp.c create mode 100644 httpudp.conf create mode 100644 httpudp.h create mode 100644 main.c create mode 100644 main.h create mode 100644 udpServer/Makefile create mode 100644 udpServer/udpServer.c diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..5bb057c --- /dev/null +++ b/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_CFLAGS = -O2 -pie -Wall +LOCAL_LDFLAGS = -O2 -pie -Wall +LOCAL_ARM_MODE = arm +LOCAL_MODULE = httpudp +LOCAL_MODULE_FILENAME = httpudp +c_src_files = $(wildcard $(LOCAL_PATH)/*.c) +LOCAL_SRC_FILES = $(c_src_files:$(LOCAL_PATH)/%=%) +include $(BUILD_EXECUTABLE) diff --git a/Application.mk b/Application.mk new file mode 100644 index 0000000..75d4de5 --- /dev/null +++ b/Application.mk @@ -0,0 +1,2 @@ +APP_ABI = arm64-v8a armeabi-v7a +APP_PLATFORM = android-29 \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9730be8 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CROSS_COMPILE ?= +CC := $(CROSS_COMPILE)gcc +STRIP := $(CROSS_COMPILE)strip +OBJ := httpudp +#如果是安卓编译 +ifeq ($(ANDROID_DATA),/data) + CFLAGS := -O2 -pie + SHELL := /system/bin/sh +else + CFLAGS := -O2 -pthread -Wall +endif + +all : main.o conf.o common.o httpudp.o + $(CC) $(CFLAGS) $(DEFS) -o $(OBJ) $^ + $(STRIP) $(OBJ) + -chmod 777 $(OBJ) 2>&- + +.c.o : + $(CC) $(CFLAGS) $(DEFS) -c $< + +clean : + rm -f *.o + rm $(OBJ) + +android: + /usr/lib/android-ndk/ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk diff --git a/README.md b/README.md new file mode 100644 index 0000000..34c4f84 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# HttpUDP + + 提取 [mmmdbybyd](https://github.com/mmmdbybyd) CProxy 中 httpudp 核心功能用于Android + + +## Build + + Linux编译: + make clean; make + + windows 10子系统交叉编译: + apt-get install gcc-aarch64-linux-gnu + make clean; CROSS_COMPILE=aarch64-linux-gnu- make + + Android NDK 编译: + make android + 或 + ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk + +## Help Information + + HttpUDP(2.0.1) + + 启动命令: + httpudp httpudp.conf + 结束命令: + killall httpudp + diff --git a/common.c b/common.c new file mode 100644 index 0000000..5b32013 --- /dev/null +++ b/common.c @@ -0,0 +1,99 @@ +#include "common.h" + +//有些头文件不声明memmem +void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); + +void error(const char *error_info) +{ + fprintf(stderr, "%s\n\n", error_info); + exit(1); +} + +int8_t copy_new_mem(char *src, int src_len, char **dest) +{ + *dest = (char *)malloc(src_len + 1); + if (*dest == NULL) + return 1; + memcpy(*dest, src, src_len); + *((*dest) + src_len) = '\0'; + + return 0; +} + +/* 字符串替换,replace_memory为可以用free释放的指针 */ +char *replace(char *replace_memory, int *replace_memory_len, const char *src, const int src_len, const char *dest, const int dest_len) +{ + if (!replace_memory || !src || !dest) + return replace_memory; + + char *p; + int diff; + + if (src_len == dest_len) { + for (p = memmem(replace_memory, *replace_memory_len, src, src_len); p; p = memmem(p, *replace_memory_len - (p - replace_memory), src, src_len)) { + memcpy(p, dest, dest_len); + p += dest_len; + } + } else if (src_len < dest_len) { + int before_len; + char *before_end, *new_replace_memory; + + diff = dest_len - src_len; + for (p = memmem(replace_memory, *replace_memory_len, src, src_len); p; p = memmem(p, *replace_memory_len - (p - replace_memory), src, src_len)) { + *replace_memory_len += diff; + before_len = p - replace_memory; + new_replace_memory = (char *)realloc(replace_memory, *replace_memory_len + 1); + if (new_replace_memory == NULL) { + free(replace_memory); + return NULL; + } + replace_memory = new_replace_memory; + before_end = replace_memory + before_len; + p = before_end + dest_len; + memmove(p, p - diff, *replace_memory_len - (p - replace_memory)); + memcpy(before_end, dest, dest_len); + } + } else if (src_len > dest_len) { + diff = src_len - dest_len; + for (p = memmem(replace_memory, *replace_memory_len, src, src_len); p; p = memmem(p, *replace_memory_len - (p - replace_memory), src, src_len)) { + *replace_memory_len -= diff; + memcpy(p, dest, dest_len); + p += dest_len; + memmove(p, p + diff, *replace_memory_len - (p - replace_memory)); + } + } + + replace_memory[*replace_memory_len] = '\0'; + return replace_memory; +} + +/* 对数据进行编码 */ +void dataEncode(char *data, int data_len, unsigned code) +{ + while (data_len-- > 0) + data[data_len] ^= code; +} + +/* 监听一个UDP接口 */ +int udp_listen(char *ip, int port) +{ + struct sockaddr_in addr; + int fd, opt = 1; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("udp socket"); + exit(1); + } + setsockopt(fd, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt)); + setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &opt, sizeof(opt)); + memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = inet_addr(ip); + addr.sin_port = htons(port); + addr.sin_family = AF_INET; + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + perror("udp bind"); + exit(1); + } + + return fd; +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..e5fadf1 --- /dev/null +++ b/common.h @@ -0,0 +1,22 @@ +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "main.h" + +extern char *replace(char *str, int *str_len, const char *src, const int src_len, const char *dest, const int dest_len); +extern void error(const char *msg); +extern int udp_listen(char *ip, int port); +extern void dataEncode(char *data, int data_len, unsigned code); +extern int8_t copy_new_mem(char *src, int src_len, char **dest); + +#endif diff --git a/conf.c b/conf.c new file mode 100644 index 0000000..2066c59 --- /dev/null +++ b/conf.c @@ -0,0 +1,197 @@ +#include "conf.h" + +/* 字符串预处理,设置转义字符 */ +static void string_pretreatment(char *str, int *len) +{ + char *lf, *p, *ori_strs[] = { "\\r", "\\n", "\\b", "\\v", "\\f", "\\t", "\\a", "\\b", "\\0" }, to_chrs[] = { '\r', '\n', '\b', '\v', '\f', '\t', '\a', '\b', '\0' }; + int i; + + while ((lf = strchr(str, '\n')) != NULL) { + for (p = lf + 1; *p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'; p++) + *len -= 1; + strcpy(lf, p); + *len -= 1; + } + for (i = 0; i < sizeof(to_chrs); i++) { + for (p = strstr(str, ori_strs[i]); p; p = strstr(p, ori_strs[i])) { + //支持\\r + *(p - 1) == '\\' ? (*p--) : (*p = to_chrs[i]); + memmove(p + 1, p + 2, strlen(p + 2)); + (*len)--; + } + } +} + +/* 在content中,设置变量(var)的首地址,值(val)的位置首地址和末地址,返回下一行指针 */ +static char *set_var_val_lineEnd(char *content, char **var, char **val_begin, char **val_end) +{ + char *p, *pn, *lineEnd; + ; + int val_len; + + while (1) { + if (content == NULL) + return NULL; + + for (; *content == ' ' || *content == '\t' || *content == '\r' || *content == '\n'; content++) ; + if (*content == '\0') + return NULL; + *var = content; + pn = strchr(content, '\n'); + p = strchr(content, '='); + if (p == NULL) { + if (pn) { + content = pn + 1; + continue; + } else + return NULL; + } + content = p; + //将变量以\0结束 + for (p--; *p == ' ' || *p == '\t'; p--) ; + *(p + 1) = '\0'; + //值的首地址 + for (content++; *content == ' ' || *content == '\t'; content++) ; + if (*content == '\0') + return NULL; + //双引号引起来的值支持换行 + if (*content == '"') { + *val_begin = content + 1; + *val_end = strstr(*val_begin, "\";"); + if (*val_end != NULL) + break; + } else + *val_begin = content; + *val_end = strchr(content, ';'); + if (pn && *val_end > pn) { + content = pn + 1; + continue; + } + break; + } + + if (*val_end) { + **val_end = '\0'; + val_len = *val_end - *val_begin; + lineEnd = *val_end; + } else { + val_len = strlen(*val_begin); + *val_end = lineEnd = *val_begin + val_len; + } + string_pretreatment(*val_begin, &val_len); + *val_end = *val_begin + val_len; + //printf("var[%s]\nbegin[%s]\n\n", *var, *val_begin); + return lineEnd; +} + +/* 在buff中读取模块(global http https httpdns httpudp)内容 */ +static char *read_module(char *buff, const char *module_name) +{ + int len; + char *p, *p0; + + len = strlen(module_name); + p = buff; + while (1) { + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + if (strncasecmp(p, module_name, len) == 0) { + p += len; + while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + if (*p == '{') + break; + } + if ((p = strchr(p, '\n')) == NULL) + return NULL; + } + if ((p0 = strchr(++p, '}')) == NULL) + return NULL; + + //printf("%s\n%s", module_name, content); + return strndup(p, p0 - p); +} + +static void parse_global_module(char *content) +{ + char *var, *val_begin, *val_end, *lineEnd, *p; + + while ((lineEnd = set_var_val_lineEnd(content, &var, &val_begin, &val_end)) != NULL) { + if (strcasecmp(var, "uid") == 0) { + global.uid = atoi(val_begin); + } else if (strcasecmp(var, "procs") == 0) { + global.procs = atol(val_begin); + } else if (strcasecmp(var, "udp_listen") == 0) { + if ((p = strchr(val_begin, ':')) != NULL && p - val_begin <= 15) { + *p = '\0'; + global.udp_listen_fd = udp_listen(val_begin, atoi(p + 1)); + } else + global.udp_listen_fd = udp_listen((char *)"0.0.0.0", atoi(val_begin)); + } else if (strcasecmp(var, "strict") == 0 && strcasecmp(val_begin, "on") == 0) { + global.strict_modify = 1; + } else if (strcasecmp(var, "timeout") == 0) { + global.timeout_m = atoi(val_begin); + } + + content = strchr(lineEnd + 1, '\n'); + } +} + +static int8_t parse_httpudp_module(char *content) +{ + char *var, *val_begin, *val_end, *lineEnd, *p; + while ((lineEnd = set_var_val_lineEnd(content, &var, &val_begin, &val_end)) != NULL) { + if (strcasecmp(var, "addr") == 0) { + if ((p = strchr(val_begin, ':')) != NULL && p - val_begin <= 15) { + *p = '\0'; + udp.dst.sin_port = htons(atoi(p + 1)); + } else { + udp.dst.sin_port = htons(80); + } + udp.dst.sin_addr.s_addr = inet_addr(val_begin); + } else if (strcasecmp(var, "http_req") == 0) { + udp.http_request_len = val_end - val_begin; + if (copy_new_mem(val_begin, udp.http_request_len, &udp.http_request) != 0) + return 1; + } else if (strcasecmp(var, "encode") == 0) { + udp.encodeCode = (unsigned)atoi(val_begin); + } + + content = strchr(lineEnd + 1, '\n'); + } + + return 0; +} + +void read_conf(char *path) +{ + char *buff, *global_content, *httpudp_content; + FILE *file; + long file_size; + + /* 读取配置文件到缓冲区 */ + file = fopen(path, "r"); + if (file == NULL) + error("cannot open config file."); + fseek(file, 0, SEEK_END); + file_size = ftell(file); + buff = (char *)alloca(file_size + 1); + if (buff == NULL) + error("out of memory."); + rewind(file); + fread(buff, file_size, 1, file); + fclose(file); + buff[file_size] = '\0'; + /* 读取global模块内容 */ + if ((global_content = read_module(buff, "global")) == NULL) + error("read global module error"); + parse_global_module(global_content); + free(global_content); + + /* 读取httpudp模块 */ + if (global.udp_listen_fd >= 0) { + if ((httpudp_content = read_module(buff, "httpudp")) == NULL || parse_httpudp_module(httpudp_content) != 0) + error("read httpudp module error"); + free(httpudp_content); + } +} diff --git a/conf.h b/conf.h new file mode 100644 index 0000000..d1cc8ba --- /dev/null +++ b/conf.h @@ -0,0 +1,10 @@ +#ifndef CONF_H +#define CONF_H + +#include +#include +#include "main.h" + +extern void read_conf(char *path); + +#endif diff --git a/httpudp.c b/httpudp.c new file mode 100644 index 0000000..985bcac --- /dev/null +++ b/httpudp.c @@ -0,0 +1,434 @@ +/* + HTTPUDP模块代理UDP过程: + 获取客户端UDP数据 + 向服务器发送一个http请求头 + 收到服务端回应后发送数据到服务端,内容为: UDP原始目标地址[struct in_addr](只有第一个数据包发送) + UDP长度[uint16_t] + UDP真实数据 + 服务端返回数据,数据内容为: UDP包的长度[uint16_t] + UDP真实数据 + 新建一个socket伪装原目标地址向客户端发送返回的数据(此功能需要root,否则部分UDP代理不上,例如QQ语音) +*/ + +#include "httpudp.h" + +#define MAX_CLIENT_INFO 512 +#define HTTP_RSP_SIZE 2048 +#define CLIENT_BUFFER_SIZE 65535 //如果可以 尽量一次性读完数据 +#define SERVER_BUFFER_SIZE 8192 + +typedef struct connection_info { + char client_data[CLIENT_BUFFER_SIZE + sizeof(struct sockaddr_in) + sizeof(uint16_t)]; + struct sockaddr_in inaddr, toaddr; + struct connection_info *next; + char *rsp_data; + int client_data_len, client_data_sent_len, http_request_sent_len, rsp_data_len, rsp_data_sent_len, server_fd, responseClientFd, timer; +} info_t; + +static info_t client_info_list[MAX_CLIENT_INFO]; +static struct epoll_event udp_evs[MAX_CLIENT_INFO * 2 + 2], udp_ev; +struct httpudp udp; +static int udp_efd; + +static void proxyStop(info_t * info) +{ + epoll_ctl(udp_efd, EPOLL_CTL_DEL, info->server_fd, NULL); + epoll_ctl(udp_efd, EPOLL_CTL_DEL, info->responseClientFd, NULL); + close(info->server_fd); + close(info->responseClientFd); + free(info->rsp_data); + info->rsp_data = NULL; + do { + info->server_fd = info->responseClientFd = -1; + info->rsp_data_len = info->rsp_data_sent_len = info->client_data_sent_len = info->http_request_sent_len = 0; + } while ((info = info->next) != NULL); +} + +void udp_timeout_check() +{ + int i; + + for (i = 0; i < MAX_CLIENT_INFO; i++) { + if (client_info_list[i].server_fd > -1) { + if (client_info_list[i].timer >= global.timeout_m) + proxyStop(client_info_list + i); + else + client_info_list[i].timer = 0; + } + } +} + +/* 创建udpfd回应客户端 */ +static int createRspFd(info_t * client) +{ + int opt = 1; + + client->responseClientFd = socket(AF_INET, SOCK_DGRAM, 0); + if (client->responseClientFd < 0) + return 1; + fcntl(client->responseClientFd, F_SETFL, O_NONBLOCK); + /* + 以下函数不做返回值判断 + 因为有些UDP客户端不需要伪装源目标地址 + */ + setsockopt(client->responseClientFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + setsockopt(client->responseClientFd, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt)); + //切换root伪装源目标地址 + seteuid(0); + setegid(0); + bind(client->responseClientFd, (struct sockaddr *)&client->toaddr, sizeof(struct sockaddr_in)); + //切换回用户设置的uid + setegid(global.uid); + seteuid(global.uid); + + return 0; +} + +/* 将服务端返回的数据发送到客户端 */ +static int outputToClient(info_t * client) +{ + char *dataPtr; + int write_len; + + if (client->responseClientFd < 0 && createRspFd(client) < 0) + return 1; + + client->timer = 0; + dataPtr = client->rsp_data; + //至少要有一个完整的udp包才返回客户端 + while ((int)(*(uint16_t *) dataPtr + sizeof(uint16_t)) <= client->rsp_data_len) { + write_len = sendto(client->responseClientFd, dataPtr + sizeof(uint16_t) + client->rsp_data_sent_len, *(uint16_t *) dataPtr - client->rsp_data_sent_len, 0, (struct sockaddr *)&client->inaddr, sizeof(struct sockaddr_in)); + //printf("rsp: [write_len:%d, dataLen:%u, sent:%u, total:%d]\n", write_len, *(uint16_t *)dataPtr, client->rsp_data_sent_len, client->rsp_data_len); + if (write_len < 0 && errno == EAGAIN) + return 0; + client->rsp_data_sent_len += write_len; + if (write_len == 0 || write_len < 0) { + //perror("toClient write()"); + return 1; + } + if (write_len < *(uint16_t *) dataPtr) { + udp_ev.data.ptr = client; + udp_ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + epoll_ctl(udp_efd, EPOLL_CTL_ADD, client->responseClientFd, &udp_ev); + return 0; + } + dataPtr += write_len + sizeof(uint16_t); + client->rsp_data_len -= write_len + sizeof(uint16_t); + client->rsp_data_sent_len = 0; + } + //发送完已读取到的所有数据 释放内存 + if (client->rsp_data_len == 0) { + free(client->rsp_data); + client->rsp_data = NULL; + udp_ev.data.ptr = client; + udp_ev.events = EPOLLIN | EPOLLET; + epoll_ctl(udp_efd, EPOLL_CTL_MOD, client->responseClientFd, &udp_ev); + } + //还有数据未返回给客户端,将未返回的数据复制到内存头 + else if (dataPtr > client->rsp_data) { + memmove(client->rsp_data, dataPtr, client->rsp_data_len); + } + + return 0; +} + +/* 读取服务器的数据并返回给客户端 */ +static void recvServer(info_t * in) +{ + in->timer = 0; + //当条件成立时表示未接收https回应状态码 + if (udp.http_request_len == in->http_request_sent_len) { + static char http_rsp[HTTP_RSP_SIZE]; + int read_len; + do { + read_len = read(in->server_fd, http_rsp, HTTP_RSP_SIZE); + if (read_len == 0 || (read_len < 0 && errno != EAGAIN)) { + proxyStop(in); + return; + } + } while (read_len == HTTP_RSP_SIZE); + in->http_request_sent_len++; //不再接收http头 + udp_ev.data.ptr = in; + udp_ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + epoll_ctl(udp_efd, EPOLL_CTL_MOD, in->server_fd, &udp_ev); + return; + } + + char *new_data; + int read_len; + do { + new_data = (char *)realloc(in->rsp_data, in->rsp_data_len + SERVER_BUFFER_SIZE); + if (new_data == NULL) { + proxyStop(in); + return; + } + in->rsp_data = new_data; + read_len = read(in->server_fd, in->rsp_data + in->rsp_data_len, SERVER_BUFFER_SIZE); + /* 判断是否关闭连接 */ + if (read_len <= 0) { + if (read_len == 0 || errno != EAGAIN || in->rsp_data_len == 0) { + proxyStop(in); + return; + } + read_len = 0; + break; + } + if (udp.httpsProxy_encodeCode) + dataEncode(in->rsp_data + in->rsp_data_len, read_len, udp.httpsProxy_encodeCode); + if (udp.encodeCode) + dataEncode(in->rsp_data + in->rsp_data_len, read_len, udp.encodeCode); + in->rsp_data_len += read_len; + } while (read_len == SERVER_BUFFER_SIZE); + outputToClient(in); +} + +/* 向服务器发送数据 */ +static int sendToServer(info_t * out) +{ + info_t *send_info; + int len; + + out->timer = 0; + /* 发送http请求头到服务器 */ + if (udp.http_request_len > out->http_request_sent_len) { + len = write(out->server_fd, udp.http_request + out->http_request_sent_len, udp.http_request_len - out->http_request_sent_len); + if (len <= 0) { + if (len == 0 || errno != EAGAIN) + return 1; + return 0; + } + if (len > 0) { + out->http_request_sent_len += len; + if (udp.http_request_len == out->http_request_sent_len) { + udp_ev.data.ptr = out; + udp_ev.events = EPOLLIN | EPOLLET; + epoll_ctl(udp_efd, EPOLL_CTL_MOD, out->server_fd, &udp_ev); + } + } + return 0; + } + + /* 发送UDP目标地址,UDP数据长度和UDP真实数据到服务器 */ + for (send_info = out; send_info; send_info = send_info->next) { + if (send_info->client_data_len == send_info->client_data_sent_len) + continue; + + len = write(out->server_fd, send_info->client_data + send_info->client_data_sent_len, send_info->client_data_len - send_info->client_data_sent_len); + //printf("server_fd: %d, write_len: %d, udp_len: %d, sent_le: %d\n", out->server_fd, len, send_info->client_data_len - send_info->client_data_sent_len, send_info->client_data_sent_len); + if (len <= 0) { + if (len == 0 || errno != EAGAIN) + return 1; + break; + } + send_info->client_data_sent_len += len; + if (send_info->client_data_sent_len < send_info->client_data_len) + break; + if (send_info != out) { + //此结构体已用完 + send_info->server_fd = -1; + send_info->client_data_sent_len = 0; + } + } + if (send_info == NULL) { + udp_ev.data.ptr = out; + udp_ev.events = EPOLLIN | EPOLLET; + epoll_ctl(udp_efd, EPOLL_CTL_MOD, out->server_fd, &udp_ev); + } + out->next = send_info; + + return 0; +} + +static void outEvent(info_t * out) +{ + if (out->server_fd == -1) + return; + + if ((out->rsp_data && outputToClient(out) != 0) || sendToServer(out) != 0) + proxyStop(out); +} + +static int recvClient(info_t * client) +{ + static char control[1024]; + struct msghdr msg; + struct iovec io; + struct cmsghdr *cmsg; + + msg.msg_name = &client->inaddr; + msg.msg_namelen = sizeof(client->inaddr); + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + io.iov_base = client->client_data + sizeof(struct sockaddr_in) + sizeof(uint16_t); + io.iov_len = CLIENT_BUFFER_SIZE; + client->client_data_len = recvmsg(global.udp_listen_fd, &msg, 0); + if (client->client_data_len <= 0) { + //perror("recvmsg()"); + return 1; + } + /* 取得客户端目标地址 */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_ORIGDSTADDR && cmsg->cmsg_len >= CMSG_LEN(sizeof(client->toaddr))) { + memcpy(&client->toaddr, CMSG_DATA(cmsg), sizeof(client->toaddr)); + break; + } + if (cmsg == NULL) + return 1; + /* + printf("src ip: [%s], port: [%d]\n", inet_ntoa(client->inaddr.sin_addr), ntohs(client->inaddr.sin_port)); + printf("dst ip: [%s], port: [%d]\n", inet_ntoa(client->toaddr.sin_addr), ntohs(client->toaddr.sin_port)); + */ + //printf("client len: %d\n", client->client_data_len); + //复制udp长度和原始目标地址 + memcpy(client->client_data, &client->toaddr, sizeof(struct sockaddr_in)); + memcpy(client->client_data + sizeof(struct sockaddr_in), &client->client_data_len, sizeof(uint16_t)); + client->client_data_len += sizeof(uint16_t) + sizeof(struct sockaddr_in); + if (udp.encodeCode) + dataEncode(client->client_data, client->client_data_len, udp.encodeCode); + if (udp.httpsProxy_encodeCode) + dataEncode(client->client_data, client->client_data_len, udp.httpsProxy_encodeCode); + + client->next = NULL; + + return 0; +} + +static void connectToServer(info_t * info) +{ + info->timer = 0; + info->server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (info->server_fd < 0) + return; + fcntl(info->server_fd, F_SETFL, O_NONBLOCK); + udp_ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + udp_ev.data.ptr = info; + if (epoll_ctl(udp_efd, EPOLL_CTL_ADD, info->server_fd, &udp_ev) != 0) { + close(info->server_fd); + info->server_fd = -1; + } else if (connect(info->server_fd, (struct sockaddr *)&udp.dst, sizeof(udp.dst)) != 0 && errno != EINPROGRESS) { + epoll_ctl(udp_efd, EPOLL_CTL_DEL, info->server_fd, NULL); + close(info->server_fd); + info->server_fd = -1; + } +} + +/* 源地址跟目标地址一样的话,服务端需要同一个socket转发 */ +static int margeClient(info_t * client) +{ + int i; + + for (i = 0; i < MAX_CLIENT_INFO; i++) { + if (client != client_info_list + i && client_info_list[i].server_fd > -1 && memcmp(((char *)&client->toaddr) + 2, ((char *)&client_info_list[i].toaddr) + 2, 6) == 0 && memcmp(((char *)&client->inaddr) + 2, ((char *)&client_info_list[i].inaddr) + 2, 6) == 0) { + info_t *lastInfo; + for (lastInfo = client_info_list + i; lastInfo->next; lastInfo = lastInfo->next) ; + lastInfo->next = client; + client->server_fd = -2; //保证下次调用margeClient()不匹配到这个结构体 并且不被其他客户端连接使用 + client->client_data_sent_len = sizeof(struct sockaddr_in); //不再发送UDP目标地址 + //没有收到服务端回应前不发送UDP的数据 + if (client_info_list[i].http_request_sent_len > udp.http_request_len) { + udp_ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + udp_ev.data.ptr = client_info_list + i; + epoll_ctl(udp_efd, EPOLL_CTL_MOD, client_info_list[i].server_fd, &udp_ev); + } + return 0; + } + } + + return 1; +} + +static void new_client() +{ + int i; + + for (i = 0; i < MAX_CLIENT_INFO; i++) { + if (client_info_list[i].server_fd == -1) { + if (recvClient(client_info_list + i) == 0) + if (margeClient(client_info_list + i) != 0) + connectToServer(client_info_list + i); + return; + } + } +} + +static void http_udp_req_init() +{ + char dest[22]; + + sprintf(dest, "%s:%u", inet_ntoa(udp.dst.sin_addr), ntohs(udp.dst.sin_port)); + if (udp.http_request) { + udp.http_request_len = strlen(udp.http_request) + 2; + udp.http_request = (char *)realloc(udp.http_request, udp.http_request_len + 1); + if (udp.http_request == NULL) + error("httpudp http request initializate failed."); + strcat(udp.http_request, "\r\n"); + udp.http_request = replace(udp.http_request, &udp.http_request_len, "[V]", 3, "HTTP/1.1", 8); + udp.http_request = replace(udp.http_request, &udp.http_request_len, "[H]", 3, dest, strlen(dest)); + udp.http_request = replace(udp.http_request, &udp.http_request_len, "\\0", 2, "\0", 1); + udp.http_request = replace(udp.http_request, &udp.http_request_len, "[M]", 3, "CONNECT", 7); + udp.http_request = replace(udp.http_request, &udp.http_request_len, "[url]", 5, "/", 1); + udp.http_request = replace(udp.http_request, &udp.http_request_len, "[U]", 3, "/", 1); + } else { /* 默认使用CONNECT请求 */ + if (https.encodeCode) { + dataEncode(dest, strlen(dest), https.encodeCode); + udp.httpsProxy_encodeCode = https.encodeCode; + } + udp.http_request_len = default_ssl_request_len; + copy_new_mem(default_ssl_request, default_ssl_request_len, &udp.http_request); + udp.http_request = replace(udp.http_request, &udp.http_request_len, "[H]", 3, dest, strlen(dest)); + memcpy(&udp.dst, &https.dst, sizeof(udp.dst)); + } + if (udp.http_request == NULL) + error("out of memory."); + /* 保存原始生成的请求头,配合usr_hdr使用 */ + if (saveHdrs) { + if (copy_new_mem(udp.http_request, udp.http_request_len, &udp.original_http_request) != 0) + error("out of memory."); + udp.original_http_request_len = udp.http_request_len; + } +} + +void udp_init() +{ + int i; + + //初始化http请求 + http_udp_req_init(); + //初始化结构体 + memset(client_info_list, 0, sizeof(info_t) * MAX_CLIENT_INFO); + for (i = 0; i < MAX_CLIENT_INFO; i++) + client_info_list[i].server_fd = client_info_list[i].responseClientFd = -1; + //创建epoll fd + udp_efd = epoll_create(MAX_CLIENT_INFO * 2 + 1); + if (udp_efd < 0) { + perror("udp epoll_create()"); + exit(1); + } + //添加监听socket到epoll + fcntl(global.udp_listen_fd, F_SETFL, O_NONBLOCK); + udp_ev.data.fd = global.udp_listen_fd; + udp_ev.events = EPOLLIN; + epoll_ctl(udp_efd, EPOLL_CTL_ADD, global.udp_listen_fd, &udp_ev); +} + +void *udp_loop(void *nullPtr) +{ + int n; + + while (1) { + n = epoll_wait(udp_efd, udp_evs, MAX_CLIENT_INFO * 2 + 1, -1); + while (n-- > 0) { + if (udp_evs[n].data.fd == global.udp_listen_fd) { + new_client(); + } else { + if (udp_evs[n].events & EPOLLIN) { + recvServer((info_t *) udp_evs[n].data.ptr); + } + if (udp_evs[n].events & EPOLLOUT) { + outEvent((info_t *) udp_evs[n].data.ptr); + } + } + } + } + + return NULL; //消除编译警告 +} diff --git a/httpudp.conf b/httpudp.conf new file mode 100644 index 0000000..4e67ccd --- /dev/null +++ b/httpudp.conf @@ -0,0 +1,9 @@ +global { + uid = 3004; + udp_listen = 10010; +} + +httpudp { + addr = 47.240.75.93:10010; + http_req = "[M] [U] [V]\r\nHost: [H]\r\n"; +} diff --git a/httpudp.h b/httpudp.h new file mode 100644 index 0000000..5260e97 --- /dev/null +++ b/httpudp.h @@ -0,0 +1,33 @@ +#ifndef HTTPUDP_H +#define HTTPUDP_H + +#include "main.h" + +/* 定义TPROXY模块需要的选项,有些编译器不带这些定义 */ +#ifndef IP_TRANSPARENT +#define IP_TRANSPARENT 19 +#endif +#ifndef IP_RECVORIGDSTADDR +#define IP_RECVORIGDSTADDR 20 +#endif +#ifndef IP_ORIGDSTADDR +#define IP_ORIGDSTADDR 20 +#endif +//默认使用HTTPS模块 +//#define HTTPUDP_REQUEST "GET / HTTP/1.1\r\nHost: [H]\r\nConnection: Upgrade\r\nSec-WebSocket-Key: ChameleonProxy httpUDP Client\r\nSec-WebSocket-Version: "VERSION"\r\nUpgrade: websocket\r\nProxy-Connection: Keep-Alive\r\n\r\n" + +struct httpudp { + struct sockaddr_in dst; + char *http_request, *original_http_request; //original_http_request为初始化生成的请求头,用来配合use_hdr语法 + int http_request_len, original_http_request_len; + unsigned encodeCode, //数据编码传输 + httpsProxy_encodeCode; //CONNECT代理编码 +}; + +extern void udp_timeout_check(); +extern void *udp_loop(void *nullPtr); +extern void udp_init(); + +extern struct httpudp udp; + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..6be545f --- /dev/null +++ b/main.c @@ -0,0 +1,79 @@ +#include +#include +#include "main.h" + +struct global global; + +struct tcp_mode http, https; +struct save_header *saveHdrs; +char *default_ssl_request; +int default_ssl_request_len; + +void *timeout_check(void *nullPtr) +{ + while (1) { + sleep(60); + if (global.udp_listen_fd >= 0) + udp_timeout_check(); + } + + return NULL; +} + +/* 初始化变量 */ +static void initVariable() +{ + memset(&global, 0, sizeof(global)); + memset(&https, 0, sizeof(https)); + memset(&udp, 0, sizeof(udp)); + saveHdrs = NULL; + http.dst.sin_family = https.dst.sin_family = udp.dst.sin_family = AF_INET; + global.tcp_listen_fd = global.udp_listen_fd = global.uid = -1; +} + +static void server_init() +{ + /* 忽略PIPE信号 */ + signal(SIGPIPE, SIG_IGN); + //不能用setgid和setuid,这两个函数不能切换回root,可能导致HTTPUDP代理失败 + if (global.uid > -1 && (setegid(global.uid) == -1 || seteuid(global.uid) == -1)) { + perror("setegid(or seteuid)"); + exit(1); + } +#ifndef DEBUG + if (daemon(1, 1) == -1) { + perror("daemon"); + exit(1); + } +#endif + /* + 一个进程只开一个子进程, + 程序结束时子进程先写入dns缓存, + 之后主进程再写入, + 否则可能导致缓存文件格式错误 + */ + //while (global.procs-- > 1 && (child_pid = fork()) == 0); +} + +static void start_server_loop() +{ + //printf("%s", udp.http_request); + //printf("%d\n", udp.http_request_len); + + pthread_t thread_id; + + if (global.timeout_m) + pthread_create(&thread_id, NULL, &timeout_check, NULL); + udp_init(); + udp_loop(NULL); +} + +int main(int argc, char *argv[]) +{ + initVariable(); + read_conf(argv[1]); + server_init(); + start_server_loop(); + + return 0; +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..e0148d1 --- /dev/null +++ b/main.h @@ -0,0 +1,44 @@ +#ifndef MAIN_H +#define MAIN_H + +#include +#include +#include +#include "common.h" +#include "httpudp.h" +#include "conf.h" + +struct global { + int tcp_listen_fd, dns_listen_fd, udp_listen_fd, uid, procs, timeout_m; + unsigned mode:3, strict_modify:1; +}; + +struct save_header { + struct save_header *next; + char *key, *value, *replace_string; + int key_len, value_len, replace_string_len, updateTime, timer; + unsigned notUpdate:1; +}; + +struct modify { + char *first, *del_hdr, *src, *dest; + struct save_header *saveHdr; + struct modify *next; + int first_len, del_hdr_len, src_len, dest_len; + unsigned flag:3; //判断修改请求头的操作 +}; + +struct tcp_mode { + struct sockaddr_in dst; + struct modify *m; + unsigned encodeCode, //wap_connect模式数据编码传输 + uri_strict:1, http_only_get_post:1; +}; + +extern struct global global; +extern struct tcp_mode https; +extern struct save_header *saveHdrs; +extern int default_ssl_request_len; +extern char *default_ssl_request; + +#endif diff --git a/udpServer/Makefile b/udpServer/Makefile new file mode 100644 index 0000000..edc8515 --- /dev/null +++ b/udpServer/Makefile @@ -0,0 +1,13 @@ +CROSS_COMPILE ?= +CC := $(CROSS_COMPILE)gcc +STRIP := $(CROSS_COMPILE)strip +CFLAGS := -O2 -pthread -Wall +BIN := udpServer + +all : udpServer.o + $(CC) $(CFLAGS) -o $(BIN) $^ + $(STRIP) $(BIN) + -chmod a+x $(BIN) + +clean : + rm -f *.o $(BIN) diff --git a/udpServer/udpServer.c b/udpServer/udpServer.c new file mode 100644 index 0000000..37a8649 --- /dev/null +++ b/udpServer/udpServer.c @@ -0,0 +1,553 @@ +/* + httpUDPServer代理UDP过程: + [接收客户端http请求并回应http请求] 这一步可有可无 + 接收客户端数据 格式为: UDP目标地址[steuct in_addr](只有第一个包有) + UDP数据长度[uint16_t] + UDP真实数据 + 发送UDP数据并接收UDP服务器回应的数据 + 将UDP服务器回应的UDP数据返回给客户端,格式: UDP数据长度[uint16_t] + UDP真实数据 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WEB_SOCKET_RSP "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ChameleonProxy httpUDP Server\r\nVia: ChameleonProxy httpUDP Server\r\n\r\n" +#define HTTP_RSP "HTTP/1.1 200 OK\r\nConnection: Keep-Alive\r\nContent-Length: 999999999\r\nServer: ChameleonProxy httpUDP Server\r\n\r\n" +#define SSL_RSP "HTTP/1.1 200 Connection established\r\nConnection: Keep-Alive\r\nServer: ChameleonProxy httpUDP Server\r\n\r\n" +#define BUFFER_SIZE 4096+65535 +#define DEFAULT_TIMEOUT_S 20 +#define DEFAULT_THEAD_POOL_SIZE 30 +#define HTTP_TYPE 0 +#define OTHER_TYPE 1 + +struct clientData { + char buffer[BUFFER_SIZE+1], *client_data, *udpData; + struct sockaddr_in udpDst; + int client_data_len, clientfd, remote_udpfd; + uint16_t udpData_len; + unsigned sentRspHttpReq :1; +}; + +struct clientData publicConn; //主线程设置该变量,子线程复制 +pthread_cond_t thCond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t thMutex = PTHREAD_MUTEX_INITIALIZER; +pthread_t master_thId; //主线程的线程id +int listenfd = -1, + timeout_s = DEFAULT_TIMEOUT_S, + thread_pool_size = DEFAULT_THEAD_POOL_SIZE; +uint8_t encodeCode = 0; + +void usage() +{ + printf("httpudp server(0.3):\n" + "Author: CuteBi\n" + "E-mail: 915445800@qq.com\n" + " -l \033[20G listen port\n" + " -t \033[20G timeout(s) defaule is %d s\n" + " -w \033[20G worker proc\n" + " -p \033[20G thread pool size default is %d\n" + " -e \033[20G encode data code(128-255) default is 0\n" + " -u \033[20G set uid\n\n", DEFAULT_TIMEOUT_S, DEFAULT_THEAD_POOL_SIZE); + exit(0); +} + +/* 对数据进行编码 */ +void dataEncode(char *data, int data_len) +{ + while (data_len-- > 0) + data[data_len] ^= encodeCode; +} + +/* 判断请求类型 */ +uint8_t request_type(char *req) +{ + if (strncmp(req, "GET", 3) == 0 || + strncmp(req, "POST", 4) == 0 || + strncmp(req, "CONNECT", 7) == 0 || + strncmp(req, "HEAD", 4) == 0 || + strncmp(req, "PUT", 3) == 0 || + strncmp(req, "OPTIONS", 7) == 0 || + strncmp(req, "MOVE", 4) == 0 || + strncmp(req, "COPY", 4) == 0 || + strncmp(req, "TRACE", 5) == 0 || + strncmp(req, "DELETE", 6) == 0 || + strncmp(req, "LINK", 4) == 0 || + strncmp(req, "UNLINK", 6) == 0 || + strncmp(req, "PATCH", 5) == 0 || + strncmp(req, "WRAPPED", 7) == 0) + return HTTP_TYPE; + else + return OTHER_TYPE; +} + +/* 回应HTTP请求 */ +int rspHttpReq(struct clientData *client) +{ + /* 回应CONNECT请求 */ + if (strncmp(client->client_data, "CON", 3) == 0) + { + if (write(client->clientfd, SSL_RSP, sizeof(SSL_RSP) - 1) <= 0) + { + perror("ssl rsp write()"); + return 1; + } + } + /* 回应WebSocket请求 */ + else if (strstr(client->client_data, "websocket")) + { + if (write(client->clientfd, WEB_SOCKET_RSP, sizeof(WEB_SOCKET_RSP) - 1) <= 0) + { + perror("websocket rsp write()"); + return 1; + } + } + /* 回应HTTP请求 */ + else + { + if (write(client->clientfd, HTTP_RSP, sizeof(HTTP_RSP) - 1) <= 0) + { + perror("http rsp write()"); + return 1; + } + } + + client->sentRspHttpReq = 1; + return 0; +} + +/* 得到客户端数据中的udpDataLen dstAddr */ +int parse_request(struct clientData *client) +{ + char *headerEnd; + + if (request_type(client->client_data) == OTHER_TYPE) + { + client->udpData = client->client_data; + } + else + { + headerEnd = strstr(client->client_data, "\n\r\n"); + if (headerEnd == NULL) + { + //puts("headerEnd NULL."); + return 1; + } + *headerEnd = '\0'; + if (client->sentRspHttpReq == 0 && rspHttpReq(client) != 0) + return 1; + *headerEnd = '\n'; + client->udpData = headerEnd + 3; + } + if ((int)(client->client_data_len - (client->udpData - client->client_data) - sizeof(struct sockaddr_in) - sizeof(uint16_t)) <= 0) + return 1; + if (encodeCode) + dataEncode(client->udpData, (int)(client->client_data_len - (client->udpData - client->client_data))); + + /* 复制UDP目标地址跟UDP长度 */ + memcpy(&client->udpDst, client->udpData, sizeof(struct sockaddr_in)); + memcpy(&(client->udpData_len), (uint16_t *)(client->udpData + sizeof(struct sockaddr_in)), sizeof(uint16_t)); + client->udpData += sizeof(struct sockaddr_in); + //printf("len: [%u] dataLen: [%d], ip: [%s], port: [%d]\n", client->udpData_len, (int)(client->client_data_len - (client->udpData - client->client_data)), inet_ntoa(client->udpDst.sin_addr), ntohs(client->udpDst.sin_port)); + + return 0; +} + +int recvServer(struct clientData *server) +{ + int read_len; + + while ((read_len = recv(server->remote_udpfd, server->buffer + sizeof(uint16_t), BUFFER_SIZE - sizeof(uint16_t), MSG_DONTWAIT)) > 0) + { + //printf("%u: read remote: [%d]\n", pthread_self(), read_len); + memcpy(server->buffer, &read_len, sizeof(uint16_t)); + //printf("server read_len: [%d], server->buffer: [%u]\n", read_len, *(uint16_t *)server->buffer); + read_len += sizeof(uint16_t); + if (encodeCode) + dataEncode(server->buffer, read_len); + if (write(server->clientfd, server->buffer, read_len) != read_len) + { + perror("write to client()"); + return 1; + } + } + if (read_len == 0 || errno != EAGAIN) + { + perror("server recv()"); + return 1; + } + return 0; +} + +/* + 发送客户端数据到服务器 + 发送失败或者发送完成返回null + 未发送完成返回未发送的数据首地址 +*/ +char *sendServer(struct clientData *client) +{ + char *dataPtr; + int write_len; + + dataPtr = client->client_data; + //client->client_data_len > 1才能满意uint16_t这个类型的储存空间 + while (client->client_data_len > 1 && (int)(*(uint16_t *)dataPtr + sizeof(uint16_t)) <= client->client_data_len) + { + if ((write_len = write(client->remote_udpfd, dataPtr+sizeof(uint16_t), *(uint16_t *)dataPtr)) != *(uint16_t *)dataPtr) + { + perror("write to remote()"); + return NULL; + } + //printf("%u, fd: %d, write_len: %d, client_data_len: %d\n", pthread_self(), client->remote_udpfd, write_len, client->client_data_len); + dataPtr += write_len + sizeof(uint16_t); + client->client_data_len -= write_len + sizeof(uint16_t); + } + + return client->client_data_len > 0 ? dataPtr : NULL; +} + +int recvClient(struct clientData *client) +{ + char *new_data, *dataPtr; + int read_len; + + do { + new_data = (char *)realloc(client->client_data, client->client_data_len + BUFFER_SIZE); + if (new_data == NULL) + return 1; + client->client_data = new_data; + read_len = recv(client->clientfd, client->client_data + client->client_data_len, BUFFER_SIZE, MSG_DONTWAIT); + printf("%lu: get client len: [%d]\n", pthread_self(), read_len); + if (read_len <= 0) + { + if (read_len == 0 || errno != EAGAIN) + { + perror("client read()"); + return 1; + } + return 0; + } + if (encodeCode) + dataEncode(client->client_data + client->client_data_len, read_len); + client->client_data_len += read_len; + dataPtr = sendServer(client); + //write()发生错误 + if (dataPtr == NULL && client->client_data_len > 0) + { + return 1; + } + else if (client->client_data_len > 0) + { + memmove(client->client_data, dataPtr, client->client_data_len); + } + else + { + free(client->client_data); + client->client_data = NULL; + client->client_data_len = 0; + } + } while (read_len == BUFFER_SIZE); + + return 0; +} + +void forwardData(struct clientData *client) +{ + struct pollfd pfds[2]; + + pfds[0].fd = client->remote_udpfd; + pfds[1].fd = client->clientfd; + pfds[0].events = pfds[1].events = POLLIN; + while (poll(pfds, 2, timeout_s*1000) > 0) + { + printf("a event %lu\n", pthread_self()); + if (pfds[0].revents & POLLIN) + { + printf("recvServer %lu\n", pthread_self()); + if (recvServer(client) != 0) + return; + } + if (pfds[1].revents & POLLIN) + { + printf("recvServer %lu\n", pthread_self()); + if (recvClient(client) != 0) + return; + } + } +} + +int sendFirstData(struct clientData *client) +{ + char *dataPtr; + + printf("%lu: sendFirstData\n", pthread_self()); + client->remote_udpfd = socket(AF_INET, SOCK_DGRAM, 0); + if (client->remote_udpfd < 0) + { + perror("socket()"); + return 1; + } + connect(client->remote_udpfd, (struct sockaddr *)&client->udpDst, sizeof(struct sockaddr_in)); + client->client_data = client->udpData; + client->client_data_len = client->udpData_len + sizeof(uint16_t); + dataPtr = sendServer(client); + if (dataPtr == NULL) + { + if (client->client_data_len > 0) + return 1; + client->client_data = NULL; + } + else + { + client->client_data = (char *)malloc(client->client_data_len); + if (client->client_data == NULL) + return 1; + memcpy(client->client_data, dataPtr, client->client_data_len); + } + + printf("%lu: sendFirstData end\n", pthread_self()); + return 0; +} + +int parseClient(struct clientData *client) +{ + int read_len, count; + + printf("%lu: parseClient\n", pthread_self()); + count = 0; + client->client_data = client->buffer; + do { + //printf("%u: start read\n", pthread_self()); + read_len = read(client->clientfd, client->client_data + client->client_data_len, BUFFER_SIZE - client->client_data_len); + //printf("%u: read_len = %d\n", pthread_self(), read_len); + if (read_len <= 0) + { + perror("parseClient read()"); + return 1; + } + client->client_data_len += read_len; + client->client_data[client->client_data_len] = '\0'; + count++; + } while (parse_request(client) != 0 && count < 5); + + //printf("%u: parseClient end\n", pthread_self()); + return count == 5 ? 1 : 0; +} + +void *new_connection(void *nullPtr) +{ + #define NO_COPY_SIZE (BUFFER_SIZE + 1 + (sizeof(char *)<<1) + sizeof(struct sockaddr_in)) //struct clientData不需要全部复制 + struct clientData client; + + //printf("new_connection: %u\n", pthread_self()) + memcpy((void *)(&client) + NO_COPY_SIZE, (void *)(&publicConn) + NO_COPY_SIZE, sizeof(struct clientData) - NO_COPY_SIZE); + pthread_kill(master_thId, SIGUSR1); + /* 读取客户端数据 */ + if (parseClient(&client) == 0 && sendFirstData(&client) == 0) + forwardData(&client); + else + puts("parseClient() client error"); + + close(client.remote_udpfd); + close(client.clientfd); + if (client.client_data != client.buffer && client.client_data != client.udpData) + free(client.client_data); + + //printf("new_connection end: %u\n", pthread_self()); + return NULL; +} + +int accept_client() +{ + struct sockaddr_in addr; + struct timeval tv = {timeout_s, 0}; + socklen_t addr_len = sizeof(addr); + + publicConn.clientfd = accept(listenfd, (struct sockaddr *)&addr, &addr_len); + if (publicConn.clientfd < 0) + { + perror("accept()"); + return 1; + } + setsockopt(publicConn.clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + return 0; +} + +void *threadPool_waitTask(void *ptr) +{ + int *isBusy; + + isBusy = (int *)ptr; + while (1) + { + pthread_cond_wait(&thCond, &thMutex); + pthread_mutex_unlock(&thMutex); //解锁,让其他线程可以并发 + *isBusy = 1; + new_connection(NULL); + *isBusy = 0; + } + + return NULL; +} + +void loop() +{ + pthread_t th_id; + pthread_attr_t attr; + sigset_t sig; + int *th_isBusy, //线程执行繁忙值为1,空闲值为0 + i, signum; + + //初始化publicConn + memset(&publicConn, 0, sizeof(struct clientData)); + publicConn.remote_udpfd = -1; + /* 创建线程池 */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + th_isBusy = (int *)calloc(thread_pool_size, sizeof(int)); + if (th_isBusy == NULL) + { + perror("calloc()"); + return; + } + for (i = 0; i < thread_pool_size; i++) + pthread_create(&th_id, &attr, &threadPool_waitTask, (void *)(th_isBusy + i)); + /* 初始化信号设置,用于子线程告诉主线程内存已经拷贝完毕 */ + sigemptyset(&sig); + sigaddset(&sig, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &sig, NULL); + master_thId = pthread_self(); + while (1) + { + if (accept_client() != 0) + { + sleep(3); + continue; + } + /* 如果线程池有空闲线程则调用空闲线程处理 */ + for (i = 0; i < thread_pool_size; i++) + { + if (th_isBusy[i] == 0) + { + pthread_cond_signal(&thCond); + break; + } + } + /* 线程池没有空闲线线程,创建新线程运行任务 */ + if (i == thread_pool_size) + { + if (pthread_create(&th_id, &attr, &new_connection, NULL) != 0) + { + close(publicConn.clientfd); + continue; + } + } + sigwait(&sig, &signum); + } +} + +int create_listen(char *ip, int port) +{ + int fd, optval = 1; + struct sockaddr_in addr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + perror("socket()"); + return -1; + } + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip); + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) + { + close(fd); + perror("setsockopt()"); + return -1; + } + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) + { + close(fd); + perror("bind()"); + return -1; + } + if (listen(fd, 500) != 0) + { + close(fd); + perror("listen()"); + return -1; + } + + return fd; +} + +void readCmd(int argc, char **argv) +{ + int opt, worker_proc; + + while ((opt = getopt(argc, argv, "l:u:e:w:p:t:h")) != -1) + { + switch (opt) + { + case 't': + timeout_s = atoi(optarg); + break; + + case 'e': + encodeCode = atoi(optarg); + break; + + case 'l': + listenfd = create_listen((char *)"0.0.0.0", atoi(optarg)); + break; + + case 'u': + if (setgid(atoi(optarg)) || setuid(atoi(optarg))) + perror("setgid(or setuid)()"); + break; + + case 'w': + worker_proc = atoi(optarg); + while (worker_proc-- > 1 && fork()); + break; + + case 'p': + thread_pool_size = atoi(optarg); + break; + + case 'h': + usage(); + break; + } + } + +} +int main(int argc, char **argv) +{ + readCmd(argc, argv); + if (listenfd < 0) + { + usage(); + return 1; + } + if (daemon(1, 1) == -1) + { + perror("daemon()"); + return 1; + } + signal(SIGPIPE, SIG_IGN); + loop(); + + return 0; +}