proxy: simple add socks5 and https proxy support

This commit is contained in:
Nick Peng
2023-01-05 00:27:28 +08:00
parent d792e5f7f7
commit 83c4901190
14 changed files with 1828 additions and 34 deletions

View File

@@ -16,7 +16,7 @@
BIN=smartdns
OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o lib/conf.o lib/nftset.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 http_parse.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 proxy.o $(OBJS_LIB)
# cflags
ifndef CFLAGS

View File

@@ -26,6 +26,7 @@
#include "hashtable.h"
#include "http_parse.h"
#include "list.h"
#include "proxy.h"
#include "tlog.h"
#include "util.h"
#include <arpa/inet.h>
@@ -91,6 +92,7 @@ struct dns_server_info {
char ip[DNS_HOSTNAME_LEN];
int port;
char proxy_name[DNS_HOSTNAME_LEN];
/* server type */
dns_server_type_t type;
long long so_mark;
@@ -103,6 +105,9 @@ struct dns_server_info {
int ssl_write_len;
SSL_CTX *ssl_ctx;
SSL_SESSION *ssl_session;
struct proxy_conn *proxy;
pthread_mutex_t lock;
char skip_check_cert;
dns_server_status status;
@@ -254,6 +259,8 @@ static LIST_HEAD(pending_servers);
static pthread_mutex_t pending_server_mutex = PTHREAD_MUTEX_INITIALIZER;
static int dns_client_has_bootstrap_dns = 0;
static int _dns_client_send_udp(struct dns_server_info *server_info, void *packet, int len);
static ssize_t _ssl_read(struct dns_server_info *server, void *buff, int num)
{
ssize_t ret = 0;
@@ -1047,6 +1054,7 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port,
server_info->skip_check_cert = skip_check_cert;
server_info->prohibit = 0;
server_info->so_mark = flags->set_mark;
safe_strncpy(server_info->proxy_name, flags->proxyname, sizeof(server_info->proxy_name));
pthread_mutex_init(&server_info->lock, NULL);
memcpy(&server_info->flags, flags, sizeof(server_info->flags));
@@ -1141,7 +1149,13 @@ static void _dns_client_close_socket(struct dns_server_info *server_info)
/* remove fd from epoll */
epoll_ctl(client.epoll_fd, EPOLL_CTL_DEL, server_info->fd, NULL);
close(server_info->fd);
if (server_info->proxy) {
proxy_conn_free(server_info->proxy);
server_info->proxy = NULL;
} else {
close(server_info->fd);
}
server_info->fd = -1;
server_info->status = DNS_SERVER_STATUS_DISCONNECTED;
@@ -1670,6 +1684,69 @@ static int _dns_client_recv(struct dns_server_info *server_info, unsigned char *
return 0;
}
static int _dns_client_create_socket_udp_proxy(struct dns_server_info *server_info)
{
struct proxy_conn *proxy = NULL;
int fd = -1;
struct epoll_event event;
int ret = -1;
proxy = proxy_conn_new(server_info->proxy_name, server_info->ip, server_info->port, 1);
if (proxy == NULL) {
tlog(TLOG_ERROR, "create proxy failed, %s", server_info->ip);
goto errout;
}
fd = proxy_conn_get_fd(proxy);
if (fd < 0) {
tlog(TLOG_ERROR, "get proxy fd failed, %s", server_info->ip);
goto errout;
}
if (server_info->so_mark >= 0) {
unsigned int so_mark = server_info->so_mark;
if (setsockopt(fd, SOL_SOCKET, SO_MARK, &so_mark, sizeof(so_mark)) != 0) {
tlog(TLOG_DEBUG, "set socket mark failed, %s", strerror(errno));
}
}
set_fd_nonblock(fd, 1);
set_sock_keepalive(fd, 15, 3, 4);
ret = proxy_conn_connect(proxy);
if (ret != 0) {
if (errno == ENETUNREACH) {
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
if (errno != EINPROGRESS) {
tlog(TLOG_ERROR, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
}
server_info->fd = fd;
server_info->status = DNS_SERVER_STATUS_CONNECTING;
server_info->proxy = proxy;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLOUT;
event.data.ptr = server_info;
if (epoll_ctl(client.epoll_fd, EPOLL_CTL_ADD, fd, &event) != 0) {
tlog(TLOG_ERROR, "epoll ctl failed, %s", strerror(errno));
return -1;
}
return 0;
errout:
if (proxy) {
proxy_conn_free(proxy);
}
return -1;
}
static int _dns_client_create_socket_udp(struct dns_server_info *server_info)
{
int fd = 0;
@@ -1679,6 +1756,10 @@ static int _dns_client_create_socket_udp(struct dns_server_info *server_info)
const int priority = SOCKET_PRIORITY;
const int ip_tos = SOCKET_IP_TOS;
if (server_info->proxy_name[0] != '\0') {
return _dns_client_create_socket_udp_proxy(server_info);
}
fd = socket(server_info->ai_family, SOCK_DGRAM, 0);
if (fd < 0) {
tlog(TLOG_ERROR, "create socket failed, %s", strerror(errno));
@@ -1750,8 +1831,20 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info)
int yes = 1;
const int priority = SOCKET_PRIORITY;
const int ip_tos = SOCKET_IP_TOS;
struct proxy_conn *proxy = NULL;
int ret = 0;
if (server_info->proxy_name[0] != '\0') {
proxy = proxy_conn_new(server_info->proxy_name, server_info->ip, server_info->port, 0);
if (proxy == NULL) {
tlog(TLOG_ERROR, "create proxy failed, %s", server_info->ip);
goto errout;
}
fd = proxy_conn_get_fd(proxy);
} else {
fd = socket(server_info->ai_family, SOCK_STREAM, 0);
}
fd = socket(server_info->ai_family, SOCK_STREAM, 0);
if (fd < 0) {
tlog(TLOG_ERROR, "create socket failed, %s", strerror(errno));
goto errout;
@@ -1781,8 +1874,14 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info)
setsockopt(fd, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, &yes, sizeof(yes));
set_sock_keepalive(fd, 15, 3, 4);
if (connect(fd, &server_info->addr, server_info->ai_addrlen) != 0) {
if (errno == ENETUNREACH || errno == EHOSTUNREACH) {
if (proxy) {
ret = proxy_conn_connect(proxy);
} else {
ret = connect(fd, &server_info->addr, server_info->ai_addrlen);
}
if (ret != 0) {
if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == ECONNREFUSED) {
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
@@ -1795,6 +1894,7 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info)
server_info->fd = fd;
server_info->status = DNS_SERVER_STATUS_CONNECTING;
server_info->proxy = proxy;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLOUT;
@@ -1814,9 +1914,14 @@ errout:
server_info->status = DNS_SERVER_STATUS_INIT;
if (fd > 0) {
if (fd > 0 && proxy == NULL) {
close(fd);
}
if (proxy) {
proxy_conn_free(proxy);
}
return -1;
}
@@ -1825,22 +1930,35 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, ch
int fd = 0;
struct epoll_event event;
SSL *ssl = NULL;
struct proxy_conn *proxy = NULL;
int yes = 1;
const int priority = SOCKET_PRIORITY;
const int ip_tos = SOCKET_IP_TOS;
int ret = -1;
if (server_info->ssl_ctx == NULL) {
tlog(TLOG_ERROR, "create ssl ctx failed, %s", server_info->ip);
goto errout;
}
if (server_info->proxy_name[0] != '\0') {
proxy = proxy_conn_new(server_info->proxy_name, server_info->ip, server_info->port, 0);
if (proxy == NULL) {
tlog(TLOG_ERROR, "create proxy failed, %s", server_info->ip);
goto errout;
}
fd = proxy_conn_get_fd(proxy);
} else {
fd = socket(server_info->ai_family, SOCK_STREAM, 0);
}
ssl = SSL_new(server_info->ssl_ctx);
if (ssl == NULL) {
tlog(TLOG_ERROR, "new ssl failed, %s", server_info->ip);
goto errout;
}
fd = socket(server_info->ai_family, SOCK_STREAM, 0);
if (fd < 0) {
tlog(TLOG_ERROR, "create socket failed, %s", strerror(errno));
goto errout;
@@ -1870,8 +1988,14 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, ch
setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority));
setsockopt(fd, IPPROTO_IP, IP_TOS, &ip_tos, sizeof(ip_tos));
if (connect(fd, &server_info->addr, server_info->ai_addrlen) != 0) {
if (errno == ENETUNREACH || errno == EHOSTUNREACH) {
if (proxy) {
ret = proxy_conn_connect(proxy);
} else {
ret = connect(fd, &server_info->addr, server_info->ai_addrlen);
}
if (ret != 0) {
if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == ECONNREFUSED) {
tlog(TLOG_DEBUG, "connect %s failed, %s", server_info->ip, strerror(errno));
goto errout;
}
@@ -1902,6 +2026,7 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, ch
server_info->ssl = ssl;
server_info->ssl_write_len = -1;
server_info->status = DNS_SERVER_STATUS_CONNECTING;
server_info->proxy = proxy;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLOUT;
@@ -1925,7 +2050,7 @@ errout:
server_info->status = DNS_SERVER_STATUS_INIT;
if (fd > 0) {
if (fd > 0 && proxy == NULL) {
close(fd);
}
@@ -1933,6 +2058,10 @@ errout:
SSL_free(ssl);
}
if (proxy) {
proxy_conn_free(proxy);
}
return -1;
}
@@ -1964,6 +2093,103 @@ static int _dns_client_create_socket(struct dns_server_info *server_info)
return 0;
}
static int _dns_client_process_send_udp_buffer(struct dns_server_info *server_info, struct epoll_event *event,
unsigned long now)
{
int send_len = 0;
if (server_info->send_buff.len <= 0 || server_info->status != DNS_SERVER_STATUS_CONNECTED) {
return 0;
}
while (server_info->send_buff.len - send_len > 0) {
int ret = 0;
int packet_len = 0;
packet_len = *(int *)(server_info->send_buff.data + send_len);
send_len += sizeof(packet_len);
if (packet_len > server_info->send_buff.len - 1) {
goto errout;
}
ret = _dns_client_send_udp(server_info, server_info->send_buff.data + send_len, packet_len);
if (ret < 0) {
tlog(TLOG_ERROR, "sendto failed, %s", strerror(errno));
goto errout;
}
send_len += packet_len;
}
server_info->send_buff.len -= send_len;
if (server_info->send_buff.len < 0) {
server_info->send_buff.len = 0;
}
return 0;
errout:
pthread_mutex_lock(&client.server_list_lock);
server_info->recv_buff.len = 0;
server_info->send_buff.len = 0;
_dns_client_close_socket(server_info);
pthread_mutex_unlock(&client.server_list_lock);
return -1;
}
static int _dns_client_process_udp_proxy(struct dns_server_info *server_info, struct epoll_event *event,
unsigned long now)
{
struct sockaddr_storage from;
socklen_t from_len = sizeof(from);
char from_host[DNS_MAX_CNAME_LEN];
unsigned char inpacket[DNS_IN_PACKSIZE];
int len = 0;
int ret = 0;
_dns_client_process_send_udp_buffer(server_info, event, now);
if (!(event->events & EPOLLIN)) {
return 0;
}
len = proxy_conn_recvfrom(server_info->proxy, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, &from_len);
if (len < 0) {
tlog(TLOG_ERROR, "recvfrom failed, %s\n", strerror(errno));
goto errout;
} else if (len == 0) {
pthread_mutex_lock(&client.server_list_lock);
_dns_client_close_socket(server_info);
server_info->recv_buff.len = 0;
if (server_info->send_buff.len > 0) {
/* still remain request data, reconnect and send*/
ret = _dns_client_create_socket(server_info);
} else {
ret = 0;
}
pthread_mutex_unlock(&client.server_list_lock);
tlog(TLOG_DEBUG, "peer close, %s", server_info->ip);
return ret;
}
tlog(TLOG_DEBUG, "recv udp packet from %s, len: %d",
gethost_by_addr(from_host, sizeof(from_host), (struct sockaddr *)&from), len);
/* update recv time */
time(&server_info->last_recv);
/* processing dns packet */
if (_dns_client_recv(server_info, inpacket, len, (struct sockaddr *)&from, from_len) != 0) {
return -1;
}
return 0;
errout:
pthread_mutex_lock(&client.server_list_lock);
server_info->recv_buff.len = 0;
server_info->send_buff.len = 0;
_dns_client_close_socket(server_info);
pthread_mutex_unlock(&client.server_list_lock);
return -1;
}
static int _dns_client_process_udp(struct dns_server_info *server_info, struct epoll_event *event, unsigned long now)
{
int len = 0;
@@ -1977,6 +2203,10 @@ static int _dns_client_process_udp(struct dns_server_info *server_info, struct e
int ttl = 0;
struct cmsghdr *cmsg = NULL;
if (server_info->proxy) {
return _dns_client_process_udp_proxy(server_info, event, now);
}
memset(&msg, 0, sizeof(msg));
iov.iov_base = (char *)inpacket;
iov.iov_len = sizeof(inpacket);
@@ -2685,8 +2915,82 @@ errout:
return -1;
}
static int _dns_proxy_handshake(struct dns_server_info *server_info, struct epoll_event *event, unsigned long now)
{
struct epoll_event fd_event;
proxy_handshake_state ret = proxy_conn_handshake(server_info->proxy);
int fd = server_info->fd;
int retval = -1;
int epoll_op = EPOLL_CTL_MOD;
if (ret == PROXY_HANDSHAKE_OK) {
return 0;
}
if (ret == PROXY_HANDSHAKE_ERR) {
goto errout;
}
memset(&fd_event, 0, sizeof(fd_event));
if (ret == PROXY_HANDSHAKE_CONNECTED) {
fd_event.events = EPOLLIN;
if (server_info->type == DNS_SERVER_UDP) {
server_info->status = DNS_SERVER_STATUS_CONNECTED;
epoll_ctl(client.epoll_fd, EPOLL_CTL_DEL, fd, NULL);
event->events = 0;
fd = proxy_conn_get_udpfd(server_info->proxy);
if (fd < 0) {
tlog(TLOG_ERROR, "get udp fd failed");
goto errout;
}
set_fd_nonblock(fd, 1);
if (server_info->so_mark >= 0) {
unsigned int so_mark = server_info->so_mark;
if (setsockopt(fd, SOL_SOCKET, SO_MARK, &so_mark, sizeof(so_mark)) != 0) {
tlog(TLOG_DEBUG, "set socket mark failed, %s", strerror(errno));
}
}
server_info->fd = fd;
epoll_op = EPOLL_CTL_ADD;
} else {
fd_event.events |= EPOLLOUT;
}
retval = 0;
}
if (ret == PROXY_HANDSHAKE_WANT_READ) {
fd_event.events = EPOLLIN;
} else if (ret == PROXY_HANDSHAKE_WANT_WRITE) {
fd_event.events = EPOLLOUT | EPOLLIN;
}
fd_event.data.ptr = server_info;
if (epoll_ctl(client.epoll_fd, epoll_op, fd, &fd_event) != 0) {
tlog(TLOG_ERROR, "epoll ctl failed, %s", strerror(errno));
goto errout;
}
return retval;
errout:
pthread_mutex_lock(&client.server_list_lock);
server_info->recv_buff.len = 0;
server_info->send_buff.len = 0;
_dns_client_close_socket(server_info);
pthread_mutex_unlock(&client.server_list_lock);
return -1;
}
static int _dns_client_process(struct dns_server_info *server_info, struct epoll_event *event, unsigned long now)
{
if (server_info->proxy) {
int ret = _dns_proxy_handshake(server_info, event, now);
if (ret != 0) {
return ret;
}
}
if (server_info->type == DNS_SERVER_UDP) {
/* receive from udp */
return _dns_client_process_udp(server_info, event, now);
@@ -2703,19 +3007,68 @@ static int _dns_client_process(struct dns_server_info *server_info, struct epoll
return 0;
}
static int _dns_client_copy_data_to_buffer(struct dns_server_info *server_info, void *packet, int len)
{
if (DNS_TCP_BUFFER - server_info->send_buff.len < len) {
errno = ENOMEM;
return -1;
}
memcpy(server_info->send_buff.data + server_info->send_buff.len, packet, len);
server_info->send_buff.len += len;
return 0;
}
static int _dns_client_send_udp(struct dns_server_info *server_info, void *packet, int len)
{
int send_len = 0;
const struct sockaddr *addr = &server_info->addr;
socklen_t addrlen = server_info->ai_addrlen;
int ret = 0;
if (server_info->fd <= 0) {
return -1;
}
if (server_info->proxy) {
if (server_info->status != DNS_SERVER_STATUS_CONNECTED) {
/*set packet len*/
_dns_client_copy_data_to_buffer(server_info, &len, sizeof(len));
return _dns_client_copy_data_to_buffer(server_info, packet, len);
}
send_len = proxy_conn_sendto(server_info->proxy, packet, len, 0, addr, addrlen);
if (send_len != len) {
_dns_client_close_socket(server_info);
server_info->recv_buff.len = 0;
if (server_info->send_buff.len > 0) {
/* still remain request data, reconnect and send*/
ret = _dns_client_create_socket(server_info);
} else {
ret = 0;
}
if (ret != 0) {
return -1;
}
_dns_client_copy_data_to_buffer(server_info, &len, sizeof(len));
return _dns_client_copy_data_to_buffer(server_info, packet, len);
}
return 0;
}
send_len = sendto(server_info->fd, packet, len, 0, NULL, 0);
if (send_len != len) {
return -1;
goto errout;
}
return 0;
errout:
return -1;
}
static int _dns_client_send_data_to_buffer(struct dns_server_info *server_info, void *packet, int len)

View File

@@ -80,8 +80,8 @@ struct dns_query_options {
};
/* query domain */
int dns_client_query(const char *domain, int qtype, dns_client_callback callback, void *user_ptr, const char *group_name,
struct dns_query_options *options);
int dns_client_query(const char *domain, int qtype, dns_client_callback callback, void *user_ptr,
const char *group_name, struct dns_query_options *options);
void dns_client_exit(void);
@@ -102,6 +102,7 @@ struct client_dns_server_flag_https {
int spi_len;
char hostname[DNS_MAX_CNAME_LEN];
char httphost[DNS_MAX_CNAME_LEN];
char proxyname[DNS_MAX_CNAME_LEN];
char path[DNS_MAX_CNAME_LEN];
char tls_host_verify[DNS_MAX_CNAME_LEN];
char skip_check_cert;
@@ -112,7 +113,7 @@ struct client_dns_server_flags {
unsigned int server_flag;
unsigned int result_flag;
long long set_mark;
char proxyname[DNS_MAX_CNAME_LEN];
union {
struct client_dns_server_flag_udp udp;
struct client_dns_server_flag_tls tls;

View File

@@ -1,6 +1,6 @@
/*************************************************************************
*
* Copyright (C) 2018-2020 Ruilin Peng (Nick) <pymumu@gmail.com>.
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -52,6 +52,7 @@ struct dns_domain_set_name_table dns_domain_set_name_table;
/* dns groups */
struct dns_group_table dns_group_table;
struct dns_proxy_table dns_proxy_table;
struct dns_ptr_table dns_ptr_table;
@@ -100,6 +101,10 @@ struct dns_domain_check_orders dns_conf_check_orders = {
};
static int dns_has_cap_ping = 0;
/* proxy servers */
struct dns_proxy_servers dns_conf_proxy_servers[PROXY_MAX_SERVERS];
int dns_conf_proxy_server_num;
/* logging */
int dns_conf_log_level = TLOG_ERROR;
char dns_conf_log_file[DNS_MAX_PATH];
@@ -345,6 +350,101 @@ static void _config_group_table_destroy(void)
}
}
struct dns_proxy_names *dns_server_get_proxy_nams(const char *proxyname)
{
uint32_t key = 0;
struct dns_proxy_names *proxy = NULL;
key = hash_string(proxyname);
hash_for_each_possible(dns_proxy_table.proxy, proxy, node, key)
{
if (strncmp(proxy->proxy_name, proxyname, DNS_MAX_IPLEN) == 0) {
return proxy;
}
}
return NULL;
}
/* create and get dns server group */
static struct dns_proxy_names *_dns_conf_get_proxy(const char *proxy_name)
{
uint32_t key = 0;
struct dns_proxy_names *proxy = NULL;
key = hash_string(proxy_name);
hash_for_each_possible(dns_proxy_table.proxy, proxy, node, key)
{
if (strncmp(proxy->proxy_name, proxy_name, DNS_MAX_IPLEN) == 0) {
return proxy;
}
}
proxy = malloc(sizeof(*proxy));
if (proxy == NULL) {
goto errout;
}
memset(proxy, 0, sizeof(*proxy));
safe_strncpy(proxy->proxy_name, proxy_name, PROXY_NAME_LEN);
hash_add(dns_proxy_table.proxy, &proxy->node, key);
INIT_LIST_HEAD(&proxy->server_list);
return proxy;
errout:
if (proxy) {
free(proxy);
}
return NULL;
}
static int _dns_conf_proxy_servers_add(const char *proxy_name, struct dns_proxy_servers *server)
{
struct dns_proxy_names *proxy = NULL;
proxy = _dns_conf_get_proxy(proxy_name);
if (proxy == NULL) {
return -1;
}
list_add_tail(&server->list, &proxy->server_list);
return 0;
}
static const char *_dns_conf_get_proxy_name(const char *proxy_name)
{
struct dns_proxy_names *proxy = NULL;
proxy = _dns_conf_get_proxy(proxy_name);
if (proxy == NULL) {
return NULL;
}
return proxy->proxy_name;
}
static void _config_proxy_table_destroy(void)
{
struct dns_proxy_names *proxy = NULL;
struct hlist_node *tmp = NULL;
unsigned int i;
struct dns_proxy_servers *server = NULL;
struct dns_proxy_servers *server_tmp = NULL;
hash_for_each_safe(dns_proxy_table.proxy, i, tmp, proxy, node)
{
hlist_del_init(&proxy->node);
list_for_each_entry_safe(server, server_tmp, &proxy->server_list, list)
{
list_del(&server->list);
free(server);
}
free(proxy);
}
}
static int _config_server(int argc, char *argv[], dns_server_type_t type, int default_port)
{
int index = dns_conf_server_num;
@@ -371,6 +471,7 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
{"no-check-certificate", no_argument, NULL, 'N'}, /* do not check certificate */
{"tls-host-verify", required_argument, NULL, 'V' }, /* verify tls hostname */
{"group", required_argument, NULL, 'g'}, /* add to group */
{"proxy", required_argument, NULL, 'P'}, /* proxy server */
{"exclude-default-group", no_argument, NULL, 'E'}, /* ecluse this from default group */
{"set-mark", required_argument, NULL, 254}, /* set mark */
{NULL, no_argument, NULL, 0}
@@ -393,6 +494,7 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
server->hostname[0] = '\0';
server->httphost[0] = '\0';
server->tls_host_verify[0] = '\0';
server->proxyname[0] = '\0';
server->set_mark = -1;
if (type == DNS_SERVER_HTTPS) {
@@ -463,6 +565,14 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
safe_strncpy(server->spki, optarg, DNS_MAX_SPKI_LEN);
break;
}
case 'P': {
if (_dns_conf_get_proxy_name(optarg) == NULL) {
tlog(TLOG_ERROR, "add proxy server failed.");
goto errout;
}
safe_strncpy(server->proxyname, optarg, PROXY_NAME_LEN);
break;
}
case 'V': {
safe_strncpy(server->tls_host_verify, optarg, DNS_MAX_CNAME_LEN);
break;
@@ -1489,6 +1599,141 @@ errout:
return 0;
}
static int _config_proxy_server(int argc, char *argv[], struct dns_proxy_servers **pserver, proxy_type_t type)
{
int index = dns_conf_proxy_server_num;
struct dns_proxy_servers *server = NULL;
char *servers_name = NULL;
int port = -1;
char *ip = NULL;
int opt = 0;
unsigned int server_flag = 0;
int use_domain = 0;
/* clang-format off */
static struct option long_options[] = {
{"name", required_argument, NULL, 'n'},
{"use-domain", no_argument, NULL, 'd'},
{"user", required_argument, NULL, 'u'},
{"password", required_argument, NULL, 'p'},
{NULL, no_argument, NULL, 0}
};
/* clang-format on */
if (argc <= 1) {
tlog(TLOG_ERROR, "invalid parameter.");
return -1;
}
ip = argv[1];
if (index >= PROXY_MAX_SERVERS) {
tlog(TLOG_WARN, "exceeds max server number, %s", ip);
return 0;
}
server = malloc(sizeof(*server));
if (server == NULL) {
tlog(TLOG_WARN, "malloc memory failed.");
return -1;
}
memset(server, 0, sizeof(*server));
/* process extra options */
optind = 1;
while (1) {
opt = getopt_long_only(argc, argv, "n:du:p:", long_options, NULL);
if (opt == -1) {
break;
}
switch (opt) {
case 'n': {
servers_name = optarg;
break;
}
case 'd': {
use_domain = 1;
break;
}
case 'u': {
safe_strncpy(server->username, optarg, sizeof(server->username));
break;
}
case 'p': {
safe_strncpy(server->password, optarg, sizeof(server->password));
break;
}
default:
break;
}
}
ip = argv[optind];
if (ip) {
/* 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 */
if (port == PORT_NOT_DEFINED) {
port = 443;
}
} else {
goto errout;
}
if (servers_name == NULL) {
tlog(TLOG_ERROR, "please set name");
goto errout;
}
if (_dns_conf_proxy_servers_add(servers_name, server) != 0) {
tlog(TLOG_ERROR, "add group failed.");
goto errout;
}
/* add new server */
server->type = type;
server->port = port;
server->server_flag = server_flag;
server->use_domain = use_domain;
dns_conf_proxy_server_num++;
tlog(TLOG_DEBUG, "add proxy server %s, flag: %X", ip, server_flag);
if (pserver) {
*pserver = server;
}
return 0;
errout:
if (server) {
free(server);
}
return -1;
}
static int _config_proxy_socks5(void *data, int argc, char *argv[])
{
struct dns_proxy_servers *server = NULL;
int ret = _config_proxy_server(argc, argv, &server, PROXY_SOCKS5);
if (ret == 0) {
server->socks5 = 1;
}
return ret;
}
static int _config_proxy_http(void *data, int argc, char *argv[])
{
struct dns_proxy_servers *server = NULL;
int ret = _config_proxy_server(argc, argv, &server, PROXY_HTTP);
if (ret == 0) {
server->https = 1;
}
return ret;
}
static radix_node_t *_create_addr_node(char *addr)
{
radix_node_t *node = NULL;
@@ -2400,6 +2645,8 @@ static struct config_item _config_item[] = {
CONF_CUSTOM("server-https", _config_server_https, NULL),
CONF_CUSTOM("nameserver", _config_nameserver, NULL),
CONF_CUSTOM("address", _config_address, NULL),
CONF_CUSTOM("proxy-socks5", _config_proxy_socks5, NULL),
CONF_CUSTOM("proxy-http", _config_proxy_http, NULL),
CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable),
CONF_CUSTOM("ipset", _config_ipset, NULL),
CONF_YESNO("nftset-timeout", &dns_conf_nftset_timeout_enable),
@@ -2635,6 +2882,7 @@ void dns_server_load_exit(void)
_config_ptr_table_destroy();
_config_host_table_destroy();
_config_qtype_soa_table_destroy();
_config_proxy_table_destroy();
}
static int _dns_conf_speed_check_mode_verify(void)

View File

@@ -1,6 +1,6 @@
/*************************************************************************
*
* Copyright (C) 2018-2020 Ruilin Peng (Nick) <pymumu@gmail.com>.
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include "hash.h"
#include "hashtable.h"
#include "list.h"
#include "proxy.h"
#include "radix.h"
#ifdef __cpluscplus
@@ -41,8 +42,13 @@ extern "C" {
#define DNS_MAX_NFTSET_FAMILYLEN 8
#define DNS_MAX_NFTSET_NAMELEN 256
#define DNS_GROUP_NAME_LEN 32
#define PROXY_NAME_LEN 32
#define PROXY_MAX_SERVERS 128
#define DNS_NAX_GROUP_NUMBER 16
#define DNS_MAX_IPLEN 64
#define DNS_PROXY_MAX_LEN 128
#define DNS_CONF_USRNAME_LEN 32
#define DNS_MAX_SPKI_LEN 64
#define DNS_MAX_URL_LEN 256
@@ -224,6 +230,17 @@ struct dns_hosts_table {
extern struct dns_hosts_table dns_hosts_table;
extern int dns_hosts_record_num;
struct dns_proxy_names {
struct hlist_node node;
char proxy_name[PROXY_NAME_LEN];
struct list_head server_list;
};
struct dns_proxy_table {
DECLARE_HASHTABLE(proxy, 4);
};
extern struct dns_proxy_table dns_proxy_table;
struct dns_servers {
char server[DNS_MAX_IPLEN];
unsigned short port;
@@ -238,6 +255,21 @@ struct dns_servers {
char httphost[DNS_MAX_CNAME_LEN];
char tls_host_verify[DNS_MAX_CNAME_LEN];
char path[DNS_MAX_URL_LEN];
char proxyname[PROXY_NAME_LEN];
};
struct dns_proxy_servers {
struct list_head list;
char server[DNS_MAX_IPLEN];
proxy_type_t type;
unsigned short port;
unsigned int server_flag;
char username[DNS_PROXY_MAX_LEN];
char password[DNS_PROXY_MAX_LEN];
int socks5;
int https;
int use_domain;
};
/* ip address lists of domain */
@@ -346,11 +378,15 @@ extern int dns_conf_serve_expired_reply_ttl;
extern struct dns_servers dns_conf_servers[DNS_MAX_SERVERS];
extern int dns_conf_server_num;
/* proxy servers */
extern struct dns_proxy_servers dns_conf_proxy_servers[PROXY_MAX_SERVERS];
extern int dns_conf_proxy_server_num;
extern int dns_conf_log_level;
extern char dns_conf_log_file[DNS_MAX_PATH];
extern size_t dns_conf_log_size;
extern int dns_conf_log_num;
extern int dns_conf_log_file_mode;;
extern int dns_conf_log_file_mode;
extern char dns_conf_ca_file[DNS_MAX_PATH];
extern char dns_conf_ca_path[DNS_MAX_PATH];
@@ -415,6 +451,8 @@ int dns_server_load_conf(const char *file);
int dns_server_check_update_hosts(void);
struct dns_proxy_names *dns_server_get_proxy_nams(const char *proxyname);
extern int config_addtional_file(void *data, int argc, char *argv[]);
#ifdef __cpluscplus
}

View File

@@ -1,6 +1,6 @@
/*************************************************************************
*
* Copyright (C) 2018-2020 Ruilin Peng (Nick) <pymumu@gmail.com>.
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -805,7 +805,7 @@ static int _dns_add_rrs(struct dns_server_post_context *context)
}
if (request->rcode != DNS_RC_NOERROR) {
tlog(TLOG_INFO, "result %s, qtype: %d, rtcode: %d", domain, context->qtype, request->rcode);
tlog(TLOG_INFO, "result: %s, qtype: %d, rtcode: %d", domain, context->qtype, request->rcode);
}
return ret;

993
src/proxy.c Normal file
View File

@@ -0,0 +1,993 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "proxy.h"
#include "dns_conf.h"
#include "hashtable.h"
#include "http_parse.h"
#include "list.h"
#include "tlog.h"
#include "util.h"
#include <arpa/inet.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/epoll.h>
#define PROXY_SOCKS5_VERSION 0x05
#define PROXY_SOCKS5_NO_AUTH 0x00
#define PROXY_SOCKS5_AUTH_USER_PASS 0x02
#define PROXY_SOCKS5_AUTH_NONE 0xFF
#define PROXY_SOCKS5_TYPE_IPV4 0x01
#define PROXY_SOCKS5_TYPE_DOMAIN 0x03
#define PROXY_SOCKS5_TYPE_IPV6 0x04
#define PROXY_SOCKS5_CONNECT_TCP 0x01
#define PROXY_SOCKS5_CONNECT_UDP 0x03
#define PROXY_MAX_EVENTS 64
#define PROXY_BUFFER_SIZE (1024 * 8)
#define PROXY_MAX_HOSTNAME_LEN 256
typedef enum PROXY_CONN_STATE {
PROXY_CONN_INIT = 0,
PROXY_CONN_INIT_ACK = 1,
PROXY_CONN_AUTH = 2,
PROXY_CONN_AUTH_ACK = 3,
PROXY_CONN_CONNECTING = 4,
PROXY_CONN_CONNECTED = 5,
} PROXY_CONN_STATE;
struct proxy_conn {
proxy_type_t type;
PROXY_CONN_STATE state;
char host[DNS_MAX_CNAME_LEN];
unsigned short port;
int fd;
int udp_fd;
int buffer_len;
int is_udp;
struct sockaddr_storage udp_dest_addr;
socklen_t udp_dest_addrlen;
struct proxy_server_info *server_info;
};
/* upstream server groups */
struct proxy_server_info {
struct hlist_node node;
char proxy_name[PROXY_NAME_LEN];
struct sockaddr_storage server_addr;
socklen_t server_addrlen;
struct proxy_info info;
};
struct proxy_struct {
int run;
int epoll_fd;
pthread_t tid;
pthread_mutex_t proxy_lock;
DECLARE_HASHTABLE(proxy_server, 4);
};
static struct proxy_struct proxy;
const char *proxy_socks5_status_code[] = {
"success",
"general SOCKS server failure",
"connection not allowed by ruleset",
"Network unreachable",
"Host unreachable",
"Connection refused",
"TTL expired",
"Command not supported",
"Address type not supported",
};
/* get server group by name */
static struct proxy_server_info *_proxy_get_server_info(const char *proxy_name)
{
unsigned long key;
struct proxy_server_info *server_info = NULL;
struct hlist_node *tmp = NULL;
if (proxy_name == NULL) {
return NULL;
}
key = hash_string(proxy_name);
hash_for_each_possible_safe(proxy.proxy_server, server_info, tmp, node, key)
{
if (strncmp(server_info->proxy_name, proxy_name, DNS_GROUP_NAME_LEN) != 0) {
continue;
}
return server_info;
}
return NULL;
}
static struct addrinfo *_proxy_getaddr(const char *host, int port, int type, int protocol)
{
struct addrinfo hints;
struct addrinfo *result = NULL;
int ret = 0;
char port_str[32];
snprintf(port_str, sizeof(port_str), "%d", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = type;
hints.ai_protocol = protocol;
ret = getaddrinfo(host, port_str, &hints, &result);
if (ret != 0) {
tlog(TLOG_ERROR, "get addr info failed. %s\n", gai_strerror(ret));
tlog(TLOG_ERROR, "host = %s, port = %d, type = %d, protocol = %d", host, port, type, protocol);
goto errout;
}
return result;
errout:
if (result) {
freeaddrinfo(result);
}
return NULL;
}
int proxy_add(const char *proxy_name, struct proxy_info *info)
{
unsigned long key;
char ip_str[PROXY_MAX_IPLEN];
int port = 0;
struct addrinfo *gai = NULL;
struct proxy_server_info *server_info = _proxy_get_server_info(proxy_name);
if (server_info) {
return -1;
}
server_info = malloc(sizeof(*server_info));
if (server_info == NULL) {
goto errout;
}
memset(server_info, 0, sizeof(*server_info));
memcpy(&server_info->info, info, sizeof(struct proxy_info));
if (parse_ip(info->server, ip_str, &port) != 0) {
goto errout;
}
port = info->port;
gai = _proxy_getaddr(info->server, port, SOCK_STREAM, 0);
if (gai == NULL) {
goto errout;
}
server_info->server_addrlen = gai->ai_addrlen;
memcpy(&server_info->server_addr, gai->ai_addr, gai->ai_addrlen);
safe_strncpy(server_info->proxy_name, proxy_name, PROXY_NAME_LEN);
key = hash_string(server_info->proxy_name);
hash_add(proxy.proxy_server, &server_info->node, key);
freeaddrinfo(gai);
return 0;
errout:
if (server_info) {
free(server_info);
server_info = NULL;
}
if (gai) {
freeaddrinfo(gai);
}
return -1;
}
static int _proxy_remove(struct proxy_server_info *server_info)
{
hash_del(&server_info->node);
free(server_info);
return 0;
}
int proxy_remove(const char *proxy_name)
{
struct proxy_server_info *server_info = _proxy_get_server_info(proxy_name);
if (server_info == NULL) {
return 0;
}
_proxy_remove(server_info);
return 0;
}
static void _proxy_remove_all(void)
{
struct proxy_server_info *server_info;
struct hlist_node *tmp = NULL;
unsigned int i = 0;
hash_for_each_safe(proxy.proxy_server, i, tmp, server_info, node)
{
_proxy_remove(server_info);
}
}
struct proxy_conn *proxy_conn_new(const char *proxy_name, const char *host, int port, int is_udp)
{
struct proxy_conn *proxy_conn = NULL;
struct proxy_server_info *server_info = NULL;
struct addrinfo *gai = NULL;
int fd = -1;
server_info = _proxy_get_server_info(proxy_name);
if (server_info == NULL) {
goto errout;
}
if (is_udp == 1 && server_info->info.type != PROXY_SOCKS5) {
tlog(TLOG_WARN, "only socks5 support udp");
goto errout;
}
fd = socket(server_info->server_addr.ss_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (fd < 0) {
goto errout;
}
proxy_conn = malloc(sizeof(*proxy_conn));
if (proxy_conn == NULL) {
goto errout;
}
memset(proxy_conn, 0, sizeof(*proxy_conn));
safe_strncpy(proxy_conn->host, host, DNS_MAX_CNAME_LEN);
proxy_conn->port = port;
proxy_conn->type = server_info->info.type;
proxy_conn->state = PROXY_CONN_INIT;
proxy_conn->server_info = server_info;
proxy_conn->fd = fd;
proxy_conn->udp_fd = -1;
proxy_conn->is_udp = is_udp;
return proxy_conn;
errout:
if (proxy_conn) {
free(proxy_conn);
proxy_conn = NULL;
}
if (fd >= 0) {
close(fd);
}
if (gai) {
freeaddrinfo(gai);
}
return NULL;
}
void proxy_conn_free(struct proxy_conn *proxy_conn)
{
if (proxy_conn == NULL) {
return;
}
if (proxy_conn->fd >= 0) {
close(proxy_conn->fd);
}
if (proxy_conn->udp_fd >= 0) {
close(proxy_conn->udp_fd);
}
free(proxy_conn);
}
int proxy_conn_connect(struct proxy_conn *proxy_conn)
{
if (proxy_conn == NULL) {
return -1;
}
return connect(proxy_conn->fd, (struct sockaddr *)&proxy_conn->server_info->server_addr,
proxy_conn->server_info->server_addrlen);
}
static int _proxy_handshake_socks5_create_udp_fd(struct proxy_conn *proxy_conn)
{
int ret = 0;
char *gai_host = NULL;
int udp_fd = -1;
struct addrinfo *gai = NULL;
switch (proxy_conn->udp_dest_addr.ss_family) {
case AF_INET:
gai_host = "0.0.0.0";
break;
case AF_INET6:
gai_host = "::";
break;
default:
goto errout;
break;
}
gai = _proxy_getaddr(gai_host, 0, SOCK_DGRAM, 0);
udp_fd = socket(gai->ai_family, gai->ai_socktype | SOCK_CLOEXEC, 0);
if (udp_fd < 0) {
goto errout;
}
ret = bind(udp_fd, gai->ai_addr, gai->ai_addrlen);
if (ret < 0) {
goto errout;
}
freeaddrinfo(gai);
return udp_fd;
errout:
if (gai) {
freeaddrinfo(gai);
}
return -1;
}
static int _proxy_handshake_socks5_connect_udp(struct proxy_conn *proxy_conn)
{
int udp_fd = -1;
if (proxy_conn->is_udp == 0) {
return 0;
}
if (proxy_conn->udp_fd < 0) {
udp_fd = _proxy_handshake_socks5_create_udp_fd(proxy_conn);
if (udp_fd < 0) {
return -1;
}
proxy_conn->udp_fd = udp_fd;
}
return connect(proxy_conn->udp_fd, (struct sockaddr *)&proxy_conn->udp_dest_addr, proxy_conn->udp_dest_addrlen);
}
static proxy_handshake_state _proxy_handshake_socks5_reply_connect_addr(struct proxy_conn *proxy_conn)
{
char buff[DNS_MAX_CNAME_LEN * 2];
int len = 0;
memset(buff, 0, sizeof(buff));
struct sockaddr_storage addr;
char *ptr = buff;
socklen_t addr_len = sizeof(addr);
buff[0] = PROXY_SOCKS5_VERSION;
if (proxy_conn->is_udp) {
buff[1] = PROXY_SOCKS5_CONNECT_UDP;
} else {
buff[1] = PROXY_SOCKS5_CONNECT_TCP;
}
buff[2] = 0x0;
ptr = buff + 3;
if (proxy_conn->server_info->info.use_domain) {
*ptr = PROXY_SOCKS5_TYPE_DOMAIN;
ptr++;
int domainlen = strnlen(proxy_conn->host, DNS_MAX_CNAME_LEN);
*ptr = domainlen;
ptr++;
memcpy(ptr, proxy_conn->host, domainlen);
ptr += domainlen;
} else {
if (proxy_conn->is_udp) {
memset(&addr, 0, proxy_conn->server_info->server_addrlen);
addr_len = proxy_conn->server_info->server_addrlen;
addr.ss_family = proxy_conn->server_info->server_addr.ss_family;
} else {
getaddr_by_host(proxy_conn->host, (struct sockaddr *)&addr, &addr_len);
}
switch (addr.ss_family) {
case AF_INET: {
struct sockaddr_in *addr_in = NULL;
addr_in = (struct sockaddr_in *)&addr;
*ptr = PROXY_SOCKS5_TYPE_IPV4;
ptr++;
memcpy(ptr, &addr_in->sin_addr.s_addr, 4);
ptr += 4;
} break;
case AF_INET6: {
struct sockaddr_in6 *addr_in6 = NULL;
addr_in6 = (struct sockaddr_in6 *)&addr;
if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) {
*ptr = PROXY_SOCKS5_TYPE_IPV4;
ptr++;
memcpy(ptr, addr_in6->sin6_addr.s6_addr + 12, 4);
ptr += 4;
} else {
*ptr = PROXY_SOCKS5_TYPE_IPV6;
ptr++;
memcpy(ptr, addr_in6->sin6_addr.s6_addr, 16);
ptr += 16;
}
} break;
default:
return PROXY_HANDSHAKE_ERR;
}
}
*((short *)(ptr)) = htons(proxy_conn->port);
ptr += 2;
len = send(proxy_conn->fd, buff, ptr - buff, MSG_NOSIGNAL);
if (len != ptr - buff) {
tlog(TLOG_ERROR, "Send proxy request failed.");
return PROXY_HANDSHAKE_ERR;
}
proxy_conn->state = PROXY_CONN_CONNECTING;
return PROXY_HANDSHAKE_WANT_READ;
}
static proxy_handshake_state _proxy_handshake_socks5_send_auth(struct proxy_conn *proxy_conn)
{
char buff[DNS_MAX_CNAME_LEN * 2];
int len = 0;
int offset = 0;
memset(buff, 0, sizeof(buff));
buff[0] = 0x1;
buff[1] = strnlen(proxy_conn->server_info->info.username, PROXY_MAX_NAMELEN);
safe_strncpy(buff + 2, proxy_conn->server_info->info.username, buff[1] + 1);
offset = buff[1] + 2;
buff[offset] = strnlen(proxy_conn->server_info->info.password, PROXY_MAX_NAMELEN);
safe_strncpy(buff + offset + 1, proxy_conn->server_info->info.password, buff[offset] + 1);
offset += buff[offset] + 1;
len = send(proxy_conn->fd, buff, offset, MSG_NOSIGNAL);
if (len != offset) {
tlog(TLOG_ERROR, "send auth failed, len = %d, errno = %s", len, strerror(errno));
return PROXY_HANDSHAKE_ERR;
}
proxy_conn->state = PROXY_CONN_AUTH_ACK;
return PROXY_HANDSHAKE_WANT_READ;
}
static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_conn)
{
int len = 0;
char buff[DNS_MAX_CNAME_LEN * 2];
memset(buff, 0, sizeof(buff));
switch (proxy_conn->state) {
case PROXY_CONN_INIT: {
buff[0] = PROXY_SOCKS5_VERSION;
buff[1] = 0x2; // 2 auth methods
buff[2] = PROXY_SOCKS5_NO_AUTH;
buff[3] = PROXY_SOCKS5_AUTH_USER_PASS;
len = send(proxy_conn->fd, buff, 4, MSG_NOSIGNAL);
if (len != 4) {
tlog(TLOG_ERROR, "init socks5 failed, errno = %s", strerror(errno));
return PROXY_HANDSHAKE_ERR;
}
proxy_conn->state = PROXY_CONN_INIT_ACK;
return PROXY_HANDSHAKE_WANT_READ;
} break;
case PROXY_CONN_INIT_ACK:
len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
if (len <= 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return PROXY_HANDSHAKE_WANT_READ;
}
tlog(TLOG_ERROR, "recv socks5 init ack failed, errno = %s", strerror(errno));
return PROXY_HANDSHAKE_ERR;
}
if (len != 2) {
tlog(TLOG_ERROR, "recv socks5 init ack failed, len = %d", len);
return PROXY_HANDSHAKE_ERR;
}
if (buff[0] != PROXY_SOCKS5_VERSION) {
tlog(TLOG_ERROR, "Server not support socks5");
return PROXY_HANDSHAKE_ERR;
}
if ((unsigned char)buff[1] == PROXY_SOCKS5_AUTH_NONE) {
tlog(TLOG_ERROR, "Server not support auth methods");
return PROXY_HANDSHAKE_ERR;
}
tlog(TLOG_INFO, "Server select auth method is %d", buff[1]);
if (buff[1] == PROXY_SOCKS5_AUTH_USER_PASS) {
return _proxy_handshake_socks5_send_auth(proxy_conn);
}
if (buff[1] == PROXY_SOCKS5_NO_AUTH) {
return _proxy_handshake_socks5_reply_connect_addr(proxy_conn);
}
tlog(TLOG_ERROR, "Server select invalid auth method %d", buff[1]);
return PROXY_HANDSHAKE_ERR;
break;
case PROXY_CONN_AUTH_ACK:
len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
if (len <= 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return PROXY_HANDSHAKE_WANT_READ;
}
tlog(TLOG_ERROR, "recv socks5 auth ack failed, errno = %s", strerror(errno));
return PROXY_HANDSHAKE_ERR;
}
if (len != 2) {
tlog(TLOG_ERROR, "recv socks5 auth ack failed, len = %d", len);
return PROXY_HANDSHAKE_ERR;
}
if (buff[0] != 0x1) {
tlog(TLOG_ERROR, "Server not support socks5");
return PROXY_HANDSHAKE_ERR;
}
if (buff[1] != 0x0) {
tlog(TLOG_ERROR, "Server auth failed, code = %d", buff[1]);
return PROXY_HANDSHAKE_ERR;
}
tlog(TLOG_INFO, "Server auth success");
proxy_conn->state = PROXY_CONN_CONNECTING;
return _proxy_handshake_socks5_reply_connect_addr(proxy_conn);
case PROXY_CONN_CONNECTING: {
unsigned char addr[16];
unsigned short port = 0;
int use_dest_ip = 0;
int addr_len = 0;
len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
if (len <= 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return PROXY_HANDSHAKE_WANT_READ;
}
tlog(TLOG_ERROR, "recv socks5 connect ack failed, errno = %s", strerror(errno));
return PROXY_HANDSHAKE_ERR;
}
if (len < 10) {
tlog(TLOG_ERROR, "Server reply connect addr failed, len = %d", len);
return PROXY_HANDSHAKE_ERR;
}
if (buff[0] != PROXY_SOCKS5_VERSION) {
tlog(TLOG_ERROR, "Server not support socks5");
return PROXY_HANDSHAKE_ERR;
}
if (buff[1] != 0) {
if ((unsigned char)buff[1] <= (sizeof(proxy_socks5_status_code) / sizeof(proxy_socks5_status_code[0]))) {
tlog(TLOG_ERROR, "Server replay failed, error code %s", proxy_socks5_status_code[(int)buff[1]]);
} else {
tlog(TLOG_ERROR, "Server replay failed, error code %x", buff[1]);
}
return PROXY_HANDSHAKE_ERR;
}
switch (buff[3]) {
case PROXY_SOCKS5_TYPE_IPV4: {
struct sockaddr_in *addr_in = NULL;
addr_in = (struct sockaddr_in *)&proxy_conn->udp_dest_addr;
proxy_conn->udp_dest_addrlen = sizeof(struct sockaddr_in);
if (len != 10) {
return PROXY_HANDSHAKE_ERR;
}
addr_len = 4;
memcpy(addr, buff + 4, addr_len);
port = ntohs(*((short *)(buff + 4 + addr_len)));
addr_in->sin_family = AF_INET;
addr_in->sin_addr.s_addr = *((int *)addr);
addr_in->sin_port = *((short *)(buff + 4 + addr_len));
if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0) {
use_dest_ip = 1;
}
tlog(TLOG_DEBUG, "proxy dest: %d.%d.%d.%d:%d\n", addr[0], addr[1], addr[2], addr[3], port);
} break;
case PROXY_SOCKS5_TYPE_IPV6: {
struct sockaddr_in6 *addr_in6 = NULL;
addr_in6 = (struct sockaddr_in6 *)&proxy_conn->udp_dest_addr;
proxy_conn->udp_dest_addrlen = sizeof(struct sockaddr_in6);
if (len != 22) {
return PROXY_HANDSHAKE_ERR;
}
addr_len = 16;
memcpy(addr, buff + 4, addr_len);
port = ntohs(*((short *)(buff + 4 + addr_len)));
addr_in6->sin6_family = AF_INET6;
memcpy(addr_in6->sin6_addr.s6_addr, addr, addr_len);
addr_in6->sin6_port = *((short *)(buff + 4 + addr_len));
if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 && addr[4] == 0 && addr[5] == 0 &&
addr[6] == 0 && addr[7] == 0 && addr[8] == 0 && addr[9] == 0 && addr[10] == 0 && addr[11] == 0 &&
addr[12] == 0 && addr[13] == 0 && addr[14] == 0 && addr[15] == 0) {
use_dest_ip = 1;
}
tlog(TLOG_DEBUG, "proxy dest: [%x:%x:%x:%x:%x:%x:%x:%x]:%d\n", ntohs(*((short *)addr)),
ntohs(*((short *)(addr + 2))), ntohs(*((short *)(addr + 4))), ntohs(*((short *)(addr + 6))),
ntohs(*((short *)(addr + 8))), ntohs(*((short *)(addr + 10))), ntohs(*((short *)(addr + 12))),
ntohs(*((short *)(addr + 14))), port);
} break;
default:
return PROXY_HANDSHAKE_ERR;
}
if (use_dest_ip && proxy_conn->is_udp) {
memcpy(&proxy_conn->udp_dest_addr, &proxy_conn->server_info->server_addr,
proxy_conn->server_info->server_addrlen);
proxy_conn->udp_dest_addrlen = proxy_conn->server_info->server_addrlen;
switch (proxy_conn->udp_dest_addr.ss_family) {
case AF_INET: {
struct sockaddr_in *addr_in = NULL;
addr_in = (struct sockaddr_in *)&proxy_conn->udp_dest_addr;
addr_in->sin_port = *((short *)(buff + 4 + addr_len));
} break;
case AF_INET6: {
struct sockaddr_in6 *addr_in6 = NULL;
addr_in6 = (struct sockaddr_in6 *)&proxy_conn->udp_dest_addr;
addr_in6->sin6_port = *((short *)(buff + 4 + addr_len));
} break;
default:
return PROXY_HANDSHAKE_ERR;
break;
}
}
if (_proxy_handshake_socks5_connect_udp(proxy_conn) != 0) {
return PROXY_HANDSHAKE_ERR;
}
proxy_conn->state = PROXY_CONN_CONNECTED;
tlog(TLOG_INFO, "connected to socks proxy server.");
return PROXY_HANDSHAKE_CONNECTED;
} break;
default:
tlog(TLOG_ERROR, "client socks status %d is invalid", proxy_conn->state);
return PROXY_HANDSHAKE_ERR;
}
return PROXY_HANDSHAKE_ERR;
}
static int _proxy_handshake_http(struct proxy_conn *proxy_conn)
{
int len = 0;
proxy_handshake_state ret = PROXY_HANDSHAKE_ERR;
char buff[4096];
struct http_head *http_head = NULL;
switch (proxy_conn->state) {
case PROXY_CONN_INIT: {
char connecthost[DNS_MAX_CNAME_LEN * 2];
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
getaddr_by_host(proxy_conn->host, (struct sockaddr *)&addr, &addr_len);
if (proxy_conn->server_info->info.use_domain) {
snprintf(connecthost, sizeof(connecthost), "%s:%d", proxy_conn->host, proxy_conn->port);
} else {
struct sockaddr_in *addr_in;
addr_in = (struct sockaddr_in *)&addr;
unsigned char *paddr = (unsigned char *)&addr_in->sin_addr.s_addr;
snprintf(connecthost, sizeof(connecthost), "%d.%d.%d.%d:%d", paddr[0], paddr[1], paddr[2], paddr[3],
proxy_conn->port);
}
int msglen = 0;
if (proxy_conn->server_info->info.username[0] == '\0') {
msglen = snprintf(buff, sizeof(buff),
"CONNECT %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Proxy-Connection: Keep-Alive\r\n\r\n",
connecthost, connecthost);
} else {
char auth[256];
char base64_auth[256 * 2];
snprintf(auth, sizeof(auth), "%s:%s", proxy_conn->server_info->info.username,
proxy_conn->server_info->info.password);
SSL_base64_encode(auth, strlen(auth), base64_auth);
msglen = snprintf(buff, sizeof(buff),
"CONNECT %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Proxy-Authorization: Basic %s\r\n"
"Proxy-Connection: Keep-Alive\r\n\r\n",
connecthost, connecthost, base64_auth);
}
len = send(proxy_conn->fd, buff, msglen, MSG_NOSIGNAL);
if (len != msglen) {
tlog(TLOG_ERROR, "init https failed, len = %d, errno = %s", len, strerror(errno));
goto out;
}
proxy_conn->state = PROXY_CONN_CONNECTING;
ret = PROXY_HANDSHAKE_WANT_READ;
goto out;
} break;
case PROXY_CONN_CONNECTING: {
http_head = http_head_init(4096);
if (http_head == NULL) {
goto out;
}
len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
if (len <= 0) {
if (len == 0) {
tlog(TLOG_ERROR, "remote server closed.");
} else {
tlog(TLOG_ERROR, "recv failed, errno = %s", strerror(errno));
}
goto out;
}
len = http_head_parse(http_head, buff, len);
if (len < 0) {
if (len == -1) {
goto out;
}
tlog(TLOG_DEBUG, "remote server not supported.");
goto out;
}
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 out;
}
proxy_conn->state = PROXY_CONN_CONNECTED;
ret = PROXY_HANDSHAKE_CONNECTED;
goto out;
} break;
default:
goto out;
break;
}
out:
if (http_head) {
http_head_destroy(http_head);
}
return ret;
}
proxy_handshake_state proxy_conn_handshake(struct proxy_conn *proxy_conn)
{
if (proxy_conn == NULL) {
return -1;
}
if (proxy_conn->state == PROXY_CONN_CONNECTED) {
return PROXY_HANDSHAKE_OK;
}
switch (proxy_conn->type) {
case PROXY_SOCKS5:
return _proxy_handshake_socks5(proxy_conn);
case PROXY_HTTP:
return _proxy_handshake_http(proxy_conn);
default:
return PROXY_HANDSHAKE_ERR;
}
return PROXY_HANDSHAKE_ERR;
}
static int _proxy_is_tcp_connected(struct proxy_conn *proxy_conn)
{
char buff[1];
int ret = 0;
ret = recv(proxy_conn->fd, buff, 1, MSG_PEEK | MSG_DONTWAIT);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return 1;
}
}
return 0;
}
int proxy_conn_sendto(struct proxy_conn *proxy_conn, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
char buffer[PROXY_BUFFER_SIZE];
int buffer_len = 0;
int ret = 0;
if (proxy_conn == NULL) {
return -1;
}
if (_proxy_is_tcp_connected(proxy_conn) == 0) {
errno = ECONNRESET;
return -1;
}
buffer[0] = 0x00;
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer_len += 3;
switch (dest_addr->sa_family) {
case AF_INET:
buffer[3] = PROXY_SOCKS5_TYPE_IPV4;
memcpy(buffer + 4, &((struct sockaddr_in *)dest_addr)->sin_addr.s_addr, 4);
memcpy(buffer + 8, &((struct sockaddr_in *)dest_addr)->sin_port, 2);
buffer_len += 7;
break;
case AF_INET6:
buffer[3] = PROXY_SOCKS5_TYPE_IPV6;
memcpy(buffer + 4, &((struct sockaddr_in6 *)dest_addr)->sin6_addr.s6_addr, 16);
memcpy(buffer + 20, &((struct sockaddr_in6 *)dest_addr)->sin6_port, 2);
buffer_len += 19;
break;
default:
return -1;
}
memcpy(buffer + buffer_len, buf, len);
buffer_len += len;
ret = sendto(proxy_conn->udp_fd, buffer, buffer_len, MSG_NOSIGNAL, (struct sockaddr *)&proxy_conn->udp_dest_addr,
proxy_conn->udp_dest_addrlen);
if (ret != buffer_len) {
return -1;
}
return len;
}
int proxy_conn_recvfrom(struct proxy_conn *proxy_conn, void *buf, size_t len, int flags, struct sockaddr *src_addr,
socklen_t *addrlen)
{
char buffer[PROXY_BUFFER_SIZE];
int buffer_len = 0;
int ret = 0;
if (proxy_conn == NULL) {
return -1;
}
ret = recvfrom(proxy_conn->udp_fd, buffer, sizeof(buffer), MSG_NOSIGNAL, NULL, 0);
if (ret <= 0) {
return -1;
}
if (buffer[0] != 0x00 || buffer[1] != 0x00 || buffer[2] != 0x00) {
return -1;
}
switch (buffer[3]) {
case PROXY_SOCKS5_TYPE_IPV4:
if (ret < 10) {
return -1;
}
if (src_addr) {
memset(src_addr, 0, sizeof(struct sockaddr_in));
((struct sockaddr_in *)src_addr)->sin_family = AF_INET;
memcpy(&((struct sockaddr_in *)src_addr)->sin_addr.s_addr, buffer + 4, 4);
memcpy(&((struct sockaddr_in *)src_addr)->sin_port, buffer + 8, 2);
}
if (addrlen) {
*addrlen = sizeof(struct sockaddr_in);
}
buffer_len = 10;
break;
case PROXY_SOCKS5_TYPE_IPV6:
if (ret < 22) {
return -1;
}
if (src_addr) {
memset(src_addr, 0, sizeof(struct sockaddr_in6));
((struct sockaddr_in6 *)src_addr)->sin6_family = AF_INET6;
memcpy(&((struct sockaddr_in6 *)src_addr)->sin6_addr.s6_addr, buffer + 4, 16);
memcpy(&((struct sockaddr_in6 *)src_addr)->sin6_port, buffer + 20, 2);
}
if (addrlen) {
*addrlen = sizeof(struct sockaddr_in6);
}
buffer_len = 22;
break;
default:
return -1;
}
if (ret - buffer_len > (int)len) {
return -1;
}
memcpy(buf, buffer + buffer_len, ret - buffer_len);
return ret - buffer_len;
}
int proxy_conn_get_fd(struct proxy_conn *proxy_conn)
{
if (proxy_conn == NULL) {
return -1;
}
return proxy_conn->fd;
}
int proxy_conn_get_udpfd(struct proxy_conn *proxy_conn)
{
if (proxy_conn == NULL) {
return -1;
}
return proxy_conn->udp_fd;
}
int proxy_conn_is_udp(struct proxy_conn *proxy_conn)
{
if (proxy_conn == NULL) {
return -1;
}
return proxy_conn->is_udp;
}
int proxy_init()
{
memset(&proxy, 0, sizeof(proxy));
hash_init(proxy.proxy_server);
return 0;
}
int proxy_exit()
{
_proxy_remove_all();
return 0;
}

88
src/proxy.h Normal file
View File

@@ -0,0 +1,88 @@
/*************************************************************************
*
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* smartdns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SMART_DNS_PROXY_H
#define SMART_DNS_PROXY_H
#include <sys/socket.h>
#include <sys/types.h>
#define PROXY_MAX_IPLEN 256
#define PROXY_MAX_NAMELEN 128
#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus */
typedef enum {
PROXY_SOCKS5,
PROXY_HTTP,
PROXY_TYPE_END,
} proxy_type_t;
typedef enum {
PROXY_HANDSHAKE_ERR = -1,
PROXY_HANDSHAKE_OK = 0,
PROXY_HANDSHAKE_CONNECTED = 1,
PROXY_HANDSHAKE_WANT_READ = 2,
PROXY_HANDSHAKE_WANT_WRITE = 3,
} proxy_handshake_state;
struct proxy_info {
proxy_type_t type;
char server[PROXY_MAX_IPLEN];
unsigned short port;
int use_domain;
char username[PROXY_MAX_NAMELEN];
char password[PROXY_MAX_NAMELEN];
};
struct proxy_conn;
int proxy_init(void);
int proxy_exit(void);
int proxy_add(const char *proxy_name, struct proxy_info *info);
int proxy_remove(const char *proxy_name);
struct proxy_conn *proxy_conn_new(const char *proxy_name, const char *host, int port, int is_udp);
int proxy_conn_get_fd(struct proxy_conn *proxy_conn);
int proxy_conn_get_udpfd(struct proxy_conn *proxy_conn);
int proxy_conn_is_udp(struct proxy_conn *proxy_conn);
void proxy_conn_free(struct proxy_conn *proxy_conn);
int proxy_conn_connect(struct proxy_conn *proxy_conn);
int proxy_conn_sendto(struct proxy_conn *proxy_conn, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
int proxy_conn_recvfrom(struct proxy_conn *proxy_conn, void *buf, size_t len, int flags, struct sockaddr *src_addr,
socklen_t *addrlen);
proxy_handshake_state proxy_conn_handshake(struct proxy_conn *proxy_conn);
#ifdef __cplusplus
}
#endif /*__cplusplus */
#endif

View File

@@ -1,6 +1,6 @@
/*************************************************************************
*
* Copyright (C) 2018-2020 Ruilin Peng (Nick) <pymumu@gmail.com>.
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -270,6 +270,7 @@ static int _smartdns_add_servers(void)
flags.server_flag = dns_conf_servers[i].server_flag;
flags.result_flag = dns_conf_servers[i].result_flag;
flags.set_mark = dns_conf_servers[i].set_mark;
safe_strncpy(flags.proxyname, dns_conf_servers[i].proxyname, sizeof(flags.proxyname));
ret = dns_client_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type,
&flags);
if (ret != 0) {
@@ -302,6 +303,33 @@ static int _smartdns_add_servers(void)
return 0;
}
static int _proxy_add_servers(void)
{
unsigned long i = 0;
struct hlist_node *tmp = NULL;
struct dns_proxy_names *proxy = NULL;
struct dns_proxy_servers *server = NULL;
struct dns_proxy_servers *server_tmp = NULL;
hash_for_each_safe(dns_proxy_table.proxy, i, tmp, proxy, node)
{
list_for_each_entry_safe(server, server_tmp, &proxy->server_list, list)
{
struct proxy_info info;
memset(&info, 0, sizeof(info));
info.type = server->type;
info.port = server->port;
safe_strncpy(info.server, server->server, PROXY_MAX_IPLEN);
safe_strncpy(info.username, server->username, PROXY_MAX_NAMELEN);
safe_strncpy(info.password, server->password, PROXY_MAX_NAMELEN);
info.use_domain = server->use_domain;
proxy_add(proxy->proxy_name, &info);
}
}
return 0;
}
static int _smartdns_set_ecs_ip(void)
{
int ret = 0;
@@ -386,6 +414,17 @@ static int _smartdns_init(void)
goto errout;
}
ret = proxy_init();
if (ret != 0) {
tlog(TLOG_ERROR, "start proxy failed.\n");
goto errout;
}
ret = _proxy_add_servers();
if (ret != 0) {
tlog(TLOG_ERROR, "add proxy servers failed.");
}
ret = dns_server_init();
if (ret != 0) {
tlog(TLOG_ERROR, "start dns server failed.\n");
@@ -397,6 +436,7 @@ static int _smartdns_init(void)
tlog(TLOG_ERROR, "start dns client failed.\n");
goto errout;
}
ret = _smartdns_add_servers();
if (ret != 0) {
tlog(TLOG_ERROR, "add servers failed.");
@@ -423,6 +463,7 @@ static void _smartdns_exit(void)
{
tlog(TLOG_INFO, "smartdns exit...");
dns_client_exit();
proxy_exit();
fast_ping_exit();
dns_server_exit();
_smartdns_destroy_ssl();

View File

@@ -1,6 +1,6 @@
/*************************************************************************
*
* Copyright (C) 2018-2020 Ruilin Peng (Nick) <pymumu@gmail.com>.
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -684,6 +684,24 @@ errout:
return -1;
}
int SSL_base64_encode(const void *in, int in_len, char *out)
{
int outlen = 0;
if (in_len == 0) {
return 0;
}
outlen = EVP_EncodeBlock((unsigned char *)out, in, in_len);
if (outlen < 0) {
goto errout;
}
return outlen;
errout:
return -1;
}
int create_pid_file(const char *pid_file)
{
int fd = 0;
@@ -1469,7 +1487,7 @@ int dns_packet_debug(const char *packet_file)
char buff[DNS_PACKSIZE];
tlog_set_maxlog_count(0);
tlog_setlogscreen(1);;
tlog_setlogscreen(1);
tlog_setlevel(TLOG_DEBUG);
info = _dns_read_packet_file(packet_file);

View File

@@ -1,6 +1,6 @@
/*************************************************************************
*
* Copyright (C) 2018-2020 Ruilin Peng (Nick) <pymumu@gmail.com>.
* Copyright (C) 2018-2023 Ruilin Peng (Nick) <pymumu@gmail.com>.
*
* smartdns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -89,6 +89,8 @@ unsigned char *SSL_SHA256(const unsigned char *d, size_t n, unsigned char *md);
int SSL_base64_decode(const char *in, unsigned char *out);
int SSL_base64_encode(const void *in, int in_len, char *out);
int create_pid_file(const char *pid_file);
/* Parse a TLS packet for the Server Name Indication extension in the client