From 5da52e4d2b2d2753682ff25ccfcc80960e714399 Mon Sep 17 00:00:00 2001 From: mmmdbybyd <915445800@qq.com> Date: Wed, 12 Jul 2017 12:29:56 +0800 Subject: [PATCH] Add files via upload --- Makefile | 14 ++ README.me | 38 ++++ dns.c | 156 ++++++++++++++ dns.h | 19 ++ http.c | 594 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ http.h | 25 +++ main.c | 215 ++++++++++++++++++++ main.h | 27 +++ 8 files changed, 1088 insertions(+) create mode 100644 Makefile create mode 100644 README.me create mode 100644 dns.c create mode 100644 dns.h create mode 100644 http.c create mode 100644 http.h create mode 100644 main.c create mode 100644 main.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3427111 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +OBJ := SpecialProxy +CC := gcc +CFLAGS := -O3 -Wall -pthread + +all : main.o http.o dns.o + $(CC) $(CFLAGS) $(DEFS) -o $(OBJ) $^ + strip $(OBJ) + -chmod 777 $(OBJ) 2>&- + +.c.o : + $(CC) $(CFLAGS) $(DEFS) -c $< + +clean : + rm -f *.o diff --git a/README.me b/README.me new file mode 100644 index 0000000..8943909 --- /dev/null +++ b/README.me @@ -0,0 +1,38 @@ +SpecialProxy +====== +用epoll多路复用io写的一个HTTP代理,自带DNS解析 + +##### SpecialProxy有如下特性: + 1. 普通HTTP代理通过请求头首行的host或者Host头域字段获得目标主机, + SpecialProxy不从首行获取目标主机, + 它可以自定义代理头域(默认是Host)。 + + 2. 普通HTTP代理SSL代理是判断CONNECT请求方法, + SpecialProxy可以通过自定义特定字符串进行SSL代理(默认是CONNECT)。 + + 3. 普通HTTP代理如果遇到多个连续的HTTP请求头只重新拼接第一个请求头, + SpecialProxy可以开启严格模式(-a参数),对所以请求头都重新拼接。 + + 4. -L参数设置重定向到本地端口的头域,比如-L Local, + 然后请求头中含有Local: 443,代理会将请求发送到127.0.0.1:443 + +##### 启动参数: + -l [监听ip:]监听端口 默认监听IP为 "0.0.0.0" + -p 代理头域 默认为 "Host" + -L 本地代理头域 默认为 "Local" + -d DNS查询IP[:端口] 默认为 "114.114.114.114" + -s SSL代理字符串 默认为 "CONNECT" + -a 对所有HTTP请求重新拼接 + -h 显示帮助 + -w 工作进程数 + +##### BUG: + 好像有些连接不关闭,需要定时重启代理 + +##### 编译: +~~~~~ +Linux/Android: + make DEFS=-DXMEMCPY +Android-ndk: + ndk-build +~~~~~ \ No newline at end of file diff --git a/dns.c b/dns.c new file mode 100644 index 0000000..2be62dc --- /dev/null +++ b/dns.c @@ -0,0 +1,156 @@ +#include "dns.h" +#include "http.h" + +struct dns dns_list[MAX_CONNECTION / 2]; //一个客户端 + 一个服务端 占用一个dns结构体 +int dnsFd; + +void read_dns_rsp() +{ + static char rsp_data[512], *p, ips[16]; + static unsigned char *_p; + static struct dns *dns; + static conn_t *client; + static int16_t len, dns_flag; + + while ((len = read(dnsFd, rsp_data, BUFFER_SIZE)) > 11) + { + memcpy(&dns_flag, rsp_data, 2); + dns = dns_list + dns_flag; + client = cts + (dns_flag << 1); + //判断是否是正常DNS回应,是否已关闭连接 + if (dns_flag > MAX_CONNECTION >> 1 || client->fd < 0) + continue; + if (dns->request_len + 12 > len || (unsigned char)rsp_data[3] != 128) //char只有7位可用,则正数最高为127 + { + close_connection(client); + continue; + } + + /* get ips */ + p = rsp_data + dns->request_len + 11; + ips[0] = '\0'; + while (p - rsp_data + 4 <= len) + { + //type + if (*(p - 8) != 1) + { + p += *p + 12; + continue; + } + _p = (unsigned char *)p + 1; + sprintf(ips, "%d.%d.%d.%d", _p[0], _p[1], _p[2], _p[3]); + break; + } + if (ips[0]) + { + //printf("ips %s\n", ips); + if (connectionToServer(ips, client + 1) != 0) + { + close_connection(client); + continue; + } + } + else + { + close_connection(client); + continue; + } + } +} + +/* 完全发送返回0,发送部分返回1,出错返回-1 */ +static int8_t send_dns_req(struct dns *dns) +{ + static int write_len; + + write_len = write(dnsFd, dns->request + dns->sent_len, dns->request_len - dns->sent_len); + if (write_len == dns->request_len - dns->sent_len) + { + dns->sent_len = dns->request_len; + return 0; + } + else if (write_len >= 0) + { + dns->sent_len += write_len; + return 1; + } + else + { + return -1; + } +} + +void dns_query() +{ + static int16_t i, ret; + + for (i = 0; i < MAX_CONNECTION >> 1; i++) + { + if (dns_list[i].request_len != dns_list[i].sent_len) + { + ret = send_dns_req(dns_list + i); + if (ret == 1) + break; + else if (ret == -1) + close_connection(cts + (i << 1)); + } + } + if (i == MAX_CONNECTION >> 1) + ev.events = EPOLLIN|EPOLLET; + else + ev.events = EPOLLIN|EPOLLOUT|EPOLLET; + ev.data.fd = dnsFd; + epoll_ctl(efd, EPOLL_CTL_MOD, dnsFd, &ev); +} + +int8_t build_dns_req(struct dns *dns, char *domain) +{ + static char *p, *_p; + static int8_t domain_size; + + domain_size = strlen(domain); + p = dns->request + 12; + memcpy(p+1, domain, domain_size); + *(p+1+domain_size) = 0; + while ((_p = strchr(p+1, '.')) != NULL) + { + *p = _p - p - 1; + p = _p; + } + *p = strlen(p+1); + p = dns->request + 14 + domain_size; + *p++ = 0; + *p++ = 1; + *p++ = 0; + *p++ = 1; + dns->request_len = p - dns->request; + switch (send_dns_req(dns)) + { + case 0: + ev.data.fd = dnsFd; + ev.events = EPOLLIN|EPOLLET; + epoll_ctl(efd, EPOLL_CTL_MOD, dnsFd, &ev); + return 0; + + case 1: + ev.data.fd = dnsFd; + ev.events = EPOLLIN|EPOLLOUT|EPOLLET; + epoll_ctl(efd, EPOLL_CTL_MOD, dnsFd, &ev); + return 1; + + default: + return -1; + } +} + +void dns_connect(struct sockaddr_in *dnsAddr) +{ + dnsFd = socket(AF_INET, SOCK_DGRAM, 0); + if (dnsFd < 0) + { + perror("socket"); + exit(1); + } + connect(dnsFd, (struct sockaddr *)dnsAddr, sizeof(struct sockaddr_in)); + fcntl(dnsFd, F_SETFL, O_NONBLOCK); +} diff --git a/dns.h b/dns.h new file mode 100644 index 0000000..ff42ef0 --- /dev/null +++ b/dns.h @@ -0,0 +1,19 @@ +#ifndef DNS_H +#define DNS_H + +#include "main.h" + +struct dns { + char request[512]; //UDP的DNS请求不超512字节 + uint16_t request_len, sent_len; +}; + +extern void dns_connect(struct sockaddr_in *dnsAddr); +extern struct dns dns_list[MAX_CONNECTION / 2]; +extern int dnsFd; + +extern int8_t build_dns_req(struct dns *dns, char *domain); +extern void read_dns_rsp(); +extern void dns_query(); + +#endif \ No newline at end of file diff --git a/http.c b/http.c new file mode 100644 index 0000000..4af5c2e --- /dev/null +++ b/http.c @@ -0,0 +1,594 @@ +#include "http.h" +#include "dns.h" + +#define SSL_RSP "HTTP/1.1 200 Connection established\r\n\r\n" +#define HTTP_TYPE 0 +#define OTHER_TYPE 1 + +conn_t cts[MAX_CONNECTION]; +char *local_header, *proxy_header, *ssl_proxy; +int lisFd, proxy_header_len, local_header_len; +uint8_t strict_spilce; + +/* + 鏈夋椂鍊欑敱浜庣▼搴忎唬鐮佸師鍥爂libc鐨刴emcpy涓嶈兘src + len > dst锛岃皟璇曡捣鏉ユ瘮杈冮夯鐑 + 杩欎釜鍑芥暟姝f槸涓轰簡瑙e喅杩欐牱鐨勯棶棰橈紝浣嗘槸鏁堢巼浼氭瘮memcpy浣 +*/ +#ifdef XMEMCPY +typedef struct byte256 {char data[256];} byte256_t; +typedef struct byte64 {char data[64];} byte64_t; +typedef struct byte16 {char data[16];} byte16_t; +static void xmemcpy(char *src, const char *dst, size_t len) +{ + static byte256_t *to256, *from256; + static byte64_t *to64, *from64; + static byte16_t *to16, *from16; + + to256 = (byte256_t *)src; + from256 = (byte256_t *)dst; + while (len >= sizeof(byte256_t)) + { + *to256++ = *from256++; + len -= sizeof(byte256_t); + } + to64 = (byte64_t *)to256; + from64 = (byte64_t *)from256; + while (len >= sizeof(byte64_t)) + { + *to64++ = *from64++; + len -= sizeof(byte64_t); + } + to16 = (byte16_t *)to64; + from16 = (byte16_t *)from64; + while (len >= sizeof(byte16_t)) + { + *to16++ = *from16++; + len -= sizeof(byte16_t); + } + src = (char *)to16; + dst = (char *)from16; + while (len--) + *src++ = *dst++; +} +#else +#define xmemcpy memcpy +#endif + +int8_t connectionToServer(char *ip, conn_t *server) +{ + server->fd = socket(AF_INET, SOCK_STREAM, 0); + if (server->fd < 0) + return 1; + fcntl(server->fd, F_SETFL, fcntl(server->fd, F_GETFL)|O_NONBLOCK); + addr.sin_addr.s_addr = inet_addr(ip); + addr.sin_port = htons(server->destPort); + connect(server->fd, (struct sockaddr *)&addr, sizeof(addr)); + ev.data.ptr = server; + ev.events = EPOLLIN|EPOLLOUT|EPOLLET; + epoll_ctl(efd, EPOLL_CTL_ADD, server->fd, &ev); + + return 0; +} + +void close_connection(conn_t *conn) +{ + epoll_ctl(efd, EPOLL_CTL_DEL, conn->fd, NULL); + close(conn->fd); + if ((conn - cts) & 1) + { + static char *server_data; + + server_data = conn->ready_data; + memset(conn, 0, sizeof(conn_t)); + conn->ready_data = server_data; + conn-- ->fd = -1; + } + else + { + static struct dns *d; + + d = dns_list + ((conn - cts) >> 1); + d->request_len = d->sent_len = 0; + free(conn->ready_data); + free(conn->incomplete_data); + memset(conn, 0, sizeof(conn_t)); + conn++ ->fd = -1; + } + if (conn->fd >= 0) + close_connection(conn); +} + + +/* 鍒ゆ柇璇锋眰绫诲瀷 */ +static int8_t request_type(char *data) +{ + if (strncmp(data, "GET", 3) == 0 || + strncmp(data, "POST", 4) == 0 || + strncmp(data, "CONNECT", 7) == 0 || + strncmp(data, "HEAD", 4) == 0 || + strncmp(data, "PUT", 3) == 0 || + strncmp(data, "OPTIONS", 7) == 0 || + strncmp(data, "MOVE", 4) == 0 || + strncmp(data, "COPY", 4) == 0 || + strncmp(data, "TRACE", 5) == 0 || + strncmp(data, "DELETE", 6) == 0 || + strncmp(data, "LINK", 4) == 0 || + strncmp(data, "UNLINK", 6) == 0 || + strncmp(data, "PATCH", 5) == 0 || + strncmp(data, "WRAPPED", 7) == 0) + return HTTP_TYPE; + return OTHER_TYPE; +} + +static char *read_data(conn_t *in, char *data, int *data_len) +{ + static char *new_data; + static int read_len; + + do { + new_data = (char *)realloc(data, *data_len + BUFFER_SIZE + 1); + if (new_data == NULL) + { + free(data); + return NULL; + } + data = new_data; + read_len = read(in->fd, data + *data_len, BUFFER_SIZE); + /* 鍒ゆ柇鏄惁鍏抽棴杩炴帴 */ + if (read_len <= 0) + { + if (read_len == 0 || *data_len == 0 || errno != EAGAIN) + { + free(data); + return NULL; + } + break; + } + *data_len += read_len; + } while (read_len == BUFFER_SIZE); + *(data + *data_len) = '\0'; + + return data; +} + +static char *get_host(char *data) +{ + static char *hostEnd, *host; + + host = strstr(data, local_header); + if (host != NULL) + { + static char *local_host; + + host += local_header_len; + while (*host == ' ') + host++; + for (hostEnd = host; *hostEnd < 58 && *hostEnd > 48; hostEnd++); + //鍒ゆ柇璇ュご鍩熸槸鍚︽纭娇鐢 + if (hostEnd - host > 5 || *hostEnd != '\r') + return NULL; + local_host = (char *)malloc(16); + if (local_host == NULL) + return NULL; + strcpy(local_host, "127.0.0.1:"); + strncpy(local_host + 10, host, hostEnd - host); + local_host[10 + (hostEnd - host)] = '\0'; + puts(local_host); + return local_host; + } + host= strstr(data, proxy_header); + if (host == NULL) + return NULL; + host += proxy_header_len; + while (*host == ' ') + host++; + hostEnd = strchr(host, '\r'); + if (hostEnd) + return strndup(host, hostEnd - host); + else + return strdup(host); +} + +/* 鍒犻櫎璇锋眰澶翠腑鐨勫ご鍩 */ +static void del_hdr(char *header, int *header_len) +{ + static char *key_end, *line_begin, *line_end; + static int key_len; + + for (line_begin = strchr(header, '\n'); line_begin++ && *line_begin != '\r'; line_begin = line_end) + { + key_end = strchr(line_begin, ':'); + if (key_end == NULL) + return; + key_len = key_end - line_begin; + line_end = strchr(key_end, '\n'); + if (strncasecmp(line_begin, "host", key_len) == 0 || strncmp(line_begin, local_header + 1, key_len) == 0 || strncmp(line_begin, proxy_header + 1, key_len) == 0) + { + if (line_end++) + { + xmemcpy(line_begin, line_end, *header_len - (line_end - header) + 1); + (*header_len) -= line_end - line_begin; + line_end = line_begin - 1; //鏂拌鍓嶄竴涓瓧绗 + } + else + { + *line_begin = '\0'; + *header_len = line_begin - header; + return; + } + } + } +} + +/* 鏋勫缓鏂拌姹傚ご */ +static char *build_request(char *client_data, int *data_len, char *host) +{ + static char *uri, *url, *p, *lf, *header, *new_data, *proxy_host; + static int len; + + del_hdr(client_data, data_len); + header = client_data; + proxy_host = host; + do { + /* 灏嗗畬鏁磚rl杞崲涓簎ri */ + url = strchr(header, ' '); + lf = strchr(header, '\n'); + if (url == NULL || lf == NULL || lf - 10 <= header) + return client_data; + if (url < lf && *(++url) != '/') + { + uri = strchr(url + 7, '/'); + p = lf - 10; //鎸囧悜HTTP鐗堟湰鍓嶉潰鐨勭┖鏍 + if (uri != NULL && uri < p) + { + xmemcpy(url, uri, *data_len - (uri - client_data) + 1); + *data_len -= uri - url; + lf -= uri - url; + } + else + { + *url++ = '/'; + xmemcpy(url, p, *data_len - (p - client_data) + 1); + *data_len -= p - url; + lf -= p - url; + } + } + + *data_len += strlen(proxy_host) + 8; //8涓 "Host: " + "\r\n"鐨勯暱搴 + new_data = (char *)malloc(*data_len + 1); + if (new_data == NULL) + { + if (proxy_host != host) + free(proxy_host); + free(client_data); + return NULL; + } + /* 璇锋眰琛屽悗闈㈡坊鍔燞ost琛 */ + len = lf + 1 - client_data; + memcpy(new_data, client_data, len); //澶嶅埗璇锋眰琛 + strcpy(new_data + len, "Host: "); + strcpy(new_data + len + 6, proxy_host); + len += strlen(proxy_host) + 6; + new_data[len++] = '\r'; + new_data[len++] = '\n'; + //len += sprintf(new_data + len, "Host: %s\r\n", proxy_host); + memcpy(new_data + len, lf + 1, *data_len - len + 1); + free(client_data); + if (proxy_host != host) + free(proxy_host); + if (strict_spilce == 0) + return new_data; + client_data = new_data; + //濡傛灉璇锋眰澶村彧鏈変竴涓ご鍩燂紝鍒欏繀椤-1鎵嶈兘鎼滅储鍒癨n\r + header = strstr(client_data + len - 1, "\n\r"); + //濡傛灉鏄繛缁殑澶氫釜璇锋眰澶达紝鍒欏叏閮ㄤ慨鏀 + if (header == NULL || request_type(header + 3) == OTHER_TYPE) + return client_data; + header += 3; + proxy_host = get_host(header); + if (proxy_host == NULL) + proxy_host = host; + } while (1); +} + +/* 瑙f瀽Host */ +int8_t parse_host(conn_t *server, char *host) +{ + static char *port, *p; + + port = strchr(host, ':'); + if (port) + { + server->destPort = atoi(port+1); + *port = '\0'; + } + else + server->destPort = 80; //榛樿80绔彛 + for (p = host; (*p > 47 && *p < 58) || *p == '.'; p++); + if (*p == '\0') + { + if (connectionToServer(host, server) != 0) + return 1; + } + else if (build_dns_req(dns_list + ((server - cts) >> 1), host) == -1) + return 1; + if (port) + *port = ':'; + + return 0; +} + +/* 璇诲彇鍒扮殑鏁版嵁鍏ㄩ儴灏辩华锛屽皢incomplete_data澶嶅埗鍒皉eady_data */ +static int8_t copy_data(conn_t *ct) +{ + if (ct->ready_data) + { + static char *new_data; + + new_data = (char *)realloc(ct->ready_data, ct->ready_data_len + ct->incomplete_data_len); + if (new_data == NULL) + return 1; + ct->ready_data = new_data; + memcpy(new_data + ct->ready_data_len, ct->incomplete_data, ct->incomplete_data_len); + ct->ready_data_len += ct->incomplete_data_len; + free(ct->incomplete_data); + } + else + { + ct->ready_data = ct->incomplete_data; + ct->ready_data_len = ct->incomplete_data_len; + } + ct->incomplete_data = NULL; + ct->incomplete_data_len = 0; + + return 0; +} + +static void serverToClient(conn_t *server) +{ + static conn_t *client; + static int write_len; + + client = server - 1; + while ((server->ready_data_len = read(server->fd, server->ready_data, BUFFER_SIZE)) > 0) + { + write_len = write(client->fd, server->ready_data, server->ready_data_len); + if (write_len == -1) + { + if (errno != EAGAIN) + close_connection(server); + else + server->sent_len = 0; + return; + } + else if (write_len < server->ready_data_len) + { + server->sent_len = write_len; + ev.events = EPOLLIN|EPOLLOUT|EPOLLET; + ev.data.ptr = client; + epoll_ctl(efd, EPOLL_CTL_MOD, client->fd, &ev); + return; + } + } + //鍒ゆ柇鏈嶅姟绔槸鍚﹀叧闂繛鎺 + if (server->ready_data_len == 0 || errno != EAGAIN) + close_connection(server); + else + server->ready_data_len = server->sent_len = 0; +} + +void tcp_out(conn_t *to) +{ + static conn_t *from; + static int write_len; + + if (to->fd == -1) + return; + else if ((to - cts) & 1) + from = to - 1; + else + from = to + 1; + write_len = write(to->fd, from->ready_data + from->sent_len, from->ready_data_len - from->sent_len); + if (write_len == from->ready_data_len - from->sent_len) + { + //鏈嶅姟绔殑鏁版嵁鍙兘娌″叏閮ㄥ啓鍏ュ埌瀹㈡埛绔 + if ((from - cts) & 1) + { + serverToClient(from); + if (from->fd >= 0 && from->ready_data_len == 0) + { + ev.events = EPOLLIN|EPOLLET; + ev.data.ptr = to; + epoll_ctl(efd, EPOLL_CTL_MOD, to->fd, &ev); + } + return; + } + else + { + ev.events = EPOLLIN|EPOLLET; + ev.data.ptr = to; + epoll_ctl(efd, EPOLL_CTL_MOD, to->fd, &ev); + free(from->ready_data); + from->ready_data = NULL; + from->ready_data_len = 0; + } + } + else if (write_len > 0) + { + from->sent_len += write_len; + ev.events = EPOLLIN|EPOLLOUT|EPOLLET; + ev.data.ptr = to; + epoll_ctl(efd, EPOLL_CTL_MOD, to->fd, &ev); + } + else if (errno != EAGAIN) + { + close_connection(to); + } +} + +void tcp_in(conn_t *in) +{ + static int write_len; + static conn_t *server; + static char *host, *headerEnd; + + if (in->fd < 0) + return; + + //濡傛灉in - cts鏄鏁帮紝閭d箞鏄湇鍔$瑙﹀彂浜嬩欢 + if ((in - cts) & 1) + { + if (in->ready_data_len == 0) + serverToClient(in); + return; + } + + in->incomplete_data = read_data(in, in->incomplete_data, &in->incomplete_data_len); + if (in->incomplete_data == NULL) + { + close_connection(in); + return; + } + server = in + 1; + if (request_type(in->incomplete_data) == OTHER_TYPE) + { + //濡傛灉鏄涓娆¤鍙栨暟鎹紝骞朵笖涓嶆槸HTTP璇锋眰鐨勶紝鍏抽棴杩炴帴銆傚鍒舵暟鎹け璐ョ殑涔熷叧闂繛鎺 + if (in->reread_data == 0 || copy_data(in) != 0) + { + close_connection(in); + return; + } + goto handle_data_complete; + } + headerEnd = strstr(in->incomplete_data, "\n\r"); + //璇锋眰澶翠笉瀹屾暣锛岀瓑寰呬笅娆¤鍙 + if (headerEnd == NULL) + return; + host = get_host(in->incomplete_data); + if (host == NULL) + { + close_connection(in); + return; + } + /* 绗竴娆¤鍙栨暟鎹 */ + if (in->reread_data == 0) + { + in->reread_data = 1; + if (parse_host(server, host) != 0) + { + free(host); + close_connection(in); + return; + } + if (strstr(in->incomplete_data, ssl_proxy)) + { + write_len = write(in->fd, SSL_RSP, 39); + if (write_len == 39) + { + ; + } + else if (write_len > 0) + { + memcpy(server->ready_data, SSL_RSP + write_len, 39 - write_len); + } + else + { + free(host); + close_connection(in); + return; + } + headerEnd += 3; + if (headerEnd - in->incomplete_data < in->incomplete_data_len) + { + in->incomplete_data_len -= headerEnd - in->incomplete_data; + xmemcpy(in->incomplete_data, headerEnd, in->incomplete_data_len + 1); + if (request_type(in->incomplete_data) == OTHER_TYPE) + { + copy_data(in); + free(host); + return; + } + } + else + { + free(in->incomplete_data); + in->incomplete_data = NULL; + in->incomplete_data_len = 0; + free(host); + return; + } + } + } + in->incomplete_data = build_request(in->incomplete_data, &in->incomplete_data_len, host); + free(host); + if (in->incomplete_data == NULL || copy_data(in) != 0) + { + close_connection(in); + return; + } + //鏁版嵁澶勭悊瀹屾瘯锛屽彲浠ュ彂閫 + handle_data_complete: + //澶氭璇诲彇瀹㈡埛绔暟鎹紝浣嗘槸鍜屾湇鍔$寤虹珛杩炴帴 + if (server->fd >= 0) + tcp_out(server); +} + +void *accept_loop(void *ptr) +{ + conn_t *client; + + while (1) + { + /* 鍋舵暟涓哄鎴风锛屽鏁颁负鏈嶅姟绔 */ + for (client = cts; client - cts < MAX_CONNECTION; client += 2) + if (client->fd < 0) + break; + if (client - cts >= MAX_CONNECTION) + { + sleep(3); + continue; + } + client->fd = accept(lisFd, (struct sockaddr *)&addr, &addr_len); + if (client->fd >= 0) + { + fcntl(client->fd, F_SETFL, fcntl(client->fd, F_GETFL)|O_NONBLOCK); + ev.data.ptr = client; + ev.events = EPOLLIN|EPOLLET; + epoll_ctl(efd, EPOLL_CTL_ADD, client->fd, &ev); + } + } + + return NULL; +} + +void create_listen(char *ip, int port) +{ + int optval = 1; + + if ((lisFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + perror("socket"); + exit(1); + } + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip); + if (setsockopt(lisFd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) + { + perror("setsockopt"); + exit(1); + } + if (bind(lisFd, (struct sockaddr *)&addr, sizeof(addr)) != 0) + { + perror("bind"); + exit(1); + } + if (listen(lisFd, 500) != 0) + { + perror("listen"); + exit(1); + } +} + + + + diff --git a/http.h b/http.h new file mode 100644 index 0000000..7299d22 --- /dev/null +++ b/http.h @@ -0,0 +1,25 @@ +#ifndef HTTP_H +#define HTTP_H + +#include "main.h" + +typedef struct tcp_connection { + char *ready_data, *incomplete_data; + int fd, ready_data_len, incomplete_data_len, sent_len; + uint16_t destPort; + unsigned reread_data :1; +} conn_t; + +extern void create_listen(char *ip, int port); +extern void *accept_loop(void *ptr); +extern void close_connection(conn_t *conn); +extern int8_t connectionToServer(char *ip, conn_t *server); +extern void tcp_in(conn_t *ct); +extern void tcp_out(conn_t *ct); + +extern conn_t cts[MAX_CONNECTION]; +extern char *local_header, *proxy_header, *ssl_proxy; +extern int lisFd, local_header_len, proxy_header_len; +extern uint8_t strict_spilce; + +#endif \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..801af77 --- /dev/null +++ b/main.c @@ -0,0 +1,215 @@ +#include "main.h" +#include "http.h" +#include "dns.h" +#include + +#define VERSION "0.1" +#define DEFAULT_DNS_IP "114.114.114.114" + +struct epoll_event evs[MAX_CONNECTION + 2], ev; +struct sockaddr_in addr; +socklen_t addr_len; +int efd; + +static void usage() +{ + puts("SpecialProxy(" VERSION "):\n" + " -l [listenip:]listenport \033[35G default ip is 0.0.0.0\n" + " -p proxy header \033[35G default is 'Host'\n" + " -L local proxy header \033[35G default is 'Local'\n" + " -d dns query address \033[35G default is " DEFAULT_DNS_IP "\n" + " -s ssl proxy string \033[35G default is 'CONNECT'\n" + " -a \033[35G all http requests repeat spilce\n" + " -h display this infomaction\n" + " -w worker process\n"); + exit(0); +} + +static void server_loop() +{ + pthread_t thread_id; + int n; + + //单独进程accept多进程并发环境下不会惊群 + pthread_create(&thread_id, NULL, accept_loop, NULL); + ev.events = EPOLLIN; + ev.data.fd = dnsFd; + epoll_ctl(efd, EPOLL_CTL_ADD, dnsFd, &ev); + while (1) + { + n = epoll_wait(efd, evs, MAX_CONNECTION + 2, -1); + while (n-- > 0) + { + if (evs[n].data.fd == dnsFd) + { + if (evs[n].events & EPOLLIN) + read_dns_rsp(); + if (evs[n].events & EPOLLOUT) + dns_query(); + } + else + { + if (evs[n].events & EPOLLIN) + tcp_in((conn_t *)evs[n].data.ptr); + if (evs[n].events & EPOLLOUT) + tcp_out((conn_t *)evs[n].data.ptr); + } + } + } +} + +static void initializate(int argc, char **argv) +{ + struct sockaddr_in dnsAddr; + char *p; + int opt, i, workers = 1; + + addr_len = sizeof(addr); + lisFd = -1; + efd = epoll_create(MAX_CONNECTION + 2); + if (efd < 0) + { + perror("epoll_create"); + exit(1); + } + dnsAddr.sin_family = addr.sin_family = AF_INET; + //默认dns地址 + dnsAddr.sin_addr.s_addr = inet_addr(DEFAULT_DNS_IP); + dnsAddr.sin_port = htons(53); + dns_connect(&dnsAddr); //主进程中的fd + strict_spilce = 0; + local_header = NULL; + ssl_proxy = (char *)"CONNECT"; + local_header = (char *)"\nLocal:"; + proxy_header = (char *)"\nHost:"; + proxy_header_len = strlen(proxy_header); + local_header_len = strlen(local_header); + /* 读取命令行参数 */ + while ((opt = getopt(argc, argv, "d:l:p:s:w:L:ah")) != -1) + { + switch (opt) + { + case 'd': + p = strchr(optarg, ':'); + if (p) + { + *p = '\0'; + dnsAddr.sin_port = htons(atoi(p+1)); + } + else + { + dnsAddr.sin_port = htons(53); + } + dnsAddr.sin_addr.s_addr = inet_addr(optarg); + connect(dnsFd, (struct sockaddr *)&dnsAddr, sizeof(dnsAddr)); + break; + + case 'l': + p = strchr(optarg, ':'); + if (p) + { + *p = '\0'; + create_listen(optarg, atoi(p+1)); + } + else + { + create_listen((char *)"0.0.0.0", atoi(optarg)); + } + break; + + case 'p': + //假如选项值为 "Proxy", proxy_header设置为 "\nProxy:" + proxy_header_len = strlen(optarg) + 2; + if (optarg[proxy_header_len] == ':') + optarg[proxy_header_len--] = '\0'; + proxy_header = (char *)malloc(proxy_header_len + 1); + if (proxy_header == NULL) + { + fputs("out of memory.\n", stderr); + exit(1); + } + sprintf(proxy_header, "\n%s:", optarg); + break; + + case 'L': + local_header_len = strlen(optarg) + 2; + if (optarg[local_header_len] == ':') + optarg[local_header_len--] = '\0'; + local_header = (char *)malloc(local_header_len + 1); + if (local_header == NULL) + { + fputs("out of memory.\n", stderr); + exit(1); + } + sprintf(local_header, "\n%s:", optarg); + break; + + case 's': + ssl_proxy = optarg; + break; + + case 'a': + strict_spilce = 1; + break; + + case 'w': + workers = atoi(optarg); + break; + + default: + usage(); + break; + } + } + if (lisFd < 0) + { + fputs("no listen address\n", stderr); + exit(1); + } + memset(cts, 0, sizeof(cts)); + for (i = MAX_CONNECTION; i--; ) + cts[i].fd = -1; + //为服务端的结构体分配内存 + for (i = 1; i < MAX_CONNECTION; i += 2) + { + cts[i].ready_data = (char *)malloc(BUFFER_SIZE); + if (cts[i].ready_data == NULL) + { + fputs("out of memory.", stderr); + exit(1); + } + } + memset(dns_list, 0, sizeof(dns_list)); + for (i = MAX_CONNECTION / 2; i--; ) + { + memcpy(dns_list[i].request, &i, sizeof(uint16_t)); + dns_list[i].request[2] = 1; + dns_list[i].request[3] = 0; + dns_list[i].request[4] = 0; + dns_list[i].request[5] = 1; + dns_list[i].request[6] = 0; + dns_list[i].request[7] = 0; + dns_list[i].request[8] = 0; + dns_list[i].request[9] = 0; + dns_list[i].request[10] = 0; + dns_list[i].request[11] = 0; + } + signal(SIGPIPE, SIG_IGN); + while (workers-- > 1 && fork() == 0) + //子进程中的dnsFd必须重新申请,不然epoll监听可能读取到其他进程得到的数据 + dns_connect(&dnsAddr); +} + +int main(int argc, char **argv) +{ + initializate(argc, argv); + //if (daemon(1, 1)) + if (daemon(1, 0)) + { + perror("daemon"); + return 1; + } + server_loop(); + + return 0; +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..af812f2 --- /dev/null +++ b/main.h @@ -0,0 +1,27 @@ +#ifndef MAIN_H +#define MAIN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 10240 +#define MAX_CONNECTION 1020 + +extern struct epoll_event evs[MAX_CONNECTION + 2], ev; +extern struct sockaddr_in addr; +extern socklen_t addr_len; +extern int efd; + +#endif \ No newline at end of file