Browse Source

plugin: add plugin framework.

Nick Peng 1 year ago
parent
commit
903c7d5b9e

+ 5 - 0
etc/smartdns/smartdns.conf

@@ -395,3 +395,8 @@ log-level info
 # group-begin [group-name]
 # group-begin [group-name]
 # group-match [-g|group group-name] [-domain domain] [-client-ip [ip-cidr|mac|ip-set]]
 # group-match [-g|group group-name] [-domain domain] [-client-ip [ip-cidr|mac|ip-set]]
 # group-end
 # group-end
+
+# load plugin
+# plugin [path/to/file] [args]
+# plugin /usr/lib/smartdns/libsmartdns-ui.so --p 8080 -i 0.0.0.0 -r /usr/share/smartdns/wwwroot
+

+ 4 - 0
plugin/demo/.gitignore

@@ -0,0 +1,4 @@
+.vscode
+*.o
+*.so
+*.swp.

+ 45 - 0
plugin/demo/Makefile

@@ -0,0 +1,45 @@
+
+# Copyright (C) 2018-2024 Ruilin Peng (Nick) <[email protected]>.
+#
+# 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/>.
+
+BIN=smartdns-demo.so 
+OBJS_MAIN=$(patsubst %.c,%.o,$(wildcard *.c))
+OBJS=$(OBJS_MAIN)
+
+# cflags
+ifndef CFLAGS
+ ifdef DEBUG
+  CFLAGS = -g -DDEBUG
+ else
+  CFLAGS = -O2
+ endif
+ CFLAGS +=-fPIC -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing -funwind-tables -Wmissing-prototypes -Wshadow -Wextra -Wno-unused-parameter -Wno-implicit-fallthrough
+endif
+
+override CFLAGS +=-Iinclude -I../../src/include -I../../src
+override CFLAGS += -DBASE_FILE_NAME='"$(notdir $<)"'
+override CFLAGS += $(EXTRA_CFLAGS)
+
+override LDFLAGS += -lpthread -shared
+
+.PHONY: all clean
+
+all: $(BIN)
+
+$(BIN) : $(OBJS)
+	$(CC) $(OBJS) -o $@ $(LDFLAGS)
+
+clean:
+	$(RM) $(OBJS) $(BIN)

+ 49 - 0
plugin/demo/demo.c

@@ -0,0 +1,49 @@
+#include "demo.h"
+#include "dns_server.h"
+#include "util.h"
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <tlog.h>
+
+static int demo_server_recv(struct dns_packet *packet, unsigned char *inpacket, int inpacket_len,
+							struct sockaddr_storage *local, socklen_t local_len, struct sockaddr_storage *from,
+							socklen_t from_len)
+{
+	char hostname[256] = {0};
+	tlog(TLOG_INFO, "recv packet from %s", get_host_by_addr(hostname, sizeof(hostname), (struct sockaddr *)from));
+	return 0;
+}
+
+static void demo_server_request_complete(struct dns_request *request)
+{
+	tlog(TLOG_INFO, "server complete request, request domain is %s", dns_server_request_get_domain(request));
+}
+
+struct smartdns_operations demo_ops = {
+	.server_recv = demo_server_recv,
+	.server_query_complete = demo_server_request_complete,
+};
+
+int dns_plugin_init(struct dns_plugin *plugin)
+{
+	char options[4096] = {0};
+	int argc = dns_plugin_get_argc(plugin);
+	const char **argv = dns_plugin_get_argv(plugin);
+
+	for (int i = 0; i < argc; i++) {
+		snprintf(options + strlen(options), sizeof(options) - strlen(options), "%s ", argv[i]);
+	}
+
+	tlog(TLOG_INFO, "demo plugin init, options: %s", options);
+	smartdns_operations_register(&demo_ops);
+	return 0;
+}
+
+int dns_plugin_exit(struct dns_plugin *plugin)
+{
+	tlog(TLOG_INFO, "demo plugin exit.");
+	smartdns_operations_unregister(&demo_ops);
+	return 0;
+}

+ 15 - 0
plugin/demo/demo.h

@@ -0,0 +1,15 @@
+
+#ifndef SMART_DNS_PLUGIN_DEMO_H
+#define SMART_DNS_PLUGIN_DEMO_H
+
+#include "dns_plugin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /*__cplusplus */
+
+
+#ifdef __cplusplus
+}
+#endif /*__cplusplus */
+#endif

+ 3 - 5
src/Makefile

@@ -48,13 +48,11 @@ endif
 CXXFLAGS=-O2 -g -Wall -std=c++11 
 CXXFLAGS=-O2 -g -Wall -std=c++11 
 override CXXFLAGS +=-Iinclude
 override CXXFLAGS +=-Iinclude
 
 
-# ldflags
 ifeq ($(STATIC), yes)
 ifeq ($(STATIC), yes)
- override LDFLAGS += -lssl -lcrypto -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -ldl -lm -static
-else ifeq ($(SSL_STATIC), yes)
- override LDFLAGS += -Wl,-Bstatic -lssl -lcrypto -Wl,-Bdynamic -lpthread -ldl -lm
+ override CFLAGS += -DBUILD_STATIC
+ override LDFLAGS += -lssl -lcrypto -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -ldl -lm -static -rdynamic
 else
 else
- override LDFLAGS += -lssl -lcrypto -lpthread -ldl -lm
+ override LDFLAGS += -lssl -lcrypto -lpthread -ldl -lm -rdynamic
 endif
 endif
 
 
 .PHONY: all clean
 .PHONY: all clean

+ 14 - 9
src/dns_cache.c

@@ -496,6 +496,19 @@ void dns_cache_data_get(struct dns_cache_data *cache_data)
 	return;
 	return;
 }
 }
 
 
+void dns_cache_flush(void)
+{
+	struct dns_cache *dns_cache = NULL;
+	struct dns_cache *tmp = NULL;
+
+	pthread_mutex_lock(&dns_cache_head.lock);
+	list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
+	{
+		_dns_cache_remove(dns_cache);
+	}
+	pthread_mutex_unlock(&dns_cache_head.lock);
+}
+
 void dns_cache_data_put(struct dns_cache_data *cache_data)
 void dns_cache_data_put(struct dns_cache_data *cache_data)
 {
 {
 	if (cache_data == NULL) {
 	if (cache_data == NULL) {
@@ -853,19 +866,11 @@ int dns_cache_print(const char *file)
 
 
 void dns_cache_destroy(void)
 void dns_cache_destroy(void)
 {
 {
-	struct dns_cache *dns_cache = NULL;
-	struct dns_cache *tmp = NULL;
-
 	if (is_cache_init == 0) {
 	if (is_cache_init == 0) {
 		return;
 		return;
 	}
 	}
 
 
-	pthread_mutex_lock(&dns_cache_head.lock);
-	list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list)
-	{
-		_dns_cache_remove(dns_cache);
-	}
-	pthread_mutex_unlock(&dns_cache_head.lock);
+	dns_cache_flush();
 
 
 	pthread_mutex_destroy(&dns_cache_head.lock);
 	pthread_mutex_destroy(&dns_cache_head.lock);
 	hash_table_free(dns_cache_head.cache_hash, free);
 	hash_table_free(dns_cache_head.cache_hash, free);

+ 2 - 0
src/dns_cache.h

@@ -166,6 +166,8 @@ void dns_cache_data_get(struct dns_cache_data *cache_data);
 
 
 void dns_cache_data_put(struct dns_cache_data *cache_data);
 void dns_cache_data_put(struct dns_cache_data *cache_data);
 
 
+void dns_cache_flush(void);
+
 void dns_cache_destroy(void);
 void dns_cache_destroy(void);
 
 
 int dns_cache_load(const char *file);
 int dns_cache_load(const char *file);

+ 93 - 0
src/dns_conf.c

@@ -169,6 +169,8 @@ struct conf_file_path {
 	char file[DNS_MAX_PATH];
 	char file[DNS_MAX_PATH];
 };
 };
 
 
+struct dns_conf_plugin_table dns_conf_plugin_table;
+
 char dns_conf_sni_proxy_ip[DNS_MAX_IPLEN];
 char dns_conf_sni_proxy_ip[DNS_MAX_IPLEN];
 
 
 static int _conf_domain_rule_nameserver(const char *domain, const char *group_name);
 static int _conf_domain_rule_nameserver(const char *domain, const char *group_name);
@@ -5451,6 +5453,81 @@ errout:
 	return -1;
 	return -1;
 }
 }
 
 
+static struct dns_conf_plugin *_config_get_plugin(const char *file)
+{
+	uint32_t key = 0;
+	struct dns_conf_plugin *plugin = NULL;
+
+	key = hash_string(file);
+	hash_for_each_possible(dns_conf_plugin_table.plugins, plugin, node, key)
+	{
+		if (strncmp(plugin->file, file, DNS_MAX_PATH) != 0) {
+			continue;
+		}
+
+		return plugin;
+	}
+
+	return NULL;
+}
+
+static int _config_plugin(void *data, int argc, char *argv[])
+{
+#ifdef BUILD_STATIC
+	tlog(TLOG_ERROR, "plugin not support in static release, please install dynamic release.");
+	goto errout;
+#endif
+	char file[DNS_MAX_PATH];
+	unsigned int key = 0;
+	int i = 0;
+	char *ptr = NULL;
+	char *ptr_end = NULL;
+
+	if (argc < 1) {
+		tlog(TLOG_ERROR, "invalid parameter.");
+		goto errout;
+	}
+
+	conf_get_conf_fullpath(argv[1], file, sizeof(file));
+	if (file[0] == '\0') {
+		tlog(TLOG_ERROR, "plugin: invalid parameter.");
+		goto errout;
+	}
+
+	struct dns_conf_plugin *plugin = _config_get_plugin(file);
+	if (plugin != NULL) {
+		tlog(TLOG_ERROR, "plugin '%s' already exists.", file);
+		goto errout;
+	}
+
+	if (access(file, F_OK) != 0) {
+		tlog(TLOG_ERROR, "plugin '%s' not exists.", file);
+		goto errout;
+	}
+
+	plugin = malloc(sizeof(*plugin));
+	if (plugin == NULL) {
+		goto errout;
+	}
+	memset(plugin, 0, sizeof(*plugin));
+	safe_strncpy(plugin->file, file, sizeof(plugin->file) - 1);
+	ptr = plugin->args;
+	ptr_end = plugin->args + sizeof(plugin->args) - 2;
+	for (i = 1; i < argc && ptr < ptr_end; i++) {
+		safe_strncpy(ptr, argv[i], ptr_end - ptr - 1);
+		ptr += strlen(argv[i]) + 1;
+	}
+	plugin->argc = argc - 1;
+	plugin->args_len = ptr - plugin->args;
+
+	key = hash_string(file);
+	hash_add(dns_conf_plugin_table.plugins, &plugin->node, key);
+
+	return 0;
+errout:
+	return -1;
+}
+
 int dns_server_check_update_hosts(void)
 int dns_server_check_update_hosts(void)
 {
 {
 	struct stat statbuf;
 	struct stat statbuf;
@@ -5672,6 +5749,7 @@ static struct config_item _config_item[] = {
 	CONF_YESNO("debug-save-fail-packet", &dns_save_fail_packet),
 	CONF_YESNO("debug-save-fail-packet", &dns_save_fail_packet),
 	CONF_YESNO("no-pidfile", &dns_no_pidfile),
 	CONF_YESNO("no-pidfile", &dns_no_pidfile),
 	CONF_YESNO("no-daemon", &dns_no_daemon),
 	CONF_YESNO("no-daemon", &dns_no_daemon),
+	CONF_CUSTOM("plugin", _config_plugin, NULL),
 	CONF_STRING("resolv-file", (char *)&dns_resolv_file, sizeof(dns_resolv_file)),
 	CONF_STRING("resolv-file", (char *)&dns_resolv_file, sizeof(dns_resolv_file)),
 	CONF_STRING("debug-save-fail-packet-dir", (char *)&dns_save_fail_packet_dir, sizeof(dns_save_fail_packet_dir)),
 	CONF_STRING("debug-save-fail-packet-dir", (char *)&dns_save_fail_packet_dir, sizeof(dns_save_fail_packet_dir)),
 	CONF_CUSTOM("conf-file", config_additional_file, NULL),
 	CONF_CUSTOM("conf-file", config_additional_file, NULL),
@@ -5854,6 +5932,7 @@ static int _dns_server_load_conf_init(void)
 	hash_init(dns_domain_set_name_table.names);
 	hash_init(dns_domain_set_name_table.names);
 	hash_init(dns_ip_set_name_table.names);
 	hash_init(dns_ip_set_name_table.names);
 	hash_init(dns_conf_srv_record_table.srv);
 	hash_init(dns_conf_srv_record_table.srv);
+	hash_init(dns_conf_plugin_table.plugins);
 
 
 	_config_current_group_push_default();
 	_config_current_group_push_default();
 
 
@@ -5909,6 +5988,19 @@ static void _config_client_rule_destroy(void)
 	_config_client_rule_destroy_mac();
 	_config_client_rule_destroy_mac();
 }
 }
 
 
+static void _config_plugin_table_destroy(void)
+{
+	struct dns_conf_plugin *plugin = NULL;
+	struct hlist_node *tmp = NULL;
+	unsigned long i = 0;
+
+	hash_for_each_safe(dns_conf_plugin_table.plugins, i, tmp, plugin, node)
+	{
+		hlist_del_init(&plugin->node);
+		free(plugin);
+	}
+}
+
 void dns_server_load_exit(void)
 void dns_server_load_exit(void)
 {
 {
 	_config_rule_group_destroy();
 	_config_rule_group_destroy();
@@ -5920,6 +6012,7 @@ void dns_server_load_exit(void)
 	_config_host_table_destroy(0);
 	_config_host_table_destroy(0);
 	_config_proxy_table_destroy();
 	_config_proxy_table_destroy();
 	_config_srv_record_table_destroy();
 	_config_srv_record_table_destroy();
+	_config_plugin_table_destroy();
 
 
 	dns_conf_server_num = 0;
 	dns_conf_server_num = 0;
 	dns_server_bind_destroy();
 	dns_server_bind_destroy();

+ 13 - 0
src/dns_conf.h

@@ -615,6 +615,19 @@ extern struct dns_dns64 dns_conf_dns_dns64;
 extern struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
 extern struct dns_bind_ip dns_conf_bind_ip[DNS_MAX_BIND_IP];
 extern int dns_conf_bind_ip_num;
 extern int dns_conf_bind_ip_num;
 
 
+struct dns_conf_plugin {
+	struct hlist_node node;
+	char name[DNS_MAX_CNAME_LEN];
+	char file[DNS_MAX_PATH];
+	char args[DNS_MAX_PATH * 4];
+	int argc;
+	int args_len;
+};
+struct dns_conf_plugin_table {
+	DECLARE_HASHTABLE(plugins, 4);
+};
+extern struct dns_conf_plugin_table dns_conf_plugin_table;
+
 extern char dns_conf_bind_ca_file[DNS_MAX_PATH];
 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_file[DNS_MAX_PATH];
 extern char dns_conf_bind_ca_key_pass[DNS_MAX_PATH];
 extern char dns_conf_bind_ca_key_pass[DNS_MAX_PATH];

+ 345 - 0
src/dns_plugin.c

@@ -0,0 +1,345 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
+ *
+ * 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/>.
+ */
+
+#include "dns_plugin.h"
+
+#include "include/conf.h"
+#include "include/hashtable.h"
+#include "include/list.h"
+#include "util.h"
+#include <dlfcn.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tlog.h"
+
+struct dns_plugin_ops {
+	struct list_head list;
+	struct smartdns_operations ops;
+};
+
+#define DNS_PLUGIN_MAX_ARGS 32
+struct dns_plugin {
+	struct hlist_node node;
+	char file[PATH_MAX];
+	char args[PATH_MAX];
+	int argc;
+	char *argv[DNS_PLUGIN_MAX_ARGS];
+	void *handle;
+	dns_plugin_init_func init_func;
+	dns_plugin_exit_func exit_func;
+};
+
+struct dns_plugins {
+	struct list_head list;
+	DECLARE_HASHTABLE(plugin, 4);
+};
+
+static struct dns_plugins plugins;
+static int is_plugin_init;
+
+int smartdns_plugin_func_server_recv(struct dns_packet *packet, unsigned char *inpacket, int inpacket_len,
+									 struct sockaddr_storage *local, socklen_t local_len, struct sockaddr_storage *from,
+									 socklen_t from_len)
+{
+	struct dns_plugin_ops *chain = NULL;
+	int ret = 0;
+
+	list_for_each_entry(chain, &plugins.list, list)
+	{
+		if (!chain->ops.server_recv) {
+			continue;
+		}
+
+		ret = chain->ops.server_recv(packet, inpacket, inpacket_len, local, local_len, from, from_len);
+		if (ret != 0) {
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+void smartdns_plugin_func_server_complete_request(struct dns_request *request)
+{
+	struct dns_plugin_ops *chain = NULL;
+
+	list_for_each_entry(chain, &plugins.list, list)
+	{
+		if (!chain->ops.server_query_complete) {
+			continue;
+		}
+
+		chain->ops.server_query_complete(request);
+	}
+
+	return;
+}
+
+int smartdns_operations_register(struct smartdns_operations *operations)
+{
+	struct dns_plugin_ops *chain = NULL;
+
+	chain = (struct dns_plugin_ops *)malloc(sizeof(struct dns_plugin_ops));
+	if (!chain) {
+		return -1;
+	}
+
+	memcpy(&chain->ops, operations, sizeof(struct smartdns_operations));
+	list_add_tail(&chain->list, &plugins.list);
+
+	return 0;
+}
+
+int smartdns_operations_unregister(struct smartdns_operations *operations)
+{
+	struct dns_plugin_ops *chain = NULL;
+	struct dns_plugin_ops *tmp = NULL;
+
+	list_for_each_entry_safe(chain, tmp, &plugins.list, list)
+	{
+		if (memcmp(&chain->ops, operations, sizeof(struct smartdns_operations)) == 0) {
+			list_del(&chain->list);
+			free(chain);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static struct dns_plugin *_dns_plugin_get(const char *plugin_file)
+{
+	struct dns_plugin *plugin = NULL;
+	unsigned int key = 0;
+
+	key = hash_string(plugin_file);
+	hash_for_each_possible(plugins.plugin, plugin, node, key)
+	{
+		if (strncmp(plugin->file, plugin_file, PATH_MAX - 1) == 0) {
+			return plugin;
+		}
+	}
+
+	return NULL;
+}
+
+static int _dns_plugin_load_library(struct dns_plugin *plugin)
+{
+	void *handle = NULL;
+	dns_plugin_init_func init_func = NULL;
+	dns_plugin_exit_func exit_func = NULL;
+
+	handle = dlopen(plugin->file, RTLD_LAZY | RTLD_LOCAL);
+	if (!handle) {
+		tlog(TLOG_ERROR, "load plugin %s failed: %s", plugin->file, dlerror());
+		return -1;
+	}
+
+	init_func = (dns_plugin_init_func)dlsym(handle, DNS_PLUGIN_INIT_FUNC);
+	if (!init_func) {
+		tlog(TLOG_ERROR, "load plugin %s failed: %s", plugin->file, dlerror());
+		goto errout;
+	}
+
+	exit_func = (dns_plugin_exit_func)dlsym(handle, DNS_PLUGIN_EXIT_FUNC);
+	if (!exit_func) {
+		tlog(TLOG_ERROR, "load plugin %s failed: %s", plugin->file, dlerror());
+		goto errout;
+	}
+
+	conf_getopt_reset();
+	int ret = init_func(plugin);
+	conf_getopt_reset();
+	if (ret != 0) {
+		tlog(TLOG_ERROR, "init plugin %s failed", plugin->file);
+		goto errout;
+	}
+
+	plugin->handle = handle;
+	plugin->init_func = init_func;
+	plugin->exit_func = exit_func;
+
+	return 0;
+
+errout:
+	if (handle) {
+		dlclose(handle);
+	}
+	return -1;
+}
+
+static int _dns_plugin_unload_library(struct dns_plugin *plugin)
+{
+	int ret = 0;
+	if (plugin->exit_func) {
+		ret = plugin->exit_func(plugin);
+		if (ret != 0) {
+			tlog(TLOG_ERROR, "exit plugin %s failed", plugin->file);
+		}
+	}
+
+	if (plugin->handle) {
+		dlclose(plugin->handle);
+		plugin->handle = NULL;
+	}
+
+	return 0;
+}
+
+static struct dns_plugin *_dns_plugin_new(const char *plugin_file)
+{
+	struct dns_plugin *plugin = NULL;
+
+	plugin = _dns_plugin_get(plugin_file);
+	if (plugin) {
+		return NULL;
+	}
+
+	plugin = (struct dns_plugin *)malloc(sizeof(struct dns_plugin));
+	if (!plugin) {
+		return NULL;
+	}
+
+	memset(plugin, 0, sizeof(struct dns_plugin));
+	strncpy(plugin->file, plugin_file, PATH_MAX - 1);
+
+	return plugin;
+}
+
+static int _dns_plugin_remove(struct dns_plugin *plugin)
+{
+	_dns_plugin_unload_library(plugin);
+	hash_del(&plugin->node);
+	free(plugin);
+
+	return 0;
+}
+
+int dns_plugin_get_argc(struct dns_plugin *plugin)
+{
+	return plugin->argc;
+}
+
+const char **dns_plugin_get_argv(struct dns_plugin *plugin)
+{
+	return (const char **)plugin->argv;
+}
+
+int dns_plugin_add(const char *plugin_file, int argc, const char *args, int args_len)
+{
+	struct dns_plugin *plugin = NULL;
+	const char *plugin_args = NULL;
+
+	plugin = _dns_plugin_new(plugin_file);
+	if (!plugin) {
+		tlog(TLOG_ERROR, "add plugin %s failed", plugin_file);
+		return -1;
+	}
+
+	memcpy(plugin->args, args, PATH_MAX - 1);
+	plugin->argc = argc;
+	plugin_args = plugin->args;
+	for (int i = 0; i < argc && i < DNS_PLUGIN_MAX_ARGS; i++) {
+		plugin->argv[i] = (char *)plugin_args;
+		plugin_args += strlen(plugin_args) + 1;
+	}
+
+	if (_dns_plugin_load_library(plugin) != 0) {
+		goto errout;
+	}
+
+	hash_add(plugins.plugin, &plugin->node, hash_string(plugin_file));
+
+	return 0;
+errout:
+	if (plugin) {
+		_dns_plugin_remove(plugin);
+	}
+	return -1;
+}
+
+int dns_plugin_remove(const char *plugin_file)
+{
+	struct dns_plugin *plugin = NULL;
+
+	plugin = _dns_plugin_get(plugin_file);
+	if (plugin == NULL) {
+		return 0;
+	}
+
+	return _dns_plugin_remove(plugin);
+}
+
+static int _dns_plugin_remove_all_ops(void)
+{
+	struct dns_plugin_ops *chain = NULL;
+	struct dns_plugin_ops *tmp = NULL;
+
+	list_for_each_entry_safe(chain, tmp, &plugins.list, list)
+	{
+		list_del(&chain->list);
+		free(chain);
+	}
+
+	return 0;
+}
+
+static int _dns_plugin_remove_all(void)
+{
+	struct dns_plugin *plugin = NULL;
+	struct hlist_node *tmp = NULL;
+	unsigned int key = 0;
+
+	hash_for_each_safe(plugins.plugin, key, tmp, plugin, node)
+	{
+		_dns_plugin_remove(plugin);
+	}
+
+	return -1;
+}
+
+int dns_server_plugin_init(void)
+{
+	if (is_plugin_init == 1) {
+		return 0;
+	}
+
+	hash_init(plugins.plugin);
+	INIT_LIST_HEAD(&plugins.list);
+	is_plugin_init = 1;
+	return 0;
+}
+
+void dns_server_plugin_exit(void)
+{
+	if (is_plugin_init == 0) {
+		return;
+	}
+
+	_dns_plugin_remove_all_ops();
+	_dns_plugin_remove_all();
+	return;
+}
+
+void smartdns_plugin_log(smartdns_log_level level, const char *file, int line, const char *func, const char *msg)
+{
+	tlog_ext((tlog_level)level, file, line, func, NULL, "%s", msg);
+}

+ 88 - 0
src/dns_plugin.h

@@ -0,0 +1,88 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
+ *
+ * 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_PLUGIN_H
+#define SMART_DNS_PLUGIN_H
+
+#include "dns.h"
+#include <sys/socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /*__cplusplus */
+
+#define DNS_PLUGIN_INIT_FUNC "dns_plugin_init"
+#define DNS_PLUGIN_EXIT_FUNC "dns_plugin_exit"
+
+struct dns_plugin;
+struct dns_plugin_ops;
+struct dns_request;
+
+typedef int (*dns_plugin_init_func)(struct dns_plugin *plugin);
+typedef int (*dns_plugin_exit_func)(struct dns_plugin *plugin);
+
+struct dns_plugin;
+int dns_plugin_init(struct dns_plugin *plugin);
+
+int dns_plugin_exit(struct dns_plugin *plugin);
+
+int dns_server_plugin_init(void);
+
+void dns_server_plugin_exit(void);
+
+int dns_plugin_add(const char *plugin_file, int argc, const char *args, int args_len);
+
+int dns_plugin_remove(const char *plugin_file);
+
+typedef enum {
+	SMARTDNS_LOG_DEBUG = 0,
+	SMARTDNS_LOG_INFO = 1,
+	SMARTDNS_LOG_NOTICE = 2,
+	SMARTDNS_LOG_WARN = 3,
+	SMARTDNS_LOG_ERROR = 4,
+	SMARTDNS_LOG_FATAL = 5,
+	SMARTDNS_LOG_OFF = 6,
+	SMARTDNS_LOG_END = 7
+} smartdns_log_level;
+
+int dns_plugin_get_argc(struct dns_plugin *plugin);
+
+const char **dns_plugin_get_argv(struct dns_plugin *plugin);
+
+void smartdns_plugin_log(smartdns_log_level level, const char *file, int line, const char *func, const char *msg);
+
+int smartdns_plugin_func_server_recv(struct dns_packet *packet, unsigned char *inpacket, int inpacket_len,
+									 struct sockaddr_storage *local, socklen_t local_len, struct sockaddr_storage *from,
+									 socklen_t from_len);
+void smartdns_plugin_func_server_complete_request(struct dns_request *request);
+
+struct smartdns_operations {
+	int (*server_recv)(struct dns_packet *packet, unsigned char *inpacket, int inpacket_len,
+					   struct sockaddr_storage *local, socklen_t local_len, struct sockaddr_storage *from,
+					   socklen_t from_len);
+	void (*server_query_complete)(struct dns_request *request);
+};
+
+int smartdns_operations_register(struct smartdns_operations *operations);
+
+int smartdns_operations_unregister(struct smartdns_operations *operations);
+
+#ifdef __cplusplus
+}
+#endif /*__cplusplus */
+#endif

+ 74 - 0
src/dns_server.c

@@ -25,6 +25,7 @@
 #include "dns_cache.h"
 #include "dns_cache.h"
 #include "dns_client.h"
 #include "dns_client.h"
 #include "dns_conf.h"
 #include "dns_conf.h"
+#include "dns_plugin.h"
 #include "fast_ping.h"
 #include "fast_ping.h"
 #include "hashtable.h"
 #include "hashtable.h"
 #include "http_parse.h"
 #include "http_parse.h"
@@ -357,6 +358,8 @@ struct dns_request {
 	int no_ipalias;
 	int no_ipalias;
 
 
 	int has_cname_loop;
 	int has_cname_loop;
+
+	void *private_data;
 };
 };
 
 
 /* dns server data */
 /* dns server data */
@@ -2735,6 +2738,13 @@ static void _dns_server_request_release_complete(struct dns_request *request, in
 		request->parent_request = NULL;
 		request->parent_request = NULL;
 	}
 	}
 
 
+	atomic_inc(&request->refcnt);
+	smartdns_plugin_func_server_complete_request(request);
+	if (atomic_dec_return(&request->refcnt) > 0) {
+		/* plugin may hold request. */
+		return;
+	}
+
 	pthread_mutex_lock(&request->ip_map_lock);
 	pthread_mutex_lock(&request->ip_map_lock);
 	hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node)
 	hash_for_each_safe(request->ip_map, bucket, tmp, addr_map, node)
 	{
 	{
@@ -2758,6 +2768,66 @@ static void _dns_server_request_get(struct dns_request *request)
 	}
 	}
 }
 }
 
 
+struct sockaddr *dns_server_request_get_remote_addr(struct dns_request *request)
+{
+	return &request->addr;
+}
+
+struct sockaddr *dns_server_request_get_local_addr(struct dns_request *request)
+{
+	return (struct sockaddr *)&request->localaddr;
+}
+
+const char *dns_server_request_get_group_name(struct dns_request *request)
+{
+	return request->dns_group_name;
+}
+
+const char *dns_server_request_get_domain(struct dns_request *request)
+{
+	return request->domain;
+}
+
+int dns_server_request_get_qtype(struct dns_request *request)
+{
+	return request->qtype;
+}
+
+int dns_server_request_get_qclass(struct dns_request *request)
+{
+	return request->qclass;
+}
+
+int dns_server_request_get_id(struct dns_request *request)
+{
+	return request->id;
+}
+
+int dns_server_request_get_rcode(struct dns_request *request)
+{
+	return request->rcode;
+}
+
+void dns_server_request_get(struct dns_request *request)
+{
+	_dns_server_request_get(request);
+}
+
+void dns_server_request_put(struct dns_request *request)
+{
+	_dns_server_request_release(request);
+}
+
+void dns_server_request_set_private(struct dns_request *request, void *private_data)
+{
+	request->private_data = private_data;
+}
+
+void *dns_server_request_get_private(struct dns_request *request)
+{
+	return request->private_data;
+}
+
 static int _dns_server_set_to_pending_list(struct dns_request *request)
 static int _dns_server_set_to_pending_list(struct dns_request *request)
 {
 {
 	struct dns_request_pending_list *pending_list = NULL;
 	struct dns_request_pending_list *pending_list = NULL;
@@ -6528,6 +6598,10 @@ static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *in
 		goto errout;
 		goto errout;
 	}
 	}
 
 
+	if (smartdns_plugin_func_server_recv(packet, inpacket, inpacket_len, local, local_len, from, from_len) != 0) {
+		return 0;
+	}
+
 	tlog(TLOG_DEBUG,
 	tlog(TLOG_DEBUG,
 		 "request qdcount = %d, ancount = %d, nscount = %d, nrcount = %d, len = %d, id = %d, tc = %d, rd = %d, ra = "
 		 "request qdcount = %d, ancount = %d, nscount = %d, nrcount = %d, len = %d, id = %d, tc = %d, rd = %d, ra = "
 		 "%d, rcode = %d\n",
 		 "%d, rcode = %d\n",

+ 26 - 0
src/dns_server.h

@@ -69,6 +69,32 @@ typedef int (*dns_result_callback)(const struct dns_result *result, void *user_p
 int dns_server_query(const char *domain, int qtype, struct dns_server_query_option *server_query_option,
 int dns_server_query(const char *domain, int qtype, struct dns_server_query_option *server_query_option,
 					 dns_result_callback callback, void *user_ptr);
 					 dns_result_callback callback, void *user_ptr);
 
 
+struct dns_request;
+
+struct sockaddr *dns_server_request_get_remote_addr(struct dns_request *request);
+
+struct sockaddr *dns_server_request_get_local_addr(struct dns_request *request);
+
+const char *dns_server_request_get_group_name(struct dns_request *request);
+
+const char *dns_server_request_get_domain(struct dns_request *request);
+
+int dns_server_request_get_qtype(struct dns_request *request);
+
+int dns_server_request_get_qclass(struct dns_request *request);
+
+int dns_server_request_get_id(struct dns_request *request);
+
+int dns_server_request_get_rcode(struct dns_request *request);
+
+void dns_server_request_get(struct dns_request *request);
+
+void dns_server_request_put(struct dns_request *request);
+
+void dns_server_request_set_private(struct dns_request *request, void *private_data);
+
+void *dns_server_request_get_private(struct dns_request *request);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 2 - 0
src/include/conf.h

@@ -210,6 +210,8 @@ int load_conf(const char *file, struct config_item items[], conf_error_handler h
 
 
 void load_exit(void);
 void load_exit(void);
 
 
+void conf_getopt_reset(void);
+
 int conf_get_current_lineno(void);
 int conf_get_current_lineno(void);
 
 
 const char *conf_get_conf_file(void);
 const char *conf_get_conf_file(void);

+ 1 - 1
src/lib/conf.c

@@ -281,7 +281,7 @@ int conf_enum(const char *item, void *data, int argc, char *argv[])
 	return -1;
 	return -1;
 }
 }
 
 
-static void conf_getopt_reset(void)
+void conf_getopt_reset(void)
 {
 {
 	static struct option long_options[] = {{"-", 0, 0, 0}, {0, 0, 0, 0}};
 	static struct option long_options[] = {{"-", 0, 0, 0}, {0, 0, 0, 0}};
 	int argc = 2;
 	int argc = 2;

+ 68 - 2
src/smartdns.c

@@ -23,6 +23,7 @@
 #include "dns_cache.h"
 #include "dns_cache.h"
 #include "dns_client.h"
 #include "dns_client.h"
 #include "dns_conf.h"
 #include "dns_conf.h"
+#include "dns_plugin.h"
 #include "dns_server.h"
 #include "dns_server.h"
 #include "fast_ping.h"
 #include "fast_ping.h"
 #include "hashtable.h"
 #include "hashtable.h"
@@ -64,6 +65,8 @@ typedef enum {
 } smartdns_run_monitor_ret;
 } smartdns_run_monitor_ret;
 
 
 static int verbose_screen;
 static int verbose_screen;
+static int exit_status;
+static int exit_restart;
 
 
 int capget(struct __user_cap_header_struct *header, struct __user_cap_data_struct *cap);
 int capget(struct __user_cap_header_struct *header, struct __user_cap_data_struct *cap);
 int capset(struct __user_cap_header_struct *header, struct __user_cap_data_struct *cap);
 int capset(struct __user_cap_header_struct *header, struct __user_cap_data_struct *cap);
@@ -397,6 +400,38 @@ static int _proxy_add_servers(void)
 	return 0;
 	return 0;
 }
 }
 
 
+static int _smartdns_plugin_init(void)
+{
+	int ret = 0;
+	unsigned long i = 0;
+	struct dns_conf_plugin *plugin = NULL;
+	struct hlist_node *tmp = NULL;
+
+	ret = dns_server_plugin_init();
+	if (ret != 0) {
+		tlog(TLOG_ERROR, "init plugin failed.");
+		goto errout;
+	}
+
+	hash_for_each_safe(dns_conf_plugin_table.plugins, i, tmp, plugin, node)
+	{
+		ret = dns_plugin_add(plugin->file, plugin->argc, plugin->args, plugin->args_len);
+		if (ret != 0) {
+			goto errout;
+		}
+	}
+
+	return 0;
+errout:
+	return -1;
+}
+
+static int _smartdns_plugin_exit(void)
+{
+	dns_server_plugin_exit();
+	return 0;
+}
+
 static int _smartdns_create_cert(void)
 static int _smartdns_create_cert(void)
 {
 {
 	uid_t uid = 0;
 	uid_t uid = 0;
@@ -640,6 +675,12 @@ static int _smartdns_init(void)
 		goto errout;
 		goto errout;
 	}
 	}
 
 
+	ret = _smartdns_plugin_init();
+	if (ret != 0) {
+		tlog(TLOG_ERROR, "init plugin failed.");
+		goto errout;
+	}
+
 	return 0;
 	return 0;
 errout:
 errout:
 
 
@@ -653,6 +694,7 @@ static int _smartdns_run(void)
 
 
 static void _smartdns_exit(void)
 static void _smartdns_exit(void)
 {
 {
+	_smartdns_plugin_exit();
 	dns_client_exit();
 	dns_client_exit();
 	proxy_exit();
 	proxy_exit();
 	fast_ping_exit();
 	fast_ping_exit();
@@ -919,6 +961,18 @@ static void _smartdns_print_error_tip(void)
 	}
 	}
 }
 }
 
 
+void smartdns_exit(int status)
+{
+	dns_server_stop();
+	exit_status = status;
+}
+
+void smartdns_restart(void)
+{
+	dns_server_stop();
+	exit_restart = 1;
+}
+
 #ifdef TEST
 #ifdef TEST
 
 
 static smartdns_post_func _smartdns_post = NULL;
 static smartdns_post_func _smartdns_post = NULL;
@@ -1118,8 +1172,20 @@ int main(int argc, char *argv[])
 
 
 	smartdns_test_notify(1);
 	smartdns_test_notify(1);
 	ret = _smartdns_run();
 	ret = _smartdns_run();
-	tlog(TLOG_INFO, "smartdns exit...");
-	_smartdns_exit();
+	if (ret == 0 && exit_status != 0) {
+		ret = exit_status;
+	}
+
+	if (exit_restart == 0) {
+		tlog(TLOG_INFO, "smartdns exit...");
+		_smartdns_exit();
+	} else {
+		tlog(TLOG_INFO, "smartdns restart...");
+		_smartdns_exit();
+		if (restart_when_crash == 0) {
+			execve(argv[0], argv, environ);
+		}
+	}
 	return ret;
 	return ret;
 errout:
 errout:
 	if (is_run_as_daemon) {
 	if (is_run_as_daemon) {

+ 4 - 0
src/smartdns.h

@@ -23,6 +23,10 @@
 extern "C" {
 extern "C" {
 #endif /*__cplusplus */
 #endif /*__cplusplus */
 
 
+void smartdns_exit(int status);
+
+void smartdns_restart(void);
+
 #ifdef TEST
 #ifdef TEST
 
 
 typedef void (*smartdns_post_func)(void *arg);
 typedef void (*smartdns_post_func)(void *arg);