|
@@ -0,0 +1,2436 @@
|
|
|
+/*
|
|
|
+ * $Id$
|
|
|
+ *
|
|
|
+ * resolver related functions
|
|
|
+ *
|
|
|
+ * Copyright (C) 2006 iptelorg GmbH
|
|
|
+ *
|
|
|
+ * This file is part of ser, a free SIP server.
|
|
|
+ *
|
|
|
+ * ser 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 2 of the License, or
|
|
|
+ * (at your option) any later version
|
|
|
+ *
|
|
|
+ * For a license to use the ser software under conditions
|
|
|
+ * other than those described here, or to purchase support for this
|
|
|
+ * software, please contact iptel.org by e-mail at the following addresses:
|
|
|
+ * [email protected]
|
|
|
+ *
|
|
|
+ * ser 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, write to the Free Software
|
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
+ */
|
|
|
+/* History:
|
|
|
+ * --------
|
|
|
+ * 2006-07-13 created by andrei
|
|
|
+ */
|
|
|
+
|
|
|
+#ifdef USE_DNS_CACHE
|
|
|
+
|
|
|
+#include "globals.h"
|
|
|
+#include "dns_cache.h"
|
|
|
+#include "dns_wrappers.h"
|
|
|
+#include "mem/shm_mem.h"
|
|
|
+#include "hashes.h"
|
|
|
+#include "clist.h"
|
|
|
+#include "locking.h"
|
|
|
+#include "atomic_ops.h"
|
|
|
+#include "ut.h"
|
|
|
+#include "timer.h"
|
|
|
+#include "timer_ticks.h"
|
|
|
+#include "error.h"
|
|
|
+#include "rpc.h"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#define DNS_CACHE_DEBUG /* extra sanity checks and debugging */
|
|
|
+
|
|
|
+
|
|
|
+#ifndef MAX
|
|
|
+ #define MAX(a,b) ( ((a)>(b))?(a):(b))
|
|
|
+#endif
|
|
|
+
|
|
|
+#define MAX_DNS_RECORDS 255 /* maximum dns records number received in a
|
|
|
+ dns answer*/
|
|
|
+
|
|
|
+#define DNS_HASH_SIZE 1024 /* must be <= 65535 */
|
|
|
+#define DEFAULT_DNS_NEG_CACHE_TTL 60 /* 1 min. */
|
|
|
+#define DEFAULT_DNS_CACHE_MIN_TTL 0 /* (disabled) */
|
|
|
+#define DEFAULT_DNS_CACHE_MAX_TTL ((unsigned int)(-1)) /* (maxint) */
|
|
|
+#define DEFAULT_DNS_MAX_MEM 500 /* 500 Kb */
|
|
|
+#define DEFAULT_DNS_TIMER_INTERVAL 120 /* 2 min. */
|
|
|
+#define DNS_HE_MAX_ADDR 10 /* maxium addresses returne in a hostent struct */
|
|
|
+#define MAX_CNAME_CHAIN 10
|
|
|
+
|
|
|
+
|
|
|
+static gen_lock_t* dns_hash_lock=0;
|
|
|
+static volatile unsigned int *dns_cache_mem_used=0; /* current mem. use */
|
|
|
+unsigned int dns_cache_max_mem=DEFAULT_DNS_MAX_MEM; /* maximum memory used for
|
|
|
+ the cached entries */
|
|
|
+unsigned int dns_neg_cache_ttl=DEFAULT_DNS_NEG_CACHE_TTL; /* neg. cache ttl */
|
|
|
+unsigned int dns_cache_max_ttl=DEFAULT_DNS_CACHE_MAX_TTL; /* maximum ttl */
|
|
|
+unsigned int dns_cache_min_ttl=DEFAULT_DNS_CACHE_MIN_TTL; /* minimum ttl */
|
|
|
+unsigned int dns_timer_interval=DEFAULT_DNS_TIMER_INTERVAL; /* in s */
|
|
|
+int dns_flags=0; /* default flags used for the dns_*resolvehost
|
|
|
+ (compatibility wrappers) */
|
|
|
+
|
|
|
+#define LOCK_DNS_HASH() lock_get(dns_hash_lock)
|
|
|
+#define UNLOCK_DNS_HASH() lock_release(dns_hash_lock)
|
|
|
+
|
|
|
+#define FIX_TTL(t) (((t)<dns_cache_min_ttl)?dns_cache_min_ttl: \
|
|
|
+ (((t)>dns_cache_max_ttl)?dns_cache_max_ttl:(t)))
|
|
|
+
|
|
|
+
|
|
|
+struct dns_hash_head{
|
|
|
+ struct dns_hash_entry* next;
|
|
|
+ struct dns_hash_entry* prev;
|
|
|
+};
|
|
|
+
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+struct dns_lu_lst* dns_last_used_lst=0;
|
|
|
+#endif
|
|
|
+
|
|
|
+static struct dns_hash_head* dns_hash=0;
|
|
|
+
|
|
|
+
|
|
|
+static struct timer_ln* dns_timer_h=0;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static const char* dns_str_errors[]={
|
|
|
+ "no error",
|
|
|
+ "no more records", /* not an error, but and end condition */
|
|
|
+ "unknown error",
|
|
|
+ "internal error",
|
|
|
+ "bad SRV entry",
|
|
|
+ "unresolvable SRV request",
|
|
|
+ "bad A or AAAA entry",
|
|
|
+ "unresovlable A or AAAA request",
|
|
|
+ "invalid ip in A or AAAA record",
|
|
|
+ "blacklisted ip",
|
|
|
+ "name too long ", /* try again with a shorter name */
|
|
|
+ "ip AF mismatch", /* address family mismatch */
|
|
|
+ "bug - critical error"
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* param: err (negative error number) */
|
|
|
+const char* dns_strerror(int err)
|
|
|
+{
|
|
|
+ err=-err;
|
|
|
+ if ((err>=0) && (err<sizeof(dns_str_errors)/sizeof(char*)))
|
|
|
+ return dns_str_errors[err];
|
|
|
+ return "bug -- bad error number";
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* "internal" only, don't use unless you really know waht you're doing */
|
|
|
+inline static void dns_destroy_entry(struct dns_hash_entry* e)
|
|
|
+{
|
|
|
+#ifdef DNS_CACHE_DEBUG
|
|
|
+ memset(e, 0, e->total_size);
|
|
|
+#endif
|
|
|
+ shm_free(e); /* nice having it in one block isn't it? :-) */
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* "internal" only, same as above, asumes shm_lock() held (tm optimization) */
|
|
|
+inline static void dns_destroy_entry_shm_unsafe(struct dns_hash_entry* e)
|
|
|
+{
|
|
|
+#ifdef DNS_CACHE_DEBUG
|
|
|
+ memset(e, 0, e->total_size);
|
|
|
+#endif
|
|
|
+ shm_free_unsafe(e); /* nice having it in one block isn't it? :-) */
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* dec. the internal refcnt and if 0 deletes the entry */
|
|
|
+void dns_hash_put(struct dns_hash_entry* e)
|
|
|
+{
|
|
|
+ if(e && atomic_dec_and_test(&e->refcnt)){
|
|
|
+ /* atomic_sub_long(dns_cache_total_used, e->total_size); */
|
|
|
+ dns_destroy_entry(e);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* same as above but uses dns_destroy_unsafe (assumes shm_lock held -- tm
|
|
|
+ * optimization) */
|
|
|
+void dns_hash_put_shm_unsafe(struct dns_hash_entry* e)
|
|
|
+{
|
|
|
+ if(e && atomic_dec_and_test(&e->refcnt)){
|
|
|
+ /* atomic_sub_long(dns_cache_total_used, e->total_size); */
|
|
|
+ dns_destroy_entry_shm_unsafe(e);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+inline static int dns_cache_clean(unsigned int no, int expired_only);
|
|
|
+inline static int dns_cache_free_mem(unsigned int target, int expired_only);
|
|
|
+
|
|
|
+static ticks_t dns_timer(ticks_t ticks, struct timer_ln* tl, void* data)
|
|
|
+{
|
|
|
+ if (*dns_cache_mem_used>12*(dns_cache_max_mem/16)){ /* ~ 75% used */
|
|
|
+ dns_cache_free_mem(dns_cache_max_mem/2, 1);
|
|
|
+ }else{
|
|
|
+ dns_cache_clean(-1, 1); /* all the table, only expired entries */
|
|
|
+ /* TODO: better strategy? */
|
|
|
+ }
|
|
|
+ return (ticks_t)(-1);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void destroy_dns_cache()
|
|
|
+{
|
|
|
+ if (dns_timer_h){
|
|
|
+ timer_del(dns_timer_h);
|
|
|
+ timer_free(dns_timer_h);
|
|
|
+ dns_timer_h=0;
|
|
|
+ }
|
|
|
+ if (dns_hash_lock){
|
|
|
+ lock_destroy(dns_hash_lock);
|
|
|
+ lock_dealloc(dns_hash_lock);
|
|
|
+ dns_hash_lock=0;
|
|
|
+ }
|
|
|
+ if (dns_hash){
|
|
|
+ shm_free(dns_hash);
|
|
|
+ dns_hash=0;
|
|
|
+ }
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ if (dns_last_used_lst){
|
|
|
+ shm_free(dns_last_used_lst);
|
|
|
+ dns_last_used_lst=0;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ if (dns_cache_mem_used){
|
|
|
+ shm_free((void*)dns_cache_mem_used);
|
|
|
+ dns_cache_mem_used=0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int init_dns_cache()
|
|
|
+{
|
|
|
+ int r;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret=0;
|
|
|
+ /* sanity check */
|
|
|
+ if (E_DNS_CRITICAL>=sizeof(dns_str_errors)/sizeof(char*)){
|
|
|
+ LOG(L_CRIT, "BUG: dns_cache_init: bad dns error table\n");
|
|
|
+ ret=E_BUG;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ dns_cache_mem_used=shm_malloc(sizeof(*dns_cache_mem_used));
|
|
|
+ if (dns_cache_mem_used==0){
|
|
|
+ ret=E_OUT_OF_MEM;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ dns_last_used_lst=shm_malloc(sizeof(*dns_last_used_lst));
|
|
|
+ if (dns_last_used_lst==0){
|
|
|
+ ret=E_OUT_OF_MEM;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ clist_init(dns_last_used_lst, next, prev);
|
|
|
+#endif
|
|
|
+ dns_hash=shm_malloc(sizeof(struct dns_hash_head)*DNS_HASH_SIZE);
|
|
|
+ if (dns_hash==0){
|
|
|
+ ret=E_OUT_OF_MEM;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ for (r=0; r<DNS_HASH_SIZE; r++)
|
|
|
+ clist_init(&dns_hash[r], next, prev);
|
|
|
+
|
|
|
+ dns_hash_lock=lock_alloc();
|
|
|
+ if (dns_hash_lock==0){
|
|
|
+ ret=E_OUT_OF_MEM;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ if (lock_init(dns_hash_lock)==0){
|
|
|
+ lock_dealloc(dns_hash_lock);
|
|
|
+ dns_hash_lock=0;
|
|
|
+ ret=-1;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* fix options */
|
|
|
+ dns_cache_max_mem<<=10; /* Kb */ /* TODO: test with 0 */
|
|
|
+ /* fix flags */
|
|
|
+ if (dns_try_ipv6==0){
|
|
|
+ dns_flags|=DNS_IPV4_ONLY;
|
|
|
+ }
|
|
|
+ if (dns_flags & DNS_IPV4_ONLY){
|
|
|
+ dns_flags&=~(DNS_IPV6_ONLY|DNS_IPV6_FIRST);
|
|
|
+ }
|
|
|
+ ;
|
|
|
+ dns_timer_h=timer_alloc();
|
|
|
+ if (dns_timer_h==0){
|
|
|
+ ret=E_OUT_OF_MEM;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ if (dns_timer_interval){
|
|
|
+ timer_init(dns_timer_h, dns_timer, 0, 0); /* "slow" timer */
|
|
|
+ if (timer_add(dns_timer_h, S_TO_TICKS(dns_timer_interval))<0){
|
|
|
+ LOG(L_CRIT, "BUG: dns_cache_init: failed to add the timer\n");
|
|
|
+ timer_free(dns_timer_h);
|
|
|
+ dns_timer_h=0;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+error:
|
|
|
+ destroy_dns_cache();
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* hash function, based on get_hash1_raw, but case insensitive
|
|
|
+ * type is not used (obsolete)
|
|
|
+ * returns the hash value
|
|
|
+ */
|
|
|
+inline static unsigned int dns_hash_no(char* s, int len, int type)
|
|
|
+{
|
|
|
+ char* p;
|
|
|
+ char* end;
|
|
|
+
|
|
|
+ register unsigned v;
|
|
|
+ register unsigned h;
|
|
|
+
|
|
|
+ h=0;
|
|
|
+
|
|
|
+ hash_update_str(s, s+len, p, v, h);
|
|
|
+ end=s+len;
|
|
|
+ for (p=s; p<=(end-4); p+=4){
|
|
|
+ v=((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3])|0x20202020;
|
|
|
+ h+=v^(v>>3);
|
|
|
+ }
|
|
|
+ v=0;
|
|
|
+ for (;p<end; p++){ v<<=8; v+=*p|0x20;}
|
|
|
+ h+=v^(v>>3);
|
|
|
+ return hash_finish(h) % DNS_HASH_SIZE;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#ifdef DNS_CACHE_DEBUG
|
|
|
+#define DEBUG_LU_LST
|
|
|
+#ifdef DEBUG_LU_LST
|
|
|
+
|
|
|
+#include <stdlib.h> /* abort() */
|
|
|
+#define check_lu_lst(l) ((((l)->next==(l)) || ((l)->prev==(l))) && \
|
|
|
+ ((l)!=dns_last_used_lst))
|
|
|
+
|
|
|
+#define dbg_lu_lst(txt, l) \
|
|
|
+ LOG(L_CRIT, "BUG: %s: crt(%p, %p, %p)," \
|
|
|
+ " prev(%p, %p, %p), next(%p, %p, %p)\n", txt, \
|
|
|
+ (l), (l)->next, (l)->prev, \
|
|
|
+ (l)->prev, (l)->prev->next, (l)->prev->prev, \
|
|
|
+ (l)->next, (l)->next->next, (l)->next->prev \
|
|
|
+ )
|
|
|
+
|
|
|
+#define debug_lu_lst( txt, l) \
|
|
|
+ do{ \
|
|
|
+ if (check_lu_lst((l))){ \
|
|
|
+ dbg_lu_lst(txt " crt:", (l)); \
|
|
|
+ abort(); \
|
|
|
+ } \
|
|
|
+ if (check_lu_lst((l)->next)){ \
|
|
|
+ dbg_lu_lst(txt " next:", (l)); \
|
|
|
+ abort(); \
|
|
|
+ } \
|
|
|
+ if (check_lu_lst((l)->prev)){ \
|
|
|
+ dbg_lu_lst(txt " prev:", (l)); \
|
|
|
+ abort(); \
|
|
|
+ } \
|
|
|
+ }while(0)
|
|
|
+
|
|
|
+#endif
|
|
|
+#endif /* DNS_CACHE_DEBUG */
|
|
|
+
|
|
|
+
|
|
|
+/* must be called with the DNS_LOCK hold
|
|
|
+ * remove and entry from the hash, dec. its refcnt and if not referenced
|
|
|
+ * anymore deletes it */
|
|
|
+void _dns_hash_remove(struct dns_hash_entry* e)
|
|
|
+{
|
|
|
+ clist_rm(e, next, prev);
|
|
|
+#ifdef DNS_CACHE_DEBUG
|
|
|
+ e->next=e->prev=0;
|
|
|
+#endif
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+#ifdef DEBUG_LU_LST
|
|
|
+ debug_lu_lst("_dns_hash_remove: pre rm:", &e->last_used_lst);
|
|
|
+#endif
|
|
|
+ clist_rm(&e->last_used_lst, next, prev);
|
|
|
+#ifdef DEBUG_LU_LST
|
|
|
+ debug_lu_lst("_dns_hash_remove: post rm:", &e->last_used_lst);
|
|
|
+#endif
|
|
|
+#ifdef DNS_CACHE_DEBUG
|
|
|
+ e->last_used_lst.next=e->last_used_lst.prev=0;
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+ *dns_cache_mem_used-=e->total_size;
|
|
|
+ dns_hash_put(e);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* non locking version (the dns hash must _be_ locked externally)
|
|
|
+ * returns 0 when not found, or the entry on success (an entry with a
|
|
|
+ * similar name but with a CNAME type will always match).
|
|
|
+ * it doesn't increase the internal refcnt
|
|
|
+ * returns the entry when found, 0 when not found and sets *err to !=0
|
|
|
+ * on error (e.g. recursive cnames)
|
|
|
+ * WARNING: - internal use only
|
|
|
+ * - always check if the returned entry type is CNAME */
|
|
|
+inline static struct dns_hash_entry* _dns_hash_find(str* name, int type,
|
|
|
+ int* h, int* err)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ struct dns_hash_entry* tmp;
|
|
|
+ struct dns_hash_entry* ret;
|
|
|
+ ticks_t now;
|
|
|
+ int cname_chain;
|
|
|
+ str cname;
|
|
|
+
|
|
|
+ cname_chain=0;
|
|
|
+ ret=0;
|
|
|
+ now=get_ticks_raw();
|
|
|
+ *err=0;
|
|
|
+again:
|
|
|
+ *h=dns_hash_no(name->s, name->len, type);
|
|
|
+ DBG("dns_hash_find(%.*s(%d), %d), h=%d\n", name->len, name->s,
|
|
|
+ name->len, type, *h);
|
|
|
+ clist_foreach_safe(&dns_hash[*h], e, tmp, next){
|
|
|
+ /* automatically remove expired elements */
|
|
|
+ if ((s_ticks_t)(now-e->expire)>=0){
|
|
|
+ _dns_hash_remove(e);
|
|
|
+ }else if ((e->type==type) && (e->name_len==name->len) &&
|
|
|
+ (strncasecmp(e->name, name->s, e->name_len)==0)){
|
|
|
+ e->last_used=now;
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ /* add it at the end */
|
|
|
+#ifdef DEBUG_LU_LST
|
|
|
+ debug_lu_lst("_dns_hash_find: pre rm:", &e->last_used_lst);
|
|
|
+#endif
|
|
|
+ clist_rm(&e->last_used_lst, next, prev);
|
|
|
+ clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
|
|
|
+#ifdef DEBUG_LU_LST
|
|
|
+ debug_lu_lst("_dns_hash_find: post append:", &e->last_used_lst);
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+ return e;
|
|
|
+ }else if ((e->type==T_CNAME) && (e->name_len==name->len) &&
|
|
|
+ (strncasecmp(e->name, name->s, e->name_len)==0)){
|
|
|
+ e->last_used=now;
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ /* add it at the end */
|
|
|
+#ifdef DEBUG_LU_LST
|
|
|
+ debug_lu_lst("_dns_hash_find: cname: pre rm:", &e->last_used_lst);
|
|
|
+#endif
|
|
|
+ clist_rm(&e->last_used_lst, next, prev);
|
|
|
+ clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
|
|
|
+#ifdef DEBUG_LU_LST
|
|
|
+ debug_lu_lst("_dns_hash_find: cname: post append:",
|
|
|
+ &e->last_used_lst);
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+ ret=e; /* if this is an unfinished cname chain, we try to
|
|
|
+ return the last cname */
|
|
|
+ /* this is a cname => retry using its value */
|
|
|
+ if (cname_chain> MAX_CNAME_CHAIN){
|
|
|
+ LOG(L_ERR, "ERROR: _dns_hash_find: cname chain too long "
|
|
|
+ "or recursive (\"%.*s\")\n", name->len, name->s);
|
|
|
+ ret=0; /* error*/
|
|
|
+ *err=-1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ cname_chain++;
|
|
|
+ cname.s=((struct cname_rdata*)e->rr_lst->rdata)->name;
|
|
|
+ cname.len= ((struct cname_rdata*)e->rr_lst->rdata)->name_len;
|
|
|
+ name=&cname;
|
|
|
+ goto again;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* frees cache entries, if expired_only=0 only expired entries will be
|
|
|
+ * removed, else all of them
|
|
|
+ * it will process maximum no entries (to process all of them use -1)
|
|
|
+ * returns the number of deleted entries
|
|
|
+ * This should be called from a timer process*/
|
|
|
+inline static int dns_cache_clean(unsigned int no, int expired_only)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ ticks_t now;
|
|
|
+ unsigned int n;
|
|
|
+ unsigned int deleted;
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ struct dns_lu_lst* l;
|
|
|
+ struct dns_lu_lst* tmp;
|
|
|
+#else
|
|
|
+ struct dns_hash_entry* t;
|
|
|
+ unsigned int h;
|
|
|
+ static unsigned int start=0;
|
|
|
+#endif
|
|
|
+
|
|
|
+ n=0;
|
|
|
+ deleted=0;
|
|
|
+ now=get_ticks_raw();
|
|
|
+ LOCK_DNS_HASH();
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ clist_foreach_safe(dns_last_used_lst, l, tmp, next){
|
|
|
+ e=(struct dns_hash_entry*)(((char*)l)-
|
|
|
+ (char*)&((struct dns_hash_entry*)(0))->last_used_lst);
|
|
|
+ if (!expired_only || ((s_ticks_t)(now-e->expire)>=0)){
|
|
|
+ _dns_hash_remove(e);
|
|
|
+ deleted++;
|
|
|
+ }
|
|
|
+ n++;
|
|
|
+ if (n>=no) break;
|
|
|
+ }
|
|
|
+#else
|
|
|
+ for(h=start; h!=(start+DNS_HASH_SIZE); h++){
|
|
|
+ clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
|
|
|
+ if ((s_ticks_t)(now-e->expire)>=0){
|
|
|
+ _dns_hash_remove(e);
|
|
|
+ deleted++;
|
|
|
+ }
|
|
|
+ n++;
|
|
|
+ if (n>=no) break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* not fair, but faster then random() */
|
|
|
+ if (!expired_only){
|
|
|
+ for(h=start; h!=(start+DNS_HASH_SIZE); h++){
|
|
|
+ clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
|
|
|
+ if ((s_ticks_t)(now-e->expire)>=0){
|
|
|
+ _dns_hash_remove(e);
|
|
|
+ deleted++;
|
|
|
+ }
|
|
|
+ n++;
|
|
|
+ if (n>=no) goto skip;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+skip:
|
|
|
+ start=h;
|
|
|
+#endif
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+ return deleted;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* frees cache entries, if expired_only=0 only expired entries will be
|
|
|
+ * removed, else all of them
|
|
|
+ * it will stop when the dns cache used memory reaches target (to process all
|
|
|
+ * of them use 0)
|
|
|
+ * returns the number of deleted entries */
|
|
|
+inline static int dns_cache_free_mem(unsigned int target, int expired_only)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ ticks_t now;
|
|
|
+ unsigned int deleted;
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ struct dns_lu_lst* l;
|
|
|
+ struct dns_lu_lst* tmp;
|
|
|
+#else
|
|
|
+ struct dns_hash_entry* t;
|
|
|
+ unsigned int h;
|
|
|
+ static unsigned int start=0;
|
|
|
+#endif
|
|
|
+
|
|
|
+ deleted=0;
|
|
|
+ now=get_ticks_raw();
|
|
|
+ LOCK_DNS_HASH();
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ clist_foreach_safe(dns_last_used_lst, l, tmp, next){
|
|
|
+ if (*dns_cache_mem_used<=target) break;
|
|
|
+ e=(struct dns_hash_entry*)(((char*)l)-
|
|
|
+ (char*)&((struct dns_hash_entry*)(0))->last_used_lst);
|
|
|
+ if (!expired_only || ((s_ticks_t)(now-e->expire)>=0)){
|
|
|
+ _dns_hash_remove(e);
|
|
|
+ deleted++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+#else
|
|
|
+ for(h=start; h!=(start+DNS_HASH_SIZE); h++){
|
|
|
+ clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
|
|
|
+ if (*dns_cache_mem_used<=target)
|
|
|
+ goto skip;
|
|
|
+ if ((s_ticks_t)(now-e->expire)>=0){
|
|
|
+ _dns_hash_remove(e);
|
|
|
+ deleted++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* not fair, but faster then random() */
|
|
|
+ if (!expired_only){
|
|
|
+ for(h=start; h!=(start+DNS_HASH_SIZE); h++){
|
|
|
+ clist_foreach_safe(&dns_hash[h%DNS_HASH_SIZE], e, t, next){
|
|
|
+ if (*dns_cache_mem_used<=target)
|
|
|
+ goto skip;
|
|
|
+ if ((s_ticks_t)(now-e->expire)>=0){
|
|
|
+ _dns_hash_remove(e);
|
|
|
+ deleted++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+skip:
|
|
|
+ start=h;
|
|
|
+#endif
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+ return deleted;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* locking version (the dns hash must _not_be locked externally)
|
|
|
+ * returns 0 when not found, the searched entry on success (with CNAMEs
|
|
|
+ * followed) or the last CNAME entry from an unfinished CNAME chain,
|
|
|
+ * if the search matches a CNAME. On error sets *err (e.g. recursive CNAMEs).
|
|
|
+ * it increases the internal refcnt => when finished dns_hash_put() must
|
|
|
+ * be called on the returned entry
|
|
|
+ * WARNING: - the return might be a CNAME even if type!=CNAME, see above */
|
|
|
+inline static struct dns_hash_entry* dns_hash_get(str* name, int type, int* h,
|
|
|
+ int* err)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+
|
|
|
+ LOCK_DNS_HASH();
|
|
|
+ e=_dns_hash_find(name, type, h, err);
|
|
|
+ if (e){
|
|
|
+ atomic_inc(&e->refcnt);
|
|
|
+ }
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* adds a fully created and init. entry (see dns_cache_mk_entry()) to the hash
|
|
|
+ * table
|
|
|
+ * returns 0 on success, -1 on error */
|
|
|
+inline static int dns_cache_add(struct dns_hash_entry* e)
|
|
|
+{
|
|
|
+ int h;
|
|
|
+
|
|
|
+ /* check space */
|
|
|
+ /* atomic_add_long(dns_cache_total_used, e->size); */
|
|
|
+ if ((*dns_cache_mem_used+e->total_size)>=dns_cache_max_mem){
|
|
|
+ LOG(L_WARN, "WARNING: dns_cache_add: cache full, trying to free...\n");
|
|
|
+ /* free ~ 12% of the cache */
|
|
|
+ dns_cache_free_mem(*dns_cache_mem_used/16*14, 1);
|
|
|
+ if ((*dns_cache_mem_used+e->total_size)>=dns_cache_max_mem){
|
|
|
+ LOG(L_ERR, "ERROR: dns_cache_add: max. cache mem size exceeded\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ atomic_inc(&e->refcnt);
|
|
|
+ h=dns_hash_no(e->name, e->name_len, e->type);
|
|
|
+ DBG("dns_cache_add: adding %.*s(%d) %d (flags=%0x) at %d\n",
|
|
|
+ e->name_len, e->name, e->name_len, e->type, e->err_flags, h);
|
|
|
+ LOCK_DNS_HASH();
|
|
|
+ *dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
|
|
|
+ only from within a lock */
|
|
|
+ clist_append(&dns_hash[h], e, next, prev);
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
|
|
|
+#endif
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* same as above, but it must be called with the dns hash lock held
|
|
|
+ * returns 0 on success, -1 on error */
|
|
|
+inline static int dns_cache_add_unsafe(struct dns_hash_entry* e)
|
|
|
+{
|
|
|
+ int h;
|
|
|
+
|
|
|
+ /* check space */
|
|
|
+ /* atomic_add_long(dns_cache_total_used, e->size); */
|
|
|
+ if ((*dns_cache_mem_used+e->total_size)>=dns_cache_max_mem){
|
|
|
+ LOG(L_WARN, "WARNING: dns_cache_add: cache full, trying to free...\n");
|
|
|
+ /* free ~ 12% of the cache */
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+ dns_cache_free_mem(*dns_cache_mem_used/16*14, 1);
|
|
|
+ LOCK_DNS_HASH();
|
|
|
+ if ((*dns_cache_mem_used+e->total_size)>=dns_cache_max_mem){
|
|
|
+ LOG(L_ERR, "ERROR: dns_cache_add: max. cache mem size exceeded\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ atomic_inc(&e->refcnt);
|
|
|
+ h=dns_hash_no(e->name, e->name_len, e->type);
|
|
|
+ DBG("dns_cache_add: adding %.*s(%d) %d (flags=%0x) at %d\n",
|
|
|
+ e->name_len, e->name, e->name_len, e->type, e->err_flags, h);
|
|
|
+ *dns_cache_mem_used+=e->total_size; /* no need for atomic ops, written
|
|
|
+ only from within a lock */
|
|
|
+ clist_append(&dns_hash[h], e, next, prev);
|
|
|
+#ifdef DNS_LU_LST
|
|
|
+ clist_append(dns_last_used_lst, &e->last_used_lst, next, prev);
|
|
|
+#endif
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* creates a "negative" entry which will be valid for ttl seconds */
|
|
|
+inline static struct dns_hash_entry* dns_cache_mk_bad_entry(str* name,
|
|
|
+ int type,
|
|
|
+ int ttl,
|
|
|
+ int flags)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ int size;
|
|
|
+ ticks_t now;
|
|
|
+
|
|
|
+ DBG("dns_cache_mk_bad_entry(%.*s, %d, %d, %d)\n", name->len, name->s,
|
|
|
+ type, ttl, flags);
|
|
|
+ size=sizeof(struct dns_hash_entry)+name->len-1+1;
|
|
|
+ e=shm_malloc(size);
|
|
|
+ if (e==0){
|
|
|
+ LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ memset(e, 0, size); /* init with 0*/
|
|
|
+ e->total_size=size;
|
|
|
+ e->name_len=name->len;
|
|
|
+ e->type=type;
|
|
|
+ now=get_ticks_raw();
|
|
|
+ e->last_used=now;
|
|
|
+ e->expire=now+S_TO_TICKS(ttl);
|
|
|
+ memcpy(e->name, name->s, name->len);
|
|
|
+ e->err_flags=flags;
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* create a a/aaaa hash entry from a name and ip address
|
|
|
+ * returns 0 on error */
|
|
|
+inline static struct dns_hash_entry* dns_cache_mk_ip_entry(str* name,
|
|
|
+ struct ip_addr* ip)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ int size;
|
|
|
+ ticks_t now;
|
|
|
+
|
|
|
+ /* everything is allocated in one block: dns_hash_entry + name +
|
|
|
+ * + dns_rr + rdata; dns_rr must start at an aligned adress,
|
|
|
+ * hence we need to round dns_hash_entry+name size to a sizeof(long)
|
|
|
+ * multiple.
|
|
|
+ * Memory image:
|
|
|
+ * struct dns_hash_entry
|
|
|
+ * name (name_len+1 bytes)
|
|
|
+ * padding to multiple of sizeof(long)
|
|
|
+ * dns_rr
|
|
|
+ * rdata (no padding needed, since for ip is just an array of chars)
|
|
|
+ */
|
|
|
+ size=ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1)+
|
|
|
+ sizeof(struct dns_rr)+ ip->len;
|
|
|
+ e=shm_malloc(size);
|
|
|
+ if (e==0){
|
|
|
+ LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ memset(e, 0, size); /* init with 0*/
|
|
|
+ e->total_size=size;
|
|
|
+ e->name_len=name->len;
|
|
|
+ e->type=(ip->af==AF_INET)?T_A:T_AAAA;
|
|
|
+ now=get_ticks_raw();
|
|
|
+ e->last_used=now;
|
|
|
+ e->expire=now-1; /* maximum expire */
|
|
|
+ memcpy(e->name, name->s, name->len); /* memset makes sure is 0-term. */
|
|
|
+ e->rr_lst=(void*)((char*)e+
|
|
|
+ ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1));
|
|
|
+ e->rr_lst->rdata=(void*)((char*)e->rr_lst+sizeof(struct dns_rr));
|
|
|
+ e->rr_lst->expire=now-1; /* maximum expire */
|
|
|
+ /* no need to align rr_lst->rdata for a or aaaa records */
|
|
|
+ memcpy(e->rr_lst->rdata, ip->u.addr, ip->len);
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* create a dns hash entry from a name and a rdata list (pkg_malloc'ed)
|
|
|
+ * (it will use only the type records with the name "name" from the
|
|
|
+ * rdata list with one exception: if a matching CNAME with the same
|
|
|
+ * name is found, the search will stop and this will be the record used)
|
|
|
+ * returns 0 on error and removes the used elements from the rdata list*/
|
|
|
+inline static struct dns_hash_entry* dns_cache_mk_rd_entry(str* name, int type,
|
|
|
+ struct rdata** rd_lst)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ struct dns_rr* rr;
|
|
|
+ struct dns_rr** tail_rr;
|
|
|
+ struct rdata** p;
|
|
|
+ struct rdata* tmp_lst;
|
|
|
+ struct rdata** tail;
|
|
|
+ struct rdata* l;
|
|
|
+ int size;
|
|
|
+ ticks_t now;
|
|
|
+ unsigned int max_ttl;
|
|
|
+ unsigned int ttl;
|
|
|
+
|
|
|
+#define rec_matches(rec, t, n) /*(struct rdata* record, int type, str* name)*/\
|
|
|
+ ( ((rec)->name_len==(n)->len) && ((rec)->type==(t)) && \
|
|
|
+ (strncasecmp((rec)->name, (n)->s, (n)->len)==0))
|
|
|
+ /* init */
|
|
|
+ tmp_lst=0;
|
|
|
+ tail=&tmp_lst;
|
|
|
+
|
|
|
+
|
|
|
+ /* everything is allocated in one block: dns_hash_entry + name +
|
|
|
+ * + dns_rr + rdata_raw+ ....; dns_rr must start at an aligned adress,
|
|
|
+ * hence we need to round dns_hash_entry+name size to a sizeof(long)
|
|
|
+ * multiple. If rdata type requires it, rdata_raw might need to be also
|
|
|
+ * aligned.
|
|
|
+ * Memory image:
|
|
|
+ * struct dns_hash_entry (e)
|
|
|
+ * name (name_len+1 bytes) (&e->name[0])
|
|
|
+ * padding to multiple of sizeof(char*)
|
|
|
+ * dns_rr1 (e->rr_lst)
|
|
|
+ * possible padding: no padding for a_rdata or aaaa_rdata,
|
|
|
+ * multipe of sizeof(short) for srv_rdata,
|
|
|
+ * multiple of sizeof(long) for naptr_rdata and others
|
|
|
+ * dns_rr1->rdata (e->rr_lst->rdata)
|
|
|
+ * padding to multipe of sizeof long
|
|
|
+ * dns_rr2 (e->rr_lst->next)
|
|
|
+ * ....
|
|
|
+ *
|
|
|
+ */
|
|
|
+ size=0;
|
|
|
+ if (*rd_lst==0)
|
|
|
+ return 0;
|
|
|
+ /* find the first matching rr, if it's a CNAME use CNAME as type,
|
|
|
+ * if not continue with the original type */
|
|
|
+ for(p=rd_lst; *p; p=&(*p)->next){
|
|
|
+ if (((*p)->name_len==name->len) &&
|
|
|
+ (((*p)->type==type) || ((*p)->type==T_CNAME)) &&
|
|
|
+ (strncasecmp((*p)->name, name->s, name->len)==0)){
|
|
|
+ type=(*p)->type;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* continue, we found the type we are looking for */
|
|
|
+ switch(type){
|
|
|
+ case T_A:
|
|
|
+ for(; *p;){
|
|
|
+ if (!rec_matches((*p), type, name)){
|
|
|
+ /* skip this record */
|
|
|
+ p=&(*p)->next; /* advance */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ size+=ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ sizeof(struct a_rdata));
|
|
|
+ /* add it to our tmp. lst */
|
|
|
+ *tail=*p;
|
|
|
+ tail=&(*p)->next;
|
|
|
+ /* detach it from the rd list */
|
|
|
+ *p=(*p)->next;
|
|
|
+ /* don't advance p, because the crt. elem. has
|
|
|
+ * just been elimintated */
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_AAAA:
|
|
|
+ for(; *p;){
|
|
|
+ if (!rec_matches((*p), type, name)){
|
|
|
+ /* skip this record */
|
|
|
+ p=&(*p)->next; /* advance */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* no padding */
|
|
|
+ size+=ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ sizeof(struct aaaa_rdata));
|
|
|
+ /* add it to our tmp. lst */
|
|
|
+ *tail=*p;
|
|
|
+ tail=&(*p)->next;
|
|
|
+ /* detach it from the rd list */
|
|
|
+ *p=(*p)->next;
|
|
|
+ /* don't advance p, because the crt. elem. has
|
|
|
+ * just been elimintated */
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_SRV:
|
|
|
+ for(; *p;){
|
|
|
+ if (!rec_matches((*p), type, name)){
|
|
|
+ /* skip this record */
|
|
|
+ p=&(*p)->next; /* advance */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* padding to short */
|
|
|
+ size+=ROUND_POINTER(ROUND_SHORT(sizeof(struct dns_rr))+
|
|
|
+ SRV_RDATA_SIZE(*(struct srv_rdata*)(*p)->rdata));
|
|
|
+ /* add it to our tmp. lst */
|
|
|
+ *tail=*p;
|
|
|
+ tail=&(*p)->next;
|
|
|
+ /* detach it from the rd list */
|
|
|
+ *p=(*p)->next;
|
|
|
+ /* don't advance p, because the crt. elem. has
|
|
|
+ * just been elimintated */
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_NAPTR:
|
|
|
+ for(; *p;){
|
|
|
+ if (!rec_matches((*p), type, name)){
|
|
|
+ /* skip this record */
|
|
|
+ p=&(*p)->next; /* advance */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* padding to char* */
|
|
|
+ size+=ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
|
|
|
+ NAPTR_RDATA_SIZE(*(struct naptr_rdata*)(*p)->rdata));
|
|
|
+ /* add it to our tmp. lst */
|
|
|
+ *tail=*p;
|
|
|
+ tail=&(*p)->next;
|
|
|
+ /* detach it from the rd list */
|
|
|
+ *p=(*p)->next;
|
|
|
+ /* don't advance p, because the crt. elem. has
|
|
|
+ * just been elimintated */
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_CNAME:
|
|
|
+ for(; *p;){
|
|
|
+ if (!rec_matches((*p), type, name)){
|
|
|
+ /* skip this record */
|
|
|
+ p=&(*p)->next; /* advance */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* no padding */
|
|
|
+ size+=ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ CNAME_RDATA_SIZE(*(struct cname_rdata*)(*p)->rdata));
|
|
|
+ /* add it to our tmp. lst */
|
|
|
+ *tail=*p;
|
|
|
+ tail=&(*p)->next;
|
|
|
+ /* detach it from the rd list */
|
|
|
+ *p=(*p)->next;
|
|
|
+ /* don't advance p, because the crt. elem. has
|
|
|
+ * just been elimintated */
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: type %d not "
|
|
|
+ "supported\n", type);
|
|
|
+ /* we don't know what to do with it, so don't
|
|
|
+ * add it to the tmp_lst */
|
|
|
+ return 0; /* error */
|
|
|
+ }
|
|
|
+ *tail=0; /* mark the end of our tmp_lst */
|
|
|
+ if (size==0){
|
|
|
+ DBG("dns_cache_mk_rd_entry: entry %.*s (%d) not found\n",
|
|
|
+ name->len, name->s, type);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ /* compute size */
|
|
|
+ size+=ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1);
|
|
|
+ e=shm_malloc(size);
|
|
|
+ if (e==0){
|
|
|
+ LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ memset(e, 0, size); /* init with 0 */
|
|
|
+ e->total_size=size;
|
|
|
+ e->name_len=name->len;
|
|
|
+ e->type=type;
|
|
|
+ now=get_ticks_raw();
|
|
|
+ e->last_used=now;
|
|
|
+ memcpy(e->name, name->s, name->len); /* memset makes sure is 0-term. */
|
|
|
+ e->rr_lst=(struct dns_rr*)((char*)e+
|
|
|
+ ROUND_POINTER(sizeof(struct dns_hash_entry)+name->len-1+1));
|
|
|
+ tail_rr=&(e->rr_lst);
|
|
|
+ rr=e->rr_lst;
|
|
|
+ max_ttl=0;
|
|
|
+ /* copy the actual data */
|
|
|
+ switch(type){
|
|
|
+ case T_A:
|
|
|
+ for(l=tmp_lst; l; l=l->next){
|
|
|
+ ttl=FIX_TTL(l->ttl);
|
|
|
+ rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ max_ttl=MAX(max_ttl, ttl);
|
|
|
+ rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
|
|
|
+ memcpy(rr->rdata, l->rdata, sizeof(struct a_rdata));
|
|
|
+ rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ sizeof(struct a_rdata)));
|
|
|
+ tail_rr=&(rr->next);
|
|
|
+ rr=rr->next;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_AAAA:
|
|
|
+ for(l=tmp_lst; l; l=l->next){
|
|
|
+ ttl=FIX_TTL(l->ttl);
|
|
|
+ rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ max_ttl=MAX(max_ttl, ttl);
|
|
|
+ rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
|
|
|
+ memcpy(rr->rdata, l->rdata, sizeof(struct aaaa_rdata));
|
|
|
+ rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ sizeof(struct aaaa_rdata)));
|
|
|
+ tail_rr=&(rr->next);
|
|
|
+ rr=rr->next;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_SRV:
|
|
|
+ for(l=tmp_lst; l; l=l->next){
|
|
|
+ ttl=FIX_TTL(l->ttl);
|
|
|
+ rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ max_ttl=MAX(max_ttl, ttl);
|
|
|
+ rr->rdata=(void*)((char*)rr+
|
|
|
+ ROUND_SHORT(sizeof(struct dns_rr)));
|
|
|
+ /* copy the whole srv_rdata block*/
|
|
|
+ memcpy(rr->rdata, l->rdata,
|
|
|
+ SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata) );
|
|
|
+ rr->next=(void*)((char*)rr+
|
|
|
+ ROUND_POINTER( ROUND_SHORT(sizeof(struct dns_rr))+
|
|
|
+ SRV_RDATA_SIZE(
|
|
|
+ *(struct srv_rdata*)l->rdata)));
|
|
|
+ tail_rr=&(rr->next);
|
|
|
+ rr=rr->next;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_NAPTR:
|
|
|
+ for(l=tmp_lst; l; l=l->next){
|
|
|
+ ttl=FIX_TTL(l->ttl);
|
|
|
+ rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ max_ttl=MAX(max_ttl, ttl);
|
|
|
+ rr->rdata=(void*)((char*)rr+
|
|
|
+ ROUND_POINTER(sizeof(struct dns_rr)));
|
|
|
+ /* copy the whole srv_rdata block*/
|
|
|
+ memcpy(rr->rdata, l->rdata,
|
|
|
+ NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
|
|
|
+ /* adjust the string pointer */
|
|
|
+ ((struct naptr_rdata*)rr->rdata)->flags=
|
|
|
+ translate_pointer((char*)rr->rdata, (char*)l->rdata,
|
|
|
+ (((struct naptr_rdata*)l->rdata)->flags));
|
|
|
+ ((struct naptr_rdata*)rr->rdata)->services=
|
|
|
+ translate_pointer((char*)rr->rdata, (char*)l->rdata,
|
|
|
+ (((struct naptr_rdata*)l->rdata)->services));
|
|
|
+ ((struct naptr_rdata*)rr->rdata)->regexp=
|
|
|
+ translate_pointer((char*)rr->rdata, (char*)l->rdata,
|
|
|
+ (((struct naptr_rdata*)l->rdata)->regexp));
|
|
|
+ ((struct naptr_rdata*)rr->rdata)->repl=
|
|
|
+ translate_pointer((char*)rr->rdata, (char*)l->rdata,
|
|
|
+ (((struct naptr_rdata*)l->rdata)->repl));
|
|
|
+ rr->next=(void*)((char*)rr+
|
|
|
+ ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
|
|
|
+ NAPTR_RDATA_SIZE(
|
|
|
+ *(struct naptr_rdata*)l->rdata)));
|
|
|
+ tail_rr=&(rr->next);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_CNAME:
|
|
|
+ for(l=tmp_lst; l; l=l->next){
|
|
|
+ ttl=FIX_TTL(l->ttl);
|
|
|
+ rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ max_ttl=MAX(max_ttl, ttl);
|
|
|
+ rr->rdata=(void*)((char*)rr+sizeof(struct dns_rr));
|
|
|
+ memcpy(rr->rdata, l->rdata,
|
|
|
+ CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
|
|
|
+ rr->next=(void*)((char*)rr+ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata)));
|
|
|
+ tail_rr=&(rr->next);
|
|
|
+ rr=rr->next;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* do nothing */
|
|
|
+ LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: create: type %d not "
|
|
|
+ "supported\n", type);
|
|
|
+ ;
|
|
|
+ }
|
|
|
+ *tail_rr=0; /* terminate the list */
|
|
|
+ e->expire=now+S_TO_TICKS(max_ttl);
|
|
|
+ free_rdata_list(tmp_lst);
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* structure used only inside dns_cache_mk_rd_entry2 to break
|
|
|
+ * the list of records into records of the same type */
|
|
|
+struct tmp_rec{
|
|
|
+ struct rdata* rd;
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ struct dns_rr* rr;
|
|
|
+ struct dns_rr** tail_rr;
|
|
|
+ int max_ttl;
|
|
|
+ int size;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* create several dns hash entries from a list of rdata structs
|
|
|
+ * returns 0 on error */
|
|
|
+inline static struct dns_hash_entry* dns_cache_mk_rd_entry2(struct rdata* rd)
|
|
|
+{
|
|
|
+ struct rdata* l;
|
|
|
+ ticks_t now;
|
|
|
+ struct tmp_rec rec[MAX_DNS_RECORDS];
|
|
|
+ int rec_idx[MAX_DNS_RECORDS];
|
|
|
+ int r, i;
|
|
|
+ int no_records; /* number of different records */
|
|
|
+ unsigned int ttl;
|
|
|
+
|
|
|
+
|
|
|
+ no_records=0;
|
|
|
+ rec[0].e=0;
|
|
|
+ /* everything is allocated in one block: dns_hash_entry + name +
|
|
|
+ * + dns_rr + rdata_raw+ ....; dns_rr must start at an aligned adress,
|
|
|
+ * hence we need to round dns_hash_entry+name size to a sizeof(long)
|
|
|
+ * multiple. If rdata type requires it, rdata_raw might need to be also
|
|
|
+ * aligned.
|
|
|
+ * Memory image:
|
|
|
+ * struct dns_hash_entry (e)
|
|
|
+ * name (name_len+1 bytes) (&e->name[0])
|
|
|
+ * padding to multiple of sizeof(char*)
|
|
|
+ * dns_rr1 (e->rr_lst)
|
|
|
+ * possible padding: no padding for a_rdata or aaaa_rdata,
|
|
|
+ * multipe of sizeof(short) for srv_rdata,
|
|
|
+ * multiple of sizeof(long) for naptr_rdata and others
|
|
|
+ * dns_rr1->rdata (e->rr_lst->rdata)
|
|
|
+ * padding to multipe of sizeof long
|
|
|
+ * dns_rr2 (e->rr_lst->next)
|
|
|
+ * ....
|
|
|
+ *
|
|
|
+ */
|
|
|
+ /* compute size */
|
|
|
+ for(l=rd, i=0; l && (i<MAX_DNS_RECORDS); l=l->next, i++){
|
|
|
+ for (r=0; r<no_records; r++){
|
|
|
+ if ((l->type==rec[r].rd->type) &&
|
|
|
+ (l->name_len==rec[r].rd->name_len)
|
|
|
+ && (strncasecmp(l->name, rec[r].rd->name, l->name_len)==0)){
|
|
|
+ /* found */
|
|
|
+ goto found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* not found, create new */
|
|
|
+ if (no_records<MAX_DNS_RECORDS){
|
|
|
+ rec[r].rd=l;
|
|
|
+ rec[r].e=0;
|
|
|
+ rec[r].size=ROUND_POINTER(sizeof(struct dns_hash_entry)+
|
|
|
+ rec[r].rd->name_len-1+1);
|
|
|
+ no_records++;
|
|
|
+ }else{
|
|
|
+ LOG(L_ERR, "ERROR: dns_cache_mk_rd_entry2: too many records: %d\n",
|
|
|
+ no_records);
|
|
|
+ /* skip */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+found:
|
|
|
+ rec_idx[i]=r;
|
|
|
+ switch(l->type){
|
|
|
+ case T_A:
|
|
|
+ /* no padding */
|
|
|
+ rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ sizeof(struct a_rdata));
|
|
|
+ break;
|
|
|
+ case T_AAAA:
|
|
|
+ /* no padding */
|
|
|
+ rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ sizeof(struct aaaa_rdata));
|
|
|
+ break;
|
|
|
+ case T_SRV:
|
|
|
+ /* padding to short */
|
|
|
+ rec[r].size+=ROUND_POINTER(ROUND_SHORT(sizeof(struct dns_rr))+
|
|
|
+ SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata));
|
|
|
+ break;
|
|
|
+ case T_NAPTR:
|
|
|
+ /* padding to char* */
|
|
|
+ rec[r].size+=ROUND_POINTER(ROUND_POINTER(
|
|
|
+ sizeof(struct dns_rr))+
|
|
|
+ NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata));
|
|
|
+ break;
|
|
|
+ case T_CNAME:
|
|
|
+ /* no padding */
|
|
|
+ rec[r].size+=ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LOG(L_CRIT, "BUG: dns_cache_mk_rd_entry: type %d not "
|
|
|
+ "supported\n", l->type);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ now=get_ticks_raw();
|
|
|
+ /* alloc & init the entries */
|
|
|
+ for (r=0; r<no_records; r++){
|
|
|
+ rec[r].e=shm_malloc(rec[r].size);
|
|
|
+ if (rec[r].e==0){
|
|
|
+ LOG(L_ERR, "ERROR: dns_cache_mk_ip_entry: out of memory\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ memset(rec[r].e, 0, rec[r].size); /* init with 0*/
|
|
|
+ rec[r].e->total_size=rec[r].size;
|
|
|
+ rec[r].e->name_len=rec[r].rd->name_len;
|
|
|
+ rec[r].e->type=rec[r].rd->type;
|
|
|
+ rec[r].e->last_used=now;
|
|
|
+ /* memset makes sure is 0-term. */
|
|
|
+ memcpy(rec[r].e->name, rec[r].rd->name, rec[r].rd->name_len);
|
|
|
+ rec[r].e->rr_lst=(struct dns_rr*)((char*)rec[r].e+
|
|
|
+ ROUND_POINTER(sizeof(struct dns_hash_entry)+rec[r].e->name_len
|
|
|
+ -1+1));
|
|
|
+ rec[r].tail_rr=&(rec[r].e->rr_lst);
|
|
|
+ rec[r].rr=rec[r].e->rr_lst;
|
|
|
+ rec[r].max_ttl=0;
|
|
|
+ /* link them in a list */
|
|
|
+ if (r==0){
|
|
|
+ clist_init(rec[r].e, next, prev);
|
|
|
+ }else{
|
|
|
+ clist_append(rec[0].e, rec[r].e, next, prev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* copy the actual data */
|
|
|
+ for(l=rd, i=0; l && (i<MAX_DNS_RECORDS); l=l->next, i++){
|
|
|
+ r=rec_idx[i];
|
|
|
+ ttl=FIX_TTL(l->ttl);
|
|
|
+ switch(l->type){
|
|
|
+ case T_A:
|
|
|
+ rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
|
|
|
+ rec[r].rr->rdata=(void*)((char*)rec[r].rr+
|
|
|
+ sizeof(struct dns_rr));
|
|
|
+ memcpy(rec[r].rr->rdata, l->rdata, sizeof(struct a_rdata));
|
|
|
+ rec[r].rr->next=(void*)((char*)rec[r].rr+
|
|
|
+ ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ sizeof(struct a_rdata)));
|
|
|
+ rec[r].tail_rr=&(rec[r].rr->next);
|
|
|
+ rec[r].rr=rec[r].rr->next;
|
|
|
+ break;
|
|
|
+ case T_AAAA:
|
|
|
+ rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
|
|
|
+ rec[r].rr->rdata=(void*)((char*)rec[r].rr+
|
|
|
+ sizeof(struct dns_rr));
|
|
|
+ memcpy(rec[r].rr->rdata, l->rdata, sizeof(struct aaaa_rdata));
|
|
|
+ rec[r].rr->next=(void*)((char*)rec[r].rr+
|
|
|
+ ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ sizeof(struct aaaa_rdata)));
|
|
|
+ rec[r].tail_rr=&(rec[r].rr->next);
|
|
|
+ rec[r].rr=rec[r].rr->next;
|
|
|
+ break;
|
|
|
+ case T_SRV:
|
|
|
+ rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
|
|
|
+ rec[r].rr->rdata=(void*)((char*)rec[r].rr+
|
|
|
+ ROUND_SHORT(sizeof(struct dns_rr)));
|
|
|
+ /* copy the whole srv_rdata block*/
|
|
|
+ memcpy(rec[r].rr->rdata, l->rdata,
|
|
|
+ SRV_RDATA_SIZE(*(struct srv_rdata*)l->rdata) );
|
|
|
+ rec[r].rr->next=(void*)((char*)rec[r].rr+
|
|
|
+ ROUND_POINTER( ROUND_SHORT(sizeof(struct dns_rr))+
|
|
|
+ SRV_RDATA_SIZE(
|
|
|
+ *(struct srv_rdata*)l->rdata)));
|
|
|
+ rec[r].tail_rr=&(rec[r].rr->next);
|
|
|
+ rec[r].rr=rec[r].rr->next;
|
|
|
+ break;
|
|
|
+ case T_NAPTR:
|
|
|
+ rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
|
|
|
+ rec[r].rr->rdata=(void*)((char*)rec[r].rr+
|
|
|
+ ROUND_POINTER(sizeof(struct dns_rr)));
|
|
|
+ /* copy the whole srv_rdata block*/
|
|
|
+ memcpy(rec[r].rr->rdata, l->rdata,
|
|
|
+ NAPTR_RDATA_SIZE(*(struct naptr_rdata*)l->rdata) );
|
|
|
+ /* adjust the string pointer */
|
|
|
+ ((struct naptr_rdata*)rec[r].rr->rdata)->flags=
|
|
|
+ translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
|
|
|
+ (((struct naptr_rdata*)l->rdata)->flags));
|
|
|
+ ((struct naptr_rdata*)rec[r].rr->rdata)->services=
|
|
|
+ translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
|
|
|
+ (((struct naptr_rdata*)l->rdata)->services));
|
|
|
+ ((struct naptr_rdata*)rec[r].rr->rdata)->regexp=
|
|
|
+ translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
|
|
|
+ (((struct naptr_rdata*)l->rdata)->regexp));
|
|
|
+ ((struct naptr_rdata*)rec[r].rr->rdata)->repl=
|
|
|
+ translate_pointer((char*)rec[r].rr->rdata, (char*)l->rdata,
|
|
|
+ (((struct naptr_rdata*)l->rdata)->repl));
|
|
|
+ rec[r].rr->next=(void*)((char*)rec[r].rr+
|
|
|
+ ROUND_POINTER(ROUND_POINTER(sizeof(struct dns_rr))+
|
|
|
+ NAPTR_RDATA_SIZE(
|
|
|
+ *(struct naptr_rdata*)l->rdata)));
|
|
|
+ rec[r].tail_rr=&(rec[r].rr->next);
|
|
|
+ break;
|
|
|
+ case T_CNAME:
|
|
|
+ rec[r].rr->expire=now+S_TO_TICKS(ttl); /* maximum expire */
|
|
|
+ rec[r].max_ttl=MAX(rec[r].max_ttl, ttl);
|
|
|
+ rec[r].rr->rdata=(void*)((char*)rec[r].rr
|
|
|
+ +sizeof(struct dns_rr));
|
|
|
+ memcpy(rec[r].rr->rdata, l->rdata,
|
|
|
+ CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata));
|
|
|
+ rec[r].rr->next=(void*)((char*)rec[r].rr+
|
|
|
+ ROUND_POINTER(sizeof(struct dns_rr)+
|
|
|
+ CNAME_RDATA_SIZE(*(struct cname_rdata*)l->rdata)));
|
|
|
+ rec[r].tail_rr=&(rec[r].rr->next);
|
|
|
+ rec[r].rr=rec[r].rr->next;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* do nothing */
|
|
|
+ ;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (r=0; r<no_records; r++){
|
|
|
+ *rec[r].tail_rr=0; /* terminate the list */
|
|
|
+ rec[r].e->expire=now+S_TO_TICKS(rec[r].max_ttl);
|
|
|
+ }
|
|
|
+ return rec[0].e;
|
|
|
+error:
|
|
|
+ for (r=0; r<no_records; r++){
|
|
|
+ dns_destroy_entry(rec[r].e);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+inline static struct dns_hash_entry* dns_get_entry(str* name, int type);
|
|
|
+
|
|
|
+
|
|
|
+#define CACHE_RELEVANT_RECS_ONLY
|
|
|
+
|
|
|
+#ifdef CACHE_RELEVANT_RECS_ONLY
|
|
|
+/* internal only: gets related entries from a rdata list, appends them
|
|
|
+ * to e (list) and returns:
|
|
|
+ * - e if e is of the requested type
|
|
|
+ * - if e is a CNAME, tries to get to the end of the CNAME chain and returns
|
|
|
+ * the final entry if the types match or 0 if the chain is unfinished
|
|
|
+ * - 0 on error/not found
|
|
|
+ * records is modified (the used records are removed from the list and freed)
|
|
|
+ *
|
|
|
+ * WARNING: - records must be pkg_malloc'ed
|
|
|
+ * Notes: - if the return is 0 and e->type==T_CNAME, the list will contain
|
|
|
+ * the CNAME chain (the last element being the last CNAME)
|
|
|
+ * */
|
|
|
+inline static struct dns_hash_entry* dns_get_related(struct dns_hash_entry* e,
|
|
|
+ int type,
|
|
|
+ struct rdata** records)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* ret;
|
|
|
+ struct dns_hash_entry* l;
|
|
|
+ struct dns_hash_entry* t;
|
|
|
+ struct dns_hash_entry* lst_end;
|
|
|
+ struct dns_rr* rr;
|
|
|
+ static int cname_chain_len=0;
|
|
|
+ str tmp;
|
|
|
+
|
|
|
+ ret=0;
|
|
|
+ l=e;
|
|
|
+ DBG("dns_get_related(%p (%.*s, %d), %d, *%p) (%d)\n", e,
|
|
|
+ e->name_len, e->name, e->type, type, *records, cname_chain_len);
|
|
|
+ clist_init(l, next, prev);
|
|
|
+ if (type==e->type){
|
|
|
+ ret=e;
|
|
|
+ switch(e->type){
|
|
|
+ case T_SRV:
|
|
|
+ for (rr=e->rr_lst; rr && *records; rr=rr->next){
|
|
|
+ tmp.s=((struct srv_rdata*)rr->rdata)->name;
|
|
|
+ tmp.len=((struct srv_rdata*)rr->rdata)->name_len;
|
|
|
+ if (!(dns_flags&DNS_IPV6_ONLY)){
|
|
|
+ t=dns_cache_mk_rd_entry(&tmp, T_A, records);
|
|
|
+ if (t){
|
|
|
+ if ((t->type==T_CNAME) && *records)
|
|
|
+ dns_get_related(t, T_A, records);
|
|
|
+ clist_append(l, t, next, prev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!(dns_flags&DNS_IPV4_ONLY)){
|
|
|
+ t=dns_cache_mk_rd_entry(&tmp, T_AAAA, records);
|
|
|
+ if (t){
|
|
|
+ if ((t->type==T_CNAME) && *records)
|
|
|
+ dns_get_related(t, T_AAAA, records);
|
|
|
+ clist_append(l, t, next, prev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* nothing extra */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }else if ((e->type==T_CNAME) && (cname_chain_len<MAX_CNAME_CHAIN)){
|
|
|
+ /* only one cname is allowed (rfc2181), so we ignore
|
|
|
+ * the others (we take only the first one) */
|
|
|
+ tmp.s=((struct cname_rdata*)e->rr_lst->rdata)->name;
|
|
|
+ tmp.len=((struct cname_rdata*)e->rr_lst->rdata)->name_len;
|
|
|
+ t=dns_cache_mk_rd_entry(&tmp, type, records);
|
|
|
+ if (t){
|
|
|
+ if (*records){
|
|
|
+ cname_chain_len++;
|
|
|
+ ret=dns_get_related(t, type, records);
|
|
|
+ cname_chain_len--;
|
|
|
+ lst_end=t->prev;
|
|
|
+ clist_append_sublist(l, t, lst_end, next, prev);
|
|
|
+ }else{
|
|
|
+ clist_append(l, t, next, prev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* calls the external resolver and populates the cache with the result
|
|
|
+ * returns: 0 on error, pointer to hash entry on success
|
|
|
+ * WARNING: make sure you use dns_hash_entry_put() when you're
|
|
|
+ * finished with the result)
|
|
|
+ * */
|
|
|
+inline static struct dns_hash_entry* dns_cache_do_request(str* name, int type)
|
|
|
+{
|
|
|
+ struct rdata* records;
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ struct dns_hash_entry* l;
|
|
|
+ struct dns_hash_entry* r;
|
|
|
+ struct dns_hash_entry* t;
|
|
|
+ struct ip_addr* ip;
|
|
|
+ str cname_val;
|
|
|
+ char name_buf[MAX_DNS_NAME];
|
|
|
+
|
|
|
+ e=0;
|
|
|
+ l=0;
|
|
|
+ cname_val.s=0;
|
|
|
+
|
|
|
+ if (type==T_A){
|
|
|
+ if ((ip=str2ip(name))!=0){
|
|
|
+ e=dns_cache_mk_ip_entry(name, ip);
|
|
|
+ if (e)
|
|
|
+ atomic_set(&e->refcnt, 1);/* because we ret. a ref. to it*/
|
|
|
+ goto end; /* we do not cache obvious stuff */
|
|
|
+ }
|
|
|
+ }else if (type==T_AAAA){
|
|
|
+ if ((ip=str2ip6(name))!=0){
|
|
|
+ e=dns_cache_mk_ip_entry(name, ip);
|
|
|
+ if (e)
|
|
|
+ atomic_set(&e->refcnt, 1);/* because we ret. a ref. to it*/
|
|
|
+ goto end;/* we do not cache obvious stuff */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (name->len>=MAX_DNS_NAME){
|
|
|
+ LOG(L_ERR, "ERROR: dns_cache_do_request: name too long (%d chars)\n",
|
|
|
+ name->len);
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+ /* null terminate the string, needed by get_record */
|
|
|
+ memcpy(name_buf, name->s, name->len);
|
|
|
+ name_buf[name->len]=0;
|
|
|
+ records=get_record(name_buf, type, RES_AR);
|
|
|
+ if (records){
|
|
|
+#ifdef CACHE_RELEVANT_RECS_ONLY
|
|
|
+ e=dns_cache_mk_rd_entry(name, type, &records);
|
|
|
+ if (e){
|
|
|
+ l=e;
|
|
|
+ e=dns_get_related(l, type, &records);
|
|
|
+ /* e should contain the searched entry (if found) and l
|
|
|
+ * all the entries (e and related) */
|
|
|
+ if (e){
|
|
|
+ atomic_set(&e->refcnt, 1); /* 1 because we return a
|
|
|
+ ref. to it */
|
|
|
+ }else{
|
|
|
+ /* e==0 => l contains a cname list => we use the last
|
|
|
+ * cname from the chain for a new resolve attempt (l->prev) */
|
|
|
+ /* only one cname record is allowed (rfc2181), so we ignore
|
|
|
+ * the others (we take only the first one) */
|
|
|
+ cname_val.s=
|
|
|
+ ((struct cname_rdata*)l->prev->rr_lst->rdata)->name;
|
|
|
+ cname_val.len=
|
|
|
+ ((struct cname_rdata*)l->prev->rr_lst->rdata)->name_len;
|
|
|
+ DBG("dns_cache_do_request: cname detected: %.*s (%d)\n",
|
|
|
+ cname_val.len, cname_val.s, cname_val.len);
|
|
|
+ }
|
|
|
+ /* add all the records to the hash */
|
|
|
+ l->prev->next=0; /* we break the double linked list for easier
|
|
|
+ searching */
|
|
|
+ LOCK_DNS_HASH(); /* optimization */
|
|
|
+ for (r=l; r; r=t){
|
|
|
+ t=r->next;
|
|
|
+ dns_cache_add_unsafe(r); /* refcnt++ inside */
|
|
|
+ if (atomic_get(&r->refcnt)==0){
|
|
|
+ /* if cache adding failed and nobody else is interested
|
|
|
+ * destroy this entry */
|
|
|
+ dns_destroy_entry(r);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+ /* if only cnames found => try to resolve the last one */
|
|
|
+ if (cname_val.s){
|
|
|
+ DBG("dns_cache_do_request: dns_get_entry(cname: %.*s (%d))\n",
|
|
|
+ cname_val.len, cname_val.s, cname_val.len);
|
|
|
+ e=dns_get_entry(&cname_val, type);
|
|
|
+ }
|
|
|
+ }
|
|
|
+#else
|
|
|
+ l=dns_cache_mk_rd_entry2(records);
|
|
|
+#endif
|
|
|
+ free_rdata_list(records);
|
|
|
+ }else if (dns_neg_cache_ttl){
|
|
|
+ e=dns_cache_mk_bad_entry(name, type, dns_neg_cache_ttl, DNS_BAD_NAME);
|
|
|
+ atomic_set(&e->refcnt, 1); /* 1 because we return a ref. to it */
|
|
|
+ dns_cache_add(e); /* refcnt++ inside*/
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+#ifndef CACHE_RELEVANT_RECS_ONLY
|
|
|
+ if (l){
|
|
|
+ /* add all the records to the cache, but return only the record
|
|
|
+ * we are looking for */
|
|
|
+ l->prev->next=0; /* we break the double linked list for easier
|
|
|
+ searching */
|
|
|
+ LOCK_DNS_HASH(); /* optimization */
|
|
|
+ for (r=l; r; r=t){
|
|
|
+ t=r->next;
|
|
|
+ if (e==0){ /* no entry found yet */
|
|
|
+ if (r->type==T_CNAME){
|
|
|
+ if ((r->name_len==name->len) && (r->rr_lst) &&
|
|
|
+ (strncasecmp(r->name, name->s, name->len)==0)){
|
|
|
+ /* update the name with the name from the cname rec. */
|
|
|
+ cname_val.s=
|
|
|
+ ((struct cname_rdata*)r->rr_lst->rdata)->name;
|
|
|
+ cname_val.len=
|
|
|
+ ((struct cname_rdata*)r->rr_lst->rdata)->name_len;
|
|
|
+ name=&cname_val;
|
|
|
+ }
|
|
|
+ }else if ((r->type==type) && (r->name_len==name->len) &&
|
|
|
+ (strncasecmp(r->name, name->s, name->len)==0)){
|
|
|
+ e=r;
|
|
|
+ atomic_set(&e->refcnt, 1); /* 1 because we return a ref.
|
|
|
+ to it */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dns_cache_add_unsafe(r); /* refcnt++ inside */
|
|
|
+ if (atomic_get(&r->refcnt)==0){
|
|
|
+ /* if cache adding failed and nobody else is interested
|
|
|
+ * destroy this entry */
|
|
|
+ dns_destroy_entry(r);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+ if ((e==0) && (cname_val.s)){ /* not found, but found a cname */
|
|
|
+ /* only one cname is allowed (rfc2181), so we ignore the
|
|
|
+ * others (we take only the first one) */
|
|
|
+ e=dns_get_entry(&cname_val, type);
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+end:
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* tries to lookup (name, type) in the hash and if not found tries to make
|
|
|
+ * a dns request
|
|
|
+ * return: 0 on error, pointer to a dns_hash_entry on success
|
|
|
+ * WARNING: when * not needed anymore dns_hash_put() must be called! */
|
|
|
+inline static struct dns_hash_entry* dns_get_entry(str* name, int type)
|
|
|
+{
|
|
|
+ int h;
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ str cname_val;
|
|
|
+ int err;
|
|
|
+ static int rec_cnt=0; /* recursion protection */
|
|
|
+
|
|
|
+ e=0;
|
|
|
+ if (rec_cnt>MAX_CNAME_CHAIN){
|
|
|
+ LOG(L_WARN, "WARNING: dns_get_entry: CNAME chain too long or"
|
|
|
+ " recursive CNAMEs (\"%.*s\")\n", name->len, name->s);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ rec_cnt++;
|
|
|
+ e=dns_hash_get(name, type, &h, &err);
|
|
|
+ if ((e==0) && ((err) || ((e=dns_cache_do_request(name, type))==0))){
|
|
|
+ goto error;
|
|
|
+ }else if ((e->type==T_CNAME) && (type!=T_CNAME)){
|
|
|
+ /* cname found instead which couldn't be resolved with the cached
|
|
|
+ * info => try a dns request */
|
|
|
+ /* only one cname record is allowed (rfc2181), so we ignore
|
|
|
+ * the others (we take only the first one) */
|
|
|
+ cname_val.s= ((struct cname_rdata*)e->rr_lst->rdata)->name;
|
|
|
+ cname_val.len=((struct cname_rdata*)e->rr_lst->rdata)->name_len;
|
|
|
+ dns_hash_put(e); /* not interested in the cname anymore */
|
|
|
+ if ((e=dns_cache_do_request(&cname_val, type))==0)
|
|
|
+ goto error; /* could not resolve cname */
|
|
|
+ }
|
|
|
+ /* found */
|
|
|
+ if ((e->rr_lst==0) || e->err_flags){
|
|
|
+ /* negative cache => not resolvable */
|
|
|
+ dns_hash_put(e);
|
|
|
+ e=0;
|
|
|
+ }
|
|
|
+error:
|
|
|
+ rec_cnt--;
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* gets the first non-expired, good record starting with record no
|
|
|
+ * from the dns_hash_entry struct e
|
|
|
+ * params: e - dns_hash_entry struct
|
|
|
+ * *no - it must contain the start record number (0 initially);
|
|
|
+ * it will be filled with the returned record number
|
|
|
+ * now - current time/ticks value
|
|
|
+ * returns pointer to the rr on success and sets no to the rr number
|
|
|
+ * 0 on error and fills the error flags
|
|
|
+ *
|
|
|
+ * Example usage:
|
|
|
+ * list all non-expired non-bad-marked ips for name:
|
|
|
+ * e=dns_get_entry(name, T_A);
|
|
|
+ * if (e){
|
|
|
+ * *no=0;
|
|
|
+ * now=get_ticks_raw();
|
|
|
+ * while(rr=dns_entry_get_rr(e, no, now){
|
|
|
+ * DBG("address %d\n", *no);
|
|
|
+ * *no++; ( get the next address next time )
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ */
|
|
|
+inline static struct dns_rr* dns_entry_get_rr( struct dns_hash_entry* e,
|
|
|
+ unsigned char* no, ticks_t now)
|
|
|
+{
|
|
|
+ struct dns_rr* rr;
|
|
|
+ int n;
|
|
|
+ int flags;
|
|
|
+
|
|
|
+ flags=0;
|
|
|
+ for(rr=e->rr_lst, n=0;rr && (n<*no);rr=rr->next, n++);/* skip *no records*/
|
|
|
+ for(;rr;rr=rr->next){
|
|
|
+ if ((s_ticks_t)(now-e->expire)>=0) /* expired entry */
|
|
|
+ continue;
|
|
|
+ if (rr->err_flags){ /* bad rr */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* everything is ok now */
|
|
|
+ *no=n;
|
|
|
+ return rr;
|
|
|
+ }
|
|
|
+ *no=n;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* gethostbyname compatibility: converts a dns_hash_entry structure
|
|
|
+ * to a statical internal hostent structure
|
|
|
+ * returns a pointer to the internal hostent structure on success or
|
|
|
+ * 0 on error
|
|
|
+ */
|
|
|
+struct hostent* dns_entry2he(struct dns_hash_entry* e)
|
|
|
+{
|
|
|
+ static struct hostent he;
|
|
|
+ static char hostname[256];
|
|
|
+ static char* p_aliases[1];
|
|
|
+ static char* p_addr[DNS_HE_MAX_ADDR+1];
|
|
|
+ static char address[16*DNS_HE_MAX_ADDR]; /* max 10 ipv6 addresses */
|
|
|
+ int af, len;
|
|
|
+ struct dns_rr* rr;
|
|
|
+ unsigned char rr_no;
|
|
|
+ ticks_t now;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ switch(e->type){
|
|
|
+ case T_A:
|
|
|
+ af=AF_INET;
|
|
|
+ len=4;
|
|
|
+ break;
|
|
|
+ case T_AAAA:
|
|
|
+ af=AF_INET6;
|
|
|
+ len=16;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LOG(L_CRIT, "BUG: dns_entry2he: wrong entry type %d for %.*s\n",
|
|
|
+ e->type, e->name_len, e->name);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ rr_no=0;
|
|
|
+ now=get_ticks_raw();
|
|
|
+ rr=dns_entry_get_rr(e, &rr_no, now);
|
|
|
+ for(i=0; rr && (i<DNS_HE_MAX_ADDR); i++,
|
|
|
+ rr=dns_entry_get_rr(e, &rr_no, now)){
|
|
|
+ p_addr[i]=&address[i*len];
|
|
|
+ memcpy(p_addr[i], ((struct a_rdata*)rr->rdata)->ip, len);
|
|
|
+ }
|
|
|
+ if (i==0){
|
|
|
+ DBG("DEBUG: dns_entry2he: no good records found (%d) for %.*s (%d)\n",
|
|
|
+ rr_no, e->name_len, e->name, e->type);
|
|
|
+ return 0; /* no good record found */
|
|
|
+ }
|
|
|
+
|
|
|
+ p_addr[i]=0; /* mark the end of the addresses */
|
|
|
+ p_aliases[0]=0; /* no aliases */
|
|
|
+ memcpy(hostname, e->name, e->name_len);
|
|
|
+ hostname[e->name_len]=0;
|
|
|
+
|
|
|
+ he.h_addrtype=af;
|
|
|
+ he.h_length=len;
|
|
|
+ he.h_addr_list=p_addr;
|
|
|
+ he.h_aliases=p_aliases;
|
|
|
+ he.h_name=hostname;
|
|
|
+
|
|
|
+ return &he;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* gethostbyname compatibility: performs an a_lookup and returns a pointer
|
|
|
+ * to a statical internal hostent structure
|
|
|
+ * returns 0 on success, <0 on error (see the error codes)
|
|
|
+ */
|
|
|
+struct hostent* dns_a_get_he(str* name)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ struct ip_addr* ip;
|
|
|
+ struct hostent* he;
|
|
|
+
|
|
|
+ e=0;
|
|
|
+ if ((ip=str2ip(name))!=0){
|
|
|
+ return ip_addr2he(name, ip);
|
|
|
+ }
|
|
|
+ if ((e=dns_get_entry(name, T_A))==0)
|
|
|
+ return 0;
|
|
|
+ /* found */
|
|
|
+ he=dns_entry2he(e);
|
|
|
+ dns_hash_put(e);
|
|
|
+ return he;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* gethostbyname compatibility: performs an aaaa_lookup and returns a pointer
|
|
|
+ * to a statical internal hostent structure
|
|
|
+ * returns 0 on success, <0 on error (see the error codes)
|
|
|
+ */
|
|
|
+struct hostent* dns_aaaa_get_he(str* name)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ struct ip_addr* ip;
|
|
|
+ struct hostent* he;
|
|
|
+
|
|
|
+ e=0;
|
|
|
+ if ((ip=str2ip6(name))!=0){
|
|
|
+ return ip_addr2he(name, ip);
|
|
|
+ }
|
|
|
+ if ((e=dns_get_entry(name, T_AAAA))==0)
|
|
|
+ return 0;
|
|
|
+ /* found */
|
|
|
+ he=dns_entry2he(e);
|
|
|
+ dns_hash_put(e);
|
|
|
+ return he;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* returns 0 on success, -1 on error (rr type does not contain an ip) */
|
|
|
+inline static int dns_rr2ip(int type, struct dns_rr* rr, struct ip_addr* ip)
|
|
|
+{
|
|
|
+ switch(type){
|
|
|
+ case T_A:
|
|
|
+ ip->af=AF_INET;
|
|
|
+ ip->len=4;
|
|
|
+ memcpy(ip->u.addr, ((struct a_rdata*)rr->rdata)->ip, 4);
|
|
|
+ return 0;
|
|
|
+ break;
|
|
|
+ case T_AAAA:
|
|
|
+ ip->af=AF_INET6;
|
|
|
+ ip->len=16;
|
|
|
+ memcpy(ip->u.addr, ((struct aaaa_rdata*)rr->rdata)->ip6, 16);
|
|
|
+ return 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* gethostbyname compatibility:
|
|
|
+ * performs an a or aaaa dns lookup, returns 0 on error and a pointer to a
|
|
|
+ * static hostent structure on success
|
|
|
+ * flags: - none set: tries first an a_lookup and if it fails an aaaa_lookup
|
|
|
+ * - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
|
|
|
+ * - DNS_IPV4_ONLY: tries only an a_lookup
|
|
|
+ * - DNS_IPV6_ONLY: tries only an aaaa_lookup
|
|
|
+ */
|
|
|
+struct hostent* dns_get_he(str* name, int flags)
|
|
|
+{
|
|
|
+ struct hostent* he;
|
|
|
+
|
|
|
+
|
|
|
+ if ((flags&(DNS_IPV6_FIRST|DNS_IPV6_ONLY))){
|
|
|
+ he=dns_aaaa_get_he(name);
|
|
|
+ if (he) return he;
|
|
|
+ }else{
|
|
|
+ he=dns_a_get_he(name);
|
|
|
+ if (he) return he;
|
|
|
+ }
|
|
|
+ if (flags&DNS_IPV6_FIRST){
|
|
|
+ he=dns_a_get_he(name);
|
|
|
+ }else if (!(flags&(DNS_IPV6_ONLY|DNS_IPV4_ONLY))){
|
|
|
+ he=dns_aaaa_get_he(name);
|
|
|
+ }
|
|
|
+ return he;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* sip_resolvehost helper: gets the first good hostent/port combination
|
|
|
+ * returns 0 on error, pointer to static hostent structure on success
|
|
|
+ * (and sets port)*/
|
|
|
+struct hostent* dns_srv_get_he(str* name, unsigned short* port, int flags)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ struct dns_rr* rr;
|
|
|
+ str rr_name;
|
|
|
+ struct hostent* he;
|
|
|
+ ticks_t now;
|
|
|
+ unsigned char rr_no;
|
|
|
+
|
|
|
+ rr=0;
|
|
|
+ he=0;
|
|
|
+ now=get_ticks_raw();
|
|
|
+ if ((e=dns_get_entry(name, T_SRV))==0)
|
|
|
+ goto error;
|
|
|
+ /* look inside the RRs for a good one (not expired or marked bad) */
|
|
|
+ rr_no=0;
|
|
|
+ while( (rr=dns_entry_get_rr(e, &rr_no, now))!=0){
|
|
|
+ /* everything is ok now, we can try to resolve the ip */
|
|
|
+ rr_name.s=((struct srv_rdata*)rr->rdata)->name;
|
|
|
+ rr_name.len=((struct srv_rdata*)rr->rdata)->name_len;
|
|
|
+ if ((he=dns_get_he(&rr_name, flags))!=0){
|
|
|
+ /* success, at least one good ip found */
|
|
|
+ *port=((struct srv_rdata*)rr->rdata)->port;
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+ rr_no++; /* try from the next record, the current one was not good */
|
|
|
+ }
|
|
|
+ /* if we reach this point => error, we couldn't find any good rr */
|
|
|
+end:
|
|
|
+ if (e) dns_hash_put(e);
|
|
|
+error:
|
|
|
+ return he;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+struct hostent* dns_resolvehost(char* name)
|
|
|
+{
|
|
|
+ str host;
|
|
|
+
|
|
|
+ if ((use_dns_cache==0) || (dns_hash==0)){ /* not init yet */
|
|
|
+ return _resolvehost(name);
|
|
|
+ }
|
|
|
+ host.s=name;
|
|
|
+ host.len=strlen(name);
|
|
|
+ return dns_get_he(&host, dns_flags);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
|
|
|
+ * if *port!=0.
|
|
|
+ * when performing SRV lookup (*port==0) it will use proto to look for
|
|
|
+ * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
|
|
|
+ * returns: hostent struct & *port filled with the port from the SRV record;
|
|
|
+ * 0 on error
|
|
|
+ */
|
|
|
+struct hostent* dns_sip_resolvehost(str* name, unsigned short* port, int proto)
|
|
|
+{
|
|
|
+ struct hostent* he;
|
|
|
+ struct ip_addr* ip;
|
|
|
+ static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
|
|
|
+ int len;
|
|
|
+ str srv_name;
|
|
|
+
|
|
|
+ if ((use_dns_cache==0) || (dns_hash==0)){
|
|
|
+ /* not init or off => use normal, non-cached version */
|
|
|
+ return _sip_resolvehost(name, port, proto);
|
|
|
+ }
|
|
|
+ len=0;
|
|
|
+ /* try SRV if no port specified (draft-ietf-sip-srv-06) */
|
|
|
+ if ((port)&&(*port==0)){
|
|
|
+ *port=(proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we don't
|
|
|
+ find another */
|
|
|
+ if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
|
|
|
+ LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too long"
|
|
|
+ " (%d), unable to perform SRV lookup\n", name->len);
|
|
|
+ }else{
|
|
|
+ /* check if it's an ip address */
|
|
|
+ if ( ((ip=str2ip(name))!=0)
|
|
|
+#ifdef USE_IPV6
|
|
|
+ || ((ip=str2ip6(name))!=0)
|
|
|
+#endif
|
|
|
+ ){
|
|
|
+ /* we are lucky, this is an ip address */
|
|
|
+ return ip_addr2he(name,ip);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch(proto){
|
|
|
+ case PROTO_NONE: /* no proto specified, use udp */
|
|
|
+ goto skip_srv;
|
|
|
+ case PROTO_UDP:
|
|
|
+ memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
|
|
|
+ memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
|
|
|
+ tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0';
|
|
|
+ len=SRV_UDP_PREFIX_LEN + name->len;
|
|
|
+ break;
|
|
|
+ case PROTO_TCP:
|
|
|
+ memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN);
|
|
|
+ memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len);
|
|
|
+ tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0';
|
|
|
+ len=SRV_TCP_PREFIX_LEN + name->len;
|
|
|
+ break;
|
|
|
+ case PROTO_TLS:
|
|
|
+ memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN);
|
|
|
+ memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len);
|
|
|
+ tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0';
|
|
|
+ len=SRV_TLS_PREFIX_LEN + name->len;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
|
|
|
+ proto);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ srv_name.s=tmp;
|
|
|
+ srv_name.len=len;
|
|
|
+ if ((he=dns_srv_get_he(&srv_name, port, dns_flags))!=0)
|
|
|
+ return he;
|
|
|
+ }
|
|
|
+ }
|
|
|
+skip_srv:
|
|
|
+ if (name->len >= MAX_DNS_NAME) {
|
|
|
+ LOG(L_ERR, "dns_sip_resolvehost: domain name too long\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ he=dns_get_he(name, dns_flags);
|
|
|
+ return he;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* performs an a lookup, fills the dns_entry pointer and the ip addr.
|
|
|
+ * (with the first good ip). if *e ==0 does the a lookup, and changes it
|
|
|
+ * to the result, if not it uses the current value and tries to use
|
|
|
+ * the rr_no record from it.
|
|
|
+ * params: e - must contain the "in-use" dns_hash_entry pointer (from
|
|
|
+ * a previous call) or *e==0 (for the first call)
|
|
|
+ * name - host name for which we do the lookup (required only
|
|
|
+ * when *e==0)
|
|
|
+ * ip - will be filled with the first good resolved ip started
|
|
|
+ * at *rr_no
|
|
|
+ * rr_no - record number to start searching for a good ip from
|
|
|
+ * (e.g. value from previous call + 1), filled on return
|
|
|
+ * with the number of the record corresponding to the
|
|
|
+ * returned ip
|
|
|
+ * returns 0 on success, <0 on error (see the error codes),
|
|
|
+ * fills e, ip and rr_no
|
|
|
+ * On end of records (when use to iterate on all the ips) it
|
|
|
+ * will return E_DNS_EOR (you should not log an error for this
|
|
|
+ * value, is just a signal that the address list end has been reached)
|
|
|
+ * WARNING: dns_hash_put(*e) must be called when you don't need
|
|
|
+ * the entry anymore and *e!=0 (failling to do so => mem. leak)
|
|
|
+ * Example:
|
|
|
+ * dns_entry=0;
|
|
|
+ * ret=dns_a_get_ip(&dns_entry, name, &ip, &rr_no); -- get the first rr.
|
|
|
+ * ...
|
|
|
+ * rr_no++;
|
|
|
+ * while((ret>=0) && dns_entry)
|
|
|
+ * dns_a_get_ip(&dns_entry, name, &ip, &rr_no); -- get the next rr
|
|
|
+ * if (ret!=-E_DNS_EOR) ERROR(....);
|
|
|
+ * ...
|
|
|
+ * dns_hash_put(dns_entry); -- finished with the entry
|
|
|
+ */
|
|
|
+int dns_a_resolve(struct dns_hash_entry** e, unsigned char* rr_no,
|
|
|
+ str* name, struct ip_addr* ip)
|
|
|
+{
|
|
|
+ struct dns_rr* rr;
|
|
|
+ int ret;
|
|
|
+ ticks_t now;
|
|
|
+ struct ip_addr* tmp;
|
|
|
+
|
|
|
+ rr=0;
|
|
|
+ ret=-E_DNS_NO_IP;
|
|
|
+ if (*e==0){ /* do lookup */
|
|
|
+ /* if ip don't set *e */
|
|
|
+ if ((tmp=str2ip(name))!=0){
|
|
|
+ *ip=*tmp;
|
|
|
+ *rr_no=0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if ((*e=dns_get_entry(name, T_A))==0)
|
|
|
+ goto error;
|
|
|
+ /* found */
|
|
|
+ *rr_no=0;
|
|
|
+ ret=-E_DNS_BAD_IP_ENTRY;
|
|
|
+ }
|
|
|
+ now=get_ticks_raw();
|
|
|
+ rr=dns_entry_get_rr(*e, rr_no, now);
|
|
|
+ if (rr){
|
|
|
+ /* everything is ok now, we can try to "convert" the ip */
|
|
|
+ dns_rr2ip((*e)->type, rr, ip);
|
|
|
+ ret=0;
|
|
|
+ }else{
|
|
|
+ ret=-E_DNS_EOR;
|
|
|
+ }
|
|
|
+error:
|
|
|
+ DBG("dns_a_resovle(%.*s, %d) returning %d\n",
|
|
|
+ name->len, name->s, *rr_no, ret);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* lookup, fills the dns_entry pointer and the ip addr.
|
|
|
+ * (with the first good ip). if *e ==0 does the a lookup, and changes it
|
|
|
+ * to the result, if not it uses the current value and tries to use
|
|
|
+ * Same as dns_a_resolve but for aaaa records (see above).
|
|
|
+ */
|
|
|
+int dns_aaaa_resolve(struct dns_hash_entry** e, unsigned char* rr_no,
|
|
|
+ str* name, struct ip_addr* ip)
|
|
|
+{
|
|
|
+ struct dns_rr* rr;
|
|
|
+ int ret;
|
|
|
+ ticks_t now;
|
|
|
+ struct ip_addr* tmp;
|
|
|
+
|
|
|
+ rr=0;
|
|
|
+ ret=-E_DNS_NO_IP;
|
|
|
+ if (*e==0){ /* do lookup */
|
|
|
+ /* if ip don't set *e */
|
|
|
+ if ((tmp=str2ip6(name))!=0){
|
|
|
+ *ip=*tmp;
|
|
|
+ *rr_no=0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if ((*e=dns_get_entry(name, T_AAAA))==0)
|
|
|
+ goto error;
|
|
|
+ /* found */
|
|
|
+ *rr_no=0;
|
|
|
+ ret=-E_DNS_BAD_IP_ENTRY;
|
|
|
+ }
|
|
|
+ now=get_ticks_raw();
|
|
|
+ rr=dns_entry_get_rr(*e, rr_no, now);
|
|
|
+ if (rr){
|
|
|
+ /* everything is ok now, we can try to "convert" the ip */
|
|
|
+ dns_rr2ip((*e)->type, rr, ip);
|
|
|
+ ret=0;
|
|
|
+ }else{
|
|
|
+ ret=-E_DNS_EOR; /* no more records */
|
|
|
+ }
|
|
|
+error:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* performs an a or aaaa dns lookup, returns <0 on error (see the
|
|
|
+ * dns error codes) and 0 on success
|
|
|
+ * flags: - none set: tries first an a_lookup and if it fails an aaaa_lookup
|
|
|
+ * - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
|
|
|
+ * - DNS_IPV4_ONLY: tries only an a_lookup
|
|
|
+ * - DNS_IPV6_ONLY: tries only an aaaa_lookup
|
|
|
+ * see dns_a_resolve() for the rest of the params., examples a.s.o
|
|
|
+ * WARNING: don't forget dns_hash_put(*e) when e is not needed anymore
|
|
|
+ */
|
|
|
+int dns_ip_resolve(struct dns_hash_entry** e, unsigned char* rr_no,
|
|
|
+ str* name, struct ip_addr* ip, int flags)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if ((flags&(DNS_IPV6_FIRST|DNS_IPV6_ONLY))){
|
|
|
+ ret=dns_aaaa_resolve(e, rr_no, name, ip);
|
|
|
+ if (ret>=0) return ret;
|
|
|
+ }else{
|
|
|
+ ret=dns_a_resolve(e, rr_no, name, ip);
|
|
|
+ if (ret>=0) return ret;
|
|
|
+ }
|
|
|
+ if (flags&DNS_IPV6_FIRST){
|
|
|
+ ret=dns_a_resolve(e, rr_no, name, ip);
|
|
|
+ }else if (!(flags&(DNS_IPV6_ONLY|DNS_IPV4_ONLY))){
|
|
|
+ ret=dns_aaaa_resolve(e, rr_no, name, ip);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* gets the first srv record starting at rr_no
|
|
|
+ * (similar to dns_a_resolve but for srv, sets host, port)
|
|
|
+ */
|
|
|
+int dns_srv_resolve(struct dns_hash_entry** e, unsigned char* rr_no,
|
|
|
+ str* name, str* host, unsigned short* port)
|
|
|
+{
|
|
|
+ struct dns_rr* rr;
|
|
|
+ int ret;
|
|
|
+ ticks_t now;
|
|
|
+
|
|
|
+ rr=0;
|
|
|
+ ret=-E_DNS_NO_SRV;
|
|
|
+ if (*e==0){
|
|
|
+ if ((*e=dns_get_entry(name, T_SRV))==0)
|
|
|
+ goto error;
|
|
|
+ /* found it */
|
|
|
+ *rr_no=0;
|
|
|
+ ret=-E_DNS_BAD_SRV_ENTRY;
|
|
|
+ }
|
|
|
+ now=get_ticks_raw();
|
|
|
+ rr=dns_entry_get_rr(*e, rr_no, now);
|
|
|
+ if (rr){
|
|
|
+ host->s=((struct srv_rdata*)rr->rdata)->name;
|
|
|
+ host->len=((struct srv_rdata*)rr->rdata)->name_len;
|
|
|
+ *port=((struct srv_rdata*)rr->rdata)->port;
|
|
|
+ ret=0;
|
|
|
+ }else{
|
|
|
+ ret=-E_DNS_EOR; /* no more records */
|
|
|
+ }
|
|
|
+error:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* gets the first srv record starting at h->srv_no, resolve it
|
|
|
+ * and get the first ip address (starting at h->ip_no)
|
|
|
+ * (similar to dns_a_resolve but for srv, sets host, port)
|
|
|
+ * WARNING: don't forget to init h prior to calling this function the first
|
|
|
+ * time and dns_srv_handle_put(h), even if error is returned
|
|
|
+ */
|
|
|
+int dns_srv_resolve_ip(struct dns_srv_handle* h,
|
|
|
+ str* name, struct ip_addr* ip, unsigned short* port,
|
|
|
+ int flags)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ str host;
|
|
|
+
|
|
|
+ host.len=0;
|
|
|
+ host.s=0;
|
|
|
+ do{
|
|
|
+ if (h->a==0){
|
|
|
+ if ((ret=dns_srv_resolve(&h->srv, &h->srv_no,
|
|
|
+ name, &host, port))<0)
|
|
|
+ goto error;
|
|
|
+ h->port=*port; /* store new port */
|
|
|
+ }else{
|
|
|
+ *port=h->port; /* return the stored port */
|
|
|
+ }
|
|
|
+ if ((ret=dns_ip_resolve(&h->a, &h->ip_no, &host, ip, flags))<0){
|
|
|
+ /* couldn't find any good ip for this record, try the next one */
|
|
|
+ h->srv_no++;
|
|
|
+ if (h->a){
|
|
|
+ dns_hash_put(h->a);
|
|
|
+ h->a=0;
|
|
|
+ }
|
|
|
+ }else if (h->a==0){
|
|
|
+ /* this was an ip, try the next srv record in the future */
|
|
|
+ h->srv_no++;
|
|
|
+ }
|
|
|
+ }while(ret<0);
|
|
|
+error:
|
|
|
+ DBG("dns_srv_resolve_ip(\"%.*s\", %d, %d), ret=%d, ip=%s\n",
|
|
|
+ name->len, name->s, h->srv_no, h->ip_no, ret, ip_addr2a(ip));
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
|
|
|
+ * if *port!=0.
|
|
|
+ * when performing SRV lookup (*port==0) it will use proto to look for
|
|
|
+ * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup
|
|
|
+ * dns_res_h must be initialized prior to calling this function and can be
|
|
|
+ * used to get the subsequent ips
|
|
|
+ * returns: <0 on error
|
|
|
+ * 0 on success and it fills *ip, *port, dns_sip_resolve_h
|
|
|
+ * WARNING: when finished, dns_sip_resolve_put(dns_res_h) must be called!
|
|
|
+ */
|
|
|
+int dns_sip_resolve(struct dns_srv_handle* h, str* name,
|
|
|
+ struct ip_addr* ip, unsigned short* port, int proto,
|
|
|
+ int flags)
|
|
|
+{
|
|
|
+ static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
|
|
|
+ int len;
|
|
|
+ str srv_name;
|
|
|
+ struct ip_addr* tmp_ip;
|
|
|
+ int ret;
|
|
|
+ struct hostent* he;
|
|
|
+
|
|
|
+ if (dns_hash==0){ /* not init => use normal, non-cached version */
|
|
|
+ LOG(L_WARN, "WARNING: dns_sip_resolve: called before dns cache"
|
|
|
+ " initialization\n");
|
|
|
+ h->srv=h->a=0;
|
|
|
+ he=_sip_resolvehost(name, port, proto);
|
|
|
+ if (he){
|
|
|
+ hostent2ip_addr(ip, he, 0);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return -E_DNS_NO_SRV;
|
|
|
+ }
|
|
|
+ len=0;
|
|
|
+ if ((h->srv==0) && (h->a==0)){
|
|
|
+ h->port=(proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we
|
|
|
+ don't find another */
|
|
|
+ if ((port) && (*port==0)){
|
|
|
+ /* try SRV if initial call & no port specified
|
|
|
+ * (draft-ietf-sip-srv-06) */
|
|
|
+ *port=h->port;
|
|
|
+ if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){
|
|
|
+ LOG(L_WARN, "WARNING: dns_sip_resolvehost: domain name too"
|
|
|
+ " long (%d), unable to perform SRV lookup\n",
|
|
|
+ name->len);
|
|
|
+ }else{
|
|
|
+ /* check if it's an ip address */
|
|
|
+ if ( ((tmp_ip=str2ip(name))!=0)
|
|
|
+#ifdef USE_IPV6
|
|
|
+ || ((tmp_ip=str2ip6(name))!=0)
|
|
|
+#endif
|
|
|
+ ){
|
|
|
+ /* we are lucky, this is an ip address */
|
|
|
+#ifdef USE_IPV6
|
|
|
+ if (((flags&DNS_IPV4_ONLY) && (tmp_ip->af==AF_INET6)) ||
|
|
|
+ ((flags&DNS_IPV6_ONLY) && (tmp_ip->af==AF_INET))){
|
|
|
+ return -E_DNS_AF_MISMATCH;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ *ip=*tmp_ip;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch(proto){
|
|
|
+ case PROTO_NONE: /* no proto specified, use udp */
|
|
|
+ goto skip_srv;
|
|
|
+ case PROTO_UDP:
|
|
|
+ memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN);
|
|
|
+ memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len);
|
|
|
+ tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0';
|
|
|
+ len=SRV_UDP_PREFIX_LEN + name->len;
|
|
|
+ break;
|
|
|
+ case PROTO_TCP:
|
|
|
+ memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN);
|
|
|
+ memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len);
|
|
|
+ tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0';
|
|
|
+ len=SRV_TCP_PREFIX_LEN + name->len;
|
|
|
+ break;
|
|
|
+ case PROTO_TLS:
|
|
|
+ memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN);
|
|
|
+ memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len);
|
|
|
+ tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0';
|
|
|
+ len=SRV_TLS_PREFIX_LEN + name->len;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n",
|
|
|
+ proto);
|
|
|
+ return -E_DNS_CRITICAL;
|
|
|
+ }
|
|
|
+ srv_name.s=tmp;
|
|
|
+ srv_name.len=len;
|
|
|
+
|
|
|
+ if ((ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags))>=0)
|
|
|
+ {
|
|
|
+ DBG("dns_sip_resolve(%.*s, %d, %d), srv0, ret=%d\n",
|
|
|
+ name->len, name->s, h->srv_no, h->ip_no, ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else if (h->srv){
|
|
|
+ srv_name.s=h->srv->name;
|
|
|
+ srv_name.len=h->srv->name_len;
|
|
|
+ /* continue srv resolving */
|
|
|
+ ret=dns_srv_resolve_ip(h, &srv_name, ip, port, flags);
|
|
|
+ DBG("dns_sip_resolve(%.*s, %d, %d), srv, ret=%d\n",
|
|
|
+ name->len, name->s, h->srv_no, h->ip_no, ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+skip_srv:
|
|
|
+ if (name->len >= MAX_DNS_NAME) {
|
|
|
+ LOG(L_ERR, "dns_sip_resolve: domain name too long\n");
|
|
|
+ return -E_DNS_NAME_TOO_LONG;
|
|
|
+ }
|
|
|
+ ret=dns_ip_resolve(&h->a, &h->ip_no, name, ip, flags);
|
|
|
+ if (port)
|
|
|
+ *port=h->port;
|
|
|
+ DBG("dns_sip_resolve(%.*s, %d, %d), ip, ret=%d\n",
|
|
|
+ name->len, name->s, h->srv_no, h->ip_no, ret);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* performs an a lookup and fills ip with the first good ip address
|
|
|
+ * returns 0 on success, <0 on error (see the error codes)
|
|
|
+ */
|
|
|
+int dns_a_get_ip(str* name, struct ip_addr* ip)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ int ret;
|
|
|
+ unsigned char rr_no;
|
|
|
+
|
|
|
+ e=0;
|
|
|
+ rr_no=0;
|
|
|
+ ret=dns_a_resolve(&e, &rr_no, name, ip);
|
|
|
+ if (e) dns_hash_put(e);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int dns_aaaa_get_ip(str* name, struct ip_addr* ip)
|
|
|
+{
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ int ret;
|
|
|
+ unsigned char rr_no;
|
|
|
+
|
|
|
+ e=0;
|
|
|
+ rr_no=0;
|
|
|
+ ret=dns_aaaa_resolve(&e, &rr_no, name, ip);
|
|
|
+ if (e) dns_hash_put(e);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* performs an a or aaaa dns lookup, returns <0 on error (see the
|
|
|
+ * dns error codes) and 0 on success
|
|
|
+ * flags: - none set: tries first an a_lookup and if it fails an aaaa_lookup
|
|
|
+ * - DNS_IPV6_FIRST: tries first an aaaa_lookup and then an a_lookup
|
|
|
+ * - DNS_IPV4_ONLY: tries only an a_lookup
|
|
|
+ * - DNS_IPV6_ONLY: tries only an aaaa_lookup
|
|
|
+ */
|
|
|
+int dns_get_ip(str* name, struct ip_addr* ip, int flags)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ unsigned char rr_no;
|
|
|
+
|
|
|
+ e=0;
|
|
|
+ rr_no=0;
|
|
|
+ ret=dns_ip_resolve(&e, &rr_no, name, ip, flags);
|
|
|
+ if (e)
|
|
|
+ dns_hash_put(e);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* fast "inline" version, gets the first good ip:port */
|
|
|
+int dns_srv_get_ip(str* name, struct ip_addr* ip, unsigned short* port,
|
|
|
+ int flags)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ struct dns_srv_handle h;
|
|
|
+
|
|
|
+ dns_srv_handle_init(&h);
|
|
|
+ ret=dns_srv_resolve_ip(&h, name, ip, port, flags);
|
|
|
+ dns_srv_handle_put(&h);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* rpc functions */
|
|
|
+void dns_cache_mem_info(rpc_t* rpc, void* ctx)
|
|
|
+{
|
|
|
+ rpc->add(ctx, "dd", *dns_cache_mem_used, dns_cache_max_mem);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void dns_cache_debug(rpc_t* rpc, void* ctx)
|
|
|
+{
|
|
|
+ int h;
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ ticks_t now;
|
|
|
+
|
|
|
+ now=get_ticks_raw();
|
|
|
+ LOCK_DNS_HASH();
|
|
|
+ for (h=0; h<DNS_HASH_SIZE; h++){
|
|
|
+ clist_foreach(&dns_hash[h], e, next){
|
|
|
+ rpc->add(ctx, "sdddddd",
|
|
|
+ e->name, e->type, e->total_size, e->refcnt.val,
|
|
|
+ (s_ticks_t)(e->expire-now)<0?-1:
|
|
|
+ TICKS_TO_S(e->expire-now),
|
|
|
+ TICKS_TO_S(now-e->last_used),
|
|
|
+ e->err_flags);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/* rpc functions */
|
|
|
+void dns_cache_debug_all(rpc_t* rpc, void* ctx)
|
|
|
+{
|
|
|
+ int h;
|
|
|
+ struct dns_hash_entry* e;
|
|
|
+ struct dns_rr* rr;
|
|
|
+ struct ip_addr ip;
|
|
|
+ int i;
|
|
|
+ ticks_t now;
|
|
|
+
|
|
|
+ now=get_ticks_raw();
|
|
|
+ LOCK_DNS_HASH();
|
|
|
+ for (h=0; h<DNS_HASH_SIZE; h++){
|
|
|
+ clist_foreach(&dns_hash[h], e, next){
|
|
|
+ for (i=0, rr=e->rr_lst; rr; i++, rr=rr->next){
|
|
|
+ rpc->add(ctx, "sddddddd",
|
|
|
+ e->name, e->type, i, e->total_size,
|
|
|
+ e->refcnt.val,
|
|
|
+ (s_ticks_t)(e->expire-now)<0?-1:
|
|
|
+ TICKS_TO_S(e->expire-now),
|
|
|
+ TICKS_TO_S(now-e->last_used),
|
|
|
+ e->err_flags);
|
|
|
+ switch(e->type){
|
|
|
+ case T_A:
|
|
|
+ case T_AAAA:
|
|
|
+ if (dns_rr2ip(e->type, rr, &ip)==0){
|
|
|
+ rpc->add(ctx, "ss", "ip", ip_addr2a(&ip) );
|
|
|
+ }else{
|
|
|
+ rpc->add(ctx, "ss", "ip", "<error: bad rr>");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case T_SRV:
|
|
|
+ rpc->add(ctx, "ss", "srv",
|
|
|
+ ((struct srv_rdata*)(rr->rdata))->name);
|
|
|
+ break;
|
|
|
+ case T_NAPTR:
|
|
|
+ rpc->add(ctx, "ss", "naptr",
|
|
|
+ ((struct naptr_rdata*)(rr->rdata))->flags);
|
|
|
+ break;
|
|
|
+ case T_CNAME:
|
|
|
+ rpc->add(ctx, "ss", "cname",
|
|
|
+ ((struct cname_rdata*)(rr->rdata))->name);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ rpc->add(ctx, "ss", "unknown", "?");
|
|
|
+ }
|
|
|
+ rpc->add(ctx, "dd",
|
|
|
+ (s_ticks_t)(rr->expire-now)<0?-1:
|
|
|
+ TICKS_TO_S(rr->expire-now),
|
|
|
+ rr->err_flags);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ UNLOCK_DNS_HASH();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#endif
|