commit 3d3169397f9656c7405921088748f52c1ec697de Author: aixiao Date: Sun Dec 19 19:01:38 2021 +0800 Initial submission 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; +}