From 1b127094517de9ca166002631272f2d7ad430e37 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Wed, 6 Sep 2023 23:25:13 +0800 Subject: [PATCH] feature: add ip-rules and ip-set options --- src/dns_conf.c | 781 +++++++++++++++++++++++++++---------- src/dns_conf.h | 71 +++- src/dns_server.c | 63 +-- src/lib/conf.c | 8 + test/cases/test-ip-rule.cc | 56 +++ 5 files changed, 723 insertions(+), 256 deletions(-) diff --git a/src/dns_conf.c b/src/dns_conf.c index 915152e..da9b66e 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -47,6 +47,8 @@ uint8_t *dns_qtype_soa_table; struct dns_domain_set_name_table dns_domain_set_name_table; +struct dns_ip_set_name_table dns_ip_set_name_table; + /* dns groups */ struct dns_group_table dns_group_table; struct dns_proxy_table dns_proxy_table; @@ -252,26 +254,6 @@ static void _dns_rule_put(struct dns_rule *rule) } } -static struct dns_iplist_ip_addresses *_new_dns_iplist_ip_addresses(void) -{ - struct dns_iplist_ip_addresses *iplist; - - iplist = malloc(sizeof(struct dns_iplist_ip_addresses)); - if (!iplist) { - return NULL; - } - memset(iplist, 0, sizeof(struct dns_iplist_ip_addresses)); - atomic_set(&iplist->refcnt, 1); - return iplist; -} - -static void _dns_iplist_ip_addresses_put(struct dns_iplist_ip_addresses *iplist) -{ - if (atomic_dec_and_test(&iplist->refcnt)) { - free(iplist); - } -} - static void _dns_iplist_ip_address_add(struct dns_iplist_ip_addresses *iplist, unsigned char addr[], int addr_len) { iplist->ipaddr = realloc(iplist->ipaddr, (iplist->ipaddr_num + 1) * sizeof(struct dns_iplist_ip_address)); @@ -798,33 +780,12 @@ static void _config_domain_destroy(void) art_tree_destroy(&dns_conf_domain_rule); } -static void _config_address_destroy(radix_node_t *node, void *cbctx) -{ - struct dns_ip_address_rule *address_rule = NULL; - if (node == NULL) { - return; - } - - if (node->data == NULL) { - return; - } - - address_rule = node->data; - if (address_rule->ip_alias) { - _dns_iplist_ip_addresses_put(address_rule->ip_alias); - address_rule->ip_alias = NULL; - } - - free(node->data); - node->data = NULL; -} - -typedef int (*domain_set_rule_add_func)(const char *domain, void *priv); -static int _config_domain_rule_each_from_list(const char *file, domain_set_rule_add_func callback, void *priv) +typedef int (*set_rule_add_func)(const char *value, void *priv); +static int _config_set_rule_each_from_list(const char *file, set_rule_add_func callback, void *priv) { FILE *fp = NULL; char line[MAX_LINE_LEN]; - char domain[DNS_MAX_CNAME_LEN]; + char value[DNS_MAX_CNAME_LEN]; int ret = 0; int line_no = 0; int filed_num = 0; @@ -838,16 +799,16 @@ static int _config_domain_rule_each_from_list(const char *file, domain_set_rule_ line_no = 0; while (fgets(line, MAX_LINE_LEN, fp)) { line_no++; - filed_num = sscanf(line, "%255s", domain); + filed_num = sscanf(line, "%255s", value); if (filed_num <= 0) { continue; } - if (domain[0] == '#' || domain[0] == '\n') { + if (value[0] == '#' || value[0] == '\n') { continue; } - ret = callback(domain, priv); + ret = callback(value, priv); if (ret != 0) { tlog(TLOG_WARN, "process file %s failed at line %d.", file, line_no); continue; @@ -858,7 +819,7 @@ static int _config_domain_rule_each_from_list(const char *file, domain_set_rule_ return ret; } -static int _config_domain_rule_set_each(const char *domain_set, domain_set_rule_add_func callback, void *priv) +static int _config_domain_rule_set_each(const char *domain_set, set_rule_add_func callback, void *priv) { struct dns_domain_set_name_list *set_name_list = NULL; struct dns_domain_set_name *set_name_item = NULL; @@ -882,7 +843,7 @@ static int _config_domain_rule_set_each(const char *domain_set, domain_set_rule_ { switch (set_name_item->type) { case DNS_DOMAIN_SET_LIST: - _config_domain_rule_each_from_list(set_name_item->file, callback, priv); + _config_set_rule_each_from_list(set_name_item->file, callback, priv); break; case DNS_DOMAIN_SET_GEOSITE: break; @@ -2474,7 +2435,7 @@ errout: return -1; } -static radix_node_t *_create_addr_node(char *addr) +static radix_node_t *_create_addr_node(const char *addr) { radix_node_t *node = NULL; void *p = NULL; @@ -2500,50 +2461,59 @@ static radix_node_t *_create_addr_node(char *addr) return node; } -static struct dns_ip_address_rule *_config_iplist_rule(char *subnet, enum address_rule rule) +static void *_new_dns_ip_rule_ext(enum ip_rule ip_rule, int ext_size) { - radix_node_t *node = NULL; - struct dns_ip_address_rule *ip_rule = NULL; + struct dns_ip_rule *rule; + int size = 0; - node = _create_addr_node(subnet); - if (node == NULL) { + if (ip_rule >= IP_RULE_MAX) { return NULL; } - if (node->data == NULL) { - ip_rule = malloc(sizeof(*ip_rule)); - if (ip_rule == NULL) { - return NULL; - } - - node->data = ip_rule; - memset(ip_rule, 0, sizeof(*ip_rule)); - } - - ip_rule = node->data; - - switch (rule) { - case ADDRESS_RULE_BLACKLIST: - ip_rule->blacklist = 1; + switch (ip_rule) { + case IP_RULE_FLAGS: + size = sizeof(struct ip_rule_flags); break; - case ADDRESS_RULE_WHITELIST: - ip_rule->whitelist = 1; + case IP_RULE_ALIAS: + size = sizeof(struct ip_rule_alias); break; - case ADDRESS_RULE_BOGUS: - ip_rule->bogus = 1; - break; - case ADDRESS_RULE_IP_IGNORE: - ip_rule->ip_ignore = 1; - break; - case ADDRESS_RULE_IP_ALIAS: { - ip_rule->ip_alias = _new_dns_iplist_ip_addresses(); - ip_rule->ip_alias_enable = 1; - } break; default: return NULL; } - return ip_rule; + size += ext_size; + rule = malloc(size); + if (!rule) { + return NULL; + } + memset(rule, 0, size); + rule->rule = ip_rule; + atomic_set(&rule->refcnt, 1); + return rule; +} + +static void *_new_dns_ip_rule(enum ip_rule ip_rule) +{ + return _new_dns_ip_rule_ext(ip_rule, 0); +} + +static void _dns_ip_rule_get(struct dns_ip_rule *rule) +{ + atomic_inc(&rule->refcnt); +} + +static void _dns_ip_rule_put(struct dns_ip_rule *rule) +{ + if (atomic_dec_and_test(&rule->refcnt)) { + if (rule->rule == IP_RULE_ALIAS) { + struct ip_rule_alias *alias = container_of(rule, struct ip_rule_alias, head); + if (alias->ip_alias.ipaddr) { + free(alias->ip_alias.ipaddr); + alias->ip_alias.ipaddr_num = 0; + } + } + free(rule); + } } static int _config_qtype_soa(void *data, int argc, char *argv[]) @@ -2619,129 +2589,6 @@ static void _config_domain_set_name_table_destroy(void) } } -static int _config_blacklist_ip(void *data, int argc, char *argv[]) -{ - if (argc <= 1) { - return -1; - } - - if (_config_iplist_rule(argv[1], ADDRESS_RULE_BLACKLIST) == NULL) { - return -1; - } - - return 0; -} - -static int _conf_bogus_nxdomain(void *data, int argc, char *argv[]) -{ - if (argc <= 1) { - return -1; - } - - if (_config_iplist_rule(argv[1], ADDRESS_RULE_BOGUS) == NULL) { - return -1; - } - - return 0; -} - -static int _conf_ip_ignore(void *data, int argc, char *argv[]) -{ - if (argc <= 1) { - return -1; - } - - if (_config_iplist_rule(argv[1], ADDRESS_RULE_IP_IGNORE) == NULL) { - return -1; - } - - return 0; -} - -static int _conf_whitelist_ip(void *data, int argc, char *argv[]) -{ - if (argc <= 1) { - return -1; - } - - if (_config_iplist_rule(argv[1], ADDRESS_RULE_WHITELIST) == NULL) { - return -1; - } - - return 0; -} - -static int _conf_ip_alias(void *data, int argc, char *argv[]) -{ - struct dns_ip_address_rule *ip_rule = NULL; - struct dns_iplist_ip_addresses *ip_alias = NULL; - char *target_ips = NULL; - - if (argc <= 2) { - return -1; - } - - ip_rule = _config_iplist_rule(argv[1], ADDRESS_RULE_IP_ALIAS); - if (ip_rule == NULL) { - return -1; - } - - ip_alias = ip_rule->ip_alias; - if (ip_alias == NULL) { - tlog(TLOG_ERROR, "cannot malloc memory"); - goto errout; - } - - target_ips = strdup(argv[2]); - if (target_ips == NULL) { - goto errout; - } - - for (char *tok = strtok(target_ips, ","); tok != NULL; tok = strtok(NULL, ",")) { - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - unsigned char *paddr = NULL; - int ret = 0; - - ret = getaddr_by_host(tok, (struct sockaddr *)&addr, &addr_len); - if (ret != 0) { - goto errout; - } - - switch (addr.ss_family) { - case AF_INET: { - struct sockaddr_in *addr_in = NULL; - addr_in = (struct sockaddr_in *)&addr; - paddr = (unsigned char *)&(addr_in->sin_addr.s_addr); - _dns_iplist_ip_address_add(ip_alias, paddr, DNS_RR_A_LEN); - } 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)) { - paddr = addr_in6->sin6_addr.s6_addr + 12; - _dns_iplist_ip_address_add(ip_alias, paddr, DNS_RR_A_LEN); - } else { - paddr = addr_in6->sin6_addr.s6_addr; - _dns_iplist_ip_address_add(ip_alias, paddr, DNS_RR_AAAA_LEN); - } - } break; - default: - goto errout; - break; - } - } - - free(target_ips); - return 0; -errout: - if (target_ips) { - free(target_ips); - } - - return -1; -} - static int _conf_client_subnet(char *subnet, struct dns_edns_client_subnet *ipv4_ecs, struct dns_edns_client_subnet *ipv6_ecs) { @@ -2971,6 +2818,514 @@ errout: return -1; } +static int _config_ip_rule_set_each(const char *ip_set, set_rule_add_func callback, void *priv) +{ + struct dns_ip_set_name_list *set_name_list = NULL; + struct dns_ip_set_name *set_name_item = NULL; + + uint32_t key = 0; + + key = hash_string(ip_set); + hash_for_each_possible(dns_ip_set_name_table.names, set_name_list, node, key) + { + if (strcmp(set_name_list->name, ip_set) == 0) { + break; + } + } + + if (set_name_list == NULL) { + tlog(TLOG_WARN, "ip set %s not found.", ip_set); + return -1; + } + + list_for_each_entry(set_name_item, &set_name_list->set_name_list, list) + { + switch (set_name_item->type) { + case DNS_IP_SET_LIST: + _config_set_rule_each_from_list(set_name_item->file, callback, priv); + break; + default: + tlog(TLOG_WARN, "ip set %s type %d not support.", set_name_list->name, set_name_item->type); + break; + } + } + + return 0; +} + +static int _config_ip_rules_free(struct dns_ip_rules *ip_rules) +{ + int i = 0; + + if (ip_rules == NULL) { + return 0; + } + + for (i = 0; i < DOMAIN_RULE_MAX; i++) { + if (ip_rules->rules[i] == NULL) { + continue; + } + + _dns_ip_rule_put(ip_rules->rules[i]); + ip_rules->rules[i] = NULL; + } + + free(ip_rules); + return 0; +} + +static int _config_ip_rule_flag_set(const char *ip_cidr, unsigned int flag, unsigned int is_clear); +static int _config_ip_rule_flag_callback(const char *ip_cidr, void *priv) +{ + struct dns_set_rule_flags_callback_args *args = (struct dns_set_rule_flags_callback_args *)priv; + return _config_ip_rule_flag_set(ip_cidr, args->flags, args->is_clear_flag); +} + +static int _config_ip_rule_flag_set(const char *ip_cidr, unsigned int flag, unsigned int is_clear) +{ + struct dns_ip_rules *ip_rules = NULL; + struct dns_ip_rules *add_ip_rules = NULL; + struct ip_rule_flags *ip_rule_flags = NULL; + radix_node_t *node = NULL; + + if (strncmp(ip_cidr, "ip-set:", sizeof("ip-set:") - 1) == 0) { + struct dns_set_rule_flags_callback_args args; + args.flags = flag; + args.is_clear_flag = is_clear; + return _config_ip_rule_set_each(ip_cidr + sizeof("ip-set:") - 1, _config_ip_rule_flag_callback, &args); + } + + /* Get existing or create domain rule */ + node = _create_addr_node(ip_cidr); + if (node == NULL) { + tlog(TLOG_ERROR, "create addr node failed."); + goto errout; + } + + ip_rules = node->data; + if (ip_rules == NULL) { + add_ip_rules = malloc(sizeof(*add_ip_rules)); + if (add_ip_rules == NULL) { + goto errout; + } + memset(add_ip_rules, 0, sizeof(*add_ip_rules)); + ip_rules = add_ip_rules; + node->data = ip_rules; + } + + /* add new rule to domain */ + if (ip_rules->rules[IP_RULE_FLAGS] == NULL) { + ip_rule_flags = _new_dns_ip_rule(IP_RULE_FLAGS); + ip_rule_flags->flags = 0; + ip_rules->rules[IP_RULE_FLAGS] = &ip_rule_flags->head; + } + + ip_rule_flags = container_of(ip_rules->rules[IP_RULE_FLAGS], struct ip_rule_flags, head); + if (is_clear == false) { + ip_rule_flags->flags |= flag; + } else { + ip_rule_flags->flags &= ~flag; + } + ip_rule_flags->is_flag_set |= flag; + + return 0; +errout: + if (add_ip_rules) { + free(add_ip_rules); + } + + tlog(TLOG_ERROR, "set ip %s flags failed", ip_cidr); + + return 0; +} + +static int _config_ip_rule_add(const char *ip_cidr, enum ip_rule type, void *rule); +static int _config_ip_rule_add_callback(const char *ip_cidr, void *priv) +{ + struct dns_set_rule_add_callback_args *args = (struct dns_set_rule_add_callback_args *)priv; + return _config_ip_rule_add(ip_cidr, args->type, args->rule); +} + +static int _config_ip_rule_add(const char *ip_cidr, enum ip_rule type, void *rule) +{ + struct dns_ip_rules *ip_rules = NULL; + struct dns_ip_rules *add_ip_rules = NULL; + radix_node_t *node = NULL; + + if (ip_cidr == NULL) { + goto errout; + } + + if (type >= IP_RULE_MAX) { + goto errout; + } + + if (strncmp(ip_cidr, "ip-set:", sizeof("ip-set:") - 1) == 0) { + struct dns_set_rule_add_callback_args args; + args.type = type; + args.rule = rule; + return _config_ip_rule_set_each(ip_cidr + sizeof("ip-set:") - 1, _config_ip_rule_add_callback, &args); + } + + /* Get existing or create domain rule */ + node = _create_addr_node(ip_cidr); + if (node == NULL) { + tlog(TLOG_ERROR, "create addr node failed."); + goto errout; + } + + ip_rules = node->data; + if (ip_rules == NULL) { + add_ip_rules = malloc(sizeof(*add_ip_rules)); + if (add_ip_rules == NULL) { + goto errout; + } + memset(add_ip_rules, 0, sizeof(*add_ip_rules)); + ip_rules = add_ip_rules; + node->data = ip_rules; + } + + /* add new rule to domain */ + if (ip_rules->rules[type]) { + _dns_ip_rule_put(ip_rules->rules[type]); + ip_rules->rules[type] = NULL; + } + + ip_rules->rules[type] = rule; + _dns_ip_rule_get(rule); + + return 0; +errout: + if (add_ip_rules) { + free(add_ip_rules); + } + + tlog(TLOG_ERROR, "add ip %s rule failed", ip_cidr); + return -1; +} + +static int _config_ip_alias(const char *ip_cidr, const char *ips) +{ + struct ip_rule_alias *ip_alias = NULL; + char *target_ips = NULL; + + if (ip_cidr == NULL || ips == NULL) { + goto errout; + } + + ip_alias = _new_dns_ip_rule(IP_RULE_ALIAS); + if (ip_alias == NULL) { + goto errout; + } + + target_ips = strdup(ips); + if (target_ips == NULL) { + goto errout; + } + + for (char *tok = strtok(target_ips, ","); tok != NULL; tok = strtok(NULL, ",")) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + unsigned char *paddr = NULL; + int ret = 0; + + ret = getaddr_by_host(tok, (struct sockaddr *)&addr, &addr_len); + if (ret != 0) { + goto errout; + } + + switch (addr.ss_family) { + case AF_INET: { + struct sockaddr_in *addr_in = NULL; + addr_in = (struct sockaddr_in *)&addr; + paddr = (unsigned char *)&(addr_in->sin_addr.s_addr); + _dns_iplist_ip_address_add(&ip_alias->ip_alias, paddr, DNS_RR_A_LEN); + } 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)) { + paddr = addr_in6->sin6_addr.s6_addr + 12; + _dns_iplist_ip_address_add(&ip_alias->ip_alias, paddr, DNS_RR_A_LEN); + } else { + paddr = addr_in6->sin6_addr.s6_addr; + _dns_iplist_ip_address_add(&ip_alias->ip_alias, paddr, DNS_RR_AAAA_LEN); + } + } break; + default: + goto errout; + break; + } + } + + if (_config_ip_rule_add(ip_cidr, IP_RULE_ALIAS, ip_alias) != 0) { + goto errout; + } + + _dns_ip_rule_put(&ip_alias->head); + free(target_ips); + + return 0; +errout: + + if (ip_alias) { + _dns_ip_rule_put(&ip_alias->head); + } + + if (target_ips) { + free(target_ips); + } + + return -1; +} + +static int _config_blacklist_ip(void *data, int argc, char *argv[]) +{ + if (argc <= 1) { + return -1; + } + + return _config_ip_rule_flag_set(argv[1], IP_RULE_FLAG_BLACKLIST, 0); +} + +static int _conf_bogus_nxdomain(void *data, int argc, char *argv[]) +{ + if (argc <= 1) { + return -1; + } + + return _config_ip_rule_flag_set(argv[1], IP_RULE_FLAG_BOGUS, 0); +} + +static int _conf_ip_ignore(void *data, int argc, char *argv[]) +{ + if (argc <= 1) { + return -1; + } + + return _config_ip_rule_flag_set(argv[1], IP_RULE_FLAG_IP_IGNORE, 0); +} + +static int _conf_whitelist_ip(void *data, int argc, char *argv[]) +{ + if (argc <= 1) { + return -1; + } + + return _config_ip_rule_flag_set(argv[1], IP_RULE_FLAG_WHITELIST, 0); +} + +static int _conf_ip_alias(void *data, int argc, char *argv[]) +{ + if (argc <= 2) { + return -1; + } + + return _config_ip_alias(argv[1], argv[2]); +} + +static int _conf_ip_rules(void *data, int argc, char *argv[]) +{ + int opt = 0; + char *ip_cidr = argv[1]; + + /* clang-format off */ + static struct option long_options[] = { + {"blacklist-ip", no_argument, NULL, 'b'}, + {"whitelist-ip", no_argument, NULL, 'w'}, + {"bogus-nxdomain", no_argument, NULL, 'n'}, + {"ignore-ip", no_argument, NULL, 'i'}, + {"ip-alias", required_argument, NULL, 'a'}, + {NULL, no_argument, NULL, 0} + }; + /* clang-format on */ + + if (argc <= 1) { + tlog(TLOG_ERROR, "invalid parameter."); + goto errout; + } + + /* process extra options */ + optind = 1; + while (1) { + opt = getopt_long_only(argc, argv, "", long_options, NULL); + if (opt == -1) { + break; + } + + switch (opt) { + case 'b': { + if (_config_ip_rule_flag_set(ip_cidr, IP_RULE_FLAG_BLACKLIST, 0) != 0) { + goto errout; + } + break; + } + case 'w': { + if (_config_ip_rule_flag_set(ip_cidr, IP_RULE_FLAG_WHITELIST, 0) != 0) { + goto errout; + } + break; + } + case 'n': { + if (_config_ip_rule_flag_set(ip_cidr, IP_RULE_FLAG_BOGUS, 0) != 0) { + goto errout; + } + break; + } + case 'i': { + if (_config_ip_rule_flag_set(ip_cidr, IP_RULE_FLAG_IP_IGNORE, 0) != 0) { + goto errout; + } + break; + } + case 'a': { + + if (_config_ip_alias(ip_cidr, optarg) != 0) { + goto errout; + } + break; + } + default: + break; + } + } + + return 0; +errout: + return -1; +} + +static int _conf_ip_set(void *data, int argc, char *argv[]) +{ + int opt = 0; + uint32_t key = 0; + struct dns_ip_set_name *ip_set = NULL; + struct dns_ip_set_name_list *ip_set_name_list = NULL; + char set_name[DNS_MAX_CNAME_LEN] = {0}; + + /* clang-format off */ + static struct option long_options[] = { + {"name", required_argument, NULL, 'n'}, + {"type", required_argument, NULL, 't'}, + {"file", required_argument, NULL, 'f'}, + {NULL, 0, NULL, 0} + }; + + if (argc <= 1) { + tlog(TLOG_ERROR, "invalid parameter."); + goto errout; + } + + ip_set = malloc(sizeof(*ip_set)); + if (ip_set == NULL) { + tlog(TLOG_ERROR, "cannot malloc memory."); + goto errout; + } + memset(ip_set, 0, sizeof(*ip_set)); + INIT_LIST_HEAD(&ip_set->list); + + optind = 1; + while (1) { + opt = getopt_long_only(argc, argv, "n:t:f:", long_options, NULL); + if (opt == -1) { + break; + } + + switch (opt) { + case 'n': + safe_strncpy(set_name, optarg, DNS_MAX_CNAME_LEN); + break; + case 't': { + const char *type = optarg; + if (strncmp(type, "list", 5) == 0) { + ip_set->type = DNS_IP_SET_LIST; + } else { + tlog(TLOG_ERROR, "invalid domain set type."); + goto errout; + } + break; + } + case 'f': + conf_get_conf_fullpath(optarg, ip_set->file, DNS_MAX_PATH); + break; + default: + break; + } + } + /* clang-format on */ + + if (set_name[0] == 0 || ip_set->file[0] == 0) { + tlog(TLOG_ERROR, "invalid parameter."); + goto errout; + } + + key = hash_string(set_name); + hash_for_each_possible(dns_ip_set_name_table.names, ip_set_name_list, node, key) + { + if (strcmp(ip_set_name_list->name, set_name) == 0) { + break; + } + } + + if (ip_set_name_list == NULL) { + ip_set_name_list = malloc(sizeof(*ip_set_name_list)); + if (ip_set_name_list == NULL) { + tlog(TLOG_ERROR, "cannot malloc memory."); + goto errout; + } + memset(ip_set_name_list, 0, sizeof(*ip_set_name_list)); + INIT_LIST_HEAD(&ip_set_name_list->set_name_list); + safe_strncpy(ip_set_name_list->name, set_name, DNS_MAX_CNAME_LEN); + hash_add(dns_ip_set_name_table.names, &ip_set_name_list->node, key); + } + + list_add_tail(&ip_set->list, &ip_set_name_list->set_name_list); + return 0; + +errout: + if (ip_set) { + free(ip_set); + } + return -1; +} + +static void _config_ip_iter_free(radix_node_t *node, void *cbctx) +{ + struct dns_ip_rules *ip_rules = NULL; + if (node == NULL) { + return; + } + + if (node->data == NULL) { + return; + } + + ip_rules = node->data; + _config_ip_rules_free(ip_rules); + node->data = NULL; +} + +static void _config_ip_set_name_table_destroy(void) +{ + struct dns_ip_set_name_list *set_name_list = NULL; + struct hlist_node *tmp = NULL; + struct dns_ip_set_name *set_name = NULL; + struct dns_ip_set_name *tmp1 = NULL; + unsigned long i = 0; + + hash_for_each_safe(dns_ip_set_name_table.names, i, tmp, set_name_list, node) + { + hlist_del_init(&set_name_list->node); + list_for_each_entry_safe(set_name, tmp1, &set_name_list->set_name_list, list) + { + list_del(&set_name->list); + free(set_name); + } + + free(set_name_list); + } +} + static int _conf_ddns_domain(void *data, int argc, char *argv[]) { if (argc <= 1) { @@ -3736,6 +4091,8 @@ static struct config_item _config_item[] = { CONF_CUSTOM("blacklist-ip", _config_blacklist_ip, NULL), CONF_CUSTOM("whitelist-ip", _conf_whitelist_ip, NULL), CONF_CUSTOM("ip-alias", _conf_ip_alias, NULL), + CONF_CUSTOM("ip-rules", _conf_ip_rules, NULL), + CONF_CUSTOM("ip-set", _conf_ip_set, NULL), CONF_CUSTOM("bogus-nxdomain", _conf_bogus_nxdomain, NULL), CONF_CUSTOM("ignore-ip", _conf_ip_ignore, NULL), CONF_CUSTOM("edns-client-subnet", _conf_edns_client_subnet, NULL), @@ -3841,6 +4198,7 @@ static int _dns_server_load_conf_init(void) hash_init(dns_hosts_table.hosts); hash_init(dns_ptr_table.ptr); hash_init(dns_domain_set_name_table.names); + hash_init(dns_ip_set_name_table.names); return 0; } @@ -3874,11 +4232,16 @@ static void dns_server_bind_destroy(void) dns_conf_bind_ip_num = 0; } +static void _config_ip_rules_destroy(void) +{ + Destroy_Radix(dns_conf_address_rule.ipv4, _config_ip_iter_free, NULL); + Destroy_Radix(dns_conf_address_rule.ipv6, _config_ip_iter_free, NULL); +} + void dns_server_load_exit(void) { _config_domain_destroy(); - Destroy_Radix(dns_conf_address_rule.ipv4, _config_address_destroy, NULL); - Destroy_Radix(dns_conf_address_rule.ipv6, _config_address_destroy, NULL); + _config_ip_rules_destroy(); _config_ipset_table_destroy(); _config_nftset_table_destroy(); _config_group_table_destroy(); @@ -4019,6 +4382,8 @@ static int _dns_conf_load_post(void) _config_domain_set_name_table_destroy(); + _config_ip_set_name_table_destroy(); + _config_update_bootstrap_dns_rule(); _config_add_default_server_if_needed(); diff --git a/src/dns_conf.h b/src/dns_conf.h index 553ff71..c7240ce 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -85,6 +85,12 @@ enum domain_rule { DOMAIN_RULE_MAX, }; +enum ip_rule { + IP_RULE_FLAGS = 0, + IP_RULE_ALIAS = 1, + IP_RULE_MAX, +}; + typedef enum { DNS_BIND_TYPE_UDP, DNS_BIND_TYPE_TCP, @@ -119,6 +125,11 @@ typedef enum { #define DOMAIN_FLAG_NO_CACHE (1 << 17) #define DOMAIN_FLAG_NO_IPALIAS (1 << 18) +#define IP_RULE_FLAG_BLACKLIST (1 << 0) +#define IP_RULE_FLAG_WHITELIST (1 << 1) +#define IP_RULE_FLAG_BOGUS (1 << 2) +#define IP_RULE_FLAG_IP_IGNORE (1 << 3) + #define SERVER_FLAG_EXCLUDE_DEFAULT (1 << 0) #define SERVER_FLAG_HITCHHIKING (1 << 1) @@ -351,14 +362,6 @@ struct dns_bogus_ip_address { }; }; -enum address_rule { - ADDRESS_RULE_BLACKLIST = 1, - ADDRESS_RULE_WHITELIST = 2, - ADDRESS_RULE_BOGUS = 3, - ADDRESS_RULE_IP_IGNORE = 4, - ADDRESS_RULE_IP_ALIAS = 5, -}; - struct dns_iplist_ip_address { int addr_len; union { @@ -369,20 +372,10 @@ struct dns_iplist_ip_address { }; struct dns_iplist_ip_addresses { - atomic_t refcnt; int ipaddr_num; struct dns_iplist_ip_address *ipaddr; }; -struct dns_ip_address_rule { - unsigned int blacklist : 1; - unsigned int whitelist : 1; - unsigned int bogus : 1; - unsigned int ip_ignore : 1; - unsigned int ip_alias_enable : 1; - struct dns_iplist_ip_addresses *ip_alias; -}; - struct dns_conf_address_rule { radix_tree_t *ipv4; radix_tree_t *ipv6; @@ -438,8 +431,48 @@ struct dns_domain_set_name_table { }; extern struct dns_domain_set_name_table dns_domain_set_name_table; +struct dns_ip_rule { + atomic_t refcnt; + enum ip_rule rule; +}; + +enum dns_ip_set_type { + DNS_IP_SET_LIST = 0, +}; + +struct dns_ip_rules { + struct dns_ip_rule *rules[IP_RULE_MAX]; +}; + +struct ip_rule_flags { + struct dns_ip_rule head; + unsigned int flags; + unsigned int is_flag_set; +}; + +struct ip_rule_alias { + struct dns_ip_rule head; + struct dns_iplist_ip_addresses ip_alias; +}; + +struct dns_ip_set_name { + struct list_head list; + enum dns_ip_set_type type; + char file[DNS_MAX_PATH]; +}; + +struct dns_ip_set_name_list { + struct hlist_node node; + char name[DNS_MAX_CNAME_LEN]; + struct list_head set_name_list; +}; +struct dns_ip_set_name_table { + DECLARE_HASHTABLE(names, 4); +}; +extern struct dns_ip_set_name_table dns_ip_set_name_table; + struct dns_set_rule_add_callback_args { - enum domain_rule type; + int type; void *rule; }; diff --git a/src/dns_server.c b/src/dns_server.c index cbcea7a..73236d8 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -2733,12 +2733,12 @@ static int _dns_server_check_speed(struct dns_request *request, char *ip) return -1; } -static struct dns_ip_address_rule *_dns_server_ip_rule_get(struct dns_request *request, unsigned char *addr, - int addr_len, dns_type_t addr_type) +static struct dns_ip_rules *_dns_server_ip_rule_get(struct dns_request *request, unsigned char *addr, int addr_len, + dns_type_t addr_type) { prefix_t prefix; radix_node_t *node = NULL; - struct dns_ip_address_rule *rule = NULL; + struct dns_ip_rules *rule = NULL; /* Match IP address rules */ if (prefix_from_blob(addr, addr_len, addr_len * 8, &prefix) == NULL) { @@ -2769,43 +2769,47 @@ static struct dns_ip_address_rule *_dns_server_ip_rule_get(struct dns_request *r return rule; } -static int _dns_server_ip_rule_check(struct dns_request *request, struct dns_ip_address_rule *rule, int result_flag) +static int _dns_server_ip_rule_check(struct dns_request *request, struct dns_ip_rules *ip_rules, int result_flag) { - if (rule == NULL) { + struct ip_rule_flags *rule_flags = NULL; + if (ip_rules == NULL) { goto rule_not_found; } - if (rule->bogus) { - request->rcode = DNS_RC_NXDOMAIN; - request->has_soa = 1; - request->force_soa = 1; - _dns_server_setup_soa(request); - goto nxdomain; - } + rule_flags = container_of(ip_rules->rules[IP_RULE_FLAGS], struct ip_rule_flags, head); + if (rule_flags != NULL) { + if (rule_flags->flags & IP_RULE_FLAG_BOGUS) { + request->rcode = DNS_RC_NXDOMAIN; + request->has_soa = 1; + request->force_soa = 1; + _dns_server_setup_soa(request); + goto nxdomain; + } - /* blacklist-ip */ - if (rule->blacklist) { - if (result_flag & DNSSERVER_FLAG_BLACKLIST_IP) { - goto match; + /* blacklist-ip */ + if (rule_flags->flags & IP_RULE_FLAG_BLACKLIST) { + if (result_flag & DNSSERVER_FLAG_BLACKLIST_IP) { + goto match; + } + } + + /* ignore-ip */ + if (rule_flags->flags & IP_RULE_FLAG_IP_IGNORE) { + goto skip; } } - /* ignore-ip */ - if (rule->ip_ignore) { - goto skip; - } - - if (rule->ip_alias_enable) { + if (ip_rules->rules[IP_RULE_ALIAS] != NULL) { goto match; } rule_not_found: if (result_flag & DNSSERVER_FLAG_WHITELIST_IP) { - if (rule == NULL) { + if (rule_flags == NULL) { goto skip; } - if (!rule->whitelist) { + if (!(rule_flags->flags & IP_RULE_FLAG_WHITELIST)) { goto skip; } } @@ -2853,18 +2857,19 @@ static int _dns_server_process_ip_alias(struct dns_request *request, struct dns_ static int _dns_server_process_ip_rule(struct dns_request *request, unsigned char *addr, int addr_len, dns_type_t addr_type, int result_flag, struct dns_iplist_ip_addresses **alias) { - struct dns_ip_address_rule *rule = NULL; + struct dns_ip_rules *ip_rules = NULL; int ret = 0; - rule = _dns_server_ip_rule_get(request, addr, addr_len, addr_type); - ret = _dns_server_ip_rule_check(request, rule, result_flag); + ip_rules = _dns_server_ip_rule_get(request, addr, addr_len, addr_type); + ret = _dns_server_ip_rule_check(request, ip_rules, result_flag); if (ret != 0) { return ret; } - if (rule->ip_alias_enable && alias != NULL) { + if (ip_rules->rules[IP_RULE_ALIAS] && alias != NULL) { if (request->no_ipalias == 0) { - *alias = rule->ip_alias; + struct ip_rule_alias *rule = container_of(ip_rules->rules[IP_RULE_ALIAS], struct ip_rule_alias, head); + *alias = &rule->ip_alias; if (alias == NULL) { return 0; } diff --git a/src/lib/conf.c b/src/lib/conf.c index 4e2c749..5c76d97 100644 --- a/src/lib/conf.c +++ b/src/lib/conf.c @@ -363,6 +363,7 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro int read_len = 0; int is_last_line_wrap = 0; int current_line_wrap = 0; + int is_func_found = 0; const char *last_file = NULL; if (handler == NULL) { @@ -431,6 +432,8 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro goto errout; } + is_func_found = 0; + for (i = last_item_index;; i++) { if (i < 0) { continue; @@ -471,8 +474,13 @@ static int load_conf_file(const char *file, struct config_item *items, conf_erro } last_item_index = i; + is_func_found = 1; break; } + + if (is_func_found == 0) { + handler(file, line_no, CONF_RET_NOENT); + } } fclose(fp); diff --git a/test/cases/test-ip-rule.cc b/test/cases/test-ip-rule.cc index 54010db..742f44f 100644 --- a/test/cases/test-ip-rule.cc +++ b/test/cases/test-ip-rule.cc @@ -154,3 +154,59 @@ cache-persist no)"""); EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); EXPECT_EQ(client.GetAnswer()[0].GetData(), "7.8.9.10"); } + +TEST_F(IPRule, ignore_ip_set) +{ + smartdns::MockServer server_upstream; + smartdns::MockServer server_upstream2; + smartdns::Server server; + std::string file = "/tmp/smartdns_test_ip_set.list" + smartdns::GenerateRandomString(5); + std::ofstream ofs(file); + ASSERT_TRUE(ofs.is_open()); + Defer + { + ofs.close(); + unlink(file.c_str()); + }; + + server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) { + if (request->qtype != DNS_T_A) { + return smartdns::SERVER_REQUEST_SOA; + } + + smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611); + smartdns::MockServer::AddIP(request, request->domain.c_str(), "4.5.6.7", 611); + smartdns::MockServer::AddIP(request, request->domain.c_str(), "7.8.9.10", 611); + return smartdns::SERVER_REQUEST_OK; + }); + + /* this ip will be discard, but is reachable */ + server.MockPing(PING_TYPE_ICMP, "1.2.3.4", 60, 10); + server.MockPing(PING_TYPE_ICMP, "4.5.6.7", 60, 90); + server.MockPing(PING_TYPE_ICMP, "7.8.9.10", 60, 40); + + std::string ipset_list = R"""( +1.2.3.0/24 +4.5.6.0/24 +)"""; + ofs.write(ipset_list.c_str(), ipset_list.length()); + ofs.flush(); + + server.Start(R"""(bind [::]:60053 +server udp://127.0.0.1:61053 -blacklist-ip +ip-set -name ip-list -file )""" + + file + R"""( +ignore-ip ip-set:ip-list +log-num 0 +speed-check-mode none +log-console yes +log-level debug +cache-persist no)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "7.8.9.10"); +}