CProxy/httpdns.c
aixiao c7d9d6ab03 Optimize gcc10 compilation error
Set the maximum number of open files per process
2020-09-09 21:55:48 +08:00

505 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "httpdns.h"
#include "http_request.h"
char http_rsp[HTTP_RSP_SIZE + 1];
struct sockaddr_in dst_addr;
char *host_value;
int dnsListenFd = -1, dns_efd;
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;
dns_t dns_list[DNS_MAX_CONNECTION];
struct epoll_event evs[DNS_MAX_CONNECTION+1], event;
int read_cache_file()
{
char *buff, *answer, *question;
long file_size;
cache = cache_temp = NULL;
cache_using = 0;
if ((cfp = fopen(cachePath, "rb+")) == NULL) {
//保持文件打开状态防止切换uid后权限不足导致无法写入文件
cfp = fopen(cachePath, "wb");
return cfp == NULL ? 1 : 0;
}
//读取文件内容
fseek(cfp, 0, SEEK_END);
file_size = ftell(cfp);
if ((buff = (char *)alloca(file_size)) == NULL) {
fclose(cfp);
return 1;
}
rewind(cfp);
fread(buff, file_size, 1, cfp);
//读取缓存,一组缓存的内容为[ipDomain\0]其中ip占5字节
for (answer = buff; answer - buff < file_size; answer = question + cache->question_len + 2) {
cache_temp = (struct dns_cache *)malloc(sizeof(*cache));
if (cache_temp == NULL)
return 1;
cache_temp->next = cache;
cache = cache_temp;
cache_using++;
cache->answer = strndup(answer, 5);
question = answer + 5;
cache->question = strdup(question);
if (cache->question == NULL || cache->answer == NULL)
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--;
}
}
}
fclose(cfp);
cfp = fopen(cachePath, "wb");
return 0;
}
void write_dns_cache()
{
while (cache) {
fputs(cache->answer, cfp);
fputs(cache->question, cfp);
fputc('\0', cfp);
cache = cache->next;
}
exit(0);
}
char *cache_lookup(char *question, dns_t * dns)
{
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;
}
void cache_record(dns_t * dns)
{
cache_temp = (struct dns_cache *)malloc(sizeof(*cache));
if (cache_temp == NULL)
return;
cache_temp->question = strdup(dns->dns_req + 12);
if (cache_temp->question == NULL) {
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++;
}
}
int respond_client(dns_t * dns)
{
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 == dns->dns_rsp_len) {
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
dns_list[i].wait_response_client = 0;
}
}
event.events = EPOLLIN;
event.data.fd = dnsListenFd;
epoll_ctl(dns_efd, EPOLL_CTL_MOD, dnsListenFd, &event);
}
/* 分析DNS请求 */
int parse_dns_request(char *dns_req, dns_t * dns)
{
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
case 1: //查询ipv4地址
dns->host = strdup(dns_req);
if (dns->host == NULL)
return 1;
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;
}
}
/* 建立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;
event.events = EPOLLIN | EPOLLOUT;
event.data.fd = dnsListenFd;
epoll_ctl(dns_efd, EPOLL_CTL_MOD, dnsListenFd, &event);
}
return 0;
}
void http_out(dns_t * out)
{
int write_len;
//puts("writing");
//printf("%s\n", out->http_request);
write_len = write(out->fd, out->http_request, out->http_request_len);
if (write_len == out->http_request_len) {
//puts("write success");
free(out->http_request);
event.events = EPOLLIN | EPOLLET;
event.data.ptr = out;
epoll_ctl(dns_efd, EPOLL_CTL_MOD, out->fd, &event);
} 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 http_in(dns_t * in)
{
char *ip_ptr, *p;
int len, i;
len = read(in->fd, http_rsp, HTTP_RSP_SIZE);
if (len <= 0) {
in->query_type = 0;
epoll_ctl(dns_efd, EPOLL_CTL_DEL, in->fd, NULL);
close(in->fd);
return;
}
if (encodeCode)
dataEncode(http_rsp, len);
http_rsp[len] = '\0';
//printf("[%s]\n", http_rsp);
p = strstr(http_rsp, "\n\r");
if (p) {
//部分代理服务器使用长连接第二次读取数据才读到域名的IP
if (p + 3 - http_rsp >= len)
return;
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 {
in->reply[3] = atoi(ip_ptr);
in->reply[4] = 0;
build_dns_response(in);
cfp ? cache_record(in) : free(in->reply);
return;
}
}
} while ((p = strchr(p, '\n')) != NULL);
error:
free(in->reply);
in->reply = NULL;
if (build_dns_response(in) == 1)
in->query_type = 0;
}
void new_client(conf *configure)
{
dns_t *dns;
int i, len;
for (i = 0; i < DNS_MAX_CONNECTION; i++)
if (dns_list[i].query_type == 0)
break;
if (i == DNS_MAX_CONNECTION)
return;
dns = &dns_list[i];
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;
}
}
if (parse_dns_request(dns->dns_req, dns) != 0) {
if (dns->host == NULL) {
if (build_dns_response(dns) != 0)
dns->query_type = 0;
} else
dns->query_type = 0;
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);
event.events = EPOLLOUT | EPOLLERR | EPOLLET;
event.data.ptr = dns;
epoll_ctl(dns_efd, EPOLL_CTL_ADD, dns->fd, &event);
}
void *httpdns_loop(void *p)
{
conf *configure = (conf *) p;
int n;
fcntl(dnsListenFd, F_SETFL, O_NONBLOCK);
dns_efd = epoll_create(DNS_MAX_CONNECTION + 1);
if (dns_efd < 0) {
perror("epoll_create");
return NULL;
}
event.data.fd = dnsListenFd;
event.events = EPOLLIN;
epoll_ctl(dns_efd, EPOLL_CTL_ADD, dnsListenFd, &event);
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;
}