From 39f6b98bc03dce6079d082c64646bb17172b9cc6 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Thu, 6 Dec 2018 00:14:41 +0800 Subject: [PATCH] Support bogus subnet search --- src/Makefile | 2 +- src/conf.c | 99 ++++++- src/conf.h | 15 +- src/dns_server.c | 38 ++- src/include/radix.h | 161 +++++++++++ src/lib/radix.c | 673 ++++++++++++++++++++++++++++++++++++++++++++ src/smartdns.c | 7 +- 7 files changed, 979 insertions(+), 16 deletions(-) create mode 100644 src/include/radix.h create mode 100644 src/lib/radix.c diff --git a/src/Makefile b/src/Makefile index 67dc8e4..9506b90 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ BIN=smartdns -OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o +OBJS_LIB=lib/rbtree.o lib/art.o lib/bitops.o lib/radix.o OBJS=smartdns.o fast_ping.o dns_client.o dns_server.o dns.o util.o tlog.o conf.o dns_cache.o $(OBJS_LIB) CFLAGS +=-O2 -Wall -Wstrict-prototypes -fno-omit-frame-pointer -Wstrict-aliasing CFLAGS +=-Iinclude diff --git a/src/conf.c b/src/conf.c index 02177e2..325cb3c 100644 --- a/src/conf.c +++ b/src/conf.c @@ -31,7 +31,9 @@ char dns_conf_audit_file[DNS_MAX_PATH]; int dns_conf_audit_size = 1024 * 1024; int dns_conf_audit_num = 2; -art_tree dns_conf_address; +art_tree dns_conf_domain_rule; +radix_tree_t *dns_conf_address_rule; + int dns_conf_rr_ttl; int dns_conf_rr_ttl_min; int dns_conf_rr_ttl_max; @@ -90,16 +92,30 @@ int config_server(char *value, dns_server_type_t type, int default_port) return 0; } -int config_address_iter_cb(void *data, const unsigned char *key, uint32_t key_len, void *value) +int config_domain_iter_cb(void *data, const unsigned char *key, uint32_t key_len, void *value) { free(value); return 0; } -void config_address_destroy(void) +void config_domain_destroy(void) { - art_iter(&dns_conf_address, config_address_iter_cb, 0); - art_tree_destroy(&dns_conf_address); + art_iter(&dns_conf_domain_rule, config_domain_iter_cb, 0); + art_tree_destroy(&dns_conf_domain_rule); +} + +void config_address_destroy(radix_node_t *node, void *cbctx) +{ + if (node == NULL) { + return; + } + + if (node->data == NULL) { + return; + } + + free(node->data); + node->data = NULL; } int config_address(char *value) @@ -184,7 +200,7 @@ int config_address(char *value) domain_key[0] = type; len++; - oldaddress = art_insert(&dns_conf_address, (unsigned char *)domain_key, len, address); + oldaddress = art_insert(&dns_conf_domain_rule, (unsigned char *)domain_key, len, address); if (oldaddress) { free(oldaddress); } @@ -465,8 +481,68 @@ void conf_bogus_nxdomain_destroy(void) } } +radix_node_t *create_addr_node(radix_tree_t *tree, char *addr) +{ + radix_node_t *node; + void *p; + prefix_t prefix; + const char *errmsg = NULL; + + p = prefix_pton(addr, -1, &prefix, &errmsg); + if (p == NULL) { + return NULL; + } + + node = radix_lookup(tree, &prefix); + return node; +} + + +int config_iplist_action(char *subnet, enum address_action act) +{ + radix_node_t *node = NULL; + struct dns_ip_address_rule *ip_rule = NULL; + + node = create_addr_node(dns_conf_address_rule, subnet); + if (node == NULL) { + return -1; + } + + if (node->data == NULL) { + ip_rule = malloc(sizeof(*ip_rule)); + if (ip_rule == NULL) { + return -1; + } + + node->data = ip_rule; + memset(ip_rule, 0, sizeof(*ip_rule)); + } + + ip_rule = node->data; + + switch (act) { + case ACTION_BLACKLIST: + ip_rule->blacklist = 1; + break; + case ACTION_BOGUS: + ip_rule->bogus = 1; + break; + } + + return 0; +} + +int config_blacklist_ip(char *value) +{ + return config_iplist_action(value, ACTION_BLACKLIST); +} + int conf_bogus_nxdomain(char *value) { + ////////////////////////////////////// + config_iplist_action(value, ACTION_BOGUS); + ////////////////////////////////////// + struct dns_bogus_ip_address *ip_addr = NULL; char ip[MAX_IP_LEN]; int port; @@ -570,6 +646,7 @@ struct config_item config_item[] = { {"rr-ttl-min", config_rr_ttl_min}, {"rr-ttl-max", config_rr_ttl_max}, {"force-AAAA-SOA", config_force_AAAA_SOA}, + {"blacklist-ip", config_blacklist_ip}, {"bogus-nxdomain", conf_bogus_nxdomain}, {"conf-file", config_addtional_file}, }; @@ -577,7 +654,12 @@ int config_item_num = sizeof(config_item) / sizeof(struct config_item); int load_conf_init(void) { - art_tree_init(&dns_conf_address); + dns_conf_address_rule = New_Radix(); + art_tree_init(&dns_conf_domain_rule); + if (dns_conf_address_rule == NULL) { + return -1; + } + hash_init(dns_conf_bogus_nxdomain.ip_hash); return 0; } @@ -585,7 +667,8 @@ int load_conf_init(void) void load_exit(void) { conf_bogus_nxdomain_destroy(); - config_address_destroy(); + config_domain_destroy(); + Destroy_Radix(dns_conf_address_rule, config_address_destroy, NULL); } int load_conf_file(const char *file) diff --git a/src/conf.h b/src/conf.h index 1ff5383..ca5d62c 100644 --- a/src/conf.h +++ b/src/conf.h @@ -3,6 +3,7 @@ #include "list.h" #include "art.h" +#include "radix.h" #include "dns.h" #include "dns_client.h" #include "hash.h" @@ -44,6 +45,17 @@ struct dns_bogus_ip_address { }; }; +enum address_action { + ACTION_BLACKLIST = 1, + ACTION_BOGUS = 2, +}; + +struct dns_ip_address_rule +{ + unsigned int blacklist : 1; + unsigned int bogus : 1; +}; + struct dns_bogus_nxdomain { DECLARE_HASHTABLE(ip_hash, 12); }; @@ -69,7 +81,8 @@ extern int dns_conf_audit_size; extern int dns_conf_audit_num; extern char dns_conf_server_name[DNS_MAX_CONF_CNAME_LEN]; -extern art_tree dns_conf_address; +extern art_tree dns_conf_domain_rule; +extern radix_tree_t *dns_conf_address_rule; extern int dns_conf_rr_ttl; extern int dns_conf_rr_ttl_min; diff --git a/src/dns_server.c b/src/dns_server.c index 0ae60f2..4dc9433 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -683,6 +683,30 @@ static int _dns_server_bogus_nxdomain_exists(struct dns_request *request, unsign return 0; } +static int _dns_server_ip_rule_check(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; + + if (prefix_from_blob(addr, addr_len, addr_len * 8, &prefix) == NULL) { + return -1; + } + + node = radix_search_best(dns_conf_address_rule, &prefix); + if (node == NULL) { + return - 1; + } + + if (node->data == NULL) { + return -1; + } + + rule = node->data; + + return 0; +} + static int _dns_server_process_answer(struct dns_request *request, char *domain, struct dns_packet *packet) { int ttl; @@ -717,6 +741,11 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain, tlog(TLOG_DEBUG, "domain: %s TTL:%d IP: %d.%d.%d.%d", name, ttl, addr[0], addr[1], addr[2], addr[3]); + if (_dns_server_ip_rule_check(request, addr, 4, DNS_T_A) == 0) { + _dns_server_request_release(request); + break; + } + /* bogus ip address, skip */ if (_dns_server_bogus_nxdomain_exists(request, addr, DNS_T_A) == 0) { _dns_server_request_release(request); @@ -770,6 +799,11 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain, tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", name, ttl, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]); + if (_dns_server_ip_rule_check(request, addr, 16, DNS_T_A) == 0) { + _dns_server_request_release(request); + break; + } + /* bogus ip address, skip */ if (_dns_server_bogus_nxdomain_exists(request, addr, DNS_T_AAAA) == 0) { _dns_server_request_release(request); @@ -1023,10 +1057,10 @@ static struct dns_address *_dns_server_get_address_by_domain(char *domain, int q domain_key[domain_len] = 0; if (likely(dns_conf_log_level > TLOG_INFO)) { - return art_substring(&dns_conf_address, (unsigned char *)domain_key, domain_len, NULL, NULL); + return art_substring(&dns_conf_domain_rule, (unsigned char *)domain_key, domain_len, NULL, NULL); } - address = art_substring(&dns_conf_address, (unsigned char *)domain_key, domain_len, matched_key, &matched_key_len); + address = art_substring(&dns_conf_domain_rule, (unsigned char *)domain_key, domain_len, matched_key, &matched_key_len); if (address == NULL) { return NULL; } diff --git a/src/include/radix.h b/src/include/radix.h new file mode 100644 index 0000000..b93b657 --- /dev/null +++ b/src/include/radix.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 1999-2000 + * + * The Regents of the University of Michigan ("The Regents") and + * Merit Network, Inc. All rights reserved. Redistribution and use + * in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of + * this software must display the following acknowledgement: + * + * This product includes software developed by the University of + * Michigan, Merit Network, Inc., and their contributors. + * + * 4. Neither the name of the University, Merit Network, nor the + * names of their contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Portions Copyright (c) 2004,2005 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: radix.h,v 1.9 2007/10/24 06:03:08 djm Exp $ */ + +#ifndef _RADIX_H +#define _RADIX_H + +#if defined(_MSC_VER) +#include +#include +#else +# include +# include +# include +# include +# include +#endif + +#if defined(_MSC_VER) +# define snprintf _snprintf +typedef unsigned __int8 u_int8_t; +typedef unsigned __int16 u_int16_t; +typedef unsigned __int32 u_int32_t; +const char *inet_ntop(int af, const void *src, char *dst, size_t size); +size_t strlcpy(char *dst, const char *src, size_t size); +#endif + +/* + * Originally from MRT include/mrt.h + * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ + */ +typedef struct _prefix_t { + u_int family; /* AF_INET | AF_INET6 */ + u_int bitlen; /* same as mask? */ + int ref_count; /* reference count */ + union { + struct in_addr sin; + struct in6_addr sin6; + } add; +} prefix_t; + +void Deref_Prefix(prefix_t *prefix); + +/* + * Originally from MRT include/radix.h + * $MRTId: radix.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ + */ +typedef struct _radix_node_t { + u_int bit; /* flag if this node used */ + prefix_t *prefix; /* who we are in radix tree */ + struct _radix_node_t *l, *r; /* left and right children */ + struct _radix_node_t *parent; /* may be used */ + void *data; /* pointer to data */ +} radix_node_t; + +typedef struct _radix_tree_t { + radix_node_t *head; + u_int maxbits; /* for IP, 32 bit addresses */ + int num_active_node; /* for debug purpose */ +} radix_tree_t; + +/* Type of callback function */ +typedef void (*rdx_cb_t)(radix_node_t *, void *); + +radix_tree_t *New_Radix(void); +void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx); +radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix); +void radix_remove(radix_tree_t *radix, radix_node_t *node); +radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix); +radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix); +void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx); + +#define RADIX_MAXBITS 128 + +#define RADIX_WALK(Xhead, Xnode) \ + do { \ + radix_node_t *Xstack[RADIX_MAXBITS+1]; \ + radix_node_t **Xsp = Xstack; \ + radix_node_t *Xrn = (Xhead); \ + while ((Xnode = Xrn)) { \ + if (Xnode->prefix) + +#define RADIX_WALK_END \ + if (Xrn->l) { \ + if (Xrn->r) { \ + *Xsp++ = Xrn->r; \ + } \ + Xrn = Xrn->l; \ + } else if (Xrn->r) { \ + Xrn = Xrn->r; \ + } else if (Xsp != Xstack) { \ + Xrn = *(--Xsp); \ + } else { \ + Xrn = (radix_node_t *) 0; \ + } \ + } \ + } while (0) + +/* Local additions */ + +prefix_t *prefix_pton(const char *string, long len, prefix_t *prefix, const char **errmsg); +prefix_t *prefix_from_blob(unsigned char *blob, int len, int prefixlen, prefix_t *prefix); +const char *prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len); +const char *prefix_ntop(prefix_t *prefix, char *buf, size_t len); + +#endif /* _RADIX_H */ diff --git a/src/lib/radix.c b/src/lib/radix.c new file mode 100644 index 0000000..e01fa9d --- /dev/null +++ b/src/lib/radix.c @@ -0,0 +1,673 @@ +/* + * Copyright (c) 1999-2000 + * + * The Regents of the University of Michigan ("The Regents") and + * Merit Network, Inc. All rights reserved. Redistribution and use + * in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of + * this software must display the following acknowledgement: + * + * This product includes software developed by the University of + * Michigan, Merit Network, Inc., and their contributors. + * + * 4. Neither the name of the University, Merit Network, nor the + * names of their contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Portions Copyright (c) 2004,2005 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "radix.h" + +/* $Id: radix.c,v 1.17 2007/10/24 06:04:31 djm Exp $ */ + +/* + * Originally from MRT include/defs.h + * $MRTId: defs.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ + */ +#define BIT_TEST(f, b) ((f) & (b)) + +/* + * Originally from MRT include/mrt.h + * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ + */ +#define prefix_tochar(prefix) ((char *)&(prefix)->add) +#define prefix_touchar(prefix) ((unsigned char *)&(prefix)->add) + +/* + * Originally from MRT lib/mrt/prefix.c + * $MRTId: prefix.c,v 1.1.1.1 2000/08/14 18:46:11 labovit Exp $ + */ + +static int +comp_with_mask(unsigned char *addr, unsigned char *dest, unsigned int mask) +{ + if (memcmp(addr, dest, mask / 8) == 0) { + unsigned int n = mask / 8; + unsigned int m = ((~0) << (8 - (mask % 8))); + + if (mask % 8 == 0 || (addr[n] & m) == (dest[n] & m)) + return (1); + } + return (0); +} + +static prefix_t +*New_Prefix2(int family, void *dest, int bitlen, prefix_t *prefix) +{ + int dynamic_allocated = 0; + int default_bitlen = 32; + + if (family == AF_INET6) { + default_bitlen = 128; + if (prefix == NULL) { + if ((prefix = malloc(sizeof(*prefix))) == NULL) + return (NULL); + memset(prefix, '\0', sizeof(*prefix)); + dynamic_allocated++; + } + memcpy(&prefix->add.sin6, dest, 16); + } else if (family == AF_INET) { + if (prefix == NULL) { + if ((prefix = malloc(sizeof(*prefix))) == NULL) + return (NULL); + memset(prefix, '\0', sizeof(*prefix)); + dynamic_allocated++; + } + memcpy(&prefix->add.sin, dest, 4); + } else + return (NULL); + + prefix->bitlen = (bitlen >= 0) ? bitlen : default_bitlen; + prefix->family = family; + prefix->ref_count = 0; + if (dynamic_allocated) + prefix->ref_count++; + return (prefix); +} + + +static prefix_t +*Ref_Prefix(prefix_t *prefix) +{ + if (prefix == NULL) + return (NULL); + if (prefix->ref_count == 0) { + /* make a copy in case of a static prefix */ + return (New_Prefix2(prefix->family, &prefix->add, + prefix->bitlen, NULL)); + } + prefix->ref_count++; + return (prefix); +} + + +void +Deref_Prefix(prefix_t *prefix) +{ + if (prefix == NULL) + return; + prefix->ref_count--; + if (prefix->ref_count <= 0) { + free(prefix); + return; + } +} + +/* + * Originally from MRT lib/radix/radix.c + * $MRTId: radix.c,v 1.1.1.1 2000/08/14 18:46:13 labovit Exp $ + */ + +/* these routines support continuous mask only */ + +radix_tree_t +*New_Radix(void) +{ + radix_tree_t *radix; + + if ((radix = malloc(sizeof(*radix))) == NULL) + return (NULL); + memset(radix, '\0', sizeof(*radix)); + + radix->maxbits = 128; + radix->head = NULL; + radix->num_active_node = 0; + return (radix); +} + +/* + * if func is supplied, it will be called as func(node->data) + * before deleting the node + */ +static void +Clear_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) +{ + if (radix->head) { + radix_node_t *Xstack[RADIX_MAXBITS + 1]; + radix_node_t **Xsp = Xstack; + radix_node_t *Xrn = radix->head; + + while (Xrn) { + radix_node_t *l = Xrn->l; + radix_node_t *r = Xrn->r; + + if (Xrn->prefix) { + Deref_Prefix(Xrn->prefix); + if (Xrn->data && func) + func(Xrn, cbctx); + } + free(Xrn); + radix->num_active_node--; + + if (l) { + if (r) + *Xsp++ = r; + Xrn = l; + } else if (r) { + Xrn = r; + } else if (Xsp != Xstack) { + Xrn = *(--Xsp); + } else { + Xrn = (radix_node_t *) 0; + } + } + } +} + +void +Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) +{ + Clear_Radix(radix, func, cbctx); + free(radix); +} + +/* + * if func is supplied, it will be called as func(node->prefix, node->data) + */ +void +radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx) +{ + radix_node_t *node; + + RADIX_WALK(radix->head, node) { + func(node, cbctx); + } RADIX_WALK_END; +} + +radix_node_t +*radix_search_exact(radix_tree_t *radix, prefix_t *prefix) +{ + radix_node_t *node; + unsigned char *addr; + unsigned int bitlen; + + if (radix->head == NULL) + return (NULL); + + node = radix->head; + addr = prefix_touchar(prefix); + bitlen = prefix->bitlen; + + while (node->bit < bitlen) { + if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) + node = node->r; + else + node = node->l; + + if (node == NULL) + return (NULL); + } + + if (node->bit > bitlen || node->prefix == NULL) + return (NULL); + + if (comp_with_mask(prefix_touchar(node->prefix), + prefix_touchar(prefix), bitlen)) + return (node); + + return (NULL); +} + + +/* if inclusive != 0, "best" may be the given prefix itself */ +static radix_node_t +*radix_search_best2(radix_tree_t *radix, prefix_t *prefix, int inclusive) +{ + radix_node_t *node; + radix_node_t *stack[RADIX_MAXBITS + 1]; + unsigned char *addr; + unsigned int bitlen; + int cnt = 0; + + if (radix->head == NULL) + return (NULL); + + node = radix->head; + addr = prefix_touchar(prefix); + bitlen = prefix->bitlen; + + while (node->bit < bitlen) { + if (node->prefix) + stack[cnt++] = node; + if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) + node = node->r; + else + node = node->l; + + if (node == NULL) + break; + } + + if (inclusive && node && node->prefix) + stack[cnt++] = node; + + + if (cnt <= 0) + return (NULL); + + while (--cnt >= 0) { + node = stack[cnt]; + if (comp_with_mask(prefix_touchar(node->prefix), + prefix_touchar(prefix), node->prefix->bitlen)) + return (node); + } + return (NULL); +} + + +radix_node_t +*radix_search_best(radix_tree_t *radix, prefix_t *prefix) +{ + return (radix_search_best2(radix, prefix, 1)); +} + + +radix_node_t +*radix_lookup(radix_tree_t *radix, prefix_t *prefix) +{ + radix_node_t *node, *new_node, *parent, *glue; + unsigned char *addr, *test_addr; + unsigned int bitlen, check_bit, differ_bit; + unsigned int i, j, r; + + if (radix->head == NULL) { + if ((node = malloc(sizeof(*node))) == NULL) + return (NULL); + memset(node, '\0', sizeof(*node)); + node->bit = prefix->bitlen; + node->prefix = Ref_Prefix(prefix); + node->parent = NULL; + node->l = node->r = NULL; + node->data = NULL; + radix->head = node; + radix->num_active_node++; + return (node); + } + addr = prefix_touchar(prefix); + bitlen = prefix->bitlen; + node = radix->head; + + while (node->bit < bitlen || node->prefix == NULL) { + if (node->bit < radix->maxbits && BIT_TEST(addr[node->bit >> 3], + 0x80 >> (node->bit & 0x07))) { + if (node->r == NULL) + break; + node = node->r; + } else { + if (node->l == NULL) + break; + node = node->l; + } + } + + test_addr = prefix_touchar(node->prefix); + /* find the first bit different */ + check_bit = (node->bit < bitlen) ? node->bit : bitlen; + differ_bit = 0; + for (i = 0; i * 8 < check_bit; i++) { + if ((r = (addr[i] ^ test_addr[i])) == 0) { + differ_bit = (i + 1) * 8; + continue; + } + /* I know the better way, but for now */ + for (j = 0; j < 8; j++) { + if (BIT_TEST(r, (0x80 >> j))) + break; + } + /* must be found */ + differ_bit = i * 8 + j; + break; + } + if (differ_bit > check_bit) + differ_bit = check_bit; + + parent = node->parent; + while (parent && parent->bit >= differ_bit) { + node = parent; + parent = node->parent; + } + + if (differ_bit == bitlen && node->bit == bitlen) { + if (node->prefix == NULL) + node->prefix = Ref_Prefix(prefix); + return (node); + } + if ((new_node = malloc(sizeof(*new_node))) == NULL) + return (NULL); + memset(new_node, '\0', sizeof(*new_node)); + new_node->bit = prefix->bitlen; + new_node->prefix = Ref_Prefix(prefix); + new_node->parent = NULL; + new_node->l = new_node->r = NULL; + new_node->data = NULL; + radix->num_active_node++; + + if (node->bit == differ_bit) { + new_node->parent = node; + if (node->bit < radix->maxbits && BIT_TEST(addr[node->bit >> 3], + 0x80 >> (node->bit & 0x07))) + node->r = new_node; + else + node->l = new_node; + + return (new_node); + } + if (bitlen == differ_bit) { + if (bitlen < radix->maxbits && BIT_TEST(test_addr[bitlen >> 3], + 0x80 >> (bitlen & 0x07))) + new_node->r = node; + else + new_node->l = node; + + new_node->parent = node->parent; + if (node->parent == NULL) + radix->head = new_node; + else if (node->parent->r == node) + node->parent->r = new_node; + else + node->parent->l = new_node; + + node->parent = new_node; + } else { + if ((glue = malloc(sizeof(*glue))) == NULL) + return (NULL); + memset(glue, '\0', sizeof(*glue)); + glue->bit = differ_bit; + glue->prefix = NULL; + glue->parent = node->parent; + glue->data = NULL; + radix->num_active_node++; + if (differ_bit < radix->maxbits && + BIT_TEST(addr[differ_bit >> 3], + 0x80 >> (differ_bit & 0x07))) { + glue->r = new_node; + glue->l = node; + } else { + glue->r = node; + glue->l = new_node; + } + new_node->parent = glue; + + if (node->parent == NULL) + radix->head = glue; + else if (node->parent->r == node) + node->parent->r = glue; + else + node->parent->l = glue; + + node->parent = glue; + } + return (new_node); +} + + +void +radix_remove(radix_tree_t *radix, radix_node_t *node) +{ + radix_node_t *parent, *child; + + if (node->r && node->l) { + /* + * this might be a placeholder node -- have to check and make + * sure there is a prefix aossciated with it ! + */ + if (node->prefix != NULL) + Deref_Prefix(node->prefix); + node->prefix = NULL; + /* Also I needed to clear data pointer -- masaki */ + node->data = NULL; + return; + } + if (node->r == NULL && node->l == NULL) { + parent = node->parent; + Deref_Prefix(node->prefix); + free(node); + radix->num_active_node--; + + if (parent == NULL) { + radix->head = NULL; + return; + } + if (parent->r == node) { + parent->r = NULL; + child = parent->l; + } else { + parent->l = NULL; + child = parent->r; + } + + if (parent->prefix) + return; + + /* we need to remove parent too */ + if (parent->parent == NULL) + radix->head = child; + else if (parent->parent->r == parent) + parent->parent->r = child; + else + parent->parent->l = child; + + child->parent = parent->parent; + free(parent); + radix->num_active_node--; + return; + } + if (node->r) + child = node->r; + else + child = node->l; + + parent = node->parent; + child->parent = parent; + + Deref_Prefix(node->prefix); + free(node); + radix->num_active_node--; + + if (parent == NULL) { + radix->head = child; + return; + } + if (parent->r == node) + parent->r = child; + else + parent->l = child; +} + +/* Local additions */ +static void +sanitise_mask(unsigned char *addr, unsigned int masklen, unsigned int maskbits) +{ + unsigned int i = masklen / 8; + unsigned int j = masklen % 8; + + if (j != 0) { + addr[i] &= (~0) << (8 - j); + i++; + } + for (; i < maskbits / 8; i++) + addr[i] = 0; +} + +prefix_t +*prefix_pton(const char *string, long len, prefix_t *prefix, const char **errmsg) +{ + char save[256], *cp, *ep; + struct addrinfo hints, *ai; + void *addr; + prefix_t *ret; + size_t slen; + int r; + + ret = NULL; + + /* Copy the string to parse, because we modify it */ + if ((slen = strlen(string) + 1) > sizeof(save)) { + *errmsg = "string too long"; + return (NULL); + } + memcpy(save, string, slen); + + if ((cp = strchr(save, '/')) != NULL) { + if (len != -1 ) { + *errmsg = "masklen specified twice"; + return (NULL); + } + *cp++ = '\0'; + len = strtol(cp, &ep, 10); + if (*cp == '\0' || *ep != '\0' || len < 0) { + *errmsg = "could not parse masklen"; + return (NULL); + } + /* More checks below */ + } + memset(&hints, '\0', sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + + if ((r = getaddrinfo(save, NULL, &hints, &ai)) != 0) { + snprintf(save, sizeof(save), "getaddrinfo: %s:", + gai_strerror(r)); + *errmsg = save; + return NULL; + } + if (ai == NULL || ai->ai_addr == NULL) { + *errmsg = "getaddrinfo returned no result"; + goto out; + } + switch (ai->ai_addr->sa_family) { + case AF_INET: + if (len == -1) + len = 32; + else if (len < 0 || len > 32) + goto out; + addr = &((struct sockaddr_in *) ai->ai_addr)->sin_addr; + sanitise_mask(addr, len, 32); + break; + case AF_INET6: + if (len == -1) + len = 128; + else if (len < 0 || len > 128) + goto out; + addr = &((struct sockaddr_in6 *) ai->ai_addr)->sin6_addr; + sanitise_mask(addr, len, 128); + break; + default: + goto out; + } + + ret = New_Prefix2(ai->ai_addr->sa_family, addr, len, prefix); + if (ret == NULL) + *errmsg = "New_Prefix2 failed"; +out: + freeaddrinfo(ai); + return (ret); +} + +prefix_t +*prefix_from_blob(unsigned char *blob, int len, int prefixlen, prefix_t *prefix) +{ + int family, maxprefix; + + switch (len) { + case 4: + /* Assume AF_INET */ + family = AF_INET; + maxprefix = 32; + break; + case 16: + /* Assume AF_INET6 */ + family = AF_INET6; + maxprefix = 128; + break; + default: + /* Who knows? */ + return NULL; + } + if (prefixlen == -1) + prefixlen = maxprefix; + if (prefixlen < 0 || prefixlen > maxprefix) + return NULL; + return (New_Prefix2(family, blob, prefixlen, prefix)); +} + +const char * +prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len) +{ + return (inet_ntop(prefix->family, &prefix->add, buf, len)); +} + +const char * +prefix_ntop(prefix_t *prefix, char *buf, size_t len) +{ + char addrbuf[128]; + + if (prefix_addr_ntop(prefix, addrbuf, sizeof(addrbuf)) == NULL) + return (NULL); + snprintf(buf, len, "%s/%d", addrbuf, prefix->bitlen); + + return (buf); +} diff --git a/src/smartdns.c b/src/smartdns.c index 28faee1..f196396 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -138,19 +138,19 @@ int create_pid_file(const char *pid_file) /* create pid file, and lock this file */ fd = open(pid_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd == -1) { - fprintf(stderr, "create pid file failed, %s", strerror(errno)); + fprintf(stderr, "create pid file failed, %s\n", strerror(errno)); return -1; } flags = fcntl(fd, F_GETFD); if (flags < 0) { - fprintf(stderr, "Could not get flags for PID file %s", pid_file); + fprintf(stderr, "Could not get flags for PID file %s\n", pid_file); goto errout; } flags |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, flags) == -1) { - fprintf(stderr, "Could not set flags for PID file %s", pid_file); + fprintf(stderr, "Could not set flags for PID file %s\n", pid_file); goto errout; } @@ -326,7 +326,6 @@ int main(int argc, char *argv[]) } if (create_pid_file(pid_file) != 0) { - fprintf(stderr, "create pid file failed, %s\n", strerror(errno)); goto errout; }