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