add httpdns client
This commit is contained in:
parent
a1c87d2bee
commit
2e19c24eff
22
CProxy.conf
22
CProxy.conf
@ -1,26 +1,32 @@
|
|||||||
global {
|
global {
|
||||||
uid=3004;
|
uid=3004;
|
||||||
process=2;
|
process=2;
|
||||||
timer=60;
|
timeout=60;
|
||||||
sslencoding=128;
|
sslencoding=128;
|
||||||
local_port=0124;
|
tcp_listen=0124;
|
||||||
|
dns_listen=0125;
|
||||||
}
|
}
|
||||||
|
|
||||||
http {
|
http {
|
||||||
http_ip=cproxy.aixiao.me;
|
http_ip=47.240.75.93;
|
||||||
http_port=8911;
|
http_port=124;
|
||||||
http_del="x-online-host,X-Online-Host,host,Host";
|
http_del="x-online-host,X-Online-Host,host,Host";
|
||||||
http_first="[M] [U] [V]\r\nHost: [host]\r\n";
|
http_first="[M] [U] [V]\r\nHost: [H]\r\n";
|
||||||
//strrep="Windows NT 10.0->Linux";
|
//strrep="Windows NT 10.0->Linux";
|
||||||
//regrep="Host*.+?->Host: hu60.cn:443";
|
//regrep="Host*.+?->Host: hu60.cn:443";
|
||||||
}
|
}
|
||||||
|
|
||||||
https {
|
https {
|
||||||
https_ip=cproxy.aixiao.me;
|
https_ip=47.240.75.93;
|
||||||
https_port=8911;
|
https_port=124;
|
||||||
https_del="Host,host,x-online-host";
|
https_del="Host,host,x-online-host";
|
||||||
https_first="[M] [H] [V]\r\nHost: [host]\r\n";
|
https_first="[M] [U] [V]\r\nHost: [H]\r\n";
|
||||||
strrep="Windows NT 10.0->Linux";
|
strrep="Windows NT 10.0->Linux";
|
||||||
//regrep="Host*.+?->Host: hu60.cn:443";
|
//regrep="Host*.+?->Host: hu60.cn: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";
|
||||||
|
encode = 0;
|
||||||
|
}
|
||||||
|
@ -1,44 +1,36 @@
|
|||||||
global {
|
global {
|
||||||
// 设置进程UID
|
|
||||||
uid=3004;
|
uid=3004;
|
||||||
|
|
||||||
// 进程数
|
|
||||||
process=2;
|
process=2;
|
||||||
|
timeout=60;
|
||||||
// 超时
|
sslencoding=128;
|
||||||
timer=60;
|
tcp_listen=0124;
|
||||||
|
dns_listen=0125;
|
||||||
// ssl编码,1-128
|
|
||||||
sslencoding=0;
|
|
||||||
|
|
||||||
// 本地端口
|
|
||||||
local_port=9606;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
http {
|
http {
|
||||||
// 代理IP
|
http_ip=cproxy.aixiao.me;
|
||||||
http_ip=192.168.1.102;
|
http_port=124;
|
||||||
// 代理端口
|
|
||||||
http_port=1080;
|
|
||||||
http_del="x-online-host,X-Online-Host,host,Host";
|
http_del="x-online-host,X-Online-Host,host,Host";
|
||||||
http_first="[M] [U] [V]\r\nHost: [host]\r\n";
|
http_first="[M] [U] [V]\r\nHost: [H]\r\n";
|
||||||
//strrep="Windows NT 10.0->Linux";
|
//strrep="Windows NT 10.0->Linux";
|
||||||
//regrep="Host*.+?->Host: hu60.cn:443";
|
//regrep="Host*.+?->Host: hu60.cn:443";
|
||||||
}
|
}
|
||||||
|
|
||||||
https {
|
https {
|
||||||
// 代理IP
|
https_ip=cproxy.aixiao.me;
|
||||||
https_ip=192.168.1.102;
|
https_port=124;
|
||||||
// 代理端口
|
|
||||||
https_port=1080;
|
|
||||||
// 删除Host行
|
|
||||||
https_del="Host,host,x-online-host";
|
https_del="Host,host,x-online-host";
|
||||||
// https头第一行
|
https_first="[M] [U] [V]\r\nhost: [host]\r\n";
|
||||||
https_first="[M] [H] [V]\r\nHost: [host]\r\n";
|
strrep="Windows NT 10.0->Linux";
|
||||||
//strrep="Windows NT 10.0->Linux";
|
|
||||||
//regrep="Host*.+?->Host: hu60.cn:443";
|
//regrep="Host*.+?->Host: hu60.cn: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";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
http、https 模块关键字: [M], [method], [uri], [U], [V], [version], [H], [host], [port], \r, \n, \v, \f, \b, \t, \a. 如果原本请求头含有关键字也会被替换.
|
http、https 模块关键字: [M], [method], [uri], [U], [V], [version], [H], [host], [port], \r, \n, \v, \f, \b, \t, \a. 如果原本请求头含有关键字也会被替换.
|
||||||
|
|
||||||
[M]、[method] 原请求方法
|
[M]、[method] 原请求方法
|
||||||
@ -55,3 +47,6 @@ strrep = "Mi MIX 2->Linux"; 以"->"为分界符,"Mi MIX 2"字符串替换为"Lin
|
|||||||
关键字regrep正则匹配替换字符串.
|
关键字regrep正则匹配替换字符串.
|
||||||
regrep = "Host*.+?->Host: iread.wo.cn:443"; 以"->"为分界符,匹配到的内容"Host*.+?"替换为"Host: iread.wo.cn:443"字符串.
|
regrep = "Host*.+?->Host: iread.wo.cn:443"; 以"->"为分界符,匹配到的内容"Host*.+?"替换为"Host: iread.wo.cn:443"字符串.
|
||||||
|
|
||||||
|
httpdns 模块关键字: [M], [D], [V], \r, \n, \v, \f, \b, \t, \a.
|
||||||
|
默认 [M] 为 GET
|
||||||
|
默认 [V] 为 HTTP/1.0
|
||||||
|
6
Makefile
6
Makefile
@ -1,11 +1,11 @@
|
|||||||
CROSS_COMPILE ?=
|
CROSS_COMPILE ?=
|
||||||
CC := $(CROSS_COMPILE)gcc
|
CC := $(CROSS_COMPILE)gcc
|
||||||
STRIP := $(CROSS_COMPILE)strip
|
STRIP := $(CROSS_COMPILE)strip
|
||||||
CFLAGS += -g -O2 -Wall -pthread
|
CFLAGS += -g -O2 -Wall -pthread -static
|
||||||
LIBS = -static
|
LIBS =
|
||||||
OBJ := CProxy
|
OBJ := CProxy
|
||||||
|
|
||||||
all: proxy.o http.o httpdns.o request.o picohttpparser.o conf.o timeout.o kill.o help.o
|
all: main.o http_proxy.o httpdns.o http_request.o conf.o timeout.o kill.o help.o
|
||||||
$(CC) $(CFLAGS) -o $(OBJ) $^ $(LIBS)
|
$(CC) $(CFLAGS) -o $(OBJ) $^ $(LIBS)
|
||||||
.c.o:
|
.c.o:
|
||||||
$(CC) $(CFLAGS) -c $< $(LIBS)
|
$(CC) $(CFLAGS) -c $< $(LIBS)
|
||||||
|
60
conf.c
60
conf.c
@ -112,12 +112,14 @@ static void parse_global_module(char *content, conf * p)
|
|||||||
p->uid = atoi(val_begin);
|
p->uid = atoi(val_begin);
|
||||||
} else if (strcasecmp(var, "process") == 0) {
|
} else if (strcasecmp(var, "process") == 0) {
|
||||||
p->process = atoi(val_begin);
|
p->process = atoi(val_begin);
|
||||||
} else if (strcasecmp(var, "timer") == 0) {
|
} else if (strcasecmp(var, "timeout") == 0) {
|
||||||
p->timer = atoi(val_begin);
|
p->timeout = atoi(val_begin);
|
||||||
} else if (strcasecmp(var, "sslencoding") == 0) {
|
} else if (strcasecmp(var, "sslencoding") == 0) {
|
||||||
p->sslencoding = atoi(val_begin);
|
p->sslencoding = atoi(val_begin);
|
||||||
} else if (strcasecmp(var, "local_port") == 0) {
|
} else if (strcasecmp(var, "tcp_listen") == 0) {
|
||||||
p->local_port = atoi(val_begin);
|
p->tcp_listen = atoi(val_begin);
|
||||||
|
} else if (strcasecmp(var, "dns_listen") == 0) {
|
||||||
|
p->dns_listen = atoi(val_begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
content = strchr(lineEnd + 1, '\n');
|
content = strchr(lineEnd + 1, '\n');
|
||||||
@ -141,10 +143,12 @@ static void parse_http_module(char *content, conf * p)
|
|||||||
val_begin_len = strlen(val_begin) + 1;
|
val_begin_len = strlen(val_begin) + 1;
|
||||||
p->http_del = (char *)malloc(val_begin_len);
|
p->http_del = (char *)malloc(val_begin_len);
|
||||||
memcpy(p->http_del, val_begin, val_begin_len);
|
memcpy(p->http_del, val_begin, val_begin_len);
|
||||||
|
p->http_del_len = val_begin_len;
|
||||||
} else if (strcasecmp(var, "http_first") == 0) {
|
} else if (strcasecmp(var, "http_first") == 0) {
|
||||||
val_begin_len = strlen(val_begin) + 1;
|
val_begin_len = strlen(val_begin) + 1;
|
||||||
p->http_first = (char *)malloc(val_begin_len);
|
p->http_first = (char *)malloc(val_begin_len);
|
||||||
memcpy(p->http_first, val_begin, val_begin_len);
|
memcpy(p->http_first, val_begin, val_begin_len);
|
||||||
|
p->http_first_len = val_begin_len;
|
||||||
} else if (strcasecmp(var, "strrep") == 0) {
|
} else if (strcasecmp(var, "strrep") == 0) {
|
||||||
val_begin_len = strlen(val_begin) + 1;
|
val_begin_len = strlen(val_begin) + 1;
|
||||||
|
|
||||||
@ -210,10 +214,12 @@ static void parse_https_module(char *content, conf * p)
|
|||||||
val_begin_len = strlen(val_begin) + 1;
|
val_begin_len = strlen(val_begin) + 1;
|
||||||
p->https_del = (char *)malloc(val_begin_len);
|
p->https_del = (char *)malloc(val_begin_len);
|
||||||
memcpy(p->https_del, val_begin, val_begin_len);
|
memcpy(p->https_del, val_begin, val_begin_len);
|
||||||
|
p->https_del_len = val_begin_len;
|
||||||
} else if (strcasecmp(var, "https_first") == 0) {
|
} else if (strcasecmp(var, "https_first") == 0) {
|
||||||
val_begin_len = strlen(val_begin) + 1;
|
val_begin_len = strlen(val_begin) + 1;
|
||||||
p->https_first = (char *)malloc(val_begin_len);
|
p->https_first = (char *)malloc(val_begin_len);
|
||||||
memcpy(p->https_first, val_begin, val_begin_len);
|
memcpy(p->https_first, val_begin, val_begin_len);
|
||||||
|
p->https_first_len = val_begin_len;
|
||||||
} else if (strcasecmp(var, "strrep") == 0) {
|
} else if (strcasecmp(var, "strrep") == 0) {
|
||||||
val_begin_len = strlen(val_begin) + 1;
|
val_begin_len = strlen(val_begin) + 1;
|
||||||
|
|
||||||
@ -260,6 +266,28 @@ static void parse_https_module(char *content, conf * p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_httpdns_module(char *content, conf * p)
|
||||||
|
{
|
||||||
|
char *var, *val_begin, *val_end, *lineEnd;
|
||||||
|
int val_begin_len;
|
||||||
|
|
||||||
|
while ((lineEnd = set_var_val_lineEnd(content, &var, &val_begin, &val_end)) != NULL) {
|
||||||
|
if (strcasecmp(var, "addr") == 0) {
|
||||||
|
val_begin_len = strlen(val_begin) + 1;
|
||||||
|
p->addr = (char *)malloc(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);
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void free_conf(conf * p)
|
void free_conf(conf * p)
|
||||||
{
|
{
|
||||||
free(p->server_pid_file);
|
free(p->server_pid_file);
|
||||||
@ -283,12 +311,15 @@ void free_conf(conf * p)
|
|||||||
free(p->https_regrep);
|
free(p->https_regrep);
|
||||||
free(p->https_regrep_aim);
|
free(p->https_regrep_aim);
|
||||||
free(p->https_regrep_obj);
|
free(p->https_regrep_obj);
|
||||||
|
|
||||||
|
free(p->addr);
|
||||||
|
free(p->http_req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_conf(char *filename, conf * configure)
|
void read_conf(char *filename, conf * configure)
|
||||||
{
|
{
|
||||||
char *buff, *global_content, *http_content, *https_content;
|
char *buff, *global_content, *http_content, *https_content, *httpdns_content;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
long file_size;
|
long file_size;
|
||||||
|
|
||||||
@ -303,7 +334,9 @@ void read_conf(char *filename, conf * configure)
|
|||||||
if (buff == NULL)
|
if (buff == NULL)
|
||||||
perror("out of memory.");
|
perror("out of memory.");
|
||||||
rewind(file);
|
rewind(file);
|
||||||
fread(buff, file_size, 1, file);
|
if (fread(buff, file_size, 1, file) < 1) {
|
||||||
|
perror("fread");
|
||||||
|
}
|
||||||
fclose(file);
|
fclose(file);
|
||||||
buff[file_size] = '\0';
|
buff[file_size] = '\0';
|
||||||
|
|
||||||
@ -322,15 +355,20 @@ void read_conf(char *filename, conf * configure)
|
|||||||
parse_https_module(https_content, configure);
|
parse_https_module(https_content, configure);
|
||||||
free(https_content);
|
free(https_content);
|
||||||
|
|
||||||
|
if ((httpdns_content = read_module(buff, "httpdns")) == NULL)
|
||||||
|
perror("read httpdns module error");
|
||||||
|
parse_httpdns_module(httpdns_content, configure);
|
||||||
|
free(httpdns_content);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printfconf(conf * configure)
|
void printfconf(conf * configure)
|
||||||
{
|
{
|
||||||
printf("%d\n", configure->uid);
|
printf("%d\n", configure->uid);
|
||||||
printf("%d\n", configure->process);
|
printf("%d\n", configure->process);
|
||||||
printf("%d\n", configure->timer);
|
printf("%d\n", configure->timeout);
|
||||||
printf("%d\n", configure->sslencoding);
|
printf("%d\n", configure->sslencoding);
|
||||||
printf("%d\n", configure->local_port);
|
printf("%d\n", configure->tcp_listen);
|
||||||
|
printf("%d\n", configure->dns_listen);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
if (configure->http_ip)
|
if (configure->http_ip)
|
||||||
printf("%s\n", configure->http_ip);
|
printf("%s\n", configure->http_ip);
|
||||||
@ -372,4 +410,10 @@ void printfconf(conf * configure)
|
|||||||
printf("%s\n", configure->https_regrep_aim);
|
printf("%s\n", configure->https_regrep_aim);
|
||||||
if (configure->https_regrep_obj)
|
if (configure->https_regrep_obj)
|
||||||
printf("%s\n", configure->https_regrep_obj);
|
printf("%s\n", configure->https_regrep_obj);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
if (configure->addr)
|
||||||
|
printf("%s\n", configure->addr);
|
||||||
|
if (configure->http_req)
|
||||||
|
printf("%s\n", configure->http_req);
|
||||||
}
|
}
|
||||||
|
12
conf.h
12
conf.h
@ -12,10 +12,11 @@ typedef struct CONF {
|
|||||||
// server module
|
// server module
|
||||||
int uid;
|
int uid;
|
||||||
int process;
|
int process;
|
||||||
int timer;
|
int timeout;
|
||||||
int sslencoding;
|
int sslencoding;
|
||||||
//int server_port;
|
//int server_port;
|
||||||
int local_port;
|
int tcp_listen;
|
||||||
|
int dns_listen;
|
||||||
char *server_pid_file;
|
char *server_pid_file;
|
||||||
int server_pid_file_len; // length
|
int server_pid_file_len; // length
|
||||||
|
|
||||||
@ -44,6 +45,13 @@ typedef struct CONF {
|
|||||||
|
|
||||||
char *https_regrep_aim, *https_regrep_obj;
|
char *https_regrep_aim, *https_regrep_obj;
|
||||||
int https_regrep_aim_len, https_regrep_obj_len;
|
int https_regrep_aim_len, https_regrep_obj_len;
|
||||||
|
|
||||||
|
|
||||||
|
// http dns_listen
|
||||||
|
char *addr;
|
||||||
|
char *http_req;
|
||||||
|
int http_req_len;
|
||||||
|
int encode;
|
||||||
} conf;
|
} conf;
|
||||||
|
|
||||||
char *strncpy_(char *dest, const char *src, size_t n);
|
char *strncpy_(char *dest, const char *src, size_t n);
|
||||||
|
10
help.c
10
help.c
@ -14,11 +14,11 @@ char help_information(void)
|
|||||||
static const char *s_help[] = {
|
static const char *s_help[] = {
|
||||||
"",
|
"",
|
||||||
"Options:",
|
"Options:",
|
||||||
" -l --local_address : localip:localport",
|
" -l --local_address : localIP:localPORT",
|
||||||
" -f --remote_address : remoteip:remote:port",
|
" -f --remote_address : remoteIP:remotePORT",
|
||||||
" -p --process : process number, default: 2",
|
" -p --process : process number",
|
||||||
" -t --timeout : timeout minute, default: no timeout",
|
" -t --timeout : timeout minute",
|
||||||
" -e --coding : ssl coding, default: [0-128]",
|
" -e --coding : ssl coding, [0-128]",
|
||||||
" -s --signal : send signal to a master process: stop, quit, restart, reload, status",
|
" -s --signal : send signal to a master process: stop, quit, restart, reload, status",
|
||||||
" -c --config : set configuration file, default: CProxy.conf",
|
" -c --config : set configuration file, default: CProxy.conf",
|
||||||
" -? -h --? --help : help information",
|
" -? -h --? --help : help information",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "http.h"
|
#include "http_proxy.h"
|
||||||
#include "proxy.h"
|
#include "main.h"
|
||||||
|
|
||||||
int sslEncodeCode;
|
int sslEncodeCode;
|
||||||
|
|
||||||
@ -132,6 +132,7 @@ void tcp_in(conn * in, conf * configure)
|
|||||||
if (in->header_buffer != NULL) {
|
if (in->header_buffer != NULL) {
|
||||||
if (request_type(in->header_buffer) == HTTP_TYPE) {
|
if (request_type(in->header_buffer) == HTTP_TYPE) {
|
||||||
in->header_buffer = request_head(in, configure);
|
in->header_buffer = request_head(in, configure);
|
||||||
|
|
||||||
struct epoll_event epollEvent;
|
struct epoll_event epollEvent;
|
||||||
conn *remote;
|
conn *remote;
|
||||||
remote = in + 1;
|
remote = in + 1;
|
@ -2,7 +2,7 @@
|
|||||||
#define HTTP_H
|
#define HTTP_H
|
||||||
|
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "proxy.h"
|
#include "main.h"
|
||||||
|
|
||||||
#define HTTP_TYPE 0
|
#define HTTP_TYPE 0
|
||||||
#define OTHER_TYPE 1
|
#define OTHER_TYPE 1
|
||||||
@ -10,7 +10,6 @@
|
|||||||
int remote_port;
|
int remote_port;
|
||||||
char remote_host[128];
|
char remote_host[128];
|
||||||
|
|
||||||
|
|
||||||
extern int sslEncodeCode;
|
extern int sslEncodeCode;
|
||||||
|
|
||||||
typedef struct conn_t {
|
typedef struct conn_t {
|
@ -1,4 +1,4 @@
|
|||||||
#include "request.h"
|
#include "http_request.h"
|
||||||
|
|
||||||
// 字符串替换
|
// 字符串替换
|
||||||
char *replace(char *replace_memory, int *replace_memory_len, const char *src, const int src_len, const char *dest, const int dest_len)
|
char *replace(char *replace_memory, int *replace_memory_len, const char *src, const int src_len, const char *dest, const int dest_len)
|
||||||
@ -154,8 +154,8 @@ char *delete_head(char *head, const char *character, int string)
|
|||||||
|
|
||||||
int extract_host(char *header, char *host, char *port)
|
int extract_host(char *header, char *host, char *port)
|
||||||
{
|
{
|
||||||
bzero(host, strlen(host));
|
memset(host, 0, strlen(host));
|
||||||
bzero(port, strlen(port));
|
memset(port, 0, strlen(port));
|
||||||
char *_p = strstr(header, "CONNECT"); // 在 CONNECT 方法中解析 隧道主机名称及端口号
|
char *_p = strstr(header, "CONNECT"); // 在 CONNECT 方法中解析 隧道主机名称及端口号
|
||||||
if (_p) {
|
if (_p) {
|
||||||
char *_p1 = strchr(_p, ' ');
|
char *_p1 = strchr(_p, ' ');
|
||||||
@ -217,51 +217,108 @@ char *get_path(char *url, char *path)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void free_http_request(struct http_request *http_request) {
|
||||||
|
if (http_request->M)
|
||||||
|
free(http_request->M);
|
||||||
|
if (http_request->U)
|
||||||
|
free(http_request->U);
|
||||||
|
if (http_request->V)
|
||||||
|
free(http_request->V);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_request_head(char *http_request_line, struct http_request *http_request) {
|
||||||
|
char *p;
|
||||||
|
char *head;
|
||||||
|
size_t head_len;
|
||||||
|
char *m, *u;
|
||||||
|
|
||||||
|
p = strstr(http_request_line, "\r\n"); // 查找"\r\n"
|
||||||
|
if(p == NULL) {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
head_len = strlen(http_request_line) - strlen(p);
|
||||||
|
head = (char *)malloc(sizeof(char) * head_len*2);
|
||||||
|
if (head == NULL)
|
||||||
|
free(head);
|
||||||
|
memset(head, 0, head_len*2);
|
||||||
|
memcpy(head, http_request_line, head_len);
|
||||||
|
//printf("HEAD: %s\n", head);
|
||||||
|
|
||||||
|
http_request->M = (char *)malloc(sizeof(char) * head_len);
|
||||||
|
http_request->U = (char *)malloc(sizeof(char) * head_len);
|
||||||
|
http_request->V = (char *)malloc(10);
|
||||||
|
if (http_request->M == NULL) {
|
||||||
|
free(http_request->M);
|
||||||
|
perror("malloc");
|
||||||
|
}
|
||||||
|
if (http_request->U == NULL) {
|
||||||
|
free(http_request->M);
|
||||||
|
perror("malloc");
|
||||||
|
}
|
||||||
|
if (http_request->V == NULL) {
|
||||||
|
free(http_request->M);
|
||||||
|
perror("malloc");
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(http_request->M, 0, head_len);
|
||||||
|
memset(http_request->U, 0, head_len);
|
||||||
|
memset(http_request->V, 0, 10);
|
||||||
|
|
||||||
|
m = strstr(head, " ");
|
||||||
|
http_request->M_len = strlen(head) - strlen(m);
|
||||||
|
memset(http_request->M, 0, head_len);
|
||||||
|
memcpy(http_request->M, head, http_request->M_len);
|
||||||
|
|
||||||
|
|
||||||
|
u = strstr(m+1, " ");
|
||||||
|
http_request->U_len = strlen(m+1) - strlen(u);
|
||||||
|
memset(http_request->U, 0, head_len);
|
||||||
|
memcpy(http_request->U, m+1, http_request->U_len);
|
||||||
|
|
||||||
|
memset(http_request->V, 0, 8);
|
||||||
|
memcpy(http_request->V, u+1, 8);
|
||||||
|
http_request->V_len = 8;
|
||||||
|
|
||||||
|
free(head);
|
||||||
|
printf("%s\n", http_request->M);
|
||||||
|
printf("%s\n", http_request->U);
|
||||||
|
printf("%s\n", http_request->V);
|
||||||
|
printf("%d\n", http_request->M_len);
|
||||||
|
printf("%d\n", http_request->U_len);
|
||||||
|
printf("%d\n", http_request->V_len);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
char *request_head(conn * in, conf * configure)
|
char *request_head(conn * in, conf * configure)
|
||||||
{
|
{
|
||||||
const char *method, *path;
|
struct http_request *http_request;
|
||||||
size_t method_len, path_len, num_headers;
|
http_request = (struct http_request *)malloc(sizeof(struct http_request));
|
||||||
int minor_version;
|
parse_request_head(in->header_buffer, http_request);
|
||||||
struct phr_header headers[32];
|
|
||||||
|
|
||||||
num_headers = sizeof(headers) / sizeof(headers[0]);
|
//printfconf(configure); // 打印配置
|
||||||
phr_parse_request(in->header_buffer, strlen(in->header_buffer) - 1, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, 0);
|
if (strncmp(http_request->M, "CONNECT", 7) == 0) {
|
||||||
|
char host[http_request->U_len], port[http_request->U_len], H[http_request->U_len * 2];
|
||||||
char M[method_len + 2];
|
|
||||||
strncpy_(M, method, method_len);
|
|
||||||
int M_len = strlen(M);
|
|
||||||
//printf("%s\n", M);
|
|
||||||
|
|
||||||
char U[path_len + 1];
|
|
||||||
strncpy_(U, path, path_len);
|
|
||||||
int U_len = strlen(U);
|
|
||||||
//printf("%s\n", U);
|
|
||||||
|
|
||||||
char V[9];
|
|
||||||
sprintf(V, "HTTP/1.%.d", minor_version);
|
|
||||||
int V_len = strlen(V);
|
|
||||||
//printf("%s\n", V);
|
|
||||||
|
|
||||||
char host[path_len];
|
|
||||||
char port[path_len];
|
|
||||||
char H[path_len * 2];
|
|
||||||
extract_host(in->header_buffer, host, port);
|
|
||||||
|
|
||||||
//printfconf(configure);
|
|
||||||
|
|
||||||
if (strncmp(M, "CONNECT", 7) == 0) {
|
|
||||||
char https_del_copy[configure->https_del_len];
|
|
||||||
char *result = NULL;
|
|
||||||
char *incomplete_head;
|
char *incomplete_head;
|
||||||
int incomplete_head_len;
|
int incomplete_head_len;
|
||||||
|
char https_del_copy[configure->https_del_len * 2];
|
||||||
|
char *result = NULL;
|
||||||
|
|
||||||
|
extract_host(in->header_buffer, host, port);
|
||||||
|
|
||||||
if (configure->https_port > 0)
|
if (configure->https_port > 0)
|
||||||
remote_port = configure->https_port;
|
remote_port = configure->https_port;
|
||||||
if (configure->https_ip != NULL)
|
if (configure->https_ip != NULL)
|
||||||
strcpy(remote_host, configure->https_ip);
|
strcpy(remote_host, configure->https_ip);
|
||||||
incomplete_head = (char *)malloc(1024 + 10240);
|
incomplete_head = (char *)malloc(sizeof(char) * (1024 + BUFFER_SIZE));
|
||||||
strcpy(incomplete_head, in->header_buffer);
|
if (incomplete_head == NULL) {
|
||||||
strcpy(https_del_copy, configure->https_del);
|
free(incomplete_head);
|
||||||
|
perror("malloc");
|
||||||
|
}
|
||||||
|
memset(incomplete_head, 0, sizeof(char) * (1024 + BUFFER_SIZE));
|
||||||
|
memcpy(incomplete_head, in->header_buffer, strlen(in->header_buffer));
|
||||||
|
memcpy(https_del_copy, configure->https_del, configure->https_del_len);
|
||||||
|
|
||||||
result = strtok(https_del_copy, ",");
|
result = strtok(https_del_copy, ",");
|
||||||
while (result != NULL) {
|
while (result != NULL) {
|
||||||
delete_head(incomplete_head, result, '\n');
|
delete_head(incomplete_head, result, '\n');
|
||||||
@ -278,15 +335,15 @@ char *request_head(conn * in, conf * configure)
|
|||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\t", 2, "\t", 1);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\t", 2, "\t", 1);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\r", 2, "\r", 1);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\r", 2, "\r", 1);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\n", 2, "\n", 1);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\n", 2, "\n", 1);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[M]", 3, M, M_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[M]", 3, http_request->M, http_request->M_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[method]", 8, M, M_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[method]", 8, http_request->M, http_request->M_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[U]", 3, U, U_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[U]", 3, http_request->U, http_request->U_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[V]", 3, V, V_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[V]", 3, http_request->V, http_request->V_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[version]", 9, V, V_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[version]", 9, http_request->V, http_request->V_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[host]", 6, host, (int)strlen(host));
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[host]", 6, host, (int)strlen(host));
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[port]", 6, port, (int)strlen(port));
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[port]", 6, port, (int)strlen(port));
|
||||||
memset(H, 0, strlen(H));
|
memset(H, 0, strlen(H));
|
||||||
memcpy(H, host, path_len);
|
strcpy(H, host);
|
||||||
strcat(H, ":");
|
strcat(H, ":");
|
||||||
strcat(H, port);
|
strcat(H, port);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[H]", 3, H, (int)strlen(H));
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[H]", 3, H, (int)strlen(H));
|
||||||
@ -296,32 +353,40 @@ char *request_head(conn * in, conf * configure)
|
|||||||
if (configure->https_regrep) {
|
if (configure->https_regrep) {
|
||||||
incomplete_head = regrep(incomplete_head, &incomplete_head_len, configure->https_regrep_aim, configure->https_regrep_obj, configure->https_regrep_obj_len);
|
incomplete_head = regrep(incomplete_head, &incomplete_head_len, configure->https_regrep_aim, configure->https_regrep_obj, configure->https_regrep_obj_len);
|
||||||
}
|
}
|
||||||
//printf("%s", incomplete_head);
|
printf("%s", incomplete_head); // 打印HTTP HEADER
|
||||||
|
|
||||||
memset(in->header_buffer, 0, strlen(in->header_buffer));
|
memset(in->header_buffer, 0, strlen(in->header_buffer));
|
||||||
strcpy(in->header_buffer, incomplete_head);
|
strcpy(in->header_buffer, incomplete_head);
|
||||||
in->header_buffer_len = strlen(in->header_buffer);
|
in->header_buffer_len = strlen(in->header_buffer);
|
||||||
|
|
||||||
free(incomplete_head);
|
free(incomplete_head);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
char host[http_request->U_len], port[http_request->U_len], H[http_request->U_len * 2];
|
||||||
|
char url[http_request->U_len], uri[http_request->U_len];
|
||||||
|
int uri_len;
|
||||||
char *incomplete_head;
|
char *incomplete_head;
|
||||||
|
int incomplete_head_len;
|
||||||
char https_del_copy[configure->http_del_len];
|
char https_del_copy[configure->http_del_len];
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
int incomplete_head_len;
|
|
||||||
char url[U_len], uri[U_len];
|
|
||||||
int uri_len;
|
|
||||||
|
|
||||||
strcpy(url, U);
|
extract_host(in->header_buffer, host, port);
|
||||||
|
strcpy(url, http_request->U);
|
||||||
get_path(url, uri);
|
get_path(url, uri);
|
||||||
uri_len = strlen(uri);
|
uri_len = strlen(uri);
|
||||||
//printf("%s\n", uri);
|
|
||||||
if (configure->http_port > 0)
|
if (configure->http_port > 0)
|
||||||
remote_port = configure->http_port;
|
remote_port = configure->http_port;
|
||||||
if (configure->https_ip != NULL)
|
if (configure->https_ip != NULL)
|
||||||
strcpy(remote_host, configure->http_ip);
|
strcpy(remote_host, configure->http_ip);
|
||||||
incomplete_head = (char *)malloc(1024 + 10240);
|
incomplete_head = (char *)malloc(sizeof(char) * (1024 + BUFFER_SIZE));
|
||||||
|
if (incomplete_head == NULL) {
|
||||||
|
free(incomplete_head);
|
||||||
|
perror("malloc");
|
||||||
|
}
|
||||||
|
memset(incomplete_head, 0, sizeof(char) * (1024 + BUFFER_SIZE));
|
||||||
strcpy(incomplete_head, in->header_buffer);
|
strcpy(incomplete_head, in->header_buffer);
|
||||||
strcpy(https_del_copy, configure->http_del);
|
strcpy(https_del_copy, configure->http_del);
|
||||||
|
|
||||||
result = strtok(https_del_copy, ",");
|
result = strtok(https_del_copy, ",");
|
||||||
while (result != NULL) {
|
while (result != NULL) {
|
||||||
delete_head(incomplete_head, result, '\n');
|
delete_head(incomplete_head, result, '\n');
|
||||||
@ -338,16 +403,16 @@ char *request_head(conn * in, conf * configure)
|
|||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\t", 2, "\t", 1);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\t", 2, "\t", 1);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\r", 2, "\r", 1);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\r", 2, "\r", 1);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\n", 2, "\n", 1);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\n", 2, "\n", 1);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[M]", 3, M, M_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[M]", 3, http_request->M, http_request->M_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[method]", 8, M, M_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[method]", 8, http_request->M, http_request->M_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[U]", 3, U, U_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[U]", 3, http_request->U, http_request->U_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[uri]", 5, uri, uri_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[uri]", 5, uri, uri_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[V]", 3, V, V_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[V]", 3, http_request->V, http_request->V_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[version]", 9, V, V_len);
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[version]", 9, http_request->V, http_request->V_len);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[host]", 6, host, (int)strlen(host));
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[host]", 6, host, (int)strlen(host));
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[port]", 6, port, (int)strlen(port));
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[port]", 6, port, (int)strlen(port));
|
||||||
memset(H, 0, strlen(H));
|
memset(H, 0, strlen(H));
|
||||||
memcpy(H, host, path_len);
|
strcpy(H, host);
|
||||||
strcat(H, ":");
|
strcat(H, ":");
|
||||||
strcat(H, port);
|
strcat(H, port);
|
||||||
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[H]", 3, H, (int)strlen(H));
|
incomplete_head = replace(incomplete_head, &incomplete_head_len, "[H]", 3, H, (int)strlen(H));
|
||||||
@ -357,12 +422,14 @@ char *request_head(conn * in, conf * configure)
|
|||||||
if (configure->http_regrep) {
|
if (configure->http_regrep) {
|
||||||
incomplete_head = regrep(incomplete_head, &incomplete_head_len, configure->http_regrep_aim, configure->http_regrep_obj, configure->http_regrep_obj_len);
|
incomplete_head = regrep(incomplete_head, &incomplete_head_len, configure->http_regrep_aim, configure->http_regrep_obj, configure->http_regrep_obj_len);
|
||||||
}
|
}
|
||||||
//printf("%s", incomplete_head);
|
printf("%s", incomplete_head);
|
||||||
|
|
||||||
memset(in->header_buffer, 0, strlen(in->header_buffer));
|
memset(in->header_buffer, 0, strlen(in->header_buffer));
|
||||||
strcpy(in->header_buffer, incomplete_head);
|
strcpy(in->header_buffer, incomplete_head);
|
||||||
in->header_buffer_len = strlen(in->header_buffer);
|
in->header_buffer_len = strlen(in->header_buffer);
|
||||||
free(incomplete_head);
|
free(incomplete_head);
|
||||||
}
|
}
|
||||||
|
free_http_request(http_request);
|
||||||
|
free(http_request);
|
||||||
return in->header_buffer;
|
return in->header_buffer;
|
||||||
}
|
}
|
@ -5,9 +5,13 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
#include "http.h"
|
#include "http_proxy.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "picohttpparser.h"
|
|
||||||
|
struct http_request {
|
||||||
|
char *M, *U, *V;
|
||||||
|
int M_len, U_len, V_len;
|
||||||
|
};
|
||||||
|
|
||||||
void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);
|
void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);
|
||||||
char *replace(char *replace_memory, int *replace_memory_len, const char *src, const int src_len, const char *dest, const int dest_len);
|
char *replace(char *replace_memory, int *replace_memory_len, const char *src, const int src_len, const char *dest, const int dest_len);
|
811
httpdns.c
811
httpdns.c
@ -1,450 +1,503 @@
|
|||||||
#include "httpdns.h"
|
#include "httpdns.h"
|
||||||
|
#include "http_request.h"
|
||||||
|
|
||||||
int encodeCode=0;
|
char http_rsp[HTTP_RSP_SIZE + 1];
|
||||||
/* hosts变量 */
|
struct sockaddr_in dst_addr;
|
||||||
char *hosts_path = NULL;
|
char *host_value;
|
||||||
FILE *hostsfp = NULL;
|
int dnsListenFd = -1, dns_efd;
|
||||||
struct dns_hosts *hosts, *last_hosts = NULL;
|
unsigned int host_value_len;
|
||||||
|
static int8_t encodeCode = 0;
|
||||||
|
/* 缓存变量 */
|
||||||
|
FILE *cfp = NULL;
|
||||||
|
char *cachePath = NULL;
|
||||||
|
struct dns_cache *cache, *cache_temp;
|
||||||
|
socklen_t addr_len = sizeof(dst_addr);
|
||||||
|
unsigned int cache_using, cacheLimit;
|
||||||
|
|
||||||
/* encode domain and ipAddress */
|
|
||||||
static void dataEncode(unsigned char *data, int data_len)
|
int read_cache_file()
|
||||||
{
|
{
|
||||||
while (data_len-- > 0)
|
char *buff, *answer, *question;
|
||||||
data[data_len] ^= encodeCode;
|
long file_size;
|
||||||
}
|
|
||||||
|
|
||||||
int read_hosts_file(char *path)
|
cache = cache_temp = NULL;
|
||||||
{
|
cache_using = 0;
|
||||||
char *ip_begin, *ip_end, *host_begin, *host_end, *buff, *next_line;
|
if ((cfp = fopen(cachePath, "rb+")) == NULL) {
|
||||||
int file_size, i;
|
//保持文件打开状态,防止切换uid后权限不足导致无法写入文件
|
||||||
|
cfp = fopen(cachePath, "wb");
|
||||||
hosts = last_hosts = NULL;
|
return cfp == NULL ? 1 : 0;
|
||||||
if ((hostsfp = fopen(path, "r")) == NULL) {
|
|
||||||
fputs("error hosts file path", stderr);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
//读取文件内容
|
//读取文件内容
|
||||||
fseek(hostsfp, 0, SEEK_END);
|
fseek(cfp, 0, SEEK_END);
|
||||||
file_size = ftell(hostsfp);
|
file_size = ftell(cfp);
|
||||||
//文件没有内容则不用读取
|
if ((buff = (char *)alloca(file_size)) == NULL) {
|
||||||
if (file_size == 0)
|
fclose(cfp);
|
||||||
return 0;
|
|
||||||
if ((buff = (char *)alloca(file_size + 1)) == NULL) {
|
|
||||||
fclose(hostsfp);
|
|
||||||
fputs("out of memory", stderr);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
rewind(hostsfp);
|
rewind(cfp);
|
||||||
fread(buff, file_size, 1, hostsfp);
|
fread(buff, file_size, 1, cfp);
|
||||||
*(buff + file_size) = '\0';
|
|
||||||
fclose(hostsfp);
|
|
||||||
|
|
||||||
struct dns_hosts *h = NULL;
|
//读取缓存,一组缓存的内容为[ipDomain\0],其中ip占5字节
|
||||||
for (ip_begin = buff; ip_begin; ip_begin = next_line) {
|
for (answer = buff; answer - buff < file_size; answer = question + cache->question_len + 2) {
|
||||||
next_line = strchr(ip_begin, '\n');
|
cache_temp = (struct dns_cache *)malloc(sizeof(*cache));
|
||||||
if (next_line != NULL)
|
if (cache_temp == NULL)
|
||||||
*next_line++ = '\0';
|
|
||||||
while (*ip_begin == '\t' || *ip_begin == ' ' || *ip_begin == '\r')
|
|
||||||
if (*ip_begin++ == '\0')
|
|
||||||
continue;
|
|
||||||
for (i = 0, ip_end = ip_begin; *ip_end != ' ' && *ip_end != '\t' && *ip_end != '\r' && *ip_end != '\0'; ip_end++) {
|
|
||||||
if (*ip_end == '.')
|
|
||||||
i++;
|
|
||||||
else if (*ip_end == '\0')
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (i != 3)
|
|
||||||
continue;
|
|
||||||
for (host_begin = ip_end; *host_begin == '\t' || *host_begin == ' ' || *host_begin == '\r';) {
|
|
||||||
if (*host_begin++ == '\0')
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (host_end = host_begin; *host_end != ' ' && *host_end != '\t' && *host_end != '\r' && *host_end != '\n' && *host_end != '\0'; host_end++) ;
|
|
||||||
if (h) {
|
|
||||||
h->next = (struct dns_hosts *)malloc(sizeof(struct dns_hosts));
|
|
||||||
if (h->next == NULL)
|
|
||||||
return 1;
|
return 1;
|
||||||
h = h->next;
|
cache_temp->next = cache;
|
||||||
} else {
|
cache = cache_temp;
|
||||||
hosts = h = (struct dns_hosts *)malloc(sizeof(struct dns_hosts));
|
cache_using++;
|
||||||
if (hosts == NULL) {
|
cache->answer = strndup(answer, 5);
|
||||||
fputs("out of memory", stderr);
|
question = answer + 5;
|
||||||
|
cache->question = strdup(question);
|
||||||
|
if (cache->question == NULL || cache->answer == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
|
cache->question_len = strlen(question) - 1;
|
||||||
|
}
|
||||||
|
/* 删除重复记录 */
|
||||||
|
struct dns_cache *before, *after;
|
||||||
|
for (; cache_temp; cache_temp = cache_temp->next) {
|
||||||
|
for (before = cache_temp; before && (after = before->next) != NULL; before = before->next) {
|
||||||
|
if (strcmp(after->question, cache_temp->question) == 0) {
|
||||||
|
before->next = after->next;
|
||||||
|
free(after->question);
|
||||||
|
free(after->answer);
|
||||||
|
free(after);
|
||||||
|
cache_using--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h->next = NULL;
|
|
||||||
h->ip = strndup(ip_begin, ip_end - ip_begin);
|
|
||||||
if (*(host_end - 1) == '.')
|
|
||||||
host_end--;
|
|
||||||
h->host = strndup(host_begin, host_end - host_begin);
|
|
||||||
if (h->ip == NULL || h->host == NULL) {
|
|
||||||
fputs("out of memory", stderr);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
fclose(cfp);
|
||||||
|
cfp = fopen(cachePath, "wb");
|
||||||
last_hosts = h;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *hosts_lookup(char *host)
|
void write_dns_cache()
|
||||||
{
|
{
|
||||||
struct dns_hosts *h;
|
while (cache) {
|
||||||
|
fputs(cache->answer, cfp);
|
||||||
|
fputs(cache->question, cfp);
|
||||||
|
fputc('\0', cfp);
|
||||||
|
cache = cache->next;
|
||||||
|
}
|
||||||
|
|
||||||
h = hosts;
|
exit(0);
|
||||||
while (h) {
|
}
|
||||||
if (strcmp(h->host, host) == 0)
|
|
||||||
return h->ip;
|
char *cache_lookup(char *question, dns_t * dns)
|
||||||
h = h->next;
|
{
|
||||||
|
struct dns_cache *c;
|
||||||
|
|
||||||
|
for (c = cache; c; c = c->next) {
|
||||||
|
if (strcmp(c->question, question) == 0) {
|
||||||
|
dns->host_len = c->question_len;
|
||||||
|
dns->query_type = 1;
|
||||||
|
return c->answer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_client(dns_t * dns)
|
void cache_record(dns_t * dns)
|
||||||
{
|
{
|
||||||
close(dns->fd);
|
cache_temp = (struct dns_cache *)malloc(sizeof(*cache));
|
||||||
if (dns->http_rsp_len != sizeof(ERROR_MSG) - 1) //ERROR_MSG not free()
|
if (cache_temp == NULL)
|
||||||
free(dns->http_rsp);
|
|
||||||
dns->http_rsp = NULL;
|
|
||||||
dns->sent_len = dns->dns_req_len = 0;
|
|
||||||
dns->fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void build_http_rsp(dns_t * dns, char *ips)
|
|
||||||
{
|
|
||||||
int ips_len = strlen(ips);
|
|
||||||
|
|
||||||
if (encodeCode)
|
|
||||||
dataEncode((unsigned char *)ips, ips_len);
|
|
||||||
dns->http_rsp_len = sizeof(SUCCESS_HEADER) - 1 + ips_len;
|
|
||||||
dns->http_rsp = (char *)malloc(dns->http_rsp_len + 1);
|
|
||||||
if (dns->http_rsp == NULL)
|
|
||||||
return;
|
return;
|
||||||
strcpy(dns->http_rsp, SUCCESS_HEADER);
|
cache_temp->question = strdup(dns->dns_req + 12);
|
||||||
memcpy(dns->http_rsp + sizeof(SUCCESS_HEADER) - 1, ips, ips_len);
|
if (cache_temp->question == NULL) {
|
||||||
dns->sent_len = 0;
|
free(cache_temp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cache_temp->next = cache;
|
||||||
|
cache = cache_temp;
|
||||||
|
cache->question_len = dns->host_len;
|
||||||
|
cache->answer = dns->reply;
|
||||||
|
if (cacheLimit) {
|
||||||
|
//到达缓存记录条目限制则释放前一半缓存
|
||||||
|
if (cache_using >= cacheLimit) {
|
||||||
|
struct dns_cache *free_c;
|
||||||
|
int i;
|
||||||
|
for (i = cache_using = cacheLimit >> 1; i--; cache_temp = cache_temp->next) ;
|
||||||
|
for (free_c = cache_temp->next, cache_temp->next = NULL; free_c; free_c = cache_temp) {
|
||||||
|
cache_temp = free_c->next;
|
||||||
|
free(free_c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache_using++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void response_client(dns_t * out)
|
int respond_client(dns_t * dns)
|
||||||
{
|
{
|
||||||
int write_len = write(out->fd, out->http_rsp + out->sent_len, out->http_rsp_len - out->sent_len);
|
int write_len = sendto(dnsListenFd, dns->dns_req, dns->dns_rsp_len, 0, (struct sockaddr *)&dns->src_addr, sizeof(struct sockaddr_in));
|
||||||
if (write_len == out->http_rsp_len - out->sent_len || write_len == -1)
|
if (write_len == dns->dns_rsp_len) {
|
||||||
close_client(out);
|
dns->query_type = 0;
|
||||||
|
return 0;
|
||||||
|
} else if (write_len == -1)
|
||||||
|
return -1;
|
||||||
|
else {
|
||||||
|
dns->dns_rsp_len -= write_len;
|
||||||
|
memcpy(dns->dns_req, dns->dns_req + write_len, dns->dns_rsp_len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void respond_clients()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < DNS_MAX_CONNECTION; i++) {
|
||||||
|
if (dns_list[i].wait_response_client) {
|
||||||
|
if (respond_client(&dns_list[i]) == 1)
|
||||||
|
return;
|
||||||
else
|
else
|
||||||
out->sent_len += write_len;
|
dns_list[i].wait_response_client = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.fd = dnsListenFd;
|
||||||
|
epoll_ctl(dns_efd, EPOLL_CTL_MOD, dnsListenFd, &ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void build_dns_req(dns_t * dns, char *domain, int domain_size)
|
/* 分析DNS请求 */
|
||||||
|
int parse_dns_request(char *dns_req, dns_t * dns)
|
||||||
{
|
{
|
||||||
char *p, *_p;
|
dns_req += 13; //跳到域名部分
|
||||||
|
dns->host_len = strlen(dns_req);
|
||||||
|
//判断请求类型
|
||||||
|
switch ((dns->query_type = *(dns_req + 2 + dns->host_len))) {
|
||||||
|
//case 28: //查询ipv6地址
|
||||||
|
//dns->query_type = 1; //httpDNS不支持查询ipv6,所以改成ipv4
|
||||||
|
|
||||||
p = dns->dns_req + 12;
|
case 1: //查询ipv4地址
|
||||||
memcpy(p + 1, domain, domain_size + 1); //copy '\0'
|
dns->host = strdup(dns_req);
|
||||||
while ((_p = strchr(p + 1, '.')) != NULL) {
|
if (dns->host == NULL)
|
||||||
*p = _p - p - 1;
|
return 1;
|
||||||
p = _p;
|
int len;
|
||||||
|
for (len = *(--dns_req); dns_req[len + 1] != 0; len += dns_req[len]) {
|
||||||
|
//防止数组越界
|
||||||
|
if (len > dns->host_len) {
|
||||||
|
free(dns->host);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dns->host[len++] = '.';
|
||||||
|
}
|
||||||
|
//printf("dns->host: %s\n", dns->host);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dns->host = NULL;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
*p = strlen(p + 1);
|
|
||||||
p = dns->dns_req + 14 + domain_size;
|
|
||||||
*p++ = 0;
|
|
||||||
*p++ = 1;
|
|
||||||
*p++ = 0;
|
|
||||||
*p++ = 1;
|
|
||||||
dns->dns_req_len = p - dns->dns_req;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int send_dns_req(char *dns_req, int req_len)
|
/* 建立DNS回应 */
|
||||||
|
int build_dns_response(dns_t * dns)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
//18: 查询资源的前(12字节)后(6字节)部分
|
||||||
|
dns->dns_rsp_len = 18 + dns->host_len + (dns->reply ? 16 : 0);
|
||||||
|
if (dns->dns_rsp_len > DATA_SIZE) {
|
||||||
|
dns->query_type = 0;
|
||||||
|
return 1; //超出缓冲大小
|
||||||
|
}
|
||||||
|
/* 问题数 */
|
||||||
|
dns->dns_req[4] = 0;
|
||||||
|
dns->dns_req[5] = 1;
|
||||||
|
/* 资源记录数 */
|
||||||
|
dns->dns_req[6] = 0;
|
||||||
|
dns->dns_req[7] = 0;
|
||||||
|
/* 授权资源记录数 */
|
||||||
|
dns->dns_req[8] = 0;
|
||||||
|
dns->dns_req[9] = 0;
|
||||||
|
/* 额外资源记录数 */
|
||||||
|
dns->dns_req[10] = 0;
|
||||||
|
dns->dns_req[11] = 0;
|
||||||
|
/* 如果有回应内容(资源记录) */
|
||||||
|
if (dns->reply) {
|
||||||
|
p = dns->dns_req + 18 + dns->host_len;
|
||||||
|
/* 资源记录数+1 */
|
||||||
|
dns->dns_req[7]++;
|
||||||
|
/* 成功标志 */
|
||||||
|
dns->dns_req[2] = (char)133;
|
||||||
|
dns->dns_req[3] = (char)128;
|
||||||
|
/* 指向主机域名 */
|
||||||
|
p[0] = (char)192;
|
||||||
|
p[1] = 12;
|
||||||
|
/* 回应类型 */
|
||||||
|
p[2] = 0;
|
||||||
|
p[3] = dns->query_type;
|
||||||
|
/* 区域类别 */
|
||||||
|
p[4] = 0;
|
||||||
|
p[5] = 1;
|
||||||
|
/* 生存时间 (1 ora) */
|
||||||
|
p[6] = 0;
|
||||||
|
p[7] = 0;
|
||||||
|
p[8] = 14;
|
||||||
|
p[9] = 16;
|
||||||
|
/* 回应长度 */
|
||||||
|
p[10] = 0;
|
||||||
|
p[11] = 4; //reply中包含回应长度
|
||||||
|
strcpy(p + 12, dns->reply);
|
||||||
|
} else {
|
||||||
|
/* 失败标志 */
|
||||||
|
dns->dns_req[2] = (char)129;
|
||||||
|
dns->dns_req[3] = (char)130;
|
||||||
|
}
|
||||||
|
if (respond_client(dns) == 1) {
|
||||||
|
dns->wait_response_client = 1;
|
||||||
|
ev.events = EPOLLIN | EPOLLOUT;
|
||||||
|
ev.data.fd = dnsListenFd;
|
||||||
|
epoll_ctl(dns_efd, EPOLL_CTL_MOD, dnsListenFd, &ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_out(dns_t * out)
|
||||||
{
|
{
|
||||||
int write_len;
|
int write_len;
|
||||||
|
|
||||||
write_len = write(dstFd, dns_req, req_len);
|
//puts("writing");
|
||||||
if (write_len == req_len)
|
//printf("%s\n", out->http_request);
|
||||||
return 0;
|
write_len = write(out->fd, out->http_request, out->http_request_len);
|
||||||
return write_len;
|
if (write_len == out->http_request_len) {
|
||||||
}
|
//puts("write success");
|
||||||
|
free(out->http_request);
|
||||||
void query_dns()
|
|
||||||
{
|
|
||||||
dns_t *dns;
|
|
||||||
int i, ret;
|
|
||||||
|
|
||||||
for (i = MAX_FD - 2, dns = &dns_list[MAX_FD - 3]; i--; dns--) {
|
|
||||||
if (dns->http_rsp == NULL && dns->dns_req_len != dns->sent_len) {
|
|
||||||
ret = send_dns_req(dns->dns_req + dns->sent_len, dns->dns_req_len - dns->sent_len);
|
|
||||||
if (ret == 0) {
|
|
||||||
dns->sent_len = dns->dns_req_len;
|
|
||||||
} else if (ret > -1) {
|
|
||||||
dns->sent_len += ret;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
close_client(dns);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ev.events = EPOLLIN | EPOLLET;
|
ev.events = EPOLLIN | EPOLLET;
|
||||||
ev.data.fd = dstFd;
|
ev.data.ptr = out;
|
||||||
epoll_ctl(eFd, EPOLL_CTL_MOD, dstFd, &ev);
|
epoll_ctl(dns_efd, EPOLL_CTL_MOD, out->fd, &ev);
|
||||||
|
} else if (write_len > 0) {
|
||||||
|
//puts("write a little");
|
||||||
|
out->http_request_len -= write_len;
|
||||||
|
memcpy(out->http_request, out->http_request + write_len, out->http_request_len);
|
||||||
|
} else {
|
||||||
|
//puts("write error");
|
||||||
|
free(out->http_request);
|
||||||
|
epoll_ctl(dns_efd, EPOLL_CTL_DEL, out->fd, NULL);
|
||||||
|
close(out->fd);
|
||||||
|
out->query_type = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void recv_dns_rsp()
|
void http_in(dns_t * in)
|
||||||
{
|
{
|
||||||
static char rsp_data[BUFF_SIZE + 1], *p, *ips, *ips_save;
|
char *ip_ptr, *p;
|
||||||
unsigned char *_p;
|
int len, i;
|
||||||
dns_t *dns;
|
|
||||||
int len, ips_len;
|
|
||||||
int16_t flag;
|
|
||||||
|
|
||||||
len = read(dstFd, rsp_data, BUFF_SIZE);
|
len = read(in->fd, http_rsp, HTTP_RSP_SIZE);
|
||||||
if (len < 2)
|
if (len <= 0) {
|
||||||
return;
|
in->query_type = 0;
|
||||||
memcpy(&flag, rsp_data, sizeof(int16_t));
|
epoll_ctl(dns_efd, EPOLL_CTL_DEL, in->fd, NULL);
|
||||||
if (flag > MAX_FD - 3)
|
close(in->fd);
|
||||||
return;
|
|
||||||
dns = dns_list + flag;
|
|
||||||
dns->sent_len = 0;
|
|
||||||
dns->http_rsp = ERROR_MSG;
|
|
||||||
dns->http_rsp_len = sizeof(ERROR_MSG) - 1;
|
|
||||||
if (dns->dns_req_len + 12 > len || (unsigned char)rsp_data[3] != 128 /*(signed char) max is 127 */ )
|
|
||||||
goto modEvToOut;
|
|
||||||
rsp_data[len] = '\0';
|
|
||||||
/* get ips */
|
|
||||||
p = rsp_data + dns->dns_req_len + 11;
|
|
||||||
ips_len = 0;
|
|
||||||
ips = NULL;
|
|
||||||
while (p - rsp_data + 4 <= len) {
|
|
||||||
//type
|
|
||||||
if (*(p - 8) != 1) {
|
|
||||||
p += *p + 12;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ips_save = ips;
|
|
||||||
ips = (char *)realloc(ips, ips_len + 16);
|
|
||||||
if (ips == NULL) {
|
|
||||||
ips = ips_save;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_p = (unsigned char *)p + 1;
|
|
||||||
ips_len += sprintf(ips + ips_len, "%d.%d.%d.%d", _p[0], _p[1], _p[2], _p[3]);
|
|
||||||
p += 16; //next address
|
|
||||||
ips[ips_len++] = ';';
|
|
||||||
}
|
|
||||||
if (ips) {
|
|
||||||
ips[ips_len - 1] = '\0';
|
|
||||||
//printf("ips %s\n", ips);
|
|
||||||
build_http_rsp(dns, ips);
|
|
||||||
free(ips);
|
|
||||||
if (dns->http_rsp) {
|
|
||||||
response_client(dns);
|
|
||||||
if (dns->http_rsp == NULL) {
|
|
||||||
dns->http_rsp = ERROR_MSG;
|
|
||||||
dns->http_rsp_len = sizeof(ERROR_MSG) - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modEvToOut:
|
|
||||||
ev.data.ptr = dns;
|
|
||||||
ev.events = EPOLLOUT | EPOLLET;
|
|
||||||
epoll_ctl(eFd, EPOLL_CTL_MOD, dns->fd, &ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_client(dns_t * in)
|
|
||||||
{
|
|
||||||
static char httpReq[BUFF_SIZE + 1];
|
|
||||||
int domain_size, httpReq_len;
|
|
||||||
char *domain_begin, *domain_end, *domain = NULL, *ips;
|
|
||||||
|
|
||||||
httpReq_len = read(in->fd, httpReq, BUFF_SIZE);
|
|
||||||
//必须大于5,否则不处理
|
|
||||||
if (httpReq_len < 6) {
|
|
||||||
close_client(in);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
httpReq[httpReq_len] = '\0';
|
|
||||||
in->http_rsp = ERROR_MSG;
|
|
||||||
in->http_rsp_len = sizeof(ERROR_MSG) - 1;
|
|
||||||
if ((domain_begin = strstr(httpReq, "?dn=")))
|
|
||||||
domain_begin += 4;
|
|
||||||
else if ((domain_begin = strstr(httpReq, "?host=")))
|
|
||||||
domain_begin += 6;
|
|
||||||
else
|
|
||||||
goto response_client;
|
|
||||||
|
|
||||||
domain_end = strchr(domain_begin, ' ');
|
|
||||||
if (domain_end == NULL)
|
|
||||||
goto response_client;
|
|
||||||
if (*(domain_end - 1) == '.')
|
|
||||||
domain_size = domain_end - domain_begin - 1;
|
|
||||||
else
|
|
||||||
domain_size = domain_end - domain_begin;
|
|
||||||
domain = strndup(domain_begin, domain_size);
|
|
||||||
if (encodeCode)
|
if (encodeCode)
|
||||||
dataEncode((unsigned char *)domain, domain_size);
|
dataEncode(http_rsp, len);
|
||||||
if (domain == NULL || domain_size <= 0)
|
http_rsp[len] = '\0';
|
||||||
goto response_client;
|
//printf("[%s]\n", http_rsp);
|
||||||
if (hostsfp && (ips = hosts_lookup(domain)) != NULL) {
|
p = strstr(http_rsp, "\n\r");
|
||||||
free(domain);
|
if (p) {
|
||||||
build_http_rsp(in, ips);
|
//部分代理服务器使用长连接,第二次读取数据才读到域名的IP
|
||||||
if (in->http_rsp == NULL) {
|
if (p + 3 - http_rsp >= len)
|
||||||
in->http_rsp = ERROR_MSG;
|
return;
|
||||||
in->http_rsp_len = sizeof(ERROR_MSG) - 1;
|
p += 3;
|
||||||
}
|
} else
|
||||||
|
p = http_rsp;
|
||||||
|
epoll_ctl(dns_efd, EPOLL_CTL_DEL, in->fd, NULL);
|
||||||
|
close(in->fd);
|
||||||
|
in->reply = (char *)malloc(5);
|
||||||
|
if (in->reply == NULL)
|
||||||
|
goto error;
|
||||||
|
do {
|
||||||
|
if (*p == '\n')
|
||||||
|
p++;
|
||||||
|
/* 匹配IP */
|
||||||
|
if (*p > 57 || *p < 49)
|
||||||
|
continue;
|
||||||
|
for (i = 0, ip_ptr = p, p = strchr(ip_ptr, '.');; ip_ptr = p + 1, p = strchr(ip_ptr, '.')) {
|
||||||
|
if (i < 3) {
|
||||||
|
if (p == NULL)
|
||||||
|
goto error;
|
||||||
|
//查找下一行
|
||||||
|
if (p - ip_ptr > 3)
|
||||||
|
break;
|
||||||
|
in->reply[i++] = atoi(ip_ptr);
|
||||||
} else {
|
} else {
|
||||||
build_dns_req(in, domain, domain_size);
|
in->reply[3] = atoi(ip_ptr);
|
||||||
free(domain);
|
in->reply[4] = 0;
|
||||||
int ret = send_dns_req(in->dns_req, in->dns_req_len);
|
build_dns_response(in);
|
||||||
switch (ret) {
|
cfp ? cache_record(in) : free(in->reply);
|
||||||
case 0:
|
|
||||||
in->sent_len = in->dns_req_len;
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case -1:
|
|
||||||
close_client(in);
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
in->sent_len += ret;
|
|
||||||
ev.events = EPOLLIN | EPOLLOUT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ev.data.fd = dstFd;
|
|
||||||
epoll_ctl(eFd, EPOLL_CTL_MOD, dstFd, &ev);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response_client:
|
|
||||||
response_client(in);
|
|
||||||
if (in->http_rsp) {
|
|
||||||
ev.data.ptr = in;
|
|
||||||
ev.events = EPOLLOUT | EPOLLET;
|
|
||||||
epoll_ctl(eFd, EPOLL_CTL_MOD, in->fd, &ev);
|
|
||||||
}
|
}
|
||||||
|
} while ((p = strchr(p, '\n')) != NULL);
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(in->reply);
|
||||||
|
in->reply = NULL;
|
||||||
|
if (build_dns_response(in) == 1)
|
||||||
|
in->query_type = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void httpdns_accept_client()
|
void new_client(conf *configure)
|
||||||
{
|
{
|
||||||
struct sockaddr_in addr;
|
dns_t *dns;
|
||||||
dns_t *client;
|
int i, len;
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = MAX_FD - 2; i--;) {
|
for (i = 0; i < DNS_MAX_CONNECTION; i++)
|
||||||
if (dns_list[i].fd < 0) {
|
if (dns_list[i].query_type == 0)
|
||||||
client = &dns_list[i];
|
|
||||||
break;
|
break;
|
||||||
}
|
if (i == DNS_MAX_CONNECTION)
|
||||||
}
|
|
||||||
//printf("i = %d\n" , i);
|
|
||||||
if (i < 0)
|
|
||||||
return;
|
return;
|
||||||
client->fd = accept(listenFd, (struct sockaddr *)&addr, &addr_len);
|
dns = &dns_list[i];
|
||||||
if (client->fd < 0) {
|
len = recvfrom(dnsListenFd, &dns->dns_req, DATA_SIZE, 0, (struct sockaddr *)&dns->src_addr, &addr_len);
|
||||||
|
//printf("addr: [%s:%d]\n", inet_ntoa(dns->src_addr.sin_addr), ntohs(dns->src_addr.sin_port));
|
||||||
|
//dns请求必须大于18
|
||||||
|
if (len <= 18)
|
||||||
|
return;
|
||||||
|
/* 查询缓存 */
|
||||||
|
if (cachePath) {
|
||||||
|
dns->reply = cache_lookup(dns->dns_req + 12, dns);
|
||||||
|
if (dns->reply != NULL) {
|
||||||
|
if (build_dns_response(dns) != 0)
|
||||||
|
dns->query_type = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fcntl(client->fd, F_SETFL, O_NONBLOCK);
|
}
|
||||||
ev.data.ptr = client;
|
if (parse_dns_request(dns->dns_req, dns) != 0) {
|
||||||
ev.events = EPOLLIN | EPOLLET;
|
if (dns->host == NULL) {
|
||||||
if (epoll_ctl(eFd, EPOLL_CTL_ADD, client->fd, &ev) != 0) {
|
if (build_dns_response(dns) != 0)
|
||||||
close(client->fd);
|
dns->query_type = 0;
|
||||||
client->fd = -1;
|
} else
|
||||||
|
dns->query_type = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
dns->fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (dns->fd < 0) {
|
||||||
|
dns->query_type = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fcntl(dns->fd, F_SETFL, O_NONBLOCK);
|
||||||
|
if (connect(dns->fd, (struct sockaddr *)&dst_addr, sizeof(dst_addr)) != 0 && errno != EINPROGRESS) {
|
||||||
|
close(dns->fd);
|
||||||
|
dns->query_type = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (encodeCode)
|
||||||
|
dataEncode(dns->host, strlen(dns->host));
|
||||||
|
/* "GET /d?dn=" + dns->host + " HTTP/1.0\r\nHost: " + host_value + "\r\n\r\n" */
|
||||||
|
dns->http_request = (char *)malloc(10 + strlen(dns->host) + 17 + host_value_len + 4 + 1);
|
||||||
|
free(dns->host);
|
||||||
|
if (dns->http_request == NULL) {
|
||||||
|
close(dns->fd);
|
||||||
|
dns->query_type = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//dns->http_request_len = sprintf(dns->http_request, "GET /d?dn=%s HTTP/1.0\r\nHost: %s\r\n\r\n", dns->host, host_value);
|
||||||
|
|
||||||
|
strcpy(dns->http_request, configure->http_req);
|
||||||
|
dns->http_request_len = strlen(dns->http_request);
|
||||||
|
int http_request_len = (int)dns->http_request_len;
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "[M]", 3, "GET", 3);
|
||||||
|
dns->host_len = strlen(dns->host);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "[D]", 3, dns->host, dns->host_len);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "[V]", 3, "HTTP/1.0", 8);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\r", 2, "\r", 1);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\n", 2, "\n", 1);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\b", 2, "\b", 1);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\v", 2, "\v", 1);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\f", 2, "\f", 1);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\a", 2, "\a", 1);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\t", 2, "\t", 1);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\r", 2, "\r", 1);
|
||||||
|
dns->http_request = replace(dns->http_request, &http_request_len, "\\n", 2, "\n", 1);
|
||||||
|
dns->http_request_len = strlen(dns->http_request);
|
||||||
|
//printf("%s\n", dns->http_request);
|
||||||
|
|
||||||
|
ev.events = EPOLLOUT | EPOLLERR | EPOLLET;
|
||||||
|
ev.data.ptr = dns;
|
||||||
|
epoll_ctl(dns_efd, EPOLL_CTL_ADD, dns->fd, &ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
int httpdns_initialize()
|
void *httpdns_loop(void *p)
|
||||||
{
|
|
||||||
struct sockaddr_in listenAddr, dnsAddr;
|
|
||||||
int optval = 0;
|
|
||||||
|
|
||||||
//ignore PIPE signal
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
dnsAddr.sin_family = listenAddr.sin_family = AF_INET; // IPv4
|
|
||||||
dnsAddr.sin_addr.s_addr = inet_addr(DEFAULT_UPPER_IP); // 本地监听
|
|
||||||
dnsAddr.sin_port = htons(53);
|
|
||||||
|
|
||||||
listenAddr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
listenAddr.sin_port = htons(53);
|
|
||||||
|
|
||||||
listenFd = socket(AF_INET, SOCK_STREAM, 0); // 本地监听,TCP协议
|
|
||||||
dstFd = socket(AF_INET, SOCK_DGRAM, 0); // DNS监听,UDP协议
|
|
||||||
if (dstFd < 0 || listenFd < 0) {
|
|
||||||
perror("socket");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
fcntl(dstFd, F_SETFL, O_NONBLOCK); // dstFd 非阻塞
|
|
||||||
fcntl(listenFd, F_SETFL, O_NONBLOCK); // listenFd 非阻塞
|
|
||||||
|
|
||||||
optval = 1;
|
|
||||||
if (setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) != 0) {
|
|
||||||
perror("setsockopt");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (bind(listenFd, (struct sockaddr *)&listenAddr, sizeof(listenAddr)) != 0) {
|
|
||||||
perror("bind");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (listen(listenFd, 20) != 0) {
|
|
||||||
perror("listen");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
eFd = epoll_create(MAX_FD - 1);
|
|
||||||
if (eFd < 0) {
|
|
||||||
perror("epoll_create");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
connect(dstFd, (struct sockaddr *)&dnsAddr, sizeof(dnsAddr));
|
|
||||||
ev.data.fd = listenFd;
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
epoll_ctl(eFd, EPOLL_CTL_ADD, listenFd, &ev);
|
|
||||||
ev.data.fd = dstFd;
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
epoll_ctl(eFd, EPOLL_CTL_ADD, dstFd, &ev);
|
|
||||||
memset(dns_list, 0, sizeof(dns_list));
|
|
||||||
//初始化DNS请求结构
|
|
||||||
int16_t i;
|
|
||||||
for (i = MAX_FD - 2; i--;) {
|
|
||||||
dns_list[i].fd = -1;
|
|
||||||
memcpy(dns_list[i].dns_req, &i, sizeof(i));
|
|
||||||
dns_list[i].dns_req[2] = 1;
|
|
||||||
dns_list[i].dns_req[3] = 0;
|
|
||||||
dns_list[i].dns_req[4] = 0;
|
|
||||||
dns_list[i].dns_req[5] = 1;
|
|
||||||
dns_list[i].dns_req[6] = 0;
|
|
||||||
dns_list[i].dns_req[7] = 0;
|
|
||||||
dns_list[i].dns_req[8] = 0;
|
|
||||||
dns_list[i].dns_req[9] = 0;
|
|
||||||
dns_list[i].dns_req[10] = 0;
|
|
||||||
dns_list[i].dns_req[11] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *httpdns_start_server(void *p)
|
|
||||||
{
|
{
|
||||||
|
conf *configure = (conf *) p;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
while (1) {
|
fcntl(dnsListenFd, F_SETFL, O_NONBLOCK);
|
||||||
n = epoll_wait(eFd, evs, MAX_FD - 1, -1);
|
dns_efd = epoll_create(DNS_MAX_CONNECTION + 1);
|
||||||
//printf("n = %d\n", n);
|
if (dns_efd < 0) {
|
||||||
while (n-- > 0) {
|
perror("epoll_create");
|
||||||
if (evs[n].data.fd == listenFd) {
|
|
||||||
httpdns_accept_client();
|
|
||||||
} else if (evs[n].data.fd == dstFd) {
|
|
||||||
if (evs[n].events & EPOLLIN) {
|
|
||||||
recv_dns_rsp();
|
|
||||||
} else if (evs[n].events & EPOLLOUT) {
|
|
||||||
query_dns();
|
|
||||||
}
|
|
||||||
} else if (evs[n].events & EPOLLIN) {
|
|
||||||
read_client(evs[n].data.ptr);
|
|
||||||
} else if (evs[n].events & EPOLLOUT) {
|
|
||||||
response_client(evs[n].data.ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
ev.data.fd = dnsListenFd;
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
epoll_ctl(dns_efd, EPOLL_CTL_ADD, dnsListenFd, &ev);
|
||||||
|
memset(dns_list, 0, sizeof(dns_list));
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
n = epoll_wait(dns_efd, evs, DNS_MAX_CONNECTION + 1, -1);
|
||||||
|
while (n-- > 0) {
|
||||||
|
if (evs[n].data.fd == dnsListenFd) {
|
||||||
|
if (evs[n].events & EPOLLIN) {
|
||||||
|
new_client(configure);
|
||||||
|
}
|
||||||
|
if (evs[n].events & EPOLLOUT) {
|
||||||
|
respond_clients();
|
||||||
|
}
|
||||||
|
} else if (evs[n].events & EPOLLIN) {
|
||||||
|
http_in(evs[n].data.ptr);
|
||||||
|
} else if (evs[n].events & EPOLLOUT) {
|
||||||
|
http_out(evs[n].data.ptr);
|
||||||
|
} else if (evs[n].events & EPOLLERR) {
|
||||||
|
dns_t *err = evs[n].data.ptr;
|
||||||
|
free(err->http_request);
|
||||||
|
epoll_ctl(dns_efd, EPOLL_CTL_DEL, err->fd, NULL);
|
||||||
|
close(err->fd);
|
||||||
|
err->query_type = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp_listen(char *ip, int port)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
|
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||||
|
perror("udp socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
addr.sin_addr.s_addr = inet_addr(ip);
|
||||||
|
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||||
|
perror("udp bind");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int httpdns_initialize(conf * configure) {
|
||||||
|
char *p;
|
||||||
|
p = strchr(configure->addr, ':');
|
||||||
|
host_value = configure->addr;
|
||||||
|
*p = '\0';
|
||||||
|
//printf("udp: %s %d %d\n", configure->addr, configure->dns_listen, atoi(p+1));
|
||||||
|
|
||||||
|
dnsListenFd = udp_listen("0.0.0.0", configure->dns_listen);
|
||||||
|
dst_addr.sin_addr.s_addr = inet_addr(configure->addr);
|
||||||
|
dst_addr.sin_family = AF_INET;
|
||||||
|
dst_addr.sin_port = htons(atoi(p+1));
|
||||||
|
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
signal(SIGTERM, write_dns_cache);
|
||||||
|
host_value_len = strlen(host_value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
72
httpdns.h
72
httpdns.h
@ -2,54 +2,52 @@
|
|||||||
#define _HTTPDNS_
|
#define _HTTPDNS_
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <unistd.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/epoll.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#define DEFAULT_UPPER_IP "114.114.114.114"
|
#include "conf.h"
|
||||||
#define MAX_FD 1024
|
|
||||||
#define DNS_REQUEST_SIZE 512
|
|
||||||
#define BUFF_SIZE 1024
|
|
||||||
|
|
||||||
typedef struct dns_request {
|
#define DNS_MAX_CONNECTION 256 //此值的大小关系到respod_clients函数的效率
|
||||||
char dns_req[DNS_REQUEST_SIZE + 1];
|
#define DATA_SIZE 512
|
||||||
char *http_rsp;
|
#define HTTP_RSP_SIZE 1024
|
||||||
unsigned int http_rsp_len, sent_len, dns_req_len;
|
|
||||||
|
typedef struct dns_connection {
|
||||||
|
char dns_req[DATA_SIZE];
|
||||||
|
struct sockaddr_in src_addr;
|
||||||
|
char *reply; //回应内容
|
||||||
|
char *http_request, *host;
|
||||||
|
unsigned int http_request_len, dns_rsp_len;
|
||||||
int fd;
|
int fd;
|
||||||
|
char query_type;
|
||||||
|
unsigned host_len :7; //域名最大长度64位
|
||||||
|
unsigned wait_response_client :1; //已构建好DNS回应,等待可写事件
|
||||||
} dns_t;
|
} dns_t;
|
||||||
|
|
||||||
struct dns_hosts {
|
struct dns_cache {
|
||||||
char *host;
|
int question_len;
|
||||||
char *ip;
|
char *question;
|
||||||
struct dns_hosts *next;
|
char *answer;
|
||||||
|
struct dns_cache *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define VERSION "0.3"
|
dns_t dns_list[DNS_MAX_CONNECTION];
|
||||||
#define ERROR_MSG "HTTP/1.0 404 Not Found\r\nConnection: close\r\nVia: Mmmdbybyd(HTTP-DNS Server "VERSION")\r\nContent-type: charset=utf-8\r\n\r\n<html><head><title>HTTP DNS Server</title></head><body>查询域名失败<br/><br/>By: 萌萌萌得不要不要哒<br/>E-mail: 915445800@qq.com</body></html>"
|
struct epoll_event evs[DNS_MAX_CONNECTION+1], ev;
|
||||||
#define SUCCESS_HEADER "HTTP/1.0 200 OK\r\nConnection: close\r\nVia: Mmmdbybyd(HTTP-DNS Server "VERSION")\r\n\r\n"
|
|
||||||
|
|
||||||
dns_t dns_list[MAX_FD - 2]; //监听客户端FD DNS服务端fd
|
|
||||||
struct epoll_event evs[MAX_FD - 1], ev;
|
|
||||||
|
|
||||||
int listenFd, dstFd, eFd;
|
void *httpdns_loop(void *p);
|
||||||
socklen_t addr_len;
|
int httpdns_initialize(conf * configure);
|
||||||
void recv_dns_rsp();
|
|
||||||
void query_dns();
|
|
||||||
void read_client(dns_t * in);
|
|
||||||
void response_client(dns_t * out);
|
|
||||||
void httpdns_accept_client();
|
|
||||||
int httpdns_initialize();
|
|
||||||
void *httpdns_start_server();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "proxy.h"
|
#include "main.h"
|
||||||
#include "http.h"
|
#include "http_proxy.h"
|
||||||
#include "request.h"
|
#include "http_request.h"
|
||||||
#include "timeout.h"
|
#include "timeout.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "kill.h"
|
#include "kill.h"
|
||||||
@ -185,19 +185,11 @@ int process_signal(int signal, char *process_name)
|
|||||||
printf("\t%d\n", num[n]);
|
printf("\t%d\n", num[n]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (signal == SERVER_STOP) { // 关闭
|
if (signal == SERVER_STOP || signal == SERVER_RELOAD) { // 关闭
|
||||||
struct passwd *pwent = NULL;
|
struct passwd *pwent = NULL;
|
||||||
pwent = getpwnam("root");
|
pwent = getpwnam("root");
|
||||||
return kill_all(15, 1, &process_name, pwent);
|
return kill_all(15, 1, &process_name, pwent);
|
||||||
}
|
}
|
||||||
if (signal == SERVER_RELOAD) { // 重启
|
|
||||||
n -= 2;
|
|
||||||
for (; n >= 0; n--) {
|
|
||||||
//printf("\t%d\n", num[n]);
|
|
||||||
//printf("kill 返回 %d\n", kill(num[n], SIGTERM));
|
|
||||||
kill(num[n], SIGTERM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -241,8 +233,8 @@ void _main(int argc, char *argv[])
|
|||||||
if (configure->sslencoding > 0) // 如果配置文件有sslencoding值,优先使用配置文件读取的值
|
if (configure->sslencoding > 0) // 如果配置文件有sslencoding值,优先使用配置文件读取的值
|
||||||
sslEncodeCode = configure->sslencoding;
|
sslEncodeCode = configure->sslencoding;
|
||||||
timeout_minute = 0; // 默认不超时
|
timeout_minute = 0; // 默认不超时
|
||||||
if (configure->timer > 0) // 如果配置文件有值,优先使用配置文件读取的值
|
if (configure->timeout > 0) // 如果配置文件有值,优先使用配置文件读取的值
|
||||||
timeout_minute = configure->timer;
|
timeout_minute = configure->timeout;
|
||||||
process = 2; // 默认开启2个进程
|
process = 2; // 默认开启2个进程
|
||||||
if (configure->process > 0) // 如果配置文件有值,优先使用配置文件读取的值
|
if (configure->process > 0) // 如果配置文件有值,优先使用配置文件读取的值
|
||||||
process = configure->process;
|
process = configure->process;
|
||||||
@ -319,7 +311,7 @@ void _main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
httpdns_initialize(); // 初始化http_dns
|
httpdns_initialize(configure); // 初始化http_dns
|
||||||
memset(cts, 0, sizeof(cts));
|
memset(cts, 0, sizeof(cts));
|
||||||
for (i = MAX_CONNECTION; i--;)
|
for (i = MAX_CONNECTION; i--;)
|
||||||
cts[i].fd = -1;
|
cts[i].fd = -1;
|
||||||
@ -331,7 +323,7 @@ void _main(int argc, char *argv[])
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server_sock = create_server_socket(configure->local_port);
|
server_sock = create_server_socket(configure->tcp_listen);
|
||||||
epollfd = epoll_create(MAX_CONNECTION);
|
epollfd = epoll_create(MAX_CONNECTION);
|
||||||
if (epollfd == -1) {
|
if (epollfd == -1) {
|
||||||
perror("epoll_create");
|
perror("epoll_create");
|
||||||
@ -347,7 +339,7 @@ void _main(int argc, char *argv[])
|
|||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
server_ini(); // 初始化http_proxy
|
server_ini(); // 初始化http_proxy
|
||||||
pthread_t thread_id;
|
pthread_t thread_id = 0;
|
||||||
sigset_t signal_mask;
|
sigset_t signal_mask;
|
||||||
sigemptyset(&signal_mask);
|
sigemptyset(&signal_mask);
|
||||||
sigaddset(&signal_mask, SIGPIPE); // 忽略PIPE信号
|
sigaddset(&signal_mask, SIGPIPE); // 忽略PIPE信号
|
||||||
@ -359,13 +351,13 @@ void _main(int argc, char *argv[])
|
|||||||
pthread_create(&thread_id, NULL, &close_timeout_connectionLoop, NULL);
|
pthread_create(&thread_id, NULL, &close_timeout_connectionLoop, NULL);
|
||||||
if (pthread_create(&thread_id, NULL, &http_proxy_loop, (void *)configure) != 0)
|
if (pthread_create(&thread_id, NULL, &http_proxy_loop, (void *)configure) != 0)
|
||||||
perror("pthread_create");
|
perror("pthread_create");
|
||||||
|
if (pthread_create(&thread_id, NULL, &httpdns_loop, (void *)configure) != 0)
|
||||||
if (pthread_create(&thread_id, NULL, &httpdns_start_server, NULL) != 0)
|
|
||||||
perror("pthread_create");
|
perror("pthread_create");
|
||||||
|
|
||||||
pthread_join(thread_id, NULL);
|
pthread_join(thread_id, NULL);
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
|
|
||||||
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
@ -21,7 +21,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#define MAX_CONNECTION 1020
|
#define MAX_CONNECTION 1020
|
||||||
#define BUFFER_SIZE 10240
|
#define BUFFER_SIZE 8192
|
||||||
#define PATH_SIZE 270
|
#define PATH_SIZE 270
|
||||||
|
|
||||||
int local_port;
|
int local_port;
|
631
picohttpparser.c
631
picohttpparser.c
@ -1,631 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
|
||||||
* Shigeo Mitsunari
|
|
||||||
*
|
|
||||||
* The software is licensed under either the MIT License (below) or the Perl
|
|
||||||
* license.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to
|
|
||||||
* deal in the Software without restriction, including without limitation the
|
|
||||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
* sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
* IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
#ifdef __SSE4_2__
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#include <nmmintrin.h>
|
|
||||||
#else
|
|
||||||
#include <x86intrin.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#include "picohttpparser.h"
|
|
||||||
|
|
||||||
#if __GNUC__ >= 3
|
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
||||||
#else
|
|
||||||
#define likely(x) (x)
|
|
||||||
#define unlikely(x) (x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define ALIGNED(n) _declspec(align(n))
|
|
||||||
#else
|
|
||||||
#define ALIGNED(n) __attribute__((aligned(n)))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
|
|
||||||
|
|
||||||
#define CHECK_EOF() \
|
|
||||||
if (buf == buf_end) { \
|
|
||||||
*ret = -2; \
|
|
||||||
return NULL; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXPECT_CHAR_NO_CHECK(ch) \
|
|
||||||
if (*buf++ != ch) { \
|
|
||||||
*ret = -1; \
|
|
||||||
return NULL; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EXPECT_CHAR(ch) \
|
|
||||||
CHECK_EOF(); \
|
|
||||||
EXPECT_CHAR_NO_CHECK(ch);
|
|
||||||
|
|
||||||
#define ADVANCE_TOKEN(tok, toklen) \
|
|
||||||
do { \
|
|
||||||
const char *tok_start = buf; \
|
|
||||||
static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
|
|
||||||
int found2; \
|
|
||||||
buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
|
|
||||||
if (!found2) { \
|
|
||||||
CHECK_EOF(); \
|
|
||||||
} \
|
|
||||||
while (1) { \
|
|
||||||
if (*buf == ' ') { \
|
|
||||||
break; \
|
|
||||||
} else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
|
|
||||||
if ((unsigned char)*buf < '\040' || *buf == '\177') { \
|
|
||||||
*ret = -1; \
|
|
||||||
return NULL; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
++buf; \
|
|
||||||
CHECK_EOF(); \
|
|
||||||
} \
|
|
||||||
tok = tok_start; \
|
|
||||||
toklen = buf - tok_start; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
|
|
||||||
{
|
|
||||||
*found = 0;
|
|
||||||
#if __SSE4_2__
|
|
||||||
if (likely(buf_end - buf >= 16)) {
|
|
||||||
__m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
|
|
||||||
|
|
||||||
size_t left = (buf_end - buf) & ~15;
|
|
||||||
do {
|
|
||||||
__m128i b16 = _mm_loadu_si128((const __m128i *)buf);
|
|
||||||
int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
|
|
||||||
if (unlikely(r != 16)) {
|
|
||||||
buf += r;
|
|
||||||
*found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buf += 16;
|
|
||||||
left -= 16;
|
|
||||||
} while (likely(left != 0));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* suppress unused parameter warning */
|
|
||||||
(void)buf_end;
|
|
||||||
(void)ranges;
|
|
||||||
(void)ranges_size;
|
|
||||||
#endif
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
|
|
||||||
{
|
|
||||||
const char *token_start = buf;
|
|
||||||
|
|
||||||
#ifdef __SSE4_2__
|
|
||||||
static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */
|
|
||||||
"\012\037" /* allow SP and up to but not including DEL */
|
|
||||||
"\177\177"; /* allow chars w. MSB set */
|
|
||||||
int found;
|
|
||||||
buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
|
|
||||||
if (found)
|
|
||||||
goto FOUND_CTL;
|
|
||||||
#else
|
|
||||||
/* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
|
|
||||||
while (likely(buf_end - buf >= 8)) {
|
|
||||||
#define DOIT() \
|
|
||||||
do { \
|
|
||||||
if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
|
|
||||||
goto NonPrintable; \
|
|
||||||
++buf; \
|
|
||||||
} while (0)
|
|
||||||
DOIT();
|
|
||||||
DOIT();
|
|
||||||
DOIT();
|
|
||||||
DOIT();
|
|
||||||
DOIT();
|
|
||||||
DOIT();
|
|
||||||
DOIT();
|
|
||||||
DOIT();
|
|
||||||
#undef DOIT
|
|
||||||
continue;
|
|
||||||
NonPrintable:
|
|
||||||
if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
|
|
||||||
goto FOUND_CTL;
|
|
||||||
}
|
|
||||||
++buf;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
for (;; ++buf) {
|
|
||||||
CHECK_EOF();
|
|
||||||
if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
|
|
||||||
if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
|
|
||||||
goto FOUND_CTL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FOUND_CTL:
|
|
||||||
if (likely(*buf == '\015')) {
|
|
||||||
++buf;
|
|
||||||
EXPECT_CHAR('\012');
|
|
||||||
*token_len = buf - 2 - token_start;
|
|
||||||
} else if (*buf == '\012') {
|
|
||||||
*token_len = buf - token_start;
|
|
||||||
++buf;
|
|
||||||
} else {
|
|
||||||
*ret = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
*token = token_start;
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
|
|
||||||
{
|
|
||||||
int ret_cnt = 0;
|
|
||||||
buf = last_len < 3 ? buf : buf + last_len - 3;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
CHECK_EOF();
|
|
||||||
if (*buf == '\015') {
|
|
||||||
++buf;
|
|
||||||
CHECK_EOF();
|
|
||||||
EXPECT_CHAR('\012');
|
|
||||||
++ret_cnt;
|
|
||||||
} else if (*buf == '\012') {
|
|
||||||
++buf;
|
|
||||||
++ret_cnt;
|
|
||||||
} else {
|
|
||||||
++buf;
|
|
||||||
ret_cnt = 0;
|
|
||||||
}
|
|
||||||
if (ret_cnt == 2) {
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*ret = -2;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PARSE_INT(valp_, mul_) \
|
|
||||||
if (*buf < '0' || '9' < *buf) { \
|
|
||||||
buf++; \
|
|
||||||
*ret = -1; \
|
|
||||||
return NULL; \
|
|
||||||
} \
|
|
||||||
*(valp_) = (mul_) * (*buf++ - '0');
|
|
||||||
|
|
||||||
#define PARSE_INT_3(valp_) \
|
|
||||||
do { \
|
|
||||||
int res_ = 0; \
|
|
||||||
PARSE_INT(&res_, 100) \
|
|
||||||
*valp_ = res_; \
|
|
||||||
PARSE_INT(&res_, 10) \
|
|
||||||
*valp_ += res_; \
|
|
||||||
PARSE_INT(&res_, 1) \
|
|
||||||
*valp_ += res_; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* returned pointer is always within [buf, buf_end), or null */
|
|
||||||
static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
|
|
||||||
{
|
|
||||||
/* we want at least [HTTP/1.<two chars>] to try to parse */
|
|
||||||
if (buf_end - buf < 9) {
|
|
||||||
*ret = -2;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
EXPECT_CHAR_NO_CHECK('H');
|
|
||||||
EXPECT_CHAR_NO_CHECK('T');
|
|
||||||
EXPECT_CHAR_NO_CHECK('T');
|
|
||||||
EXPECT_CHAR_NO_CHECK('P');
|
|
||||||
EXPECT_CHAR_NO_CHECK('/');
|
|
||||||
EXPECT_CHAR_NO_CHECK('1');
|
|
||||||
EXPECT_CHAR_NO_CHECK('.');
|
|
||||||
PARSE_INT(minor_version, 1);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
|
|
||||||
{
|
|
||||||
for (;; ++*num_headers) {
|
|
||||||
CHECK_EOF();
|
|
||||||
if (*buf == '\015') {
|
|
||||||
++buf;
|
|
||||||
EXPECT_CHAR('\012');
|
|
||||||
break;
|
|
||||||
} else if (*buf == '\012') {
|
|
||||||
++buf;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (*num_headers == max_headers) {
|
|
||||||
*ret = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
|
|
||||||
/* parsing name, but do not discard SP before colon, see
|
|
||||||
* http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
|
|
||||||
headers[*num_headers].name = buf;
|
|
||||||
static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
|
|
||||||
"\"\"" /* 0x22 */
|
|
||||||
"()" /* 0x28,0x29 */
|
|
||||||
",," /* 0x2c */
|
|
||||||
"//" /* 0x2f */
|
|
||||||
":@" /* 0x3a-0x40 */
|
|
||||||
"[]" /* 0x5b-0x5d */
|
|
||||||
"{\377"; /* 0x7b-0xff */
|
|
||||||
int found;
|
|
||||||
buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
|
|
||||||
if (!found) {
|
|
||||||
CHECK_EOF();
|
|
||||||
}
|
|
||||||
while (1) {
|
|
||||||
if (*buf == ':') {
|
|
||||||
break;
|
|
||||||
} else if (!token_char_map[(unsigned char)*buf]) {
|
|
||||||
*ret = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
++buf;
|
|
||||||
CHECK_EOF();
|
|
||||||
}
|
|
||||||
if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
|
|
||||||
*ret = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
++buf;
|
|
||||||
for (;; ++buf) {
|
|
||||||
CHECK_EOF();
|
|
||||||
if (!(*buf == ' ' || *buf == '\t')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
headers[*num_headers].name = NULL;
|
|
||||||
headers[*num_headers].name_len = 0;
|
|
||||||
}
|
|
||||||
const char *value;
|
|
||||||
size_t value_len;
|
|
||||||
if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* remove trailing SPs and HTABs */
|
|
||||||
const char *value_end = value + value_len;
|
|
||||||
for (; value_end != value; --value_end) {
|
|
||||||
const char c = *(value_end - 1);
|
|
||||||
if (!(c == ' ' || c == '\t')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
headers[*num_headers].value = value;
|
|
||||||
headers[*num_headers].value_len = value_end - value;
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
|
|
||||||
{
|
|
||||||
/* skip first empty line (some clients add CRLF after POST content) */
|
|
||||||
CHECK_EOF();
|
|
||||||
if (*buf == '\015') {
|
|
||||||
++buf;
|
|
||||||
EXPECT_CHAR('\012');
|
|
||||||
} else if (*buf == '\012') {
|
|
||||||
++buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse request line */
|
|
||||||
ADVANCE_TOKEN(*method, *method_len);
|
|
||||||
do {
|
|
||||||
++buf;
|
|
||||||
} while (*buf == ' ');
|
|
||||||
ADVANCE_TOKEN(*path, *path_len);
|
|
||||||
do {
|
|
||||||
++buf;
|
|
||||||
} while (*buf == ' ');
|
|
||||||
if (*method_len == 0 || *path_len == 0) {
|
|
||||||
*ret = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (*buf == '\015') {
|
|
||||||
++buf;
|
|
||||||
EXPECT_CHAR('\012');
|
|
||||||
} else if (*buf == '\012') {
|
|
||||||
++buf;
|
|
||||||
} else {
|
|
||||||
*ret = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
|
|
||||||
{
|
|
||||||
const char *buf = buf_start, *buf_end = buf_start + len;
|
|
||||||
size_t max_headers = *num_headers;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
*method = NULL;
|
|
||||||
*method_len = 0;
|
|
||||||
*path = NULL;
|
|
||||||
*path_len = 0;
|
|
||||||
*minor_version = -1;
|
|
||||||
*num_headers = 0;
|
|
||||||
|
|
||||||
/* if last_len != 0, check if the request is complete (a fast countermeasure
|
|
||||||
againt slowloris */
|
|
||||||
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers, &r)) == NULL) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)(buf - buf_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg, size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
|
|
||||||
{
|
|
||||||
/* parse "HTTP/1.x" */
|
|
||||||
if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* skip space */
|
|
||||||
if (*buf != ' ') {
|
|
||||||
*ret = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
++buf;
|
|
||||||
} while (*buf == ' ');
|
|
||||||
/* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
|
|
||||||
if (buf_end - buf < 4) {
|
|
||||||
*ret = -2;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PARSE_INT_3(status);
|
|
||||||
|
|
||||||
/* get message includig preceding space */
|
|
||||||
if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (*msg_len == 0) {
|
|
||||||
/* ok */
|
|
||||||
} else if (**msg == ' ') {
|
|
||||||
/* remove preceding space */
|
|
||||||
do {
|
|
||||||
++*msg;
|
|
||||||
--*msg_len;
|
|
||||||
} while (**msg == ' ');
|
|
||||||
} else {
|
|
||||||
/* garbage found after status code */
|
|
||||||
*ret = -1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t last_len)
|
|
||||||
{
|
|
||||||
const char *buf = buf_start, *buf_end = buf + len;
|
|
||||||
size_t max_headers = *num_headers;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
*minor_version = -1;
|
|
||||||
*status = 0;
|
|
||||||
*msg = NULL;
|
|
||||||
*msg_len = 0;
|
|
||||||
*num_headers = 0;
|
|
||||||
|
|
||||||
/* if last_len != 0, check if the response is complete (a fast countermeasure
|
|
||||||
against slowloris */
|
|
||||||
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)(buf - buf_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
|
|
||||||
{
|
|
||||||
const char *buf = buf_start, *buf_end = buf + len;
|
|
||||||
size_t max_headers = *num_headers;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
*num_headers = 0;
|
|
||||||
|
|
||||||
/* if last_len != 0, check if the response is complete (a fast countermeasure
|
|
||||||
against slowloris */
|
|
||||||
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)(buf - buf_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum {
|
|
||||||
CHUNKED_IN_CHUNK_SIZE,
|
|
||||||
CHUNKED_IN_CHUNK_EXT,
|
|
||||||
CHUNKED_IN_CHUNK_DATA,
|
|
||||||
CHUNKED_IN_CHUNK_CRLF,
|
|
||||||
CHUNKED_IN_TRAILERS_LINE_HEAD,
|
|
||||||
CHUNKED_IN_TRAILERS_LINE_MIDDLE
|
|
||||||
};
|
|
||||||
|
|
||||||
static int decode_hex(int ch)
|
|
||||||
{
|
|
||||||
if ('0' <= ch && ch <= '9') {
|
|
||||||
return ch - '0';
|
|
||||||
} else if ('A' <= ch && ch <= 'F') {
|
|
||||||
return ch - 'A' + 0xa;
|
|
||||||
} else if ('a' <= ch && ch <= 'f') {
|
|
||||||
return ch - 'a' + 0xa;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
|
|
||||||
{
|
|
||||||
size_t dst = 0, src = 0, bufsz = *_bufsz;
|
|
||||||
ssize_t ret = -2; /* incomplete */
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
switch (decoder->_state) {
|
|
||||||
case CHUNKED_IN_CHUNK_SIZE:
|
|
||||||
for (;; ++src) {
|
|
||||||
int v;
|
|
||||||
if (src == bufsz)
|
|
||||||
goto Exit;
|
|
||||||
if ((v = decode_hex(buf[src])) == -1) {
|
|
||||||
if (decoder->_hex_count == 0) {
|
|
||||||
ret = -1;
|
|
||||||
goto Exit;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (decoder->_hex_count == sizeof(size_t) * 2) {
|
|
||||||
ret = -1;
|
|
||||||
goto Exit;
|
|
||||||
}
|
|
||||||
decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
|
|
||||||
++decoder->_hex_count;
|
|
||||||
}
|
|
||||||
decoder->_hex_count = 0;
|
|
||||||
decoder->_state = CHUNKED_IN_CHUNK_EXT;
|
|
||||||
/* fallthru */
|
|
||||||
case CHUNKED_IN_CHUNK_EXT:
|
|
||||||
/* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
|
|
||||||
for (;; ++src) {
|
|
||||||
if (src == bufsz)
|
|
||||||
goto Exit;
|
|
||||||
if (buf[src] == '\012')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++src;
|
|
||||||
if (decoder->bytes_left_in_chunk == 0) {
|
|
||||||
if (decoder->consume_trailer) {
|
|
||||||
decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
goto Complete;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
decoder->_state = CHUNKED_IN_CHUNK_DATA;
|
|
||||||
/* fallthru */
|
|
||||||
case CHUNKED_IN_CHUNK_DATA:{
|
|
||||||
size_t avail = bufsz - src;
|
|
||||||
if (avail < decoder->bytes_left_in_chunk) {
|
|
||||||
if (dst != src)
|
|
||||||
memmove(buf + dst, buf + src, avail);
|
|
||||||
src += avail;
|
|
||||||
dst += avail;
|
|
||||||
decoder->bytes_left_in_chunk -= avail;
|
|
||||||
goto Exit;
|
|
||||||
}
|
|
||||||
if (dst != src)
|
|
||||||
memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
|
|
||||||
src += decoder->bytes_left_in_chunk;
|
|
||||||
dst += decoder->bytes_left_in_chunk;
|
|
||||||
decoder->bytes_left_in_chunk = 0;
|
|
||||||
decoder->_state = CHUNKED_IN_CHUNK_CRLF;
|
|
||||||
}
|
|
||||||
/* fallthru */
|
|
||||||
case CHUNKED_IN_CHUNK_CRLF:
|
|
||||||
for (;; ++src) {
|
|
||||||
if (src == bufsz)
|
|
||||||
goto Exit;
|
|
||||||
if (buf[src] != '\015')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (buf[src] != '\012') {
|
|
||||||
ret = -1;
|
|
||||||
goto Exit;
|
|
||||||
}
|
|
||||||
++src;
|
|
||||||
decoder->_state = CHUNKED_IN_CHUNK_SIZE;
|
|
||||||
break;
|
|
||||||
case CHUNKED_IN_TRAILERS_LINE_HEAD:
|
|
||||||
for (;; ++src) {
|
|
||||||
if (src == bufsz)
|
|
||||||
goto Exit;
|
|
||||||
if (buf[src] != '\015')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (buf[src++] == '\012')
|
|
||||||
goto Complete;
|
|
||||||
decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
|
|
||||||
/* fallthru */
|
|
||||||
case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
|
|
||||||
for (;; ++src) {
|
|
||||||
if (src == bufsz)
|
|
||||||
goto Exit;
|
|
||||||
if (buf[src] == '\012')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++src;
|
|
||||||
decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(!"decoder is corrupt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Complete:
|
|
||||||
ret = bufsz - src;
|
|
||||||
Exit:
|
|
||||||
if (dst != src)
|
|
||||||
memmove(buf + dst, buf + src, bufsz - src);
|
|
||||||
*_bufsz = dst;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
|
|
||||||
{
|
|
||||||
return decoder->_state == CHUNKED_IN_CHUNK_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef CHECK_EOF
|
|
||||||
#undef EXPECT_CHAR
|
|
||||||
#undef ADVANCE_TOKEN
|
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
|
||||||
* Shigeo Mitsunari
|
|
||||||
*
|
|
||||||
* The software is licensed under either the MIT License (below) or the Perl
|
|
||||||
* license.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to
|
|
||||||
* deal in the Software without restriction, including without limitation the
|
|
||||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
* sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
* IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef picohttpparser_h
|
|
||||||
#define picohttpparser_h
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define ssize_t intptr_t
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* contains name and value of a header (name == NULL if is a continuing line
|
|
||||||
* of a multiline header */
|
|
||||||
struct phr_header {
|
|
||||||
const char *name;
|
|
||||||
size_t name_len;
|
|
||||||
const char *value;
|
|
||||||
size_t value_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* returns number of bytes consumed if successful, -2 if request is partial,
|
|
||||||
* -1 if failed */
|
|
||||||
int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
|
|
||||||
|
|
||||||
/* ditto */
|
|
||||||
int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t last_len);
|
|
||||||
|
|
||||||
/* ditto */
|
|
||||||
int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
|
|
||||||
|
|
||||||
/* should be zero-filled before start */
|
|
||||||
struct phr_chunked_decoder {
|
|
||||||
size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
|
|
||||||
char consume_trailer; /* if trailing headers should be consumed */
|
|
||||||
char _hex_count;
|
|
||||||
char _state;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
|
|
||||||
* encoding headers. When the function returns without an error, bufsz is
|
|
||||||
* updated to the length of the decoded data available. Applications should
|
|
||||||
* repeatedly call the function while it returns -2 (incomplete) every time
|
|
||||||
* supplying newly arrived data. If the end of the chunked-encoded data is
|
|
||||||
* found, the function returns a non-negative number indicating the number of
|
|
||||||
* octets left undecoded at the tail of the supplied buffer. Returns -1 on
|
|
||||||
* error.
|
|
||||||
*/
|
|
||||||
ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
|
|
||||||
|
|
||||||
/* returns if the chunked decoder is in middle of chunked data */
|
|
||||||
int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue
Block a user