CProxy/httpdns.c

504 lines
15 KiB
C
Raw Normal View History

2020-06-20 16:59:51 +08:00
#include "httpdns.h"
2020-07-30 18:10:31 +08:00
#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;
int read_cache_file()
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
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;
2020-06-20 16:59:51 +08:00
}
//读取文件内容
2020-07-30 18:10:31 +08:00
fseek(cfp, 0, SEEK_END);
file_size = ftell(cfp);
if ((buff = (char *)alloca(file_size)) == NULL) {
fclose(cfp);
2020-06-20 16:59:51 +08:00
return 1;
}
2020-07-30 18:10:31 +08:00
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)
2020-06-20 16:59:51 +08:00
return 1;
2020-07-30 18:10:31 +08:00
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--;
}
2020-06-20 16:59:51 +08:00
}
}
2020-07-30 18:10:31 +08:00
fclose(cfp);
cfp = fopen(cachePath, "wb");
2020-06-20 16:59:51 +08:00
return 0;
}
2020-07-30 18:10:31 +08:00
void write_dns_cache()
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
while (cache) {
fputs(cache->answer, cfp);
fputs(cache->question, cfp);
fputc('\0', cfp);
cache = cache->next;
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
exit(0);
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
char *cache_lookup(char *question, dns_t * dns)
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
struct dns_cache *c;
2020-06-20 16:59:51 +08:00
2020-07-30 18:10:31 +08:00
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;
}
}
2020-06-20 16:59:51 +08:00
2020-07-30 18:10:31 +08:00
return NULL;
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
void cache_record(dns_t * dns)
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
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++;
}
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
int respond_client(dns_t * dns)
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
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;
2020-06-20 16:59:51 +08:00
}
}
2020-07-30 18:10:31 +08:00
void respond_clients()
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
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;
}
}
ev.events = EPOLLIN;
ev.data.fd = dnsListenFd;
epoll_ctl(dns_efd, EPOLL_CTL_MOD, dnsListenFd, &ev);
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
/* 分析DNS请求 */
int parse_dns_request(char *dns_req, dns_t * dns)
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
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;
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
dns->host[len++] = '.';
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
//printf("dns->host: %s\n", dns->host);
return 0;
default:
dns->host = NULL;
return 1;
2020-06-20 16:59:51 +08:00
}
}
2020-07-30 18:10:31 +08:00
/* 建立DNS回应 */
int build_dns_response(dns_t * dns)
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
char *p;
2020-06-20 16:59:51 +08:00
2020-07-30 18:10:31 +08:00
//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; //超出缓冲大小
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
/* 问题数 */
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;
//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);
ev.events = EPOLLIN | EPOLLET;
ev.data.ptr = out;
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;
2020-06-20 16:59:51 +08:00
}
}
2020-07-30 18:10:31 +08:00
void http_in(dns_t * in)
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
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);
2020-06-20 16:59:51 +08:00
return;
}
if (encodeCode)
2020-07-30 18:10:31 +08:00
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)
2020-06-20 16:59:51 +08:00
return;
2020-07-30 18:10:31 +08:00
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;
}
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
} while ((p = strchr(p, '\n')) != NULL);
2020-06-20 16:59:51 +08:00
2020-07-30 18:10:31 +08:00
error:
free(in->reply);
in->reply = NULL;
if (build_dns_response(in) == 1)
in->query_type = 0;
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
void new_client(conf *configure)
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
dns_t *dns;
int i, len;
2020-06-20 16:59:51 +08:00
2020-07-30 18:10:31 +08:00
for (i = 0; i < DNS_MAX_CONNECTION; i++)
if (dns_list[i].query_type == 0)
2020-06-20 16:59:51 +08:00
break;
2020-07-30 18:10:31 +08:00
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;
2020-06-20 16:59:51 +08:00
}
}
2020-07-30 18:10:31 +08:00
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;
2020-06-20 16:59:51 +08:00
return;
2020-07-30 18:10:31 +08:00
}
dns->fd = socket(AF_INET, SOCK_STREAM, 0);
if (dns->fd < 0) {
dns->query_type = 0;
2020-06-20 16:59:51 +08:00
return;
}
2020-07-30 18:10:31 +08:00
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;
2020-06-20 16:59:51 +08:00
return;
}
2020-07-30 18:10:31 +08:00
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);
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
void *httpdns_loop(void *p)
2020-06-20 16:59:51 +08:00
{
2020-07-30 18:10:31 +08:00
conf *configure = (conf *) p;
int n;
2020-06-20 16:59:51 +08:00
2020-07-30 18:10:31 +08:00
fcntl(dnsListenFd, F_SETFL, O_NONBLOCK);
dns_efd = epoll_create(DNS_MAX_CONNECTION + 1);
if (dns_efd < 0) {
2020-06-20 16:59:51 +08:00
perror("epoll_create");
2020-07-30 18:10:31 +08:00
return NULL;
2020-06-20 16:59:51 +08:00
}
2020-07-30 18:10:31 +08:00
ev.data.fd = dnsListenFd;
2020-06-20 16:59:51 +08:00
ev.events = EPOLLIN;
2020-07-30 18:10:31 +08:00
epoll_ctl(dns_efd, EPOLL_CTL_ADD, dnsListenFd, &ev);
2020-06-20 16:59:51 +08:00
memset(dns_list, 0, sizeof(dns_list));
while (1) {
2020-07-30 18:10:31 +08:00
n = epoll_wait(dns_efd, evs, DNS_MAX_CONNECTION + 1, -1);
2020-06-20 16:59:51 +08:00
while (n-- > 0) {
2020-07-30 18:10:31 +08:00
if (evs[n].data.fd == dnsListenFd) {
2020-06-20 16:59:51 +08:00
if (evs[n].events & EPOLLIN) {
2020-07-30 18:10:31 +08:00
new_client(configure);
}
if (evs[n].events & EPOLLOUT) {
respond_clients();
2020-06-20 16:59:51 +08:00
}
} else if (evs[n].events & EPOLLIN) {
2020-07-30 18:10:31 +08:00
http_in(evs[n].data.ptr);
2020-06-20 16:59:51 +08:00
} else if (evs[n].events & EPOLLOUT) {
2020-07-30 18:10:31 +08:00
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;
2020-06-20 16:59:51 +08:00
}
}
}
2020-07-30 18:10:31 +08:00
2020-06-20 16:59:51 +08:00
return NULL;
}
2020-07-30 18:10:31 +08:00
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;
}