diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 4e02419..3496533 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -4,7 +4,7 @@ # IPV4: :53 # IPV6 [::]:53 -bind [::]:53 +bind [::]:535 # dns cache size # cache-size [number] diff --git a/src/dns_cache.c b/src/dns_cache.c new file mode 100644 index 0000000..ef9c8cb --- /dev/null +++ b/src/dns_cache.c @@ -0,0 +1,201 @@ +#include "dns_cache.h" +#include + +struct dns_cache_head { + DECLARE_HASHTABLE(cache_hash, 10); + struct list_head cache_list; + int num; + int size; + pthread_mutex_t lock; +}; + +struct dns_cache_head dns_cache_head; + +int dns_cache_init(int size) +{ + INIT_LIST_HEAD(&dns_cache_head.cache_list); + hash_init(dns_cache_head.cache_hash); + dns_cache_head.num = 0; + dns_cache_head.size = size; + + pthread_mutex_init(&dns_cache_head.lock, 0); + + return 0; +} + +struct dns_cache *_dns_cache_last(void) +{ + return list_last_entry(&dns_cache_head.cache_list, struct dns_cache, list); +} + +struct dns_cache *_dns_cache_first(void) +{ + return list_first_entry(&dns_cache_head.cache_list, struct dns_cache, list); +} + +void _dns_cache_delete(struct dns_cache *dns_cache) +{ + hash_del(&dns_cache->node); + list_del_init(&dns_cache->list); + free(dns_cache); +} + +void dns_cache_release(struct dns_cache *dns_cache) +{ + if (!atomic_dec_and_test(&dns_cache->ref)) { + return; + } + + _dns_cache_delete(dns_cache); +} + +int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len) +{ + unsigned int key = 0; + struct dns_cache *dns_cache = NULL; + + if (dns_cache_head.size <= 0) { + return 0; + } + + dns_cache = dns_cache_get(domain, qtype); + if (dns_cache) { + dns_cache_release(dns_cache); + return 0; + } + + dns_cache = malloc(sizeof(*dns_cache)); + if (dns_cache == NULL) { + goto errout; + } + + key = hash_string(domain); + key = jhash(&qtype, sizeof(qtype), key); + strncpy(dns_cache->domain, domain, DNS_MAX_CNAME_LEN); + dns_cache->qtype = qtype; + dns_cache->ttl = ttl; + atomic_set(&dns_cache->ref, 1); + time(&dns_cache->insert_time); + if (qtype == DNS_T_A) { + if (addr_len != DNS_RR_A_LEN) { + goto errout; + } + memcpy(dns_cache->addr, addr, DNS_RR_A_LEN); + } else if (qtype == DNS_T_AAAA) { + if (addr_len != DNS_RR_AAAA_LEN) { + goto errout; + } + memcpy(dns_cache->addr, addr, DNS_RR_AAAA_LEN); + } else { + goto errout; + } + + pthread_mutex_lock(&dns_cache_head.lock); + hash_add(dns_cache_head.cache_hash, &dns_cache->node, key); + list_add(&dns_cache->list, &dns_cache_head.cache_list); + + dns_cache_head.num++; + if (dns_cache_head.num > dns_cache_head.size) { + struct dns_cache *del_cache; + del_cache = _dns_cache_last(); + dns_cache_release(del_cache); + } + pthread_mutex_unlock(&dns_cache_head.lock); + + return 0; +errout: + if (dns_cache) { + free(dns_cache); + } + + return -1; +} + +struct dns_cache *dns_cache_get(char *domain, dns_type_t qtype) +{ + unsigned int key = 0; + struct dns_cache *dns_cache = NULL; + struct dns_cache *dns_cache_ret = NULL; + time_t now; + + if (dns_cache_head.size <= 0) { + return NULL; + } + + key = hash_string(domain); + key = jhash(&qtype, sizeof(qtype), key); + + time(&now); + pthread_mutex_lock(&dns_cache_head.lock); + hash_for_each_possible(dns_cache_head.cache_hash, dns_cache, node, key) + { + if (dns_cache->qtype != qtype) { + continue; + } + + if (strncmp(domain, dns_cache->domain, DNS_MAX_CNAME_LEN) != 0) { + continue; + } + + dns_cache_ret = dns_cache; + break; + } + + if (dns_cache_ret) { + if (now - dns_cache_ret->insert_time > dns_cache_ret->ttl) { + _dns_cache_delete(dns_cache_ret); + dns_cache_ret = NULL; + } else { + atomic_inc(&dns_cache_ret->ref); + } + } + + pthread_mutex_unlock(&dns_cache_head.lock); + + return dns_cache_ret; +} + +int dns_cache_get_ttl(struct dns_cache *dns_cache) +{ + time_t now; + int ttl = 0; + time(&now); + + ttl = dns_cache->insert_time + dns_cache->ttl - now; + if (ttl < 0) { + return 0; + } + + return ttl; +} + +void dns_cache_delete(struct dns_cache *dns_cache) +{ + pthread_mutex_lock(&dns_cache_head.lock); + hash_del(&dns_cache->node); + list_del_init(&dns_cache->list); + pthread_mutex_unlock(&dns_cache_head.lock); + dns_cache_release(dns_cache); +} + +void dns_cache_update(struct dns_cache *dns_cache) +{ + pthread_mutex_lock(&dns_cache_head.lock); + list_del_init(&dns_cache->list); + list_add(&dns_cache->list, &dns_cache_head.cache_list); + pthread_mutex_unlock(&dns_cache_head.lock); +} + +void dns_cache_destroy(void) +{ + struct dns_cache *dns_cache = NULL; + struct dns_cache *tmp; + pthread_mutex_lock(&dns_cache_head.lock); + list_for_each_entry_safe(dns_cache, tmp, &dns_cache_head.cache_list, list) + { + _dns_cache_delete(dns_cache); + } + pthread_mutex_unlock(&dns_cache_head.lock); + + pthread_mutex_destroy(&dns_cache_head.lock); +} \ No newline at end of file diff --git a/src/dns_cache.h b/src/dns_cache.h new file mode 100644 index 0000000..00d4980 --- /dev/null +++ b/src/dns_cache.h @@ -0,0 +1,41 @@ +#ifndef _SMARTDNS_CACHE_H +#define _SMARTDNS_CACHE_H + +#include "dns.h" +#include "hashtable.h" +#include "hash.h" +#include "list.h" +#include "atomic.h" + +struct dns_cache { + struct hlist_node node; + struct list_head list; + atomic_t ref; + char domain[DNS_MAX_CNAME_LEN]; + unsigned ttl; + time_t insert_time; + dns_type_t qtype; + union { + unsigned char ipv4_addr[DNS_RR_A_LEN]; + unsigned char ipv6_addr[DNS_RR_AAAA_LEN]; + unsigned char addr[0]; + }; +}; + +int dns_cache_init(int size); + +int dns_cache_insert(char *domain, int ttl, dns_type_t qtype, unsigned char *addr, int addr_len); + +struct dns_cache *dns_cache_get(char *domain, dns_type_t qtype); + +void dns_cache_delete(struct dns_cache *dns_cache); + +void dns_cache_release(struct dns_cache *dns_cache); + +void dns_cache_update(struct dns_cache *dns_cache); + +int dns_cache_get_ttl(struct dns_cache *dns_cache); + +void dns_cache_destroy(void); + +#endif // !_SMARTDNS_CACHE_H \ No newline at end of file diff --git a/src/dns_server.c b/src/dns_server.c index cee82e1..a6b4d55 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -20,6 +20,7 @@ #include "atomic.h" #include "conf.h" #include "dns.h" +#include "dns_cache.h" #include "dns_client.h" #include "fast_ping.h" #include "hashtable.h" @@ -332,6 +333,8 @@ void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *hos { struct dns_request *request = userptr; int may_complete = 0; + int addr_type = 0; + if (request == NULL) { return; } @@ -352,6 +355,7 @@ void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *hos request->ping_ttl_v4 = rtt; request->has_ipv4 = 1; memcpy(request->ipv4_addr, &addr_in->sin_addr.s_addr, 4); + addr_type = 4; } } break; case AF_INET6: { @@ -362,12 +366,14 @@ void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *hos request->ping_ttl_v4 = rtt; request->has_ipv4 = 1; memcpy(request->ipv4_addr, addr_in6->sin6_addr.s6_addr + 12, 4); + addr_type = 4; } } else { if (request->ping_ttl_v6 > rtt) { request->ping_ttl_v6 = rtt; request->has_ipv6 = 1; memcpy(request->ipv6_addr, addr_in6->sin6_addr.s6_addr, 16); + addr_type = 6; } } } break; @@ -390,6 +396,11 @@ void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *hos if (may_complete) { _dns_server_request_complete(request); _dns_server_request_remove(request); + if (addr_type == 4) { + dns_cache_insert(request->domain, request->ttl_v4, DNS_T_A, request->ipv4_addr, DNS_RR_A_LEN); + } else if (addr_type == 6) { + dns_cache_insert(request->domain, request->ttl_v6, DNS_T_AAAA, request->ipv6_addr, DNS_RR_AAAA_LEN); + } } } @@ -754,6 +765,44 @@ errout: return -1; } +static int _dns_server_process_cache(struct dns_request *request, struct dns_packet *packet) +{ + struct dns_cache *dns_cache = NULL; + + dns_cache = dns_cache_get(request->domain, request->qtype); + if (dns_cache == NULL) { + goto errout; + } + + if (request->qtype != dns_cache->qtype) { + goto errout; + } + + switch (request->qtype) { + case DNS_T_A: + memcpy(request->ipv4_addr, dns_cache->ipv4_addr, DNS_RR_A_LEN); + request->ttl_v4 = dns_cache_get_ttl(dns_cache); + request->has_ipv4 = 1; + break; + case DNS_T_AAAA: + memcpy(request->ipv6_addr, dns_cache->ipv6_addr, DNS_RR_AAAA_LEN); + request->ttl_v6 = dns_cache_get_ttl(dns_cache); + request->has_ipv6 = 1; + break; + default: + goto errout; + break; + } + + request->rcode = DNS_RC_NOERROR; + _dns_reply(request); + dns_cache_release(dns_cache); + + return 0; +errout: + return -1; +} + static int _dns_server_recv(unsigned char *inpacket, int inpacket_len, struct sockaddr_storage *from, socklen_t from_len) { int decode_len; @@ -836,6 +885,11 @@ static int _dns_server_recv(unsigned char *inpacket, int inpacket_len, struct so return 0; } + if (_dns_server_process_cache(request, packet) == 0) { + free(request); + return 0; + } + tlog(TLOG_INFO, "query server %s from %s, qtype = %d\n", request->domain, gethost_by_addr(name, (struct sockaddr *)from, from_len), qtype); _dns_server_request_get(request); @@ -1099,6 +1153,11 @@ int dns_server_init(void) return -1; } + if (dns_cache_init(1024) != 0) { + tlog(TLOG_ERROR, "init cache failed."); + return -1; + } + memset(&server, 0, sizeof(server)); pthread_attr_init(&attr); @@ -1139,6 +1198,8 @@ errout: pthread_mutex_destroy(&server.request_list_lock); + dns_cache_destroy(); + return -1; } @@ -1172,4 +1233,6 @@ void dns_server_exit(void) } pthread_mutex_destroy(&server.request_list_lock); + + dns_cache_destroy(); } diff --git a/src/include/cache.h b/src/include/cache.h deleted file mode 100644 index 6300348..0000000 --- a/src/include/cache.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _GENERIC_CACHE_H - -#include "list.h" -#include "hashtable.h" - -struct cache_node { - struct hlist_node list; -}; - -struct cache_head; - -struct cache_head *cache_new(int hashsize, void (*item_free)(struct cache_head *head, struct cache_node *node)); - -int cache_add(struct cache_head *head, struct cache_node *node, void *key, int key_len); - -int cache_del(struct cache_node *node); - -struct cache_node *cache_lookup(struct cache_head *head, void *key, int key_len); - -int cache_update(struct cache_head *head, void *key, int key_len); - -void cache_free(struct cache_head *head); - -#endif // !_GENERIC_CACHE_H diff --git a/src/lib/cache.c b/src/lib/cache.c deleted file mode 100644 index c37fe19..0000000 --- a/src/lib/cache.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "cache.h" -#include - -struct cache_head { - struct hlist_head hash_head; - int hash_size; - pthread_rwlock_t *rwlock; -}; - -struct cache_head *cache_new(int hashsize, void (*item_free)(struct cache_head *head, struct cache_node *node)) -{ - return NULL; -} - -int cache_add(struct cache_head *head, struct cache_node *node, void *key, int key_len) -{ - return 0; -} - -int cache_del(struct cache_node *node) -{ - return 0; -} - -struct cache_node *cache_lookup(struct cache_head *head, void *key, int key_len) -{ - return 0; -} - -int cache_update(struct cache_head *head, void *key, int key_len) -{ - return 0; -} - -void cache_free(struct cache_head *head) -{ - return -} \ No newline at end of file