resolve.h
4e2fdd79
 /*
  * $Id$
  *
  * resolver related functions
7dd0b342
  *
53c7e0f1
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
  * 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:
  *    info@iptel.org
  *
  * 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
4e2fdd79
  */
a7e13a89
 /* History:
  * --------
  *  2003-04-12  support for resolving ipv6 address references added (andrei)
194b6a35
  *  2004-07-28  darwin needs nameser_compat.h (andrei)
2edb7ef8
  *  2006-07-13  rdata structures put on diet (andrei)
dcb59e67
  *  2006-07-17  rdata contains now also the record name (andrei)
  *  2006-08-18  get_record uses flags (andrei)
dd4ffbb7
  *  2006-06-16  naptr support (andrei)
a7e13a89
  */
4e2fdd79
 
 
7dd0b342
 
994af6b1
 #ifndef __resolve_h
 #define __resolve_h
4e2fdd79
 
994af6b1
 #include <sys/types.h>
 #include <sys/socket.h>
2b9b1191
 #include <netinet/in.h>
4e2fdd79
 #include <netdb.h>
1b71270a
 #include <arpa/nameser.h>
740a5978
 #include <resolv.h>
4e040217
 #include "counters.h"
4e2fdd79
 
194b6a35
 #ifdef __OS_darwin
 #include <arpa/nameser_compat.h>
 #endif
 
4c9f3fde
 #include "ip_addr.h"
dcb59e67
 #ifdef USE_DNS_CACHE
 #include "dns_wrappers.h"
 #endif
4c9f3fde
 
dd4ffbb7
 /* define RESOLVE_DBG for debugging info (very noisy) */
 #define RESOLVE_DBG
 /* define NAPTR_DBG for naptr related debugging info (very noisy) */
 #define NAPTR_DBG
 
c6c85c8e
 
1b71270a
 #define MAX_QUERY_SIZE 8192
 #define ANS_SIZE       8192
 #define DNS_HDR_SIZE     12
 #define MAX_DNS_NAME 256
a2acf4e8
 #define MAX_DNS_STRING 255
4e2fdd79
 
2c2dd816
 #ifndef T_EBL
 /** not official yet - iEnum. */
 #define T_EBL 65300
 #endif
4e2fdd79
 
dcb59e67
 /* get_record flags */
 #define RES_ONLY_TYPE 1   /* return only the specified type records */
 #define RES_AR		  2   /* return also the additional records */
4e2fdd79
 
4e040217
 /* counter for failed DNS requests
 */
 struct dns_counters_h {
     counter_handle_t failed_dns_req;
 };
 
 extern struct dns_counters_h dns_cnts_h;
 
1b71270a
 /* query union*/
 union dns_query{
 	HEADER hdr;
 	unsigned char buff[MAX_QUERY_SIZE];
 };
 
 
 /* rdata struct*/
 struct rdata {
 	unsigned short type;
113d8990
 	unsigned short pclass;
1b71270a
 	unsigned int   ttl;
 	void* rdata;
 	struct rdata* next;
dcb59e67
 	unsigned char name_len; /* name length w/o the terminating 0 */
 	char name[1]; /* null terminated name (len=name_len+1) */
1b71270a
 };
dcb59e67
 /* real size of the structure */
 #define RDATA_SIZE(s) (sizeof(struct rdata)+(s).name_len) /* +1-1 */
1b71270a
 
 
 /* srv rec. struct*/
 struct srv_rdata {
 	unsigned short priority;
 	unsigned short weight;
 	unsigned short port;
2edb7ef8
 	unsigned char name_len; /* name length w/o the terminating 0 */
 	char name[1]; /* null terminated name (len=name_len+1) */
1b71270a
 };
 
2edb7ef8
 
 /* real size of the structure */
dcb59e67
 #define SRV_RDATA_SIZE(s) (sizeof(struct srv_rdata)+(s).name_len)
2edb7ef8
 
a2acf4e8
 /* naptr rec. struct*/
 struct naptr_rdata {
2edb7ef8
 	char* flags;    /* points inside str_table */
 	char* services; /* points inside str_table */
 	char* regexp;   /* points inside str_table */
 	char* repl;     /* points inside str_table, null terminated */
 	
a2acf4e8
 	unsigned short order;
 	unsigned short pref;
2edb7ef8
 	
 	unsigned char flags_len;
 	unsigned char services_len;
 	unsigned char regexp_len;
 	unsigned char repl_len; /* not currently used */
 	
 	char str_table[1]; /* contains all the strings */
a2acf4e8
 };
 
2edb7ef8
 /* real size of the structure */
dcb59e67
 #define NAPTR_RDATA_SIZE(s) (sizeof(struct naptr_rdata) \
2edb7ef8
 								+ (s).flags_len \
 								+ (s).services_len \
 								+ (s).regexp_len \
 								+ (s).repl_len + 1 - 1)
 
1b71270a
 
 /* A rec. struct */
 struct a_rdata {
 	unsigned char ip[4];
 };
 
 struct aaaa_rdata {
 	unsigned char ip6[16];
 };
 
4c55b6cf
 /* cname rec. struct*/
 struct cname_rdata {
2edb7ef8
 	unsigned char name_len; /* name length w/o the terminating 0 */
 	char name[1]; /* null terminated name (len=name_len+1) */
4c55b6cf
 };
 
2edb7ef8
 /* real size of the structure */
dcb59e67
 #define CNAME_RDATA_SIZE(s) (sizeof(struct cname_rdata)+(s).name_len)
2edb7ef8
 
8f4ba87e
 /* dns character-string */
 struct dns_cstr{
 	char* cstr; /* pointer to null term. string */
 	unsigned char cstr_len;
 };
 
 /* txt rec. struct */
 struct txt_rdata {
 	unsigned short cstr_no; /* number of strings */
 	unsigned short tslen; /* total strings table len */
 	struct dns_cstr txt[1]; /* at least 1 */
 	/* all txt[*].cstr point inside a string table at the end of the struct.*/
 };
 
 #define TXT_RDATA_SIZE(s) \
 	(sizeof(struct txt_rdata)+((s).cstr_no-1)*sizeof(struct dns_cstr)+\
 	 	(s).tslen)
 
2c2dd816
 /* ebl rec. struct, see
    http://tools.ietf.org/html/draft-ietf-enum-branch-location-record-03 */
 struct ebl_rdata {
 	char* separator; /* points inside str_table */
 	char* apex;      /* point inside str_table */
 	unsigned char separator_len; /* separator len w/o the terminating 0 */
 	unsigned char apex_len;      /* apex len w/p the terminating 0 */
 	unsigned char position;
 	char str_table[1]; /* contains the 2 strings: separator and apex */
 };
 #define EBL_RDATA_SIZE(s) \
 	(sizeof(struct ebl_rdata)-1+(s).separator_len+1+(s).apex_len+1)
 
1b71270a
 
d354557c
 struct ptr_rdata {
 	unsigned char ptrdname_len; /* name length w/o the terminating 0 */
 	char ptrdname[1]; /* null terminated name (len=name_len+1) */
 };
 /* real size of the structure */
 #define PTR_RDATA_SIZE(s) (sizeof(struct ptr_rdata)-1+(s).ptrdname_len+1)
 
 
740a5978
 #ifdef HAVE_RESOLV_RES
62d2baec
 int match_search_list(const struct __res_state* res, char* name);
740a5978
 #endif
dcb59e67
 struct rdata* get_record(char* name, int type, int flags);
1b71270a
 void free_rdata_list(struct rdata* head);
 
 
4e2fdd79
 
637c7320
 #define rev_resolvehost(ip)\
ac4f791f
 					gethostbyaddr((char*)(ip)->u.addr, (ip)->len, (ip)->af)
4e2fdd79
 
 
c6c85c8e
 
 #define HEX2I(c) \
 	(	(((c)>='0') && ((c)<='9'))? (c)-'0' :  \
 		(((c)>='A') && ((c)<='F'))? ((c)-'A')+10 : \
 		(((c)>='a') && ((c)<='f'))? ((c)-'a')+10 : -1 )
 
 
4e2fdd79
 
 
c6c85c8e
 
4c9f3fde
 /* converts a str to an ipv4 address, returns the address or 0 on error
    Warning: the result is a pointer to a statically allocated structure */
a6982b85
 static inline struct ip_addr* str2ip(str* st)
4c9f3fde
 {
 	int i;
 	unsigned char *limit;
 	static struct ip_addr ip;
a6982b85
 	unsigned char* s;
 
6419a43f
 	s=(unsigned char*)st->s;
4c9f3fde
 
 	/*init*/
 	ip.u.addr32[0]=0;
 	i=0;
6419a43f
 	limit=(unsigned char*)(st->s + st->len);
4c9f3fde
 
a6982b85
 	for(;s<limit ;s++){
 		if (*s=='.'){
4c9f3fde
 				i++;
 				if (i>3) goto error_dots;
a6982b85
 		}else if ( (*s <= '9' ) && (*s >= '0') ){
 				ip.u.addr[i]=ip.u.addr[i]*10+*s-'0';
4c9f3fde
 		}else{
 				//error unknown char
 				goto error_char;
 		}
 	}
ff4b78b0
 	if (i<3) goto error_dots;
4c9f3fde
 	ip.af=AF_INET;
 	ip.len=4;
 	
 	return &ip;
 error_dots:
dd4ffbb7
 #ifdef RESOLVE_DBG
ff4b78b0
 	DBG("str2ip: ERROR: too %s dots in [%.*s]\n", (i>3)?"many":"few", 
 			st->len, st->s);
dd4ffbb7
 #endif
4c9f3fde
 	return 0;
a6982b85
  error_char:
a7e13a89
 	/*
a6982b85
 	DBG("str2ip: WARNING: unexpected char %c in [%.*s]\n", *s, st->len, st->s);
a7e13a89
 	*/
4c9f3fde
 	return 0;
 }
 
 
145d6ad0
 #ifdef USE_IPV6
4c9f3fde
 /* returns an ip_addr struct.; on error returns 0
  * the ip_addr struct is static, so subsequent calls will destroy its content*/
a6982b85
 static inline struct ip_addr* str2ip6(str* st)
c6c85c8e
 {
 	int i, idx1, rest;
 	int no_colons;
 	int double_colon;
 	int hex;
 	static struct ip_addr ip;
 	unsigned short* addr_start;
 	unsigned short addr_end[8];
 	unsigned short* addr;
 	unsigned char* limit;
a6982b85
 	unsigned char* s;
c6c85c8e
 	
 	/* init */
a7e13a89
 	if ((st->len) && (st->s[0]=='[')){
 		/* skip over [ ] */
 		if (st->s[st->len-1]!=']') goto error_char;
 		s=(unsigned char*)(st->s+1);
 		limit=(unsigned char*)(st->s+st->len-1);
 	}else{
 		s=(unsigned char*)st->s;
 		limit=(unsigned char*)(st->s+st->len);
 	}
c6c85c8e
 	i=idx1=rest=0;
 	double_colon=0;
 	no_colons=0;
 	ip.af=AF_INET6;
 	ip.len=16;
 	addr_start=ip.u.addr16;
 	addr=addr_start;
 	memset(addr_start, 0 , 8*sizeof(unsigned short));
 	memset(addr_end, 0 , 8*sizeof(unsigned short));
a6982b85
 	for (; s<limit; s++){
 		if (*s==':'){
c6c85c8e
 			no_colons++;
 			if (no_colons>7) goto error_too_many_colons;
 			if (double_colon){
 				idx1=i;
 				i=0;
 				if (addr==addr_end) goto error_colons;
 				addr=addr_end;
 			}else{
 				double_colon=1;
4c9f3fde
 				addr[i]=htons(addr[i]);
c6c85c8e
 				i++;
 			}
a6982b85
 		}else if ((hex=HEX2I(*s))>=0){
c6c85c8e
 				addr[i]=addr[i]*16+hex;
 				double_colon=0;
 		}else{
 			/* error, unknown char */
 			goto error_char;
 		}
 	}
4c9f3fde
 	if (!double_colon){ /* not ending in ':' */
 		addr[i]=htons(addr[i]);
 		i++; 
 	}
f4e042be
 	/* if address contained '::' fix it */
 	if (addr==addr_end){
 		rest=8-i-idx1;
 		memcpy(addr_start+idx1+rest, addr_end, i*sizeof(unsigned short));
ff4b78b0
 	}else{
 		/* no double colons inside */
 		if (no_colons<7) goto error_too_few_colons;
f4e042be
 	}
 /*
 	DBG("str2ip6: idx1=%d, rest=%d, no_colons=%d, hex=%x\n",
 			idx1, rest, no_colons, hex);
 	DBG("str2ip6: address %x:%x:%x:%x:%x:%x:%x:%x\n", 
 			addr_start[0], addr_start[1], addr_start[2],
 			addr_start[3], addr_start[4], addr_start[5],
 			addr_start[6], addr_start[7] );
 */
c6c85c8e
 	return &ip;
 
 error_too_many_colons:
dd4ffbb7
 #ifdef RESOLVE_DBG
a6982b85
 	DBG("str2ip6: ERROR: too many colons in [%.*s]\n", st->len, st->s);
dd4ffbb7
 #endif
4c9f3fde
 	return 0;
 
 error_too_few_colons:
dd4ffbb7
 #ifdef RESOLVE_DBG
a6982b85
 	DBG("str2ip6: ERROR: too few colons in [%.*s]\n", st->len, st->s);
dd4ffbb7
 #endif
c6c85c8e
 	return 0;
 
 error_colons:
dd4ffbb7
 #ifdef RESOLVE_DBG
a6982b85
 	DBG("str2ip6: ERROR: too many double colons in [%.*s]\n", st->len, st->s);
dd4ffbb7
 #endif
c6c85c8e
 	return 0;
 
 error_char:
a7e13a89
 	/*
a6982b85
 	DBG("str2ip6: WARNING: unexpected char %c in  [%.*s]\n", *s, st->len,
a7e13a89
 			st->s);*/
c6c85c8e
 	return 0;
 }
145d6ad0
 #endif /* USE_IPV6 */
4c9f3fde
 
 
 
dd4ffbb7
 struct hostent* _sip_resolvehost(str* name, unsigned short* port, char* proto);
4c9f3fde
 
 
 
dcb59e67
 /* gethostbyname wrapper, handles ip/ipv6 automatically */
 static inline struct hostent* _resolvehost(char* name)
4c9f3fde
 {
994af6b1
 	static struct hostent* he=0;
087d0976
 #ifdef HAVE_GETIPNODEBYNAME 
476e1991
 #ifdef USE_IPV6
994af6b1
 	int err;
ba506d03
 	static struct hostent* he2=0;
994af6b1
 #endif
476e1991
 #endif
a7e13a89
 #ifndef DNS_IP_HACK
9f4c52ce
 #ifdef USE_IPV6
a7e13a89
 	int len;
 #endif
9f4c52ce
 #endif
4c9f3fde
 #ifdef DNS_IP_HACK
 	struct ip_addr* ip;
a6982b85
 	str s;
 
 	s.s = (char*)name;
 	s.len = strlen(name);
 
4c9f3fde
 	/* check if it's an ip address */
a6982b85
 	if ( ((ip=str2ip(&s))!=0)
4c9f3fde
 #ifdef	USE_IPV6
a6982b85
 		  || ((ip=str2ip6(&s))!=0)
4c9f3fde
 #endif
 		){
 		/* we are lucky, this is an ip address */
a6982b85
 		return ip_addr2he(&s, ip);
4c9f3fde
 	}
 	
a7e13a89
 #else /* DNS_IP_HACK */
9f4c52ce
 #ifdef USE_IPV6
a7e13a89
 	len=0;
 	if (*name=='['){
 		len=strlen(name);
 		if (len && (name[len-1]==']')){
 			name[len-1]=0; /* remove '[' */
 			name++; /* skip '[' */
 			goto skip_ipv4;
 		}
 	}
c6c85c8e
 #endif
9f4c52ce
 #endif
994af6b1
 	/* ipv4 */
 	he=gethostbyname(name);
4c9f3fde
 #ifdef USE_IPV6
2cfcc6bb
 	if(he==0 && cfg_get(core, core_cfg, dns_try_ipv6)){
a7e13a89
 #ifndef DNS_IP_HACK
 skip_ipv4:
 #endif
4c9f3fde
 		/*try ipv6*/
087d0976
 	#ifdef HAVE_GETHOSTBYNAME2
 		he=gethostbyname2(name, AF_INET6);
 	#elif defined HAVE_GETIPNODEBYNAME
ba506d03
 		/* on solaris 8 getipnodebyname has a memory leak,
 		 * after some time calls to it will fail with err=3
 		 * solution: patch your solaris 8 installation */
 		if (he2) freehostent(he2);
 		he=he2=getipnodebyname(name, AF_INET6, 0, &err);
994af6b1
 	#else
087d0976
 		#error neither gethostbyname2 or getipnodebyname present
994af6b1
 	#endif
a7e13a89
 #ifndef DNS_IP_HACK
 		if (len) name[len-2]=']'; /* restore */
 #endif
4c9f3fde
 	}
 #endif
 	return he;
 }
c6c85c8e
 
 
878fc194
 int resolv_init();
 
2cfcc6bb
 /* callback/fixup functions executed by the configuration framework */
33bfeb9d
 void resolv_reinit(str *gname, str *name);
 int dns_reinit_fixup(void *handle, str *gname, str *name, void **val);
 int dns_try_ipv6_fixup(void *handle, str *gname, str *name, void **val);
 void reinit_naptr_proto_prefs(str *gname, str *name);
2cfcc6bb
 
 #ifdef DNS_WATCHDOG_SUPPORT
 /* callback function that is called by the child processes
  * when they reinitialize the resolver
  *
  * Note, that this callback is called by each chiled process separately!!!
  * If the callback is registered after forking, only the child process
  * that installs the hook will call the callback.
  */
d1df3d2f
 typedef void (*on_resolv_reinit)(str*);
2cfcc6bb
 int register_resolv_reinit_cb(on_resolv_reinit cb);
 #endif
 
 
2f0f1a30
 int sip_hostport2su(union sockaddr_union* su, str* host, unsigned short port,
dd4ffbb7
 						char* proto);
dcb59e67
 
 
 
 /* wrappers */
 #ifdef USE_DNS_CACHE
 #define resolvehost dns_resolvehost
 #define sip_resolvehost dns_sip_resolvehost
 #else
 #define resolvehost _resolvehost
 #define sip_resolvehost _sip_resolvehost
 #endif
 
 
dd4ffbb7
 
 #ifdef USE_NAPTR
 /* NAPTR helper functions */
 typedef unsigned int naptr_bmp_t; /* type used for keeping track of tried
 									 naptr records*/
 #define MAX_NAPTR_RRS (sizeof(naptr_bmp_t)*8)
 
 /* use before first call to naptr_sip_iterate */
 #define naptr_iterate_init(bmp) \
 	do{ \
 		*(bmp)=0; \
 	}while(0) \
 
 struct rdata* naptr_sip_iterate(struct rdata* naptr_head, 
 										naptr_bmp_t* tried,
 										str* srv_name, char* proto);
 /* returns sip proto if valis sip naptr record, .-1 otherwise */
 char naptr_get_sip_proto(struct naptr_rdata* n);
 /* returns true if new_proto is preferred over old_proto */
 int naptr_proto_preferred(char new_proto, char old_proto);
 /* returns true if we support the protocol */
 int naptr_proto_supported(char proto);
 /* choose between 2 naptr records, should take into account local
  * preferences too
  * returns 1 if the new record was selected, 0 otherwise */
 int naptr_choose (struct naptr_rdata** crt, char* crt_proto,
 									struct naptr_rdata* n , char n_proto);
 
 #endif/* USE_NAPTR */
 
4e2fdd79
 #endif