Browse Source

dns_server: support bind tls server

Nick Peng 2 years ago
parent
commit
c42f98979c
9 changed files with 751 additions and 17 deletions
  1. 6 1
      ReadMe.md
  2. 6 1
      ReadMe_en.md
  3. 8 0
      etc/smartdns/smartdns.conf
  4. 73 5
      src/dns_conf.c
  5. 8 0
      src/dns_conf.h
  6. 530 9
      src/dns_server.c
  7. 1 1
      src/include/conf.h
  8. 117 0
      src/util.c
  9. 2 0
      src/util.h

+ 6 - 1
ReadMe.md

@@ -103,7 +103,7 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms
    支持从域名所属 IP 地址列表中查找到访问速度最快的 IP 地址,并返回给客户端,提高网络访问速度。
 
 1. **支持多种查询协议**  
-   支持 UDP、TCP、DOT 和 DOH 查询,以及非 53 端口查询;支持通过socks5,HTTP代理查询
+   支持 UDP、TCP、DOT 和 DOH 查询及服务,以及非 53 端口查询;支持通过socks5,HTTP代理查询;
 
 1. **特定域名 IP 地址指定**  
    支持指定域名的 IP 地址,达到广告过滤效果、避免恶意网站的效果。
@@ -578,6 +578,10 @@ entware|ipkg update<br />ipkg install smartdns|软件源路径:<https://bin.en
 | server-name | DNS 服务器名称 | 操作系统主机名 / smartdns | 符合主机名规格的字符串 | server-name smartdns |
 | bind | DNS 监听端口号  | [::]:53 | 可绑定多个端口。<br />IP:PORT@DEVICE: 服务器 IP:端口号@设备名<br />[-group]: 请求时使用的 DNS 服务器组<br />[-no-rule-addr]:跳过 address 规则<br />[-no-rule-nameserver]:跳过 Nameserver 规则<br />[-no-rule-ipset]:跳过 ipset 和 nftset 规则<br />[-no-rule-soa]:跳过 SOA(#) 规则<br />[-no-dualstack-selection]:停用双栈测速<br />[-no-speed-check]:停用测速<br />[-no-cache]:停止缓存 | bind :53@eth0 |
 | bind-tcp | DNS TCP 监听端口号 | [::]:53 | 可绑定多个端口。<br />IP:PORT@DEVICE: 服务器 IP:端口号@设备名<br />[-group]: 请求时使用的 DNS 服务器组<br />[-no-rule-addr]:跳过 address 规则<br />[-no-rule-nameserver]:跳过 nameserver 规则<br />[-no-rule-ipset]:跳过 ipset 和 nftset 规则。<br />[-no-rule-soa]:跳过 SOA(#) 规则<br />[-no-dualstack-selection]:停用双栈测速<br />[-no-speed-check]:停用测速<br />[-no-cache]:停止缓存 | bind-tcp :53 |
+| bind-tls | DNS Over TLS 监听端口号 | [::]:853 | 可绑定多个端口。<br />IP:PORT@DEVICE: 服务器 IP:端口号@设备名<br />[-group]: 请求时使用的 DNS 服务器组<br />[-no-rule-addr]:跳过 address 规则<br />[-no-rule-nameserver]:跳过 nameserver 规则<br />[-no-rule-ipset]:跳过 ipset 和 nftset 规则。<br />[-no-rule-soa]:跳过 SOA(#) 规则<br />[-no-dualstack-selection]:停用双栈测速<br />[-no-speed-check]:停用测速<br />[-no-cache]:停止缓存 | bind-tls :853 |
+| bind-cert-file | SSL证书文件路径 | smartdns-cert.pem | 合法路径字符串 | bind-cert-file cert.pem |
+| bind-cert-key-file | SSL证书KEY文件路径 | smartdns-key.pem | 合法路径字符串 | bind-cert-key-file key.pem |
+| bind-cert-key-pass | SSL证书KEY文件密码 | 无 | 字符串 | bind-cert-key-pass password |
 | cache-size | 域名结果缓存个数 | 512 | 大于等于 0 的数字 | cache-size 512 |
 | cache-persist | 是否持久化缓存 | 自动。<br />当 cache-file 所在的位置有超过 128 MB 的可用空间时启用,否则禁用。 | [yes\|no] | cache-persist yes |
 | cache-file | 缓存持久化文件路径 | /tmp/<br />smartdns.cache | 合法路径字符串 | cache-file /tmp/smartdns.cache |
@@ -609,6 +613,7 @@ entware|ipkg update<br />ipkg install smartdns|软件源路径:<https://bin.en
 | address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6] <br />- 表示忽略 <br /># 表示返回 SOA <br />4 表示 IPv4 <br />6 表示 IPv6 | address /www.example.com/1.2.3.4 |
 | cname | 指定域名别名 | 无 | cname /domain/target <br />- 表示忽略 <br />指定对应域名的cname | cname /www.example.com/cdn.example.com |
 | dns64 | DNS64转换 | 无 | dns64 ip-prefix/mask <br /> ipv6前缀和掩码 | dns64 64:ff9b::/96 |
+| edns-client-subnet | DNS ECS | 无 | edns-client-subnet ip-prefix/mask <br /> 指定EDNS客户端子网 | ip-prefix/mask 1.2.3.4/23 |
 | nameserver | 指定域名使用 server 组解析 | 无 | nameserver /domain/[group\|-], group 为组名,- 表示忽略此规则,配套 server 中的 -group 参数使用 | nameserver /www.example.com/office |
 | ipset | 域名 ipset | 无 | ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]],-表示忽略 | ipset /www.example.com/#4:dns4,#6:- <br />ipset /www.example.com/dns |
 | ipset-timeout | 设置 ipset 超时功能启用  | no | [yes\|no] | ipset-timeout yes |

+ 6 - 1
ReadMe_en.md

@@ -101,7 +101,7 @@ From the comparison, smartdns found the fastest IP address to visit www.baidu.co
    Supports finding the fastest access IP address from the IP address list of the domain name and returning it to the client to avoid DNS pollution and improve network access speed.
 
 1. **Support for multiple query protocols**  
-   Support UDP, TCP, DOT(DNS over TLS), DOH(DNS over HTTPS) queries, and non-53 port queries, effectively avoiding DNS pollution and protect privacy, and support query DNS over socks5, http proxy.
+   Support UDP, TCP, DOT(DNS over TLS), DOH(DNS over HTTPS) queries and service, and non-53 port queries, effectively avoiding DNS pollution and protect privacy, and support query DNS over socks5, http proxy.
 
 1. **Domain IP address specification**  
    Support configuring IP address of specific domain to achieve the effect of advertising filtering, and avoid malicious websites.
@@ -542,6 +542,10 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
 |server-name|DNS name|host name/smartdns|any string like hostname|server-name smartdns
 |bind|DNS listening port number|[::]:53|Support binding multiple ports<br />`IP:PORT@DEVICE`: server IP, port number, and device. <br />`[-group]`: The DNS server group used when requesting. <br />`[-no-rule-addr]`: Skip the address rule. <br />`[-no-rule-nameserver]`: Skip the Nameserver rule. <br />`[-no-rule-ipset]`: Skip the Ipset or nftset rules. <br />`[-no-rule-soa]`: Skip address SOA(#) rules.<br />`[-no-dualstack-selection]`: Disable dualstack ip selection.<br />`[-no-speed-check]`: Disable speed measurement. <br />`[-no-cache]`: stop caching |bind :53@eth0
 |bind-tcp|TCP mode DNS listening port number|[::]:53|Support binding multiple ports<br />`IP:PORT@DEVICE`: server IP, port number and device. <br />`[-group]`: The DNS server group used when requesting. <br />`[-no-rule-addr]`: Skip the address rule. <br />`[-no-rule-nameserver]`: Skip the Nameserver rule. <br />`[-no-rule-ipset]`: Skip the ipset or nftset rules. <br />`[-no-rule-soa]`: Skip address SOA(#) rules.<br />`[-no-dualstack-selection]`: Disable dualstack ip selection.<br />`[-no-speed-check]`: Disable speed measurement. <br />`[-no-cache]`: stop caching |bind-tcp :53
+|bind-tls|DOT mode DNS listening port number|[::]:853|Support binding multiple ports<br />`IP:PORT@DEVICE`: server IP, port number and device. <br />`[-group]`: The DNS server group used when requesting. <br />`[-no-rule-addr]`: Skip the address rule. <br />`[-no-rule-nameserver]`: Skip the Nameserver rule. <br />`[-no-rule-ipset]`: Skip the ipset or nftset rules. <br />`[-no-rule-soa]`: Skip address SOA(#) rules.<br />`[-no-dualstack-selection]`: Disable dualstack ip selection.<br />`[-no-speed-check]`: Disable speed measurement. <br />`[-no-cache]`: stop caching |bind-tcp :853
+|bind-cert-file|SSL Certificate file path|smartdns-cert.pem|path| bind-cert-file cert.pem |
+|bind-cert-key-file|SSL Certificate key file path|none|smartdns-key.pem| bind-cert-key-file key.pem |
+|bind-cert-key-pass|SSL Certificate key file password|none|string| bind-cert-key-pass password |
 |cache-size|Domain name result cache number|512|integer|cache-size 512
 |cache-persist|enable persist cache|Auto: Enabled if the location of `cache-file` has more than 128MB of free space.|[yes\|no]|cache-persist yes
 |cache-file|cache persist file|/tmp/<br />smartdns.cache|path|cache-file /tmp/smartdns.cache
@@ -573,6 +577,7 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
 |address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4
 |cname|set cname to domain| None | cname /domain/target <br />- for ignore <br />set cname to domain. | cname /www.example.com/cdn.example.com |
 |dns64|dns64 translation | None | dns64 ip-prefix/mask <br /> ipv6 prefix and mask. | dns64 64:ff9b::/96 |
+|edns-client-subnet| DNS ECS | None |edns-client-subnet ip-prefix/mask <br /> set EDNS client subnet | ip-prefix/mask 1.2.3.4/23 |
 |nameserver|To query domain with specific server group|None|nameserver /domain/[group\|-], `group` is the group name, `-` means ignore this rule, use the `-group` parameter in the related server|nameserver /www.example.com/office
 |ipset|Domain IPSet|None|ipset /domain/[ipset\|-\|#[4\|6]:[ipset\|-][,#[4\|6]:[ipset\|-]]], `-` for ignore|ipset /www.example.com/#4:dns4,#6:-
 |ipset-timeout|ipset timeout enable|no|[yes\|no]|ipset-timeout yes

+ 8 - 0
etc/smartdns/smartdns.conf

@@ -22,6 +22,14 @@
 #   bind [IP]:[port][@device] [-group [group]] [-no-rule-addr] [-no-rule-nameserver] [-no-rule-ipset] [-no-speed-check] [-no-cache] [-no-rule-soa] [-no-dualstack-selection]
 # bind tcp server
 #   bind-tcp [IP]:[port][@device] [-group [group]] [-no-rule-addr] [-no-rule-nameserver] [-no-rule-ipset] [-no-speed-check] [-no-cache] [-no-rule-soa] [-no-dualstack-selection]
+# bind tls server
+#   bind-tls [IP]:[port][@device] [-group [group]] [-no-rule-addr] [-no-rule-nameserver] [-no-rule-ipset] [-no-speed-check] [-no-cache] [-no-rule-soa] [-no-dualstack-selection]
+#   bind-cert-key-file [path to file]
+#      tls private key file
+#   bind-cert-file [path to file]
+#      tls cert file
+#   bind-cert-key-pass [password]
+#      tls private key password
 # option:
 #   -group: set domain request to use the appropriate server group.
 #   -no-rule-addr: skip address rule.

+ 73 - 5
src/dns_conf.c

@@ -68,6 +68,10 @@ struct dns_dns64 dns_conf_dns_dns64;
 struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
 int dns_conf_bind_ip_num = 0;
 int dns_conf_tcp_idle_time = 120;
+char dns_conf_bind_ca_file[DNS_MAX_PATH];
+char dns_conf_bind_ca_key_file[DNS_MAX_PATH];
+char dns_conf_bind_ca_key_pass[DNS_MAX_PATH];
+char dns_conf_need_cert = 0;
 
 int dns_conf_max_reply_ip_num = DNS_MAX_REPLY_IP_NUM;
 
@@ -1357,7 +1361,7 @@ static int _config_nftset_no_speed(void *data, int argc, char *argv[])
 		goto errout;
 	}
 
-	for (char *tok = strtok(copied_name, ","); tok && nftset_num <=2 ; tok = strtok(NULL, ",")) {
+	for (char *tok = strtok(copied_name, ","); tok && nftset_num <= 2; tok = strtok(NULL, ",")) {
 		char *saveptr = NULL;
 		char *tok_set = NULL;
 
@@ -1853,6 +1857,14 @@ static int _config_bind_ip(int argc, char *argv[], DNS_BIND_TYPE type)
 	bind_ip->flags = server_flag;
 	bind_ip->group = group;
 	dns_conf_bind_ip_num++;
+	if (bind_ip->type == DNS_BIND_TYPE_TLS) {
+		if (bind_ip->ssl_cert_file == NULL || bind_ip->ssl_cert_key_file == NULL) {
+			bind_ip->ssl_cert_file = dns_conf_bind_ca_file;
+			bind_ip->ssl_cert_key_file = dns_conf_bind_ca_key_file;
+			bind_ip->ssl_cert_key_pass = dns_conf_bind_ca_key_pass;
+			dns_conf_need_cert = 1;
+		}
+	}
 	tlog(TLOG_DEBUG, "bind ip %s, type: %d, flag: %X", ip, type, server_flag);
 
 	return 0;
@@ -1871,6 +1883,23 @@ static int _config_bind_ip_tcp(void *data, int argc, char *argv[])
 	return _config_bind_ip(argc, argv, DNS_BIND_TYPE_TCP);
 }
 
+static int _config_bind_ip_tls(void *data, int argc, char *argv[])
+{
+	return _config_bind_ip(argc, argv, DNS_BIND_TYPE_TLS);
+}
+
+static int _config_option_parser_filepath(void *data, int argc, char *argv[])
+{
+	if (argc <= 1) {
+		tlog(TLOG_ERROR, "invalid parameter.");
+		return -1;
+	}
+
+	conf_get_conf_fullpath(argv[1], data, DNS_MAX_PATH);
+
+	return 0;
+}
+
 static int _config_server_udp(void *data, int argc, char *argv[])
 {
 	return _config_server(argc, argv, DNS_SERVER_UDP, DEFAULT_DNS_PORT);
@@ -3041,6 +3070,10 @@ static struct config_item _config_item[] = {
 	CONF_YESNO("resolv-hostname", &dns_conf_resolv_hostname),
 	CONF_CUSTOM("bind", _config_bind_ip_udp, NULL),
 	CONF_CUSTOM("bind-tcp", _config_bind_ip_tcp, NULL),
+	CONF_CUSTOM("bind-tls", _config_bind_ip_tls, NULL),
+	CONF_CUSTOM("bind-cert-file", _config_option_parser_filepath, &dns_conf_bind_ca_file),
+	CONF_CUSTOM("bind-cert-key-file", _config_option_parser_filepath, &dns_conf_bind_ca_key_file),
+	CONF_STRING("bind-cert-key-pass", dns_conf_bind_ca_key_pass, DNS_MAX_PATH),
 	CONF_CUSTOM("server", _config_server_udp, NULL),
 	CONF_CUSTOM("server-tcp", _config_server_tcp, NULL),
 	CONF_CUSTOM("server-tls", _config_server_tls, NULL),
@@ -3071,13 +3104,13 @@ static struct config_item _config_item[] = {
 	CONF_INT("dualstack-ip-selection-threshold", &dns_conf_dualstack_ip_selection_threshold, 0, 1000),
 	CONF_CUSTOM("dns64", _config_dns64, NULL),
 	CONF_CUSTOM("log-level", _config_log_level, NULL),
-	CONF_STRING("log-file", (char *)dns_conf_log_file, DNS_MAX_PATH),
+	CONF_CUSTOM("log-file", _config_option_parser_filepath, (char *)dns_conf_log_file),
 	CONF_SIZE("log-size", &dns_conf_log_size, 0, 1024 * 1024 * 1024),
 	CONF_INT("log-num", &dns_conf_log_num, 0, 1024),
 	CONF_INT_BASE("log-file-mode", &dns_conf_log_file_mode, 0, 511, 8),
 	CONF_YESNO("audit-enable", &dns_conf_audit_enable),
 	CONF_YESNO("audit-SOA", &dns_conf_audit_log_SOA),
-	CONF_STRING("audit-file", (char *)&dns_conf_audit_file, DNS_MAX_PATH),
+	CONF_CUSTOM("audit-file", _config_option_parser_filepath, (char *)&dns_conf_audit_file),
 	CONF_INT_BASE("audit-file-mode", &dns_conf_audit_file_mode, 0, 511, 8),
 	CONF_SIZE("audit-size", &dns_conf_audit_size, 0, 1024 * 1024 * 1024),
 	CONF_INT("audit-num", &dns_conf_audit_num, 0, 1024),
@@ -3264,6 +3297,31 @@ errout:
 	return -1;
 }
 
+static int _check_and_create_cert(void)
+{
+	if (dns_conf_need_cert == 0) {
+		return 0;
+	}
+
+	if (dns_conf_bind_ca_file[0] != 0 && dns_conf_bind_ca_key_file[0] != 0) {
+		return -1;
+	}
+
+	conf_get_conf_fullpath("smartdns-cert.pem", dns_conf_bind_ca_file, sizeof(dns_conf_bind_ca_file));
+	conf_get_conf_fullpath("smartdns-key.pem", dns_conf_bind_ca_key_file, sizeof(dns_conf_bind_ca_key_file));
+	if (access(dns_conf_bind_ca_file, F_OK) == 0 && access(dns_conf_bind_ca_key_file, F_OK) == 0) {
+		return 0;
+	}
+
+	tlog(TLOG_INFO, "Generate default ssl cert and key file.");
+	if (generate_cert_key(dns_conf_bind_ca_key_file, dns_conf_bind_ca_file, NULL, 365 * 3) != 0) {
+		tlog(TLOG_WARN, "Generate default ssl cert and key file failed.");
+		return -1;
+	}
+
+	return 0;
+}
+
 static int _dns_conf_load_post(void)
 {
 	_config_setup_smartdns_domain();
@@ -3289,16 +3347,26 @@ static int _dns_conf_load_post(void)
 
 	_config_domain_set_name_table_destroy();
 
+	_check_and_create_cert();
+
 	return 0;
 }
 
 int dns_server_load_conf(const char *file)
 {
 	int ret = 0;
-	_dns_conf_load_pre();
+	ret = _dns_conf_load_pre();
+	if (ret != 0) {
+		return ret;
+	}
+
 	openlog("smartdns", LOG_CONS | LOG_NDELAY, LOG_LOCAL1);
 	ret = load_conf(file, _config_item, _conf_printf);
 	closelog();
-	_dns_conf_load_post();
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = _dns_conf_load_post();
 	return ret;
 }

+ 8 - 0
src/dns_conf.h

@@ -83,6 +83,7 @@ typedef enum {
 	DNS_BIND_TYPE_UDP,
 	DNS_BIND_TYPE_TCP,
 	DNS_BIND_TYPE_TLS,
+	DNS_BIND_TYPE_HTTPS,
 } DNS_BIND_TYPE;
 
 typedef enum {
@@ -344,6 +345,9 @@ struct dns_bind_ip {
 	DNS_BIND_TYPE type;
 	uint32_t flags;
 	char ip[DNS_MAX_IPLEN];
+	const char *ssl_cert_file;
+	const char *ssl_cert_key_file;
+	const char *ssl_cert_key_pass;
 	const char *group;
 };
 
@@ -406,6 +410,10 @@ extern struct dns_dns64 dns_conf_dns_dns64;
 extern struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
 extern int dns_conf_bind_ip_num;
 
+extern char dns_conf_bind_ca_file[DNS_MAX_PATH];
+extern char dns_conf_bind_ca_key_file[DNS_MAX_PATH];
+extern char dns_conf_bind_ca_key_pass[DNS_MAX_PATH];
+
 extern int dns_conf_tcp_idle_time;
 extern int dns_conf_cachesize;
 extern int dns_conf_prefetch;

+ 530 - 9
src/dns_server.c

@@ -37,6 +37,8 @@
 #include <net/if.h>
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -46,6 +48,10 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
 
 #define DNS_MAX_EVENTS 256
 #define IPV6_READY_CHECK_TIME 180
@@ -76,6 +82,8 @@ typedef enum {
 	DNS_CONN_TYPE_TCP_CLIENT,
 	DNS_CONN_TYPE_TLS_SERVER,
 	DNS_CONN_TYPE_TLS_CLIENT,
+	DNS_CONN_TYPE_HTTPS_SERVER,
+	DNS_CONN_TYPE_HTTPS_CLIENT,
 } DNS_CONN_TYPE;
 
 typedef enum DNS_CHILD_POST_RESULT {
@@ -132,6 +140,14 @@ struct dns_server_post_context {
 	int no_release_parent;
 };
 
+typedef enum dns_server_client_status {
+	DNS_SERVER_CLIENT_STATUS_INIT = 0,
+	DNS_SERVER_CLIENT_STATUS_CONNECTING,
+	DNS_SERVER_CLIENT_STATUS_CONNECTIONLESS,
+	DNS_SERVER_CLIENT_STATUS_CONNECTED,
+	DNS_SERVER_CLIENT_STATUS_DISCONNECTED,
+} dns_server_client_status;
+
 struct dns_server_conn_udp {
 	struct dns_server_conn_head head;
 	socklen_t addr_len;
@@ -142,6 +158,11 @@ struct dns_server_conn_tcp_server {
 	struct dns_server_conn_head head;
 };
 
+struct dns_server_conn_tls_server {
+	struct dns_server_conn_head head;
+	SSL_CTX *ssl_ctx;
+};
+
 struct dns_server_conn_tcp_client {
 	struct dns_server_conn_head head;
 	struct dns_conn_buf recvbuff;
@@ -151,6 +172,23 @@ struct dns_server_conn_tcp_client {
 
 	socklen_t localaddr_len;
 	struct sockaddr_storage localaddr;
+
+	dns_server_client_status status;
+};
+
+struct dns_server_conn_tls_client {
+	struct dns_server_conn_head head;
+	struct dns_conn_buf recvbuff;
+	struct dns_conn_buf sndbuff;
+	socklen_t addr_len;
+	struct sockaddr_storage addr;
+
+	socklen_t localaddr_len;
+	struct sockaddr_storage localaddr;
+	dns_server_client_status status;
+
+	SSL *ssl;
+	pthread_mutex_t ssl_lock;
 };
 
 /* ip address lists of domain */
@@ -306,6 +344,7 @@ static int _dns_request_post(struct dns_server_post_context *context);
 static int _dns_server_reply_all_pending_list(struct dns_request *request, struct dns_server_post_context *context);
 static void *_dns_server_get_dns_rule(struct dns_request *request, enum domain_rule rule);
 static const char *_dns_server_get_request_groupname(struct dns_request *request);
+static int _dns_server_tcp_socket_send(struct dns_server_conn_tcp_client *tcp_client, void *data, int data_len);
 
 static void _dns_server_wakeup_thread(void)
 {
@@ -460,7 +499,7 @@ static int _dns_server_is_return_soa(struct dns_request *request)
 	unsigned int flags = 0;
 
 	if (_dns_server_has_bind_flag(request, BIND_FLAG_NO_RULE_SOA) == 0) {
-		/* when both has no rule SOA and force AAAA soa, foce AAAA soa has high priority */
+		/* when both has no rule SOA and force AAAA soa, force AAAA soa has high priority */
 		if (request->qtype == DNS_T_AAAA && _dns_server_has_bind_flag(request, BIND_FLAG_FORCE_AAAA_SOA) == 0) {
 			return 1;
 		}
@@ -961,6 +1000,21 @@ static void _dns_server_conn_release(struct dns_server_conn_head *conn)
 		conn->fd = -1;
 	}
 
+	if (conn->type == DNS_CONN_TYPE_TLS_CLIENT || conn->type == DNS_CONN_TYPE_HTTPS_CLIENT) {
+		struct dns_server_conn_tls_client *tls_client = (struct dns_server_conn_tls_client *)conn;
+		if (tls_client->ssl != NULL) {
+			SSL_free(tls_client->ssl);
+			tls_client->ssl = NULL;
+		}
+		pthread_mutex_destroy(&tls_client->ssl_lock);
+	} else if (conn->type == DNS_CONN_TYPE_TLS_SERVER) {
+		struct dns_server_conn_tls_server *tls_server = (struct dns_server_conn_tls_server *)conn;
+		if (tls_server->ssl_ctx != NULL) {
+			SSL_CTX_free(tls_server->ssl_ctx);
+			tls_server->ssl_ctx = NULL;
+		}
+	}
+
 	list_del_init(&conn->list);
 	free(conn);
 }
@@ -1007,7 +1061,7 @@ static int _dns_server_reply_tcp(struct dns_request *request, struct dns_server_
 	memcpy(inpacket + 2, packet, len);
 	len += 2;
 
-	send_len = send(tcpclient->head.fd, inpacket, len, MSG_NOSIGNAL);
+	send_len = _dns_server_tcp_socket_send(tcpclient, inpacket, len);
 	if (send_len < 0) {
 		if (errno == EAGAIN) {
 			/* save data to buffer, and retry when EPOLLOUT is available */
@@ -1101,7 +1155,7 @@ static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpac
 	} else if (conn->type == DNS_CONN_TYPE_TCP_CLIENT) {
 		ret = _dns_server_reply_tcp(request, (struct dns_server_conn_tcp_client *)conn, inpacket, inpacket_len);
 	} else if (conn->type == DNS_CONN_TYPE_TLS_CLIENT) {
-		ret = -1;
+		ret = _dns_server_reply_tcp(request, (struct dns_server_conn_tcp_client *)conn, inpacket, inpacket_len);
 	} else {
 		ret = -1;
 	}
@@ -5389,6 +5443,209 @@ errout:
 	return -1;
 }
 
+static ssize_t _ssl_read(struct dns_server_conn_tls_client *conn, void *buff, int num)
+{
+	ssize_t ret = 0;
+	if (conn == NULL || buff == NULL) {
+		return SSL_ERROR_SYSCALL;
+	}
+
+	pthread_mutex_lock(&conn->ssl_lock);
+	ret = SSL_read(conn->ssl, buff, num);
+	pthread_mutex_unlock(&conn->ssl_lock);
+	return ret;
+}
+
+static ssize_t _ssl_write(struct dns_server_conn_tls_client *conn, const void *buff, int num)
+{
+	ssize_t ret = 0;
+	if (conn == NULL || buff == NULL || conn->ssl == NULL) {
+		return SSL_ERROR_SYSCALL;
+	}
+
+	pthread_mutex_lock(&conn->ssl_lock);
+	ret = SSL_write(conn->ssl, buff, num);
+	pthread_mutex_unlock(&conn->ssl_lock);
+	return ret;
+}
+
+static int _ssl_get_error(struct dns_server_conn_tls_client *conn, int ret)
+{
+	int err = 0;
+	if (conn == NULL || conn->ssl == NULL) {
+		return SSL_ERROR_SYSCALL;
+	}
+
+	pthread_mutex_lock(&conn->ssl_lock);
+	err = SSL_get_error(conn->ssl, ret);
+	pthread_mutex_unlock(&conn->ssl_lock);
+	return err;
+}
+
+static int _ssl_do_accept(struct dns_server_conn_tls_client *conn)
+{
+	int err = 0;
+	if (conn == NULL || conn->ssl == NULL) {
+		return SSL_ERROR_SYSCALL;
+	}
+
+	pthread_mutex_lock(&conn->ssl_lock);
+	err = SSL_accept(conn->ssl);
+	pthread_mutex_unlock(&conn->ssl_lock);
+	return err;
+}
+
+static int _dns_server_socket_ssl_send(struct dns_server_conn_tls_client *tls_client, const void *buf, int num)
+{
+	int ret = 0;
+	int ssl_ret = 0;
+	unsigned long ssl_err = 0;
+
+	if (tls_client->ssl == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (num < 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	ret = _ssl_write(tls_client, buf, num);
+	if (ret > 0) {
+		return ret;
+	}
+
+	ssl_ret = _ssl_get_error(tls_client, ret);
+	switch (ssl_ret) {
+	case SSL_ERROR_NONE:
+		return 0;
+		break;
+	case SSL_ERROR_ZERO_RETURN:
+	case SSL_ERROR_WANT_READ:
+		errno = EAGAIN;
+		ret = -1;
+		break;
+	case SSL_ERROR_WANT_WRITE:
+		errno = EAGAIN;
+		ret = -1;
+		break;
+	case SSL_ERROR_SSL:
+		ssl_err = ERR_get_error();
+		int ssl_reason = ERR_GET_REASON(ssl_err);
+		if (ssl_reason == SSL_R_UNINITIALIZED || ssl_reason == SSL_R_PROTOCOL_IS_SHUTDOWN ||
+			ssl_reason == SSL_R_BAD_LENGTH || ssl_reason == SSL_R_SHUTDOWN_WHILE_IN_INIT ||
+			ssl_reason == SSL_R_BAD_WRITE_RETRY) {
+			errno = EAGAIN;
+			return -1;
+		}
+
+		tlog(TLOG_ERROR, "SSL write fail error no:  %s(%d)\n", ERR_reason_error_string(ssl_err), ssl_reason);
+		errno = EFAULT;
+		ret = -1;
+		break;
+	case SSL_ERROR_SYSCALL:
+		tlog(TLOG_DEBUG, "SSL syscall failed, %s", strerror(errno));
+		return ret;
+	default:
+		errno = EFAULT;
+		ret = -1;
+		break;
+	}
+
+	return ret;
+}
+
+static int _dns_server_socket_ssl_recv(struct dns_server_conn_tls_client *tls_client, void *buf, int num)
+{
+	ssize_t ret = 0;
+	int ssl_ret = 0;
+	unsigned long ssl_err = 0;
+
+	if (tls_client->ssl == NULL) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	ret = _ssl_read(tls_client, buf, num);
+	if (ret >= 0) {
+		return ret;
+	}
+
+	ssl_ret = _ssl_get_error(tls_client, ret);
+	switch (ssl_ret) {
+	case SSL_ERROR_NONE:
+	case SSL_ERROR_ZERO_RETURN:
+		return 0;
+		break;
+	case SSL_ERROR_WANT_READ:
+		errno = EAGAIN;
+		ret = -1;
+		break;
+	case SSL_ERROR_WANT_WRITE:
+		errno = EAGAIN;
+		ret = -1;
+		break;
+	case SSL_ERROR_SSL:
+		ssl_err = ERR_get_error();
+		int ssl_reason = ERR_GET_REASON(ssl_err);
+		if (ssl_reason == SSL_R_UNINITIALIZED) {
+			errno = EAGAIN;
+			return -1;
+		}
+
+		if (ssl_reason == SSL_R_SHUTDOWN_WHILE_IN_INIT || ssl_reason == SSL_R_PROTOCOL_IS_SHUTDOWN) {
+			return 0;
+		}
+
+		tlog(TLOG_INFO, "SSL read fail error no: %s(%lx), len: %d\n", ERR_reason_error_string(ssl_err), ssl_err, num);
+		errno = EFAULT;
+		ret = -1;
+		break;
+	case SSL_ERROR_SYSCALL:
+		if (errno == 0) {
+			return 0;
+		}
+
+		if (errno != ECONNRESET) {
+			tlog(TLOG_INFO, "SSL syscall failed, %s ", strerror(errno));
+		}
+		ret = -1;
+		return ret;
+	default:
+		errno = EFAULT;
+		ret = -1;
+		break;
+	}
+
+	return ret;
+}
+
+static int _dns_server_tcp_socket_send(struct dns_server_conn_tcp_client *tcp_client, void *data, int data_len)
+{
+	if (tcp_client->head.type == DNS_CONN_TYPE_TCP_CLIENT) {
+		return send(tcp_client->head.fd, data, data_len, MSG_NOSIGNAL);
+	} else if (tcp_client->head.type == DNS_CONN_TYPE_TLS_CLIENT ||
+			   tcp_client->head.type == DNS_CONN_TYPE_HTTPS_CLIENT) {
+		int ret = _dns_server_socket_ssl_send((struct dns_server_conn_tls_client *)tcp_client, data, data_len);
+		return ret;
+	} else {
+		return -1;
+	}
+}
+
+static int _dns_server_tcp_socket_recv(struct dns_server_conn_tcp_client *tcp_client, void *data, int data_len)
+{
+	if (tcp_client->head.type == DNS_CONN_TYPE_TCP_CLIENT) {
+		return recv(tcp_client->head.fd, data, data_len, MSG_NOSIGNAL);
+	} else if (tcp_client->head.type == DNS_CONN_TYPE_TLS_CLIENT ||
+			   tcp_client->head.type == DNS_CONN_TYPE_HTTPS_CLIENT) {
+		return _dns_server_socket_ssl_recv((struct dns_server_conn_tls_client *)tcp_client, data, data_len);
+	} else {
+		return -1;
+	}
+}
+
 static int _dns_server_tcp_recv(struct dns_server_conn_tcp_client *tcpclient)
 {
 	ssize_t len = 0;
@@ -5399,8 +5656,8 @@ static int _dns_server_tcp_recv(struct dns_server_conn_tcp_client *tcpclient)
 			return 0;
 		}
 
-		len = recv(tcpclient->head.fd, tcpclient->recvbuff.buf + tcpclient->recvbuff.size,
-				   sizeof(tcpclient->recvbuff.buf) - tcpclient->recvbuff.size, 0);
+		len = _dns_server_tcp_socket_recv(tcpclient, tcpclient->recvbuff.buf + tcpclient->recvbuff.size,
+										  sizeof(tcpclient->recvbuff.buf) - tcpclient->recvbuff.size);
 		if (len < 0) {
 			if (errno == EAGAIN) {
 				return RECV_ERROR_AGAIN;
@@ -5517,7 +5774,7 @@ static int _dns_server_tcp_send(struct dns_server_conn_tcp_client *tcpclient)
 {
 	int len = 0;
 	while (tcpclient->sndbuff.size > 0) {
-		len = send(tcpclient->head.fd, tcpclient->sndbuff.buf, tcpclient->sndbuff.size, MSG_NOSIGNAL);
+		len = _dns_server_tcp_socket_send(tcpclient, tcpclient->sndbuff.buf, tcpclient->sndbuff.size);
 		if (len < 0) {
 			if (errno == EAGAIN) {
 				return RECV_ERROR_AGAIN;
@@ -5566,6 +5823,137 @@ static int _dns_server_process_tcp(struct dns_server_conn_tcp_client *dnsserver,
 	return 0;
 }
 
+static int _dns_server_tls_accept(struct dns_server_conn_tls_server *tls_server, struct epoll_event *event,
+								  unsigned long now)
+{
+	struct sockaddr_storage addr;
+	struct dns_server_conn_tls_client *tls_client = NULL;
+	socklen_t addr_len = sizeof(addr);
+	int fd = -1;
+	SSL *ssl = NULL;
+
+	fd = accept4(tls_server->head.fd, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
+	if (fd < 0) {
+		tlog(TLOG_ERROR, "accept failed, %s", strerror(errno));
+		return -1;
+	}
+
+	tls_client = malloc(sizeof(*tls_client));
+	if (tls_client == NULL) {
+		tlog(TLOG_ERROR, "malloc for tls_client failed.");
+		goto errout;
+	}
+	memset(tls_client, 0, sizeof(*tls_client));
+
+	tls_client->head.fd = fd;
+	tls_client->head.type = DNS_CONN_TYPE_TLS_CLIENT;
+	tls_client->head.server_flags = tls_server->head.server_flags;
+	tls_client->head.dns_group = tls_server->head.dns_group;
+	atomic_set(&tls_client->head.refcnt, 0);
+	memcpy(&tls_client->addr, &addr, addr_len);
+	tls_client->addr_len = addr_len;
+	tls_client->localaddr_len = sizeof(struct sockaddr_storage);
+	if (_dns_server_epoll_ctl(&tls_client->head, EPOLL_CTL_ADD, EPOLLIN) != 0) {
+		tlog(TLOG_ERROR, "epoll ctl failed.");
+		return -1;
+	}
+
+	if (getsocket_inet(tls_client->head.fd, (struct sockaddr *)&tls_client->localaddr, &tls_client->localaddr_len) !=
+		0) {
+		tlog(TLOG_ERROR, "get local addr failed, %s", strerror(errno));
+		goto errout;
+	}
+
+	ssl = SSL_new(tls_server->ssl_ctx);
+	if (ssl == NULL) {
+		tlog(TLOG_ERROR, "SSL_new failed.");
+		goto errout;
+	}
+
+	if (SSL_set_fd(ssl, fd) != 1) {
+		tlog(TLOG_ERROR, "SSL_set_fd failed.");
+		goto errout;
+	}
+
+	tls_client->ssl = ssl;
+	tls_client->status = DNS_SERVER_CLIENT_STATUS_CONNECTING;
+	pthread_mutex_init(&tls_client->ssl_lock, NULL);
+	_dns_server_client_touch(&tls_client->head);
+
+	list_add(&tls_client->head.list, &server.conn_list);
+	_dns_server_conn_get(&tls_client->head);
+
+	return 0;
+errout:
+	if (fd > 0) {
+		close(fd);
+	}
+
+	if (ssl) {
+		SSL_free(ssl);
+	}
+
+	if (tls_client) {
+		free(tls_client);
+	}
+	return -1;
+}
+
+static int _dns_server_process_tls(struct dns_server_conn_tls_client *tls_client, struct epoll_event *event,
+								   unsigned long now)
+{
+	int ret = 0;
+	int ssl_ret = 0;
+	struct epoll_event fd_event;
+
+	if (tls_client->status == DNS_SERVER_CLIENT_STATUS_CONNECTING) {
+		/* do SSL hand shake */
+		ret = _ssl_do_accept(tls_client);
+		if (ret == 0) {
+			goto errout;
+		} else if (ret < 0) {
+			memset(&fd_event, 0, sizeof(fd_event));
+			ssl_ret = _ssl_get_error(tls_client, ret);
+			if (ssl_ret == SSL_ERROR_WANT_READ) {
+				fd_event.events = EPOLLIN;
+			} else if (ssl_ret == SSL_ERROR_WANT_WRITE) {
+				fd_event.events = EPOLLOUT | EPOLLIN;
+			} else if (ssl_ret == SSL_ERROR_SYSCALL) {
+				goto errout;
+			} else {
+				unsigned long ssl_err = ERR_get_error();
+				int ssl_reason = ERR_GET_REASON(ssl_err);
+				tlog(TLOG_DEBUG, "Handshake with %s failed, error no: %s(%d, %d, %d)\n", "",
+					 ERR_reason_error_string(ssl_err), ret, ssl_ret, ssl_reason);
+				ret = 0;
+				goto errout;
+			}
+
+			fd_event.data.ptr = tls_client;
+			if (epoll_ctl(server.epoll_fd, EPOLL_CTL_MOD, tls_client->head.fd, &fd_event) != 0) {
+				tlog(TLOG_ERROR, "epoll ctl failed, %s", strerror(errno));
+				goto errout;
+			}
+
+			return 0;
+		}
+
+		tls_client->status = DNS_SERVER_CLIENT_STATUS_CONNECTED;
+		memset(&fd_event, 0, sizeof(fd_event));
+		fd_event.events = EPOLLIN | EPOLLOUT;
+		fd_event.data.ptr = tls_client;
+		if (epoll_ctl(server.epoll_fd, EPOLL_CTL_MOD, tls_client->head.fd, &fd_event) != 0) {
+			tlog(TLOG_ERROR, "epoll ctl failed, %s", strerror(errno));
+			goto errout;
+		}
+	}
+
+	return _dns_server_process_tcp((struct dns_server_conn_tcp_client *)tls_client, event, now);
+errout:
+	_dns_server_client_close(&tls_client->head);
+	return ret;
+}
+
 static int _dns_server_process(struct dns_server_conn_head *conn, struct epoll_event *event, unsigned long now)
 {
 	int ret = 0;
@@ -5586,10 +5974,19 @@ static int _dns_server_process(struct dns_server_conn_head *conn, struct epoll_e
 				 get_host_by_addr(name, sizeof(name), (struct sockaddr *)&tcpclient->addr));
 		}
 	} else if (conn->type == DNS_CONN_TYPE_TLS_SERVER) {
-		tlog(TLOG_ERROR, "unsupported dns server type %d", conn->type);
-		ret = -1;
+		struct dns_server_conn_tls_server *tls_server = (struct dns_server_conn_tls_server *)conn;
+		ret = _dns_server_tls_accept(tls_server, event, now);
+	} else if (conn->type == DNS_CONN_TYPE_TLS_CLIENT) {
+		struct dns_server_conn_tls_client *tls_client = (struct dns_server_conn_tls_client *)conn;
+		ret = _dns_server_process_tls(tls_client, event, now);
+		if (ret != 0) {
+			char name[DNS_MAX_CNAME_LEN];
+			tlog(TLOG_DEBUG, "process TLS packet from %s failed.",
+				 get_host_by_addr(name, sizeof(name), (struct sockaddr *)&tls_client->addr));
+		}
 	} else {
 		tlog(TLOG_ERROR, "unsupported dns server type %d", conn->type);
+		_dns_server_client_close(conn);
 		ret = -1;
 	}
 	_dns_server_conn_release(conn);
@@ -5818,9 +6215,18 @@ static void _dns_server_close_socket_server(void)
 	list_for_each_entry_safe(conn, tmp, &server.conn_list, list)
 	{
 		switch (conn->type) {
+		case DNS_CONN_TYPE_HTTPS_SERVER:
+		case DNS_CONN_TYPE_TLS_SERVER: {
+			struct dns_server_conn_tls_server *tls_server = (struct dns_server_conn_tls_server *)conn;
+			if (tls_server->ssl_ctx) {
+				SSL_CTX_free(tls_server->ssl_ctx);
+				tls_server->ssl_ctx = NULL;
+			}
+			_dns_server_client_close(conn);
+			break;
+		}
 		case DNS_CONN_TYPE_UDP_SERVER:
 		case DNS_CONN_TYPE_TCP_SERVER:
-		case DNS_CONN_TYPE_TLS_SERVER:
 			_dns_server_client_close(conn);
 			break;
 		default:
@@ -6113,6 +6519,7 @@ static int _dns_server_socket_tcp(struct dns_bind_ip *bind_ip)
 	const char *host_ip = NULL;
 	struct dns_server_conn_tcp_server *conn = NULL;
 	int fd = -1;
+	const int on = 1;
 
 	host_ip = bind_ip->ip;
 	conn = malloc(sizeof(struct dns_server_conn_tcp_server));
@@ -6126,6 +6533,8 @@ static int _dns_server_socket_tcp(struct dns_bind_ip *bind_ip)
 		goto errout;
 	}
 
+	setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &on, sizeof(on));
+
 	conn->head.type = DNS_CONN_TYPE_TCP_SERVER;
 	conn->head.fd = fd;
 	_dns_server_set_flags(&conn->head, bind_ip);
@@ -6144,6 +6553,110 @@ errout:
 	return -1;
 }
 
+static int _dns_server_socket_tls_ssl_pass_callback(char *buf, int size, int rwflag, void *userdata)
+{
+	struct dns_bind_ip *bind_ip = userdata;
+	if (bind_ip->ssl_cert_key_pass == NULL || bind_ip->ssl_cert_key_pass[0] == '\0') {
+		return 0;
+	}
+	safe_strncpy(buf, bind_ip->ssl_cert_key_pass, size);
+	return strlen(buf);
+}
+
+static int _dns_server_socket_tls(struct dns_bind_ip *bind_ip, DNS_CONN_TYPE conn_type)
+{
+	const char *host_ip = NULL;
+	const char *ssl_cert_file = NULL;
+	const char *ssl_cert_key_file = NULL;
+
+	struct dns_server_conn_tls_server *conn = NULL;
+	int fd = -1;
+	const SSL_METHOD *method = NULL;
+	SSL_CTX *ssl_ctx = NULL;
+	const int on = 1;
+
+	host_ip = bind_ip->ip;
+	ssl_cert_file = bind_ip->ssl_cert_file;
+	ssl_cert_key_file = bind_ip->ssl_cert_key_file;
+
+	if (ssl_cert_file == NULL || ssl_cert_key_file == NULL) {
+		tlog(TLOG_WARN, "no cert or cert key file");
+		goto errout;
+	}
+
+	if (ssl_cert_file[0] == '\0' || ssl_cert_key_file[0] == '\0') {
+		tlog(TLOG_WARN, "no cert or cert key file");
+		goto errout;
+	}
+
+	conn = malloc(sizeof(struct dns_server_conn_tls_server));
+	if (conn == NULL) {
+		goto errout;
+	}
+	INIT_LIST_HEAD(&conn->head.list);
+
+	fd = _dns_create_socket(host_ip, SOCK_STREAM);
+	if (fd <= 0) {
+		goto errout;
+	}
+
+	setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &on, sizeof(on));
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+	method = TLS_server_method();
+	if (method == NULL) {
+		goto errout;
+	}
+#else
+	method = SSLv23_server_method();
+#endif
+
+	ssl_ctx = SSL_CTX_new(method);
+	if (ssl_ctx == NULL) {
+		goto errout;
+	}
+
+	SSL_CTX_set_session_cache_mode(ssl_ctx,
+								   SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+	SSL_CTX_set_default_passwd_cb(ssl_ctx, _dns_server_socket_tls_ssl_pass_callback);
+	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, bind_ip);
+
+	/* Set the key and cert */
+	if (ssl_cert_file[0] != '\0' && SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert_file, SSL_FILETYPE_PEM) <= 0) {
+		tlog(TLOG_ERROR, "load cert %s failed, %s", ssl_cert_file, ERR_error_string(ERR_get_error(), NULL));
+		goto errout;
+	}
+
+	if (ssl_cert_key_file[0] != '\0' &&
+		SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_cert_key_file, SSL_FILETYPE_PEM) <= 0) {
+		tlog(TLOG_ERROR, "load cert key %s failed, %s", ssl_cert_key_file, ERR_error_string(ERR_get_error(), NULL));
+		goto errout;
+	}
+
+	conn->head.type = conn_type;
+	conn->head.fd = fd;
+	conn->ssl_ctx = ssl_ctx;
+	_dns_server_set_flags(&conn->head, bind_ip);
+	_dns_server_conn_get(&conn->head);
+
+	return 0;
+errout:
+	if (ssl_ctx) {
+		SSL_CTX_free(ssl_ctx);
+		ssl_ctx = NULL;
+	}
+
+	if (conn) {
+		free(conn);
+		conn = NULL;
+	}
+
+	if (fd > 0) {
+		close(fd);
+	}
+	return -1;
+}
+
 static int _dns_server_socket(void)
 {
 	int i = 0;
@@ -6161,7 +6674,15 @@ static int _dns_server_socket(void)
 				goto errout;
 			}
 			break;
+		case DNS_BIND_TYPE_HTTPS:
+			if (_dns_server_socket_tls(bind_ip, DNS_CONN_TYPE_HTTPS_SERVER) != 0) {
+				goto errout;
+			}
+			break;
 		case DNS_BIND_TYPE_TLS:
+			if (_dns_server_socket_tls(bind_ip, DNS_CONN_TYPE_TLS_SERVER) != 0) {
+				goto errout;
+			}
 			break;
 		default:
 			break;

+ 1 - 1
src/include/conf.h

@@ -159,7 +159,7 @@ extern int conf_enum(const char *item, void *data, int argc, char *argv[]);
  * Example:
  *  int num = 0;
  *
- *  struct config_item itmes [] = {
+ *  struct config_item items [] = {
  *       CONF_INT("CONF_NUM", &num, -1, 10),
  *       CONF_END();
  *  }

+ 117 - 0
src/util.c

@@ -828,6 +828,123 @@ errout:
 	return -1;
 }
 
+int generate_cert_key(const char *key_path, const char *cert_path, const char *san, int days)
+{
+	int ret = -1;
+#if (OPENSSL_VERSION_NUMBER <= 0x30000000L)
+	RSA *rsa = NULL;
+	BIGNUM *bn = NULL;
+#endif
+	X509_EXTENSION *cert_ext = NULL;
+	BIO *cert_file = NULL;
+	BIO *key_file = NULL;
+	X509 *cert = NULL;
+	EVP_PKEY *pkey = NULL;
+	const int RSA_KEY_LENGTH = 2048;
+
+	if (key_path == NULL || cert_path == NULL) {
+		return ret;
+	}
+
+	key_file = BIO_new_file(key_path, "wb");
+	cert_file = BIO_new_file(cert_path, "wb");
+	cert = X509_new();
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+	pkey = EVP_RSA_gen(RSA_KEY_LENGTH);
+#else
+	bn = BN_new();
+	rsa = RSA_new();
+	pkey = EVP_PKEY_new();
+	if (rsa == NULL || pkey == NULL || bn == NULL) {
+		goto out;
+	}
+
+	EVP_PKEY_assign(pkey, EVP_PKEY_RSA, rsa);
+	BN_set_word(bn, RSA_F4);
+	if (RSA_generate_key_ex(rsa, RSA_KEY_LENGTH, bn, NULL) != 1) {
+		goto out;
+	}
+#endif
+
+	if (key_file == NULL || cert_file == NULL || cert == NULL || pkey == NULL) {
+		goto out;
+	}
+
+	ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);           // serial number
+	X509_gmtime_adj(X509_get_notBefore(cert), 0);               // now
+	X509_gmtime_adj(X509_get_notAfter(cert), days * 24 * 3600); // accepts secs
+
+	X509_set_pubkey(cert, pkey);
+
+	X509_NAME *name = X509_get_subject_name(cert);
+
+	const unsigned char *country = (unsigned char *)"smartdns";
+	const unsigned char *company = (unsigned char *)"smartdns";
+	const unsigned char *common_name = (unsigned char *)"smartdns";
+
+	X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, country, -1, -1, 0);
+	X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, company, -1, -1, 0);
+	X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, common_name, -1, -1, 0);
+
+	if (san != NULL) {
+		cert_ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, san);
+		if (cert_ext == NULL) {
+			goto out;
+		}
+		X509_add_ext(cert, cert_ext, -1);
+	}
+
+	X509_set_issuer_name(cert, name);
+	X509_sign(cert, pkey, EVP_sha256());
+
+	ret = PEM_write_bio_PrivateKey(key_file, pkey, NULL, NULL, 0, NULL, NULL);
+	if (ret != 1) {
+		goto out;
+	}
+
+	ret = PEM_write_bio_X509(cert_file, cert);
+	if (ret != 1) {
+		goto out;
+	}
+
+	chmod(key_path, S_IRUSR);
+	chmod(cert_path, S_IRUSR);
+
+	ret = 0;
+out:
+	if (cert_ext) {
+		X509_EXTENSION_free(cert_ext);
+	}
+
+	if (pkey) {
+		EVP_PKEY_free(pkey);
+	}
+
+#if (OPENSSL_VERSION_NUMBER <= 0x30000000L)
+	if (rsa && pkey == NULL) {
+		RSA_free(rsa);
+	}
+
+	if (bn) {
+		BN_free(bn);
+	}
+#endif
+
+	if (cert_file) {
+		BIO_free_all(cert_file);
+	}
+
+	if (key_file) {
+		BIO_free_all(key_file);
+	}
+
+	if (cert) {
+		X509_free(cert);
+	}
+
+	return ret;
+}
+
 #if OPENSSL_API_COMPAT < 0x10100000
 #define THREAD_STACK_SIZE (16 * 1024)
 static pthread_mutex_t *lock_cs;

+ 2 - 0
src/util.h

@@ -97,6 +97,8 @@ int SSL_base64_decode(const char *in, unsigned char *out);
 
 int SSL_base64_encode(const void *in, int in_len, char *out);
 
+int generate_cert_key(const char *key_path, const char *cert_path, const char *san, int days);
+
 int create_pid_file(const char *pid_file);
 
 /* Parse a TLS packet for the Server Name Indication extension in the client