Support DNS Over HTTPS

This commit is contained in:
Nick Peng
2019-03-30 20:10:27 +08:00
parent 5501bfb6a3
commit 2d01ed8a04
21 changed files with 1264 additions and 88 deletions

View File

@@ -1,7 +1,7 @@
BIN=smartdns
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o
OBJS=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o $(OBJS_LIB)
OBJS=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o dns_conf.o dns_cache.o http_parse.o $(OBJS_LIB)
CFLAGS +=-O2 -g -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing
CFLAGS +=-Iinclude
CFLAGS += -DBASE_FILE_NAME=\"$(notdir $<)\"

View File

@@ -22,6 +22,7 @@
#include "dns_conf.h"
#include "fast_ping.h"
#include "hashtable.h"
#include "http_parse.h"
#include "list.h"
#include "tlog.h"
#include "util.h"
@@ -96,9 +97,6 @@ struct dns_server_info {
/* server type */
dns_server_type_t type;
unsigned char *spki;
int spki_len;
/* client socket */
int fd;
int ttl;
@@ -107,7 +105,6 @@ struct dns_server_info {
SSL_CTX *ssl_ctx;
SSL_SESSION *ssl_session;
dns_server_status status;
unsigned int result_flag;
struct dns_server_buff send_buff;
struct dns_server_buff recv_buff;
@@ -124,6 +121,8 @@ struct dns_server_info {
struct sockaddr_in6 in6;
struct sockaddr addr;
};
struct client_dns_server_flags flags;
};
/* upstream server group member */
@@ -298,8 +297,9 @@ static struct dns_server_info *_dns_client_get_server(char *server_ip, int port,
case DNS_SERVER_UDP:
sock_type = SOCK_DGRAM;
break;
case DNS_SERVER_TLS:
case DNS_SERVER_TCP:
case DNS_SERVER_TLS:
case DNS_SERVER_HTTPS:
sock_type = SOCK_STREAM;
break;
default:
@@ -572,26 +572,88 @@ static void _dns_client_group_remove_all(void)
}
}
int dns_client_spki_decode(const char *spki, unsigned char *spki_data_out)
{
int spki_data_len = -1;
spki_data_len = SSL_base64_decode(spki, spki_data_out);
if (spki_data_len != SHA256_DIGEST_LENGTH) {
return -1;
}
return spki_data_len;
}
static char *_dns_client_server_get_spki(struct dns_server_info *server_info, int *spki_len)
{
*spki_len = 0;
char *spki = NULL;
switch (server_info->type) {
case DNS_SERVER_UDP: {
} break;
case DNS_SERVER_HTTPS: {
struct client_dns_server_flag_https *flag_https = &server_info->flags.https;
spki = flag_https->spki;
*spki_len = flag_https->spi_len;
} break;
case DNS_SERVER_TLS: {
struct client_dns_server_flag_tls *flag_tls = &server_info->flags.tls;
spki = flag_tls->spki;
*spki_len = flag_tls->spi_len;
} break;
break;
case DNS_SERVER_TCP:
break;
default:
return NULL;
break;
}
if (*spki_len <= 0) {
return NULL;
}
return spki;
}
/* add dns server information */
static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, unsigned int server_flag, unsigned int result_flag,
int ttl, char *spki)
static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, struct client_dns_server_flags *flags)
{
struct dns_server_info *server_info = NULL;
unsigned char *spki_data = NULL;
int spki_data_len = 0;
int ttl = 0;
/* read SPKI value, base64 sha256 value */
if (spki && (strlen(spki) < DNS_MAX_SPKI_LEN)) {
spki_data = malloc(DNS_MAX_SPKI_LEN);
if (spki_data) {
memset(spki_data, 0, DNS_MAX_SPKI_LEN);
spki_data_len = SSL_base64_decode(spki, spki_data);
if (spki_data_len != SHA256_DIGEST_LENGTH) {
free(spki_data);
spki_data = NULL;
spki_data_len = 0;
}
switch (server_type) {
case DNS_SERVER_UDP: {
struct client_dns_server_flag_udp *flag_udp = &flags->udp;
ttl = flag_udp->ttl;
if (ttl > 255) {
ttl = 255;
} else if (ttl < -32) {
ttl = -32;
}
} break;
case DNS_SERVER_HTTPS: {
struct client_dns_server_flag_https *flag_https = &flags->https;
spki_data_len = flag_https->spi_len;
} break;
case DNS_SERVER_TLS: {
struct client_dns_server_flag_tls *flag_tls = &flags->tls;
spki_data_len = flag_tls->spi_len;
} break;
break;
case DNS_SERVER_TCP:
break;
default:
return -1;
break;
}
if (spki_data_len > DNS_SERVER_SPKI_LEN) {
tlog(TLOG_ERROR, "spki data length is invalid.");
return -1;
}
/* if server exist, return */
@@ -605,7 +667,7 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
}
if (server_type != DNS_SERVER_UDP) {
result_flag &= (~DNSSERVER_FLAG_CHECK_TTL);
flags->result_flag &= (~DNSSERVER_FLAG_CHECK_TTL);
}
memset(server_info, 0, sizeof(*server_info));
@@ -615,14 +677,12 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
server_info->type = server_type;
server_info->fd = 0;
server_info->status = DNS_SERVER_STATUS_INIT;
server_info->result_flag = result_flag;
server_info->ttl = ttl;
server_info->ttl_range = 0;
server_info->spki = spki_data;
server_info->spki_len = spki_data_len;
memcpy(&server_info->flags, flags, sizeof(server_info->flags));
/* exclude this server from default group */
if ((server_flag & SERVER_FLAG_EXCLUDE_DEFAULT) == 0) {
if ((server_info->flags.server_flag & SERVER_FLAG_EXCLUDE_DEFAULT) == 0) {
if (_dns_client_add_to_group(DNS_SERVER_GROUP_DEFAULT, server_info) != 0) {
tlog(TLOG_ERROR, "add server to default group failed.");
goto errout;
@@ -630,7 +690,7 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
}
/* if server type is TLS, create ssl context */
if (server_type == DNS_SERVER_TLS) {
if (server_type == DNS_SERVER_TLS || server_type == DNS_SERVER_HTTPS) {
server_info->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (server_info->ssl_ctx == NULL) {
tlog(TLOG_ERROR, "init ssl failed.");
@@ -646,15 +706,17 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
memcpy(&server_info->addr, gai->ai_addr, gai->ai_addrlen);
/* start ping task */
if (ttl <= 0 && (result_flag & DNSSERVER_FLAG_CHECK_TTL)) {
server_info->ping_host = fast_ping_start(PING_TYPE_DNS, server_ip, 0, 60000, 1000, _dns_client_server_update_ttl, server_info);
if (server_info->ping_host == NULL) {
tlog(TLOG_ERROR, "start ping failed.");
goto errout;
}
if (server_type == DNS_SERVER_UDP) {
if (ttl <= 0 && (server_info->flags.result_flag & DNSSERVER_FLAG_CHECK_TTL)) {
server_info->ping_host = fast_ping_start(PING_TYPE_DNS, server_ip, 0, 60000, 1000, _dns_client_server_update_ttl, server_info);
if (server_info->ping_host == NULL) {
tlog(TLOG_ERROR, "start ping failed.");
goto errout;
}
if (ttl < 0) {
server_info->ttl_range = -ttl;
if (ttl < 0) {
server_info->ttl_range = -ttl;
}
}
}
@@ -741,10 +803,6 @@ static void _dns_client_server_remove_all(void)
{
list_del(&server_info->list);
_dns_client_server_close(server_info);
if (server_info->spki) {
free(server_info->spki);
server_info->spki = NULL;
}
free(server_info);
}
pthread_mutex_unlock(&client.server_list_lock);
@@ -779,8 +837,7 @@ static int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_
return -1;
}
static int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, unsigned int server_flag, unsigned int result_flag, int ttl,
char *spki, int operate)
static int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags, int operate)
{
char port_s[8];
int sock_type;
@@ -796,8 +853,9 @@ static int _dns_client_server_operate(char *server_ip, int port, dns_server_type
case DNS_SERVER_UDP:
sock_type = SOCK_DGRAM;
break;
case DNS_SERVER_TLS:
case DNS_SERVER_TCP:
case DNS_SERVER_TLS:
case DNS_SERVER_HTTPS:
sock_type = SOCK_STREAM;
break;
default:
@@ -815,7 +873,7 @@ static int _dns_client_server_operate(char *server_ip, int port, dns_server_type
if (operate == 0) {
/* add server */
ret = _dns_client_server_add(server_ip, gai, server_type, server_flag, result_flag, ttl, spki);
ret = _dns_client_server_add(server_ip, gai, server_type, flags);
if (ret != 0) {
goto errout;
}
@@ -835,14 +893,14 @@ errout:
return -1;
}
int dns_client_add_server(char *server_ip, int port, dns_server_type_t server_type, unsigned server_flag, unsigned int result_flag, int ttl, char *spki)
int dns_client_add_server(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags)
{
return _dns_client_server_operate(server_ip, port, server_type, server_flag, result_flag, ttl, spki, 0);
return _dns_client_server_operate(server_ip, port, server_type, flags, 0);
}
int dns_client_remove_server(char *server_ip, int port, dns_server_type_t server_type)
{
return _dns_client_server_operate(server_ip, port, server_type, 0, 0, 0, NULL, 1);
return _dns_client_server_operate(server_ip, port, server_type, NULL, 1);
}
int dns_server_num(void)
@@ -1101,7 +1159,7 @@ static int _dns_client_recv(struct dns_server_info *server_info, unsigned char *
/* get query reference */
query = _dns_client_get_request(packet->head.id, domain);
if (query == NULL || (query && has_opt == 0 && server_info->result_flag & DNSSERVER_FLAG_CHECK_EDNS)) {
if (query == NULL || (query && has_opt == 0 && server_info->flags.result_flag & DNSSERVER_FLAG_CHECK_EDNS)) {
if (query) {
_dns_client_query_release(query);
}
@@ -1123,7 +1181,7 @@ static int _dns_client_recv(struct dns_server_info *server_info, unsigned char *
/* notify caller dns query result */
if (query->callback) {
ret = query->callback(query->domain, DNS_QUERY_RESULT, server_info->result_flag, packet, inpacket, inpacket_len, query->user_ptr);
ret = query->callback(query->domain, DNS_QUERY_RESULT, server_info->flags.result_flag, packet, inpacket, inpacket_len, query->user_ptr);
if (request_num == 0 || ret) {
/* if all server replied, or done, stop query, release resource */
_dns_client_query_remove(query);
@@ -1326,7 +1384,7 @@ static int _dns_client_create_socket(struct dns_server_info *server_info)
return _dns_client_create_socket_udp(server_info);
} else if (server_info->type == DNS_SERVER_TCP) {
return _DNS_client_create_socket_tcp(server_info);
} else if (server_info->type == DNS_SERVER_TLS) {
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS) {
return _DNS_client_create_socket_tls(server_info);
} else {
return -1;
@@ -1382,7 +1440,7 @@ static int _dns_client_process_udp(struct dns_server_info *server_info, struct e
tlog(TLOG_DEBUG, "recv udp packet from %s, len: %d, ttl: %d", gethost_by_addr(from_host, sizeof(from_host), (struct sockaddr *)&from), len, ttl);
if ((ttl != server_info->ttl) && (server_info->ttl > 0) && (server_info->result_flag & DNSSERVER_FLAG_CHECK_TTL)) {
if ((ttl != server_info->ttl) && (server_info->ttl > 0) && (server_info->flags.result_flag & DNSSERVER_FLAG_CHECK_TTL)) {
/* If TTL check is enabled but the TTL is inconsistent, it is considered to be a fake dns packet */
if ((ttl < server_info->ttl - server_info->ttl_range) || (ttl > server_info->ttl + server_info->ttl_range)) {
/* tlog(TLOG_DEBUG, "TTL mismatch, from:%d, local %d, discard result", ttl, server_info->ttl); */
@@ -1521,7 +1579,7 @@ static int _dns_client_socket_send(struct dns_server_info *server_info)
return -1;
} else if (server_info->type == DNS_SERVER_TCP) {
return send(server_info->fd, server_info->send_buff.data, server_info->send_buff.len, MSG_NOSIGNAL);
} else if (server_info->type == DNS_SERVER_TLS) {
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS) {
return _dns_client_socket_ssl_send(server_info->ssl, server_info->send_buff.data, server_info->send_buff.len);
} else {
return -1;
@@ -1534,7 +1592,7 @@ static int _dns_client_socket_recv(struct dns_server_info *server_info)
return -1;
} else if (server_info->type == DNS_SERVER_TCP) {
return recv(server_info->fd, server_info->recv_buff.data + server_info->recv_buff.len, DNS_TCP_BUFFER - server_info->recv_buff.len, 0);
} else if (server_info->type == DNS_SERVER_TLS) {
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS) {
return _dns_client_socket_ssl_recv(server_info->ssl, server_info->recv_buff.data + server_info->recv_buff.len,
DNS_TCP_BUFFER - server_info->recv_buff.len);
} else {
@@ -1545,7 +1603,9 @@ static int _dns_client_socket_recv(struct dns_server_info *server_info)
static int _dns_client_process_tcp(struct dns_server_info *server_info, struct epoll_event *event, unsigned long now)
{
int len;
int dns_packet_len = 0;
int ret = -1;
struct http_head *http_head = NULL;
unsigned char *inpacket_data = NULL;
if (event->events & EPOLLIN) {
@@ -1591,29 +1651,61 @@ static int _dns_client_process_tcp(struct dns_server_info *server_info, struct e
}
while (1) {
/* tcp result format
* | len (short) | dns query result |
*/
inpacket_data = server_info->recv_buff.data;
len = ntohs(*((unsigned short *)(inpacket_data)));
if (len <= 0 || len >= DNS_IN_PACKSIZE) {
/* data len is invalid */
goto errout;
}
if (server_info->type == DNS_SERVER_HTTPS) {
http_head = http_head_init(4096);
if (http_head == NULL) {
goto errout;
}
if (len > server_info->recv_buff.len - 2) {
/* len is not expceded, wait and recv */
break;
}
len = http_head_parse(http_head, (char *)server_info->recv_buff.data, server_info->recv_buff.len);
if (len < 0) {
http_head_destroy(http_head);
if (len == -1) {
break;
}
goto errout;
}
inpacket_data = server_info->recv_buff.data + 2;
if (http_head_get_httpcode(http_head) != 200) {
tlog(TLOG_WARN, "http server query failed, server return http code : %d, %s", http_head_get_httpcode(http_head),
http_head_get_httpcode_msg(http_head));
goto errout;
}
dns_packet_len = http_head_get_data_len(http_head);
inpacket_data = (unsigned char *)http_head_get_data(http_head);
} else {
/* tcp result format
* | len (short) | dns query result |
*/
inpacket_data = server_info->recv_buff.data;
len = ntohs(*((unsigned short *)(inpacket_data)));
if (len <= 0 || len >= DNS_IN_PACKSIZE) {
/* data len is invalid */
goto errout;
}
if (len > server_info->recv_buff.len - 2) {
/* len is not expceded, wait and recv */
break;
}
inpacket_data = server_info->recv_buff.data + 2;
dns_packet_len = len;
len += 2;
}
tlog(TLOG_DEBUG, "recv tcp packet from %s, len = %d", server_info->ip, len);
/* process result */
if (_dns_client_recv(server_info, inpacket_data, len, &server_info->addr, server_info->ai_addrlen) != 0) {
if (_dns_client_recv(server_info, inpacket_data, dns_packet_len, &server_info->addr, server_info->ai_addrlen) != 0) {
goto errout;
}
len += 2;
if (http_head) {
http_head_destroy(http_head);
http_head = NULL;
}
server_info->recv_buff.len -= len;
/* move to next result */
@@ -1677,6 +1769,10 @@ static int _dns_client_process_tcp(struct dns_server_info *server_info, struct e
return 0;
errout:
if (http_head) {
http_head_destroy(http_head);
}
pthread_mutex_lock(&client.server_list_lock);
server_info->recv_buff.len = 0;
server_info->send_buff.len = 0;
@@ -1705,6 +1801,8 @@ static int _dns_client_tls_verify(struct dns_server_info *server_info)
unsigned char *key_data = NULL;
unsigned char *key_data_tmp = NULL;
unsigned char *key_sha256 = NULL;
char *spki = NULL;
int spki_len = 0;
cert = SSL_get_peer_certificate(server_info->ssl);
if (cert == NULL) {
@@ -1751,9 +1849,10 @@ static int _dns_client_tls_verify(struct dns_server_info *server_info)
*ptr = 0;
tlog(TLOG_DEBUG, "cert SPKI pin(%s): %s", "sha256", cert_fingerprint);
if (server_info->spki) {
spki = _dns_client_server_get_spki(server_info, &spki_len);
if (spki) {
/* check SPKI */
if (memcmp(server_info->spki, key_sha256, server_info->spki_len) != 0) {
if (memcmp(spki, key_sha256, spki_len) != 0) {
tlog(TLOG_INFO, "server %s cert spki is invalid", server_info->ip);
goto errout;
} else {
@@ -1858,7 +1957,7 @@ static int _dns_client_process(struct dns_server_info *server_info, struct epoll
} else if (server_info->type == DNS_SERVER_TCP) {
/* receive from tcp */
return _dns_client_process_tcp(server_info, event, now);
} else if (server_info->type == DNS_SERVER_TLS) {
} else if (server_info->type == DNS_SERVER_TLS || server_info->type == DNS_SERVER_HTTPS) {
/* recive from tls */
return _dns_client_process_tls(server_info, event, now);
} else {
@@ -1992,6 +2091,56 @@ static int _dns_client_send_tls(struct dns_server_info *server_info, void *packe
return 0;
}
static int _dns_client_send_https(struct dns_server_info *server_info, void *packet, unsigned short len)
{
int send_len = 0;
int http_len = 0;
unsigned char inpacket_data[DNS_IN_PACKSIZE];
unsigned char *inpacket = inpacket_data;
struct client_dns_server_flag_https *https_flag = NULL;
if (len > sizeof(inpacket_data) - 2) {
tlog(TLOG_ERROR, "packet size is invalid.");
return -1;
}
https_flag = &server_info->flags.https;
http_len = snprintf((char *)inpacket, DNS_IN_PACKSIZE,
"POST %s HTTP/1.1\r\n"
"Host: %s\r\n"
"content-type: application/dns-message\r\n"
"Content-Length: %d\r\n"
"\r\n",
https_flag->path, https_flag->host, len);
memcpy(inpacket + http_len, packet, len);
http_len += len;
if (server_info->status != DNS_SERVER_STATUS_CONNECTED) {
return _dns_client_send_data_to_buffer(server_info, inpacket, http_len);
}
if (server_info->ssl == NULL) {
return -1;
}
send_len = _dns_client_socket_ssl_send(server_info->ssl, inpacket, http_len);
if (send_len < 0) {
if (errno == EAGAIN || server_info->ssl == NULL) {
/* save data to buffer, and retry when EPOLLOUT is available */
return _dns_client_send_data_to_buffer(server_info, inpacket, http_len);
} else if (server_info->ssl && errno != ENOMEM) {
SSL_shutdown(server_info->ssl);
}
return -1;
} else if (send_len < http_len) {
/* save remain data to buffer, and retry when EPOLLOUT is available */
return _dns_client_send_data_to_buffer(server_info, inpacket + send_len, http_len - send_len);
}
return 0;
}
static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, int len)
{
struct dns_server_info *server_info = NULL;
@@ -2031,6 +2180,11 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet,
ret = _dns_client_send_tls(server_info, packet, len);
send_err = errno;
break;
case DNS_SERVER_HTTPS:
/* https query */
ret = _dns_client_send_https(server_info, packet, len);
send_err = errno;
break;
default:
/* unsupport query type */
ret = -1;

View File

@@ -2,6 +2,7 @@
#define _SMART_DNS_CLIENT_H
#include "dns.h"
#define DNS_SERVER_SPKI_LEN 64
#define DNS_SERVER_GROUP_DEFAULT "default"
typedef enum {
@@ -35,8 +36,39 @@ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void
void dns_client_exit(void);
struct client_dns_server_flag_udp {
int ttl;
};
struct client_dns_server_flag_tls {
char spki[DNS_SERVER_SPKI_LEN];
int spi_len;
char host[DNS_MAX_CNAME_LEN];
};
struct client_dns_server_flag_https {
char spki[DNS_SERVER_SPKI_LEN];
int spi_len;
char host[DNS_MAX_CNAME_LEN];
char path[DNS_MAX_CNAME_LEN];
};
struct client_dns_server_flags {
dns_server_type_t type;
unsigned int server_flag;
unsigned int result_flag;
union {
struct client_dns_server_flag_udp udp;
struct client_dns_server_flag_tls tls;
struct client_dns_server_flag_https https;
};
};
int dns_client_spki_decode(const char *spki, unsigned char *spki_data_out);
/* add remote dns server */
int dns_client_add_server(char *server_ip, int port, dns_server_type_t server_type, unsigned int server_flag, unsigned int result_flag, int ttl, char *spki);
int dns_client_add_server(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags);
/* remove remote dns server */
int dns_client_remove_server(char *server_ip, int port, dns_server_type_t server_type);

View File

@@ -68,6 +68,8 @@ int dns_conf_ipset_timeout_enable;
struct dns_edns_client_subnet dns_conf_ipv4_ecs;
struct dns_edns_client_subnet dns_conf_ipv6_ecs;
char dns_conf_sni_proxy_ip[DNS_MAX_IPLEN];
/* create and get dns server group */
static struct dns_server_groups *_dns_conf_get_group(const char *group_name)
{
@@ -186,11 +188,24 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
server = &dns_conf_servers[index];
server->spki[0] = '\0';
server->path[0] = '\0';
server->hostname[0] = '\0';
ip = argv[1];
/* parse ip, port from ip */
if (parse_ip(ip, server->server, &port) != 0) {
return -1;
if (type == DNS_SERVER_HTTPS) {
if (parse_uri(ip, NULL, server->server, &port, server->path) != 0) {
return -1;
}
strncpy(server->hostname, server->server, sizeof(server->hostname));
if (server->path[0] == 0) {
strcpy(server->path, "/");
}
} else {
/* parse ip, port from ip */
if (parse_ip(ip, server->server, &port) != 0) {
return -1;
}
}
/* if port is not defined, set port to default 53 */
@@ -706,6 +721,14 @@ static int _config_server_tls(void *data, int argc, char *argv[])
return _config_server(argc, argv, DNS_SERVER_TLS, DEFAULT_DNS_TLS_PORT);
}
static int _config_server_https(void *data, int argc, char *argv[])
{
int ret = 0;
ret = _config_server(argc, argv, DNS_SERVER_HTTPS, DEFAULT_DNS_HTTPS_PORT);
return ret;
}
static int _config_nameserver(void *data, int argc, char *argv[])
{
struct dns_nameserver_rule *nameserver_rule = NULL;
@@ -954,6 +977,7 @@ static struct config_item _config_item[] = {
CONF_CUSTOM("server", _config_server_udp, NULL),
CONF_CUSTOM("server-tcp", _config_server_tcp, NULL),
CONF_CUSTOM("server-tls", _config_server_tls, NULL),
CONF_CUSTOM("server-https", _config_server_https, NULL),
CONF_CUSTOM("nameserver", _config_nameserver, NULL),
CONF_CUSTOM("address", _config_address, NULL),
CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable),

View File

@@ -16,9 +16,11 @@
#define DNS_NAX_GROUP_NUMBER 16
#define DNS_MAX_IPLEN 64
#define DNS_MAX_SPKI_LEN 64
#define DNS_MAX_URL_LEN 256
#define DNS_MAX_PATH 1024
#define DEFAULT_DNS_PORT 53
#define DEFAULT_DNS_TLS_PORT 853
#define DEFAULT_DNS_HTTPS_PORT 443
#define DNS_MAX_CONF_CNAME_LEN 128
#define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf"
#define SMARTDNS_LOG_FILE "/var/log/smartdns.log"
@@ -93,6 +95,8 @@ struct dns_servers {
int ttl;
dns_server_type_t type;
char spki[DNS_MAX_SPKI_LEN];
char hostname[DNS_MAX_CNAME_LEN];
char path[DNS_MAX_URL_LEN];
};
/* ip address lists of domain */
@@ -131,6 +135,7 @@ struct dns_conf_address_rule {
extern char dns_conf_server_ip[DNS_MAX_IPLEN];
extern char dns_conf_server_tcp_ip[DNS_MAX_IPLEN];
extern int dns_conf_tcp_idle_time;
extern int dns_conf_cachesize;
extern int dns_conf_prefetch;
@@ -166,6 +171,8 @@ extern int dns_conf_ipset_timeout_enable;
extern struct dns_edns_client_subnet dns_conf_ipv4_ecs;
extern struct dns_edns_client_subnet dns_conf_ipv6_ecs;
extern char dns_conf_sni_proxy_ip[DNS_MAX_IPLEN];
void dns_server_load_exit(void);
int dns_server_load_conf(const char *file);

View File

@@ -119,6 +119,9 @@ struct dns_request {
struct sockaddr addr;
};
dns_result_callback result_callback;
void *user_ptr;
int has_ping_result;
int has_ping_tcp;
int has_ptr;
@@ -522,6 +525,45 @@ static int _dns_setup_ipset(struct dns_request *request)
return ret;
}
static int _dns_result_callback(struct dns_request *request)
{
char ip[DNS_MAX_CNAME_LEN];
if (request->result_callback == NULL) {
return 0;
}
ip[0] = 0;
if (request->qtype == DNS_T_A) {
if (request->has_ipv4 == 0) {
goto out;
}
sprintf(ip, "%d.%d.%d.%d", request->ipv4_addr[0], request->ipv4_addr[1], request->ipv4_addr[2], request->ipv4_addr[3]);
return request->result_callback(request->domain, request->rcode, request->qtype, ip, request->user_ptr);
} else if (request->qtype == DNS_T_AAAA) {
if (request->has_ipv6 == 0) {
goto out;
}
sprintf(ip, "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
request->ipv6_addr[0], request->ipv6_addr[1], request->ipv6_addr[2], request->ipv6_addr[3], request->ipv6_addr[4], request->ipv6_addr[5],
request->ipv6_addr[6], request->ipv6_addr[7], request->ipv6_addr[8], request->ipv6_addr[9], request->ipv6_addr[10], request->ipv6_addr[11],
request->ipv6_addr[12], request->ipv6_addr[13], request->ipv6_addr[14], request->ipv6_addr[15]);
return request->result_callback(request->domain, request->rcode, request->qtype, ip, request->user_ptr);
}
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, request->user_ptr);
return 0;
out:
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, request->user_ptr);
return 0;
}
static int _dns_server_request_complete(struct dns_request *request)
{
char *cname = NULL;
@@ -610,6 +652,8 @@ static int _dns_server_request_complete(struct dns_request *request)
/* update ipset */
_dns_setup_ipset(request);
_dns_result_callback(request);
if (request->prefetch) {
return 0;
}
@@ -1514,7 +1558,13 @@ static int _dns_server_process_cache(struct dns_request *request, struct dns_pac
}
request->rcode = DNS_RC_NOERROR;
_dns_reply(request);
_dns_result_callback(request);
if (request->prefetch == 0) {
_dns_reply(request);
}
dns_cache_update(dns_cache);
dns_cache_release(dns_cache);
@@ -1781,6 +1831,68 @@ static int _dns_server_process_udp(struct dns_server_conn *dnsserver, struct epo
return _dns_server_recv(dnsserver, inpacket, len, &from, from_len);
}
int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr)
{
int ret = -1;
struct dns_request *request = NULL;
const char *group_name = NULL;
request = malloc(sizeof(*request));
if (request == NULL) {
tlog(TLOG_ERROR, "malloc failed.\n");
goto errout;
}
memset(request, 0, sizeof(*request));
pthread_mutex_init(&request->ip_map_lock, NULL);
atomic_set(&request->adblock, 0);
request->ping_ttl_v4 = -1;
request->ping_ttl_v6 = -1;
request->prefetch = 1;
request->qtype = qtype;
request->rcode = DNS_RC_SERVFAIL;
request->result_callback = callback;
request->user_ptr = user_ptr;
request->id = 0;
hash_init(request->ip_map);
strncpy(request->domain, domain, DNS_MAX_CNAME_LEN);
/* lookup domain rule */
request->domain_rule = _dns_server_get_domain_rule(request->domain);
tlog(TLOG_INFO, "query domain %s, qtype = %d\n", request->domain, qtype);
/* process cache */
if (_dns_server_process_cache(request, NULL) == 0) {
ret = 0;
goto clean_exit;
}
_dns_server_request_get(request);
pthread_mutex_lock(&server.request_list_lock);
list_add_tail(&request->list, &server.request_list);
pthread_mutex_unlock(&server.request_list_lock);
_dns_server_request_get(request);
request->send_tick = get_tick_count();
request->request_wait++;
if (request->domain_rule) {
/* get nameserver rule */
if (request->domain_rule->rules[DOMAIN_RULE_NAMESERVER]) {
struct dns_nameserver_rule *nameserver_rule = request->domain_rule->rules[DOMAIN_RULE_NAMESERVER];
group_name = nameserver_rule->group_name;
}
}
/* send request */
ret = dns_client_query(request->domain, qtype, dns_server_resolve_callback, request, group_name);
clean_exit:
return ret;
errout:
return ret;
}
static void _dns_server_client_touch(struct dns_server_conn *client)
{
time(&client->last_request_time);

View File

@@ -1,6 +1,8 @@
#ifndef _SMART_DNS_SERVER_H
#define _SMART_DNS_SERVER_H
#include "dns.h"
#ifdef __cpluscplus
extern "C" {
#endif
@@ -15,6 +17,12 @@ void dns_server_stop(void);
void dns_server_exit(void);
/* query result notify function */
typedef int (*dns_result_callback)(char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip, void *user_ptr);
/* query domain */
int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr);
#ifdef __cpluscplus
}
#endif

445
src/http_parse.c Normal file
View File

@@ -0,0 +1,445 @@
#include "http_parse.h"
#include "hash.h"
#include "hashtable.h"
#include "jhash.h"
#include "list.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct http_head_fields {
struct hlist_node node;
struct list_head list;
char *name;
char *value;
};
struct http_head {
HTTP_HEAD_TYPE head_type;
HTTP_METHOD method;
char *url;
char *version;
int code;
char *code_msg;
int buff_size;
int buff_len;
char *buff;
int head_ok;
int head_len;
char *data;
int data_len;
int expect_data_len;
struct http_head_fields field_head;
DECLARE_HASHTABLE(field_map, 4);
};
/*
* Returns:
* >=0 - success http data len
* -1 - Incomplete request
* -2 - parse failed
*/
struct http_head *http_head_init(int buffsize)
{
struct http_head *http_head = NULL;
char *buffer = NULL;
http_head = malloc(sizeof(*http_head));
if (http_head == NULL) {
goto errout;
}
memset(http_head, 0, sizeof(*http_head));
INIT_LIST_HEAD(&http_head->field_head.list);
hash_init(http_head->field_map);
buffer = malloc(buffsize);
if (buffer == NULL) {
goto errout;
}
http_head->buff = buffer;
http_head->buff_size = buffsize;
return http_head;
errout:
if (buffer) {
free(buffer);
}
if (http_head) {
free(http_head);
}
return NULL;
}
struct http_head_fields *http_head_first_fields(struct http_head *http_head)
{
struct http_head_fields *first = NULL;
first = list_first_entry(&http_head->field_head.list, struct http_head_fields, list);
if (first->name == NULL && first->value == NULL) {
return NULL;
}
return first;
}
const char *http_head_get_fields_value(struct http_head *http_head, const char *name)
{
unsigned long key;
struct http_head_fields *filed;
key = hash_string(name);
hash_for_each_possible(http_head->field_map, filed, node, key)
{
if (strncmp(filed->name, name, 128) == 0) {
return filed->value;
}
}
return NULL;
}
struct http_head_fields *http_head_next_fields(struct http_head_fields *fields)
{
struct http_head_fields *next = NULL;
next = list_next_entry(fields, list);
if (next->name == NULL && next->value == NULL) {
return NULL;
}
return next;
}
int http_head_lookup_fields(struct http_head_fields *fields, const char **name, const char **value)
{
if (fields == NULL) {
return -1;
}
if (name) {
*name = fields->name;
}
if (value) {
*value = fields->value;
}
return 0;
}
HTTP_METHOD http_head_get_method(struct http_head *http_head)
{
return http_head->method;
}
const char *http_head_get_url(struct http_head *http_head)
{
return http_head->url;
}
const char *http_head_get_httpversion(struct http_head *http_head)
{
return http_head->version;
}
int http_head_get_httpcode(struct http_head *http_head)
{
return http_head->code;
}
char *http_head_get_httpcode_msg(struct http_head *http_head)
{
return http_head->code_msg;
}
HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head)
{
return http_head->head_type;
}
char *http_head_get_data(struct http_head *http_head)
{
return http_head->data;
}
int http_head_get_data_len(struct http_head *http_head)
{
return http_head->data_len;
}
static int _http_head_add_fields(struct http_head *http_head, char *name, char *value)
{
unsigned long key = 0;
struct http_head_fields *fields = NULL;
fields = malloc(sizeof(*fields));
if (fields == NULL) {
return -1;
}
memset(fields, 0, sizeof(*fields));
fields->name = name;
fields->value = value;
list_add_tail(&fields->list, &http_head->field_head.list);
key = hash_string(name);
hash_add(http_head->field_map, &fields->node, key);
return 0;
}
static int _http_head_parse_response(struct http_head *http_head, char *key, char *value)
{
char *field_start = NULL;
char *tmp_ptr = NULL;
char *result = NULL;
char *ret_code = NULL;
if (strstr(key, "HTTP/") == NULL) {
return -1;
}
for (tmp_ptr = value; *tmp_ptr != 0; tmp_ptr++) {
if (field_start == NULL) {
field_start = tmp_ptr;
}
if (*tmp_ptr == ' ') {
*tmp_ptr = '\0';
if (ret_code == NULL) {
ret_code = field_start;
} else if (result == NULL) {
result = field_start;
break;
}
field_start = NULL;
}
}
if (field_start && result == NULL) {
result = field_start;
}
if (ret_code == NULL || result == NULL) {
return -1;
}
http_head->code = atol(ret_code);
http_head->code_msg = result;
http_head->version = key;
http_head->head_type = HTTP_HEAD_RESPONSE;
return 0;
}
static int _http_head_parse_request(struct http_head *http_head, char *key, char *value)
{
int method = HTTP_METHOD_INVALID;
char *url = NULL;
char *version = NULL;
char *tmp_ptr = value;
char *field_start = NULL;
if (strncmp(key, "GET", sizeof("GET")) == 0) {
method = HTTP_METHOD_GET;
} else if (strncmp(key, "POST", sizeof("POST")) == 0) {
method = HTTP_METHOD_POST;
} else if (strncmp(key, "PUT", sizeof("PUT")) == 0) {
method = HTTP_METHOD_PUT;
} else if (strncmp(key, "DELETE", sizeof("DELETE")) == 0) {
method = HTTP_METHOD_DELETE;
} else if (strncmp(key, "TRACE", sizeof("TRACE")) == 0) {
method = HTTP_METHOD_TRACE;
} else if (strncmp(key, "CONNECT", sizeof("CONNECT")) == 0) {
method = HTTP_METHOD_CONNECT;
} else {
return _http_head_parse_response(http_head, key, value);
}
for (tmp_ptr = value; *tmp_ptr != 0; tmp_ptr++) {
if (field_start == NULL) {
field_start = tmp_ptr;
}
if (*tmp_ptr == ' ') {
*tmp_ptr = '\0';
if (url == NULL) {
url = field_start;
}
field_start = NULL;
}
}
if (field_start && version == NULL) {
version = field_start;
}
http_head->method = method;
http_head->url = url;
http_head->version = version;
http_head->head_type = HTTP_HEAD_REQUEST;
return 0;
}
static int _http_head_parse(struct http_head *http_head)
{
int i = 0;
char *key = NULL;
char *value = NULL;
char *data;
int has_first_line = 0;
int inkey = 1;
int invalue = 0;
data = http_head->buff;
for (i = 0; i < http_head->head_len; i++, data++) {
if (inkey) {
if (key == NULL && *data != ' ' && *data != '\r' && *data != '\n') {
key = data;
continue;
}
if (*data == ':' || *data == ' ') {
*data = '\0';
inkey = 0;
invalue = 1;
continue;
}
}
if (invalue) {
if (value == NULL && *data != ' ') {
value = data;
continue;
}
if (*data == '\r' || *data == '\n') {
*data = '\0';
inkey = 1;
invalue = 0;
}
}
if (key && value && invalue == 0) {
if (has_first_line == 0) {
if (_http_head_parse_request(http_head, key, value) != 0) {
return -2;
}
has_first_line = 1;
} else {
if (_http_head_add_fields(http_head, key, value) != 0) {
return -2;
}
}
key = NULL;
value = NULL;
inkey = 1;
invalue = 0;
}
}
return 0;
}
int http_head_parse(struct http_head *http_head, const char *data, int data_len)
{
int i = 0;
char *buff_end = NULL;
int left_size = 0;
int process_data_len = 0;
left_size = http_head->buff_size - http_head->buff_len;
if (left_size < data_len) {
return -3;
}
buff_end = http_head->buff + http_head->buff_len;
if (http_head->head_ok == 0) {
for (i = 0; i < data_len; i++, data++) {
*(buff_end + i) = *data;
if (*data == '\n') {
if (http_head->buff_len + i < 2) {
continue;
}
if (*(buff_end + i - 2) == '\n') {
http_head->head_ok = 1;
http_head->head_len = http_head->buff_len + i - 2;
i++;
buff_end += i;
data_len -= i;
data++;
if (_http_head_parse(http_head) != 0) {
return -2;
}
const char *content_len = NULL;
content_len = http_head_get_fields_value(http_head, "Content-Length");
if (content_len) {
http_head->expect_data_len = atol(content_len);
} else {
http_head->expect_data_len = 0;
}
if (http_head->expect_data_len < 0) {
return -2;
}
break;
}
}
}
process_data_len += i;
if (http_head->head_ok == 0) {
// Read data again */
http_head->buff_len += process_data_len;
return -1;
}
}
if (http_head->head_ok == 1) {
int get_data_len = (http_head->expect_data_len > data_len) ? data_len : http_head->expect_data_len;
if (http_head->data == NULL) {
http_head->data = buff_end;
}
memcpy(buff_end, data, get_data_len);
process_data_len += get_data_len;
http_head->data_len += get_data_len;
}
http_head->buff_len += process_data_len;
if (http_head->data_len < http_head->expect_data_len) {
return -1;
}
return process_data_len;
}
void http_head_destroy(struct http_head *http_head)
{
struct http_head_fields *fields, *tmp;
list_for_each_entry_safe(fields, tmp, &http_head->field_head.list, list)
{
list_del(&fields->list);
free(fields);
}
if (http_head->buff) {
free(http_head->buff);
}
free(http_head);
}

69
src/http_parse.h Normal file
View File

@@ -0,0 +1,69 @@
#ifndef HTTP_PARSER_H
#define HTTP_PARSER_H
#ifdef __cpluscplus
extern "C" {
#endif
struct http_head;
struct http_head_fields;
typedef enum HTTP_METHOD {
HTTP_METHOD_INVALID = 0,
HTTP_METHOD_GET,
HTTP_METHOD_HEAD,
HTTP_METHOD_POST,
HTTP_METHOD_PUT,
HTTP_METHOD_DELETE,
HTTP_METHOD_TRACE,
HTTP_METHOD_CONNECT,
} HTTP_METHOD;
typedef enum HTTP_HEAD_TYPE {
HTTP_HEAD_INVALID = 0,
HTTP_HEAD_REQUEST = 1,
HTTP_HEAD_RESPONSE = 2,
} HTTP_HEAD_TYPE;
struct http_head *http_head_init(int buffsize);
HTTP_HEAD_TYPE http_head_get_head_type(struct http_head *http_head);
HTTP_METHOD http_head_get_method(struct http_head *http_head);
const char *http_head_get_url(struct http_head *http_head);
const char *http_head_get_httpversion(struct http_head *http_head);
int http_head_get_httpcode(struct http_head *http_head);
char *http_head_get_httpcode_msg(struct http_head *http_head);
char *http_head_get_data(struct http_head *http_head);
int http_head_get_data_len(struct http_head *http_head);
struct http_head_fields *http_head_first_fields(struct http_head *http_head);
struct http_head_fields *http_head_next_fields(struct http_head_fields *fields);
const char *http_head_get_fields_value(struct http_head *http_head, const char *name);
int http_head_lookup_fields(struct http_head_fields *fields, const char **name, const char **value);
/*
* Returns:
* >=0 - success http data len
* -1 - Incomplete request
* -2 - parse failed
* -3 - buffer is small
*/
int http_head_parse(struct http_head *http_head, const char *data, int data_len);
void http_head_destroy(struct http_head *http_head);
#ifdef __cpluscplus
}
#endif
#endif // !HTTP_PARSER_H

View File

@@ -127,10 +127,38 @@ static int _smartdns_add_servers(void)
int ret = 0;
struct dns_server_groups *group = NULL;
struct dns_servers *server = NULL;
struct client_dns_server_flags flags;
for (i = 0; i < dns_conf_server_num; i++) {
ret = dns_client_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type, dns_conf_servers[i].server_flag,
dns_conf_servers[i].result_flag, dns_conf_servers[i].ttl, dns_conf_servers[i].spki);
memset(&flags, 0, sizeof(flags));
switch (dns_conf_servers[i].type) {
case DNS_SERVER_UDP: {
struct client_dns_server_flag_udp *flag_udp = &flags.udp;
flag_udp->ttl = dns_conf_servers[i].ttl;
} break;
case DNS_SERVER_HTTPS: {
struct client_dns_server_flag_https *flag_http = &flags.https;
flag_http->spi_len = dns_client_spki_decode(dns_conf_servers[i].spki, (unsigned char *)flag_http->spki);
strncpy(flag_http->host, dns_conf_servers[i].hostname, sizeof(flag_http->host));
strncpy(flag_http->path, dns_conf_servers[i].path, sizeof(flag_http->path));
} break;
case DNS_SERVER_TLS: {
struct client_dns_server_flag_tls *flag_tls = &flags.tls;
flag_tls->spi_len = dns_client_spki_decode(dns_conf_servers[i].spki, (unsigned char *)flag_tls->spki);
strncpy(flag_tls->host, dns_conf_servers[i].hostname, sizeof(flag_tls->host));
} break;
break;
case DNS_SERVER_TCP:
break;
default:
return -1;
break;
}
flags.type = dns_conf_servers[i].type;
flags.server_flag = dns_conf_servers[i].server_flag;
flags.result_flag = dns_conf_servers[i].result_flag;
ret = dns_client_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type, &flags);
if (ret != 0) {
tlog(TLOG_ERROR, "add server failed, %s:%d", dns_conf_servers[i].server, dns_conf_servers[i].port);
return -1;
@@ -297,7 +325,7 @@ static void _sig_error_exit(int signo, siginfo_t *siginfo, void *ct)
#elif defined(__mips__)
PC = context->uc_mcontext.pc;
#endif
tlog(TLOG_ERROR, "process exit with signal %d, code = %d, errno = %d, pid = %d, self = %d, pc = %#lx, addr = %#lx, build(%s %s)\n", signo, siginfo->si_code,
tlog(TLOG_FATAL, "process exit with signal %d, code = %d, errno = %d, pid = %d, self = %d, pc = %#lx, addr = %#lx, build(%s %s)\n", signo, siginfo->si_code,
siginfo->si_errno, siginfo->si_pid, getpid(), PC, (unsigned long)siginfo->si_addr, __DATE__, __TIME__);
sleep(1);

View File

@@ -12,10 +12,11 @@
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <inttypes.h>
#define TMP_BUFF_LEN_32 32
@@ -192,6 +193,53 @@ int parse_ip(const char *value, char *ip, int *port)
return 0;
}
int parse_uri(char *value, char *scheme, char *host, int *port, char *path)
{
char *scheme_end = NULL;
int field_len = 0;
char *process_ptr = value;
char host_name[PATH_MAX];
char *host_end = NULL;
scheme_end = strstr(value, "://");
if (scheme_end) {
field_len = scheme_end - value;
if (scheme) {
memcpy(scheme, value, field_len);
scheme[field_len + 1] = 0;
}
process_ptr += field_len + 3;
} else {
if (scheme) {
scheme[0] = '\0';
}
}
host_end = strstr(process_ptr, "/");
if (host_end == NULL) {
return parse_ip(process_ptr, host, port);
};
field_len = host_end - process_ptr;
if (field_len >= sizeof(host_name)) {
return -1;
}
memcpy(host_name, process_ptr, field_len);
host_name[field_len + 1] = 0;
if (parse_ip(host_name, host, port) != 0) {
return -1;
}
process_ptr += field_len;
if (path) {
strcpy(path, process_ptr);
}
return 0;
}
int set_fd_nonblock(int fd, int nonblock)
{
int ret;
@@ -457,7 +505,7 @@ static __attribute__((unused)) void _pthreads_locking_callback(int mode, int typ
}
}
static __attribute__((unused)) unsigned long _pthreads_thread_id(void)
static __attribute__((unused)) unsigned long _pthreads_thread_id(void)
{
unsigned long ret;
@@ -499,3 +547,197 @@ void SSL_CRYPTO_thread_cleanup(void)
OPENSSL_free(lock_cs);
OPENSSL_free(lock_count);
}
#define SERVER_NAME_LEN 256
#define TLS_HEADER_LEN 5
#define TLS_HANDSHAKE_CONTENT_TYPE 0x16
#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
#ifndef MIN
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#endif
typedef struct Protocol {
const char *const name;
const uint16_t default_port;
int (*const parse_packet)(const char*, size_t, char *, const char **);
const char *const abort_message;
const size_t abort_message_len;
} protocol_t;
static int parse_extensions(const char *, size_t, char *, const char **);
static int parse_server_name_extension(const char *, size_t, char *, const char **);
const struct Protocol *const tls_protocol;
static const protocol_t tls_protocol_st = {
.default_port = 443,
.parse_packet = &parse_tls_header,
};
const protocol_t *const tls_protocol = &tls_protocol_st;
/* Parse a TLS packet for the Server Name Indication extension in the client
* hello handshake, returning the first servername found (pointer to static
* array)
*
* Returns:
* >=0 - length of the hostname and updates *hostname
* caller is responsible for freeing *hostname
* -1 - Incomplete request
* -2 - No Host header included in this request
* -3 - Invalid hostname pointer
* -4 - malloc failure
* < -4 - Invalid TLS client hello
*/
int parse_tls_header(const char *data, size_t data_len, char *hostname, const char **hostname_ptr)
{
char tls_content_type;
char tls_version_major;
char tls_version_minor;
size_t pos = TLS_HEADER_LEN;
size_t len;
if (hostname == NULL)
return -3;
/* Check that our TCP payload is at least large enough for a TLS header */
if (data_len < TLS_HEADER_LEN)
return -1;
/* SSL 2.0 compatible Client Hello
*
* High bit of first byte (length) and content type is Client Hello
*
* See RFC5246 Appendix E.2
*/
if (data[0] & 0x80 && data[2] == 1) {
return -2;
}
tls_content_type = data[0];
if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
return -5;
}
tls_version_major = data[1];
tls_version_minor = data[2];
if (tls_version_major < 3) {
return -2;
}
/* TLS record length */
len = ((unsigned char)data[3] << 8) + (unsigned char)data[4] + TLS_HEADER_LEN;
data_len = MIN(data_len, len);
/* Check we received entire TLS record length */
if (data_len < len)
return -1;
/*
* Handshake
*/
if (pos + 1 > data_len) {
return -5;
}
if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
return -5;
}
/* Skip past fixed length records:
* 1 Handshake Type
* 3 Length
* 2 Version (again)
* 32 Random
* to Session ID Length
*/
pos += 38;
/* Session ID */
if (pos + 1 > data_len)
return -5;
len = (unsigned char)data[pos];
pos += 1 + len;
/* Cipher Suites */
if (pos + 2 > data_len)
return -5;
len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1];
pos += 2 + len;
/* Compression Methods */
if (pos + 1 > data_len)
return -5;
len = (unsigned char)data[pos];
pos += 1 + len;
if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) {
return -2;
}
/* Extensions */
if (pos + 2 > data_len)
return -5;
len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1];
pos += 2;
if (pos + len > data_len)
return -5;
return parse_extensions(data + pos, len, hostname, hostname_ptr);
}
static int parse_extensions(const char *data, size_t data_len, char *hostname, const char **hostname_ptr)
{
size_t pos = 0;
size_t len;
/* Parse each 4 bytes for the extension header */
while (pos + 4 <= data_len) {
/* Extension Length */
len = ((unsigned char)data[pos + 2] << 8) + (unsigned char)data[pos + 3];
/* Check if it's a server name extension */
if (data[pos] == 0x00 && data[pos + 1] == 0x00) {
/* There can be only one extension of each type, so we break
* our state and move p to beinnging of the extension here */
if (pos + 4 + len > data_len)
return -5;
return parse_server_name_extension(data + pos + 4, len, hostname, hostname_ptr);
}
pos += 4 + len; /* Advance to the next extension header */
}
/* Check we ended where we expected to */
if (pos != data_len)
return -5;
return -2;
}
static int parse_server_name_extension(const char *data, size_t data_len, char *hostname, const char **hostname_ptr)
{
size_t pos = 2; /* skip server name list length */
size_t len;
while (pos + 3 < data_len) {
len = ((unsigned char)data[pos + 1] << 8) + (unsigned char)data[pos + 2];
if (pos + 3 + len > data_len)
return -5;
switch (data[pos]) { /* name type */
case 0x00: /* host_name */
strncpy(hostname, data + pos + 3, len);
if (hostname_ptr) {
*hostname_ptr = data + pos + 3;
}
hostname[len] = '\0';
return len;
default:
break;
}
pos += 3 + len;
}
/* Check we ended where we expected to */
if (pos != data_len)
return -5;
return -2;
}

View File

@@ -16,6 +16,8 @@ int getaddr_by_host(char *host, struct sockaddr *addr, socklen_t *addr_len);
int parse_ip(const char *value, char *ip, int *port);
int parse_uri(char *value, char *scheme, char *host, int *port, char *path);
int set_fd_nonblock(int fd, int nonblock);
char *reverse_string(char *output, char *input, int len);
@@ -36,4 +38,19 @@ int SSL_base64_decode(const char *in, unsigned char *out);
int create_pid_file(const char *pid_file);
/* Parse a TLS packet for the Server Name Indication extension in the client
* hello handshake, returning the first servername found (pointer to static
* array)
*
* Returns:
* >=0 - length of the hostname and updates *hostname
* caller is responsible for freeing *hostname
* -1 - Incomplete request
* -2 - No Host header included in this request
* -3 - Invalid hostname pointer
* -4 - malloc failure
* < -4 - Invalid TLS client hello
*/
int parse_tls_header(const char *data, size_t data_len, char *hostname, const char **hostname_ptr);
#endif