From 40df4da8b808f11f876f592ed1b98ce8407ece0c Mon Sep 17 00:00:00 2001 From: aixiao Date: Thu, 23 Dec 2021 09:02:41 +0800 Subject: [PATCH] Httpudp support, fixed error reading httpdns "http_req". --- CProxy.conf | 12 +- CProxy.conf.explain | 44 ++-- Makefile | 2 +- README.md | 3 +- conf.c | 55 ++++- conf.h | 9 +- http_proxy.c | 17 ++ http_proxy.h | 2 + http_request.c | 6 + http_request.h | 1 + httpudp.c | 434 +++++++++++++++++++++++++++++++++ httpudp.h | 33 +++ main.c | 58 ++++- main.h | 38 ++- timeout.c | 22 -- timeout.h | 7 - udpServer/Makefile | 13 + udpServer/udpServer.c | 553 ++++++++++++++++++++++++++++++++++++++++++ 18 files changed, 1237 insertions(+), 72 deletions(-) create mode 100755 httpudp.c create mode 100755 httpudp.h delete mode 100755 timeout.c delete mode 100755 timeout.h create mode 100755 udpServer/Makefile create mode 100755 udpServer/udpServer.c diff --git a/CProxy.conf b/CProxy.conf index 9f383c2..1d1a7e6 100755 --- a/CProxy.conf +++ b/CProxy.conf @@ -1,14 +1,15 @@ global { uid=3004; process=2; - timeout=1; + timeout=7; encode=128; tcp_listen=0124; dns_listen=0126; + udp_listen = 10010; } http { - http_ip="2408:8221:9916:15e0:3cf9:3d8d:64ac:b880"; + http_ip="47.240.75.93"; http_port=127; http_del="Host"; http_first="[M] http://[host][U] [V]\r\nHost: [H]\r\n"; @@ -17,7 +18,7 @@ http { } https { - https_ip="127.0.0.1"; + https_ip="47.240.75.93"; https_port=127; https_del="Host,host,x-online-host"; https_first="[M] [U] [V]\r\nHost: [host]\r\n"; @@ -29,3 +30,8 @@ httpdns { addr = 119.29.29.29:80; http_req = "[M] [U] [V]\r\nHost: [H]\r\n\r\n"; } + +httpudp { + addr = 47.240.75.93:10010; + http_req = "[M] [U] [V]\r\nHost: [H]\r\n"; +} diff --git a/CProxy.conf.explain b/CProxy.conf.explain index ec53af8..558a386 100755 --- a/CProxy.conf.explain +++ b/CProxy.conf.explain @@ -1,34 +1,39 @@ global { uid=3004; process=2; - timeout=60; - sslencoding=128; + timeout=7; + encode=128; tcp_listen=0124; - tcp6_listen=0124; - dns_listen=0125; + dns_listen=0126; + udp_listen = 10010; } http { - http_ip=cproxy.aixiao.me; - http_port=124; - http_del="x-online-host,X-Online-Host,host,Host"; - http_first="[M] [U] [V]\r\nHost: [H]\r\n"; + http_ip="47.240.75.93"; + http_port=127; + http_del="Host"; + http_first="[M] http://[host][U] [V]\r\nHost: [H]\r\n"; //strrep="Windows NT 10.0->Linux"; - //regrep="Host*.+?->Host: hu60.cn:443"; + //regrep="Host:*.+?->Host: [host]:80"; } https { - https_ip=cproxy.aixiao.me; - https_port=124; + https_ip="47.240.75.93"; + https_port=127; https_del="Host,host,x-online-host"; - https_first="[M] [U] [V]\r\nhost: [host]\r\n"; - strrep="Windows NT 10.0->Linux"; - //regrep="Host*.+?->Host: hu60.cn:443"; + https_first="[M] [U] [V]\r\nHost: [host]\r\n"; + //strrep="Windows NT 10.0->Linux"; + //regrep="Host*.+?->host: [host]:443"; } httpdns { - addr=119.29.29.29:53; - http_req="[M] http://wap.10010.com/d?dn=[D] [V]\r\nHost: wap.10010.com\r\n"; + addr = 119.29.29.29:80; + http_req = "[M] [U] [V]\r\nHost: [H]\r\n\r\n"; +} + +httpudp { + addr = 47.240.75.93:10010; + http_req = "[M] [U] [V]\r\nHost: [H]\r\n"; } @@ -68,3 +73,10 @@ httpdns 模块关键字: [M], [D], [V], \r, \n, \v, \f, \b, \t, \a. addr=119.29.29.29:80; //HTTPDNS服务器IP http_req = "[M] [U] [V]\r\nHost: [H]\r\n\r\n"; //自定义HTTPDNS请求头 +httpudp模块 +httpudp 模块关键字: [M], [U], [V], [H]. +默认 [M] 为 CONNECT +默认 [V] 为 HTTP/1.1 +addr=47.240.75.93:10010; //HTTPUDP服务器IP +http_req = "[M] [U] [V]\r\nHost: [H]\r\n\r\n"; //自定义HTTPUDP请求头 + diff --git a/Makefile b/Makefile index 73b16b7..1517bb5 100755 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CFLAGS += -g -O2 -Wall -pthread LIBS = OBJ := CProxy -all: main.o http_proxy.o httpdns.o http_request.o conf.o timeout.o help.o +all: main.o http_proxy.o http_request.o httpdns.o httpudp.o conf.o help.o $(CC) $(CFLAGS) -o $(OBJ) $^ $(LIBS) .c.o: $(CC) $(CFLAGS) -c $< diff --git a/README.md b/README.md index 587d05d..950c055 100755 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ 可以修改HTTP协议消息头(request). 可以修改HTTP协议CONNECT方法消息头. 可以修改HTTP协议GET方法消息头. - 支持IPV4/IPV6. + httptcp支持IPV4/IPV6. + 支持httpdns、httpudp代理 ## Build diff --git a/conf.c b/conf.c index ebb102d..cfa905b 100755 --- a/conf.c +++ b/conf.c @@ -207,6 +207,8 @@ static void parse_global_module(char *content, conf * p) p->tcp6_listen = atoi(val_begin); } else if (strcasecmp(var, "dns_listen") == 0) { p->dns_listen = atoi(val_begin); + } else if (strcasecmp(var, "udp_listen") == 0) { + p->udp_listen = atoi(val_begin);; } content = strchr(lineEnd + 1, '\n'); @@ -381,20 +383,49 @@ static void parse_httpdns_module(char *content, conf * p) memset(p->addr, 0, val_begin_len); memcpy(p->addr, val_begin, val_begin_len); } else if (strcasecmp(var, "http_req") == 0) { - val_begin_len = strlen(val_begin) + 1; - p->http_req = (char *)malloc(val_begin_len); + //val_begin_len = strlen(val_begin) + 1; + val_begin_len = val_end - val_begin; + p->http_req = (char *)malloc(val_begin_len + 1); memset(p->http_req, 0, val_begin_len); memcpy(p->http_req, val_begin, val_begin_len); p->http_req_len = val_begin_len; } else if (strcasecmp(var, "encode") == 0) { p->encode = atoi(val_begin); } + + content = strchr(lineEnd + 1, '\n'); + } +} + +static void parse_httpudp_module(char *content, conf * p) +{ + char *var, *val_begin, *val_end, *lineEnd; + int val_begin_len; + + while ((lineEnd = set_var_val_lineEnd2(content, &var, &val_begin, &val_end)) != NULL) { + if (strcasecmp(var, "addr") == 0) { + val_begin_len = strlen(val_begin) + 1; + p->httpudp_addr = (char *)malloc(val_begin_len); + memset(p->httpudp_addr, 0, val_begin_len); + memcpy(p->httpudp_addr, val_begin, val_begin_len); + } else if (strcasecmp(var, "http_req") == 0) { + //val_begin_len = strlen(val_begin) + 1; + val_begin_len = val_end - val_begin; + p->httpudp_http_req = (char *)malloc(val_begin_len + 1); + memset(p->httpudp_http_req, 0, val_begin_len); + memcpy(p->httpudp_http_req, val_begin, val_begin_len); + p->httpudp_http_req_len = val_begin_len; + } else if (strcasecmp(var, "encode") == 0) { + p->httpudp_encode = atoi(val_begin); + } + content = strchr(lineEnd + 1, '\n'); } } void free_conf(conf * p) { + // http module if (p->http_ip) free(p->http_ip); if (p->http_del) @@ -414,6 +445,7 @@ void free_conf(conf * p) if (p->http_regrep_obj) free(p->http_regrep_obj); + // https module if (p->https_ip) free(p->https_ip); if (p->https_del) @@ -433,19 +465,28 @@ void free_conf(conf * p) if (p->https_regrep_obj) free(p->https_regrep_obj); + // httpdns module if (p->addr) free(p->addr); if (p->http_req) { - bzero(p->http_req, 0); p->http_req_len = 0; free(p->http_req); } + + // httpudp module + if(p->httpudp_addr) + free(p->httpudp_addr); + if (p->httpudp_http_req) { + p->httpudp_http_req_len = 0; + free(p->httpudp_http_req); + } + return; } void read_conf(char *filename, conf * configure) { - char *buff, *global_content, *http_content, *https_content, *httpdns_content; + char *buff, *global_content, *http_content, *https_content, *httpdns_content, *httpudp_content; FILE *file; long file_size; @@ -489,4 +530,10 @@ void read_conf(char *filename, conf * configure) else parse_httpdns_module(httpdns_content, configure); free(httpdns_content); + + if ((httpudp_content = read_module(buff, "httpudp")) == NULL) + perror("read httpdns module error"); + else + parse_httpudp_module(httpudp_content, configure); + free(httpudp_content); } diff --git a/conf.h b/conf.h index ca825f9..b624893 100755 --- a/conf.h +++ b/conf.h @@ -18,6 +18,7 @@ typedef struct CONF { int tcp_listen; int tcp6_listen; int dns_listen; + int udp_listen; // http module int http_port; @@ -45,11 +46,17 @@ typedef struct CONF { char *https_regrep_aim, *https_regrep_obj; int https_regrep_aim_len, https_regrep_obj_len; - // http dns_listen + // httpdns module char *addr; char *http_req; int http_req_len; int encode; + + // httpudp module + char *httpudp_addr; + char *httpudp_http_req; + int httpudp_http_req_len; + int httpudp_encode; } conf; char *strncpy_(char *dest, const char *src, size_t n); diff --git a/http_proxy.c b/http_proxy.c index 51db613..ef4d883 100755 --- a/http_proxy.c +++ b/http_proxy.c @@ -4,6 +4,7 @@ int sslEncodeCode; int remote_port; char remote_host[CACHE_SIZE]; +int timeout_minute; /* 对数据进行编码 */ void dataEncode(char *data, int data_len, unsigned code) @@ -12,6 +13,22 @@ void dataEncode(char *data, int data_len, unsigned code) data[data_len] ^= code; } +void *tcp_timeout_check(void *nullPtr) +{ + int i; + + for (i = 0; i < MAX_CONNECTION; i += 2) { + if (cts[i].fd > -1) { + if (cts[i].timer >= timeout_minute) { + close_connection(cts + i); + } else + cts[i].timer++; + } + } + + return NULL; +} + static char *read_data(conn_t * in, char *data, int *data_len) { char *new_data; diff --git a/http_proxy.h b/http_proxy.h index d2d71d7..f4a6fce 100755 --- a/http_proxy.h +++ b/http_proxy.h @@ -24,5 +24,7 @@ extern void tcp_out(conn_t * out); extern void close_connection(conn_t * conn); extern char *request_head(conn_t * in, conf * configure); extern void dataEncode(char *data, int data_len, unsigned code); +extern int timeout_minute; +void *tcp_timeout_check(void *nullPtr); #endif diff --git a/http_request.c b/http_request.c index 764a4c7..d928da8 100755 --- a/http_request.c +++ b/http_request.c @@ -1,5 +1,11 @@ #include "http_request.h" +void errors(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); diff --git a/http_request.h b/http_request.h index 3c62c44..4a48fa1 100755 --- a/http_request.h +++ b/http_request.h @@ -22,5 +22,6 @@ void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_ extern char *replace(char *replace_memory, int *replace_memory_len, const char *src, const int src_len, const char *dest, const int dest_len); char *request_head(conn_t * in, conf * configure); extern int8_t copy_new_mem(char *src, int src_len, char **dest); +extern void errors(const char *msg); #endif diff --git a/httpudp.c b/httpudp.c new file mode 100755 index 0000000..4e0a18f --- /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 "http_request.h" +#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) + errors("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) + errors("out of memory."); + /* 保存原始生成的请求头,配合usr_hdr使用 */ + if (saveHdrs) { + if (copy_new_mem(udp.http_request, udp.http_request_len, &udp.original_http_request) != 0) + errors("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.h b/httpudp.h new file mode 100755 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 index a055149..a054e90 100755 --- a/main.c +++ b/main.c @@ -2,7 +2,7 @@ #include "http_proxy.h" #include "http_request.h" #include "httpdns.h" -#include "timeout.h" +#include "httpudp.h" #include "conf.h" #include "help.h" @@ -10,9 +10,15 @@ #define SERVER_RELOAD 2 #define SERVER_STATUS 3 + + struct global global; +struct tcp_mode http, https; +struct save_header *saveHdrs; +char *default_ssl_request; +int default_ssl_request_len; uint16_t tcp_listen_port; -struct httpudp udp; + struct epoll_event ev, events[MAX_CONNECTION + 1]; int epollfd, server_sock, server_sock6, local_port, process; conn_t cts[MAX_CONNECTION]; @@ -291,7 +297,8 @@ int process_signal(int signal, char *process_name) if (signal == SERVER_STOP || signal == SERVER_RELOAD) { // 关闭 n -= 2; for (; n >= 0; n--) { - kill(number[n], SIGTERM); + //kill(number[n], SIGTERM); + kill(number[n], SIGKILL); } } @@ -351,8 +358,12 @@ void *timeout_check(void *nullPtr) { while (1) { sleep(60); + if (server_sock >= 0 || server_sock6 >= 0) + tcp_timeout_check(NULL); if (global.dns_listen_fd >= 0) dns_timeout_check(); + if (global.udp_listen_fd >= 0) + udp_timeout_check(); } return NULL; @@ -377,8 +388,14 @@ void initialize(conf * configure) memset(&global, 0, sizeof(global)); memset(&httpdns, 0, sizeof(httpdns)); + memset(&https, 0, sizeof(https)); + memset(&udp, 0, sizeof(udp)); + saveHdrs = NULL; httpdns.dst.sin_family = AF_INET; + udp.dst.sin_family = AF_INET; global.tcp_listen_fd = global.dns_listen_fd = global.udp_listen_fd = global.uid = -1; + + // httpdns module global.dns_listen_fd = udp_listen((char *)"127.0.0.1", configure->dns_listen); if ((p = strchr(configure->addr, ':')) != NULL) { *p = '\0'; @@ -390,6 +407,19 @@ void initialize(conf * configure) httpdns.http_req_len = configure->http_req_len; copy_new_mem(configure->http_req, httpdns.http_req_len, &httpdns.http_req); + // httpudp module + global.udp_listen_fd = udp_listen((char *)"0.0.0.0", configure->udp_listen); + if ((p = strchr(configure->httpudp_addr, ':')) != NULL) { + *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(configure->httpudp_addr); + udp.http_request_len = configure->httpudp_http_req_len; + copy_new_mem(configure->httpudp_http_req, udp.http_request_len, &udp.http_request); + + // global module server_sock = create_server_socket(configure->tcp_listen); // IPV4 server_sock6 = create_server_socket6(configure->tcp_listen); // IPV6 epollfd = epoll_create(MAX_CONNECTION); @@ -409,18 +439,29 @@ void thread_loop(conf * configure) if (pthread_sigmask(SIG_BLOCK, &signal_mask, NULL) != 0) { printf("block sigpipe error\n"); } - if (timeout_minute) - pthread_create(&thread_id, NULL, &tcp_timeout_check, NULL); - if (pthread_create(&thread_id, NULL, &http_proxy_loop, (void *)configure) != 0) - perror("pthread_create"); + //if (timeout_minute) + // pthread_create(&thread_id, NULL, &tcp_timeout_check, NULL); + if (global.timeout_m) pthread_create(&thread_id, NULL, &timeout_check, NULL); + + if (pthread_create(&thread_id, NULL, &http_proxy_loop, (void *)configure) != 0) + perror("pthread_create"); + if (global.dns_listen_fd >= 0) { dns_init(); - dns_loop(NULL); + pthread_create(&thread_id, NULL, &dns_loop, NULL); } + + if (global.udp_listen_fd >= 0) { + udp_init(); + udp_loop(NULL); + //pthread_create(&thread_id, NULL, &udp_loop, NULL); + } + pthread_join(thread_id, NULL); pthread_exit(NULL); + /* 线程分离 pthread_attr_t attr; @@ -528,6 +569,7 @@ void _main(int argc, char *argv[]) } } + server_ini(); // 守护进程 // 设置每个进程允许打开的最大文件数 diff --git a/main.h b/main.h index 55f41c8..9ce50dc 100755 --- a/main.h +++ b/main.h @@ -22,6 +22,7 @@ #include #include + #define MAX_CONNECTION 1020 #define BUFFER_SIZE 8192 #define PATH_SIZE 270 @@ -29,26 +30,45 @@ #define HTTP_HEAD_CACHE_SIZE 1024 #define ERRDEBUG fprintf(stderr,"Error Occured at File: %s, Function: %s, Line: %d, Date: %s, Time: %s.\n", __FILE__, __FUNCTION__, __LINE__, __DATE__, __TIME__); -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代理编码 -}; 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; + extern char local_host[CACHE_SIZE]; extern int epollfd, local_port, process; extern struct epoll_event ev, events[MAX_CONNECTION + 1]; int create_connection(char *remote_host, int remote_port); int create_connection6(char *remote_host, int remote_port); -extern struct global global; extern uint16_t tcp_listen_port; -extern struct httpudp udp; #endif diff --git a/timeout.c b/timeout.c deleted file mode 100755 index 8d9d9d8..0000000 --- a/timeout.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "timeout.h" -#include "main.h" -#include "http_proxy.h" - -int timeout_minute; - -void *tcp_timeout_check(void *nullPtr) -{ - int i; - - while (1) { - sleep(10); - for (i = 0; i < MAX_CONNECTION; i += 2) { - if (cts[i].fd > -1) { - if (cts[i].timer >= timeout_minute) { - close_connection(cts + i); - } else - cts[i].timer++; - } - } - } -} diff --git a/timeout.h b/timeout.h deleted file mode 100755 index 51cf7ef..0000000 --- a/timeout.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef TIME_H -#define TIME_H - -extern int timeout_minute; -void *tcp_timeout_check(void *nullPtr); - -#endif diff --git a/udpServer/Makefile b/udpServer/Makefile new file mode 100755 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 100755 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; +}