浏览代码

- added SRV capable resolver (see get_record)
- minimal (read quick hack) SRV support

Andrei Pelinescu-Onciul 23 年之前
父节点
当前提交
1b71270a03
共有 10 个文件被更改,包括 580 次插入13 次删除
  1. 2 2
      Makefile.defs
  2. 1 1
      action.c
  3. 4 1
      config.h
  4. 2 1
      forward.c
  5. 42 3
      proxy.c
  6. 306 0
      resolve.c
  7. 49 3
      resolve.h
  8. 150 0
      test/dns_query.c
  9. 23 0
      test/inv_srv.sip
  10. 1 2
      test/stateless.cfg

+ 2 - 2
Makefile.defs

@@ -8,7 +8,7 @@
 VERSION = 0
 PATCHLEVEL = 8
 SUBLEVEL = 7
-EXTRAVERSION = -6-ipv6
+EXTRAVERSION = -7-srv
 
 RELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 OS = $(shell uname -s)
@@ -373,7 +373,7 @@ YACC=bison
 YACC_FLAGS=-d -b cfg
 # on linux and freebsd keep it empty (e.g. LIBS= )
 # on solaris add -lxnet (e.g. LIBS= -lxnet)
-LIBS=-lfl -ldl
+LIBS=-lfl -ldl -lresolv
 
 
 #os specific stuff

+ 1 - 1
action.c

@@ -91,7 +91,7 @@ int do_action(struct action* a, struct sip_msg* msg)
 											ret=E_BAD_URI;
 											goto error_fwd_uri;
 										}
-									}else port=SIP_PORT;
+									}else port=0;
 									break;
 					case NUMBER_ST:
 									port=a->p2.number;

+ 4 - 1
config.h

@@ -54,9 +54,12 @@
 #define CRLF "\r\n"
 #define CRLF_LEN 2
 
-#define RECEIVED ";received="
+#define RECEIVED   ";received="
 #define RECEIVED_LEN 10
 
+#define SRV_PREFIX "_sip._udp."
+#define SRV_PREFIX_LEN 10
+
 /*used only if PKG_MALLOC is defined*/
 #define PKG_MEM_POOL_SIZE 1024*1024
 

+ 2 - 1
forward.c

@@ -161,7 +161,8 @@ int update_sock_struct_from_via( union sockaddr_union* to,
 		if (via->host.s[via->host.len]){
 			host_copy=pkg_malloc( via->host.len+1 );
 			if (!host_copy) {
-				LOG(L_NOTICE, "ERROR: update_sock_struct_from_via: not enough memory\n");
+				LOG(L_NOTICE, "ERROR: update_sock_struct_from_via:"
+								" not enough memory\n");
 				return -1;
 			}
 			memcpy(host_copy, via->host.s, via->host.len );

+ 42 - 3
proxy.c

@@ -6,6 +6,7 @@
  */
 
 
+#include "config.h"
 #include "proxy.h"
 #include "error.h"
 #include "dprint.h"
@@ -158,15 +159,21 @@ error:
 
 
 
-/* same as add_proxy, but it doesn't add the proxy to the list*/
+/* same as add_proxy, but it doesn't add the proxy to the list
+ * uses also SRV if possible (quick hack) */
+
 struct proxy_l* mk_proxy(char* name, unsigned short port)
 {
 	struct proxy_l* p;
 	struct hostent* he;
+	struct rdata* head;
+	struct rdata* l;
+	struct srv_rdata* srv;
+	static char tmp[MAX_DNS_NAME]; /* tmp buff. for SRV lookups*/
+	int len;
 #ifdef DNS_IP_HACK
 	int err;
 	unsigned int ip;
-	int len;
 #endif
 
 	p=(struct proxy_l*) malloc(sizeof(struct proxy_l));
@@ -177,7 +184,7 @@ struct proxy_l* mk_proxy(char* name, unsigned short port)
 	}
 	memset(p,0,sizeof(struct proxy_l));
 	p->name=name;
-	p->port=port;
+	p->port=port?port:SIP_PORT;
 #ifdef DNS_IP_HACK
 	/* fast ipv4 string to address conversion*/
 	len=strlen(name);
@@ -219,6 +226,37 @@ struct proxy_l* mk_proxy(char* name, unsigned short port)
 #endif
 	/* fail over to normal lookup */
 
+	/* try SRV if no port specified (draft-ietf-sip-srv-06) */
+	if (port==0){
+		len=strlen(name);
+		if ((len+SRV_PREFIX_LEN+1)>MAX_DNS_NAME){
+			LOG(L_WARN, "WARNING: domain name too long (%d), unable"
+					" to perform SRV lookup\n", len);
+		}else{
+			memcpy(tmp, SRV_PREFIX, SRV_PREFIX_LEN);
+			memcpy(tmp+SRV_PREFIX_LEN, name, len+1); /*include the ending 0*/
+			
+			head=get_record(tmp, T_SRV);
+			for(l=head; l; l=l->next){
+				if (l->type!=T_SRV) continue; /*should never happen*/
+				srv=(struct srv_rdata*) l->rdata;
+				if (srv==0){
+					LOG(L_CRIT, "mk_proxy: BUG: null rdata\n");
+					free_rdata_list(head);
+					break;
+				}
+				he=resolvehost(srv->name);
+				if (he!=0){
+					DBG("mk_proxy: SRV(%s) = %s:%d\n",
+							tmp, srv->name, srv->port);
+					p->port=srv->port;
+					free_rdata_list(head); /*clean up*/
+					goto copy_he;
+				}
+			}
+			DBG(" not SRV record found for %s\n", name);
+		}
+	}
 	he=resolvehost(name);
 	if (he==0){
 		ser_error=E_BAD_ADDRESS;
@@ -227,6 +265,7 @@ struct proxy_l* mk_proxy(char* name, unsigned short port)
 		free(p);
 		goto error;
 	}
+copy_he:
 	if (hostent_cpy(&(p->host), he)!=0){
 		free(p);
 		goto error;

+ 306 - 0
resolve.c

@@ -0,0 +1,306 @@
+/* $Id$*/
+
+/* #include <arpa/nameser.h> -- included from resolve.h*/
+#include <netinet/in.h>
+#include <resolv.h>
+#include <string.h>
+
+#include "resolve.h"
+#include "dprint.h"
+#include "mem/mem.h"
+
+
+
+/* mallocs for local stuff */
+#define local_malloc pkg_malloc
+#define local_free   pkg_free
+
+
+
+ /*  skips over a domain name in a dns message
+ *  (it can be  a sequence of labels ending in \0, a pointer or
+ *   a sequence of labels ending in a pointer -- see rfc1035
+ *   returns pointer after the domain name or null on error*/
+unsigned char* dns_skipname(unsigned char* p, unsigned char* end)
+{
+	while(p<end){
+		/* check if \0 (root label length) */
+		if (*p==0){
+			p+=1;
+			break;
+		}
+		/* check if we found a pointer */
+		if (((*p)&0xc0)==0xc0){
+			/* if pointer skip over it (2 bytes) & we found the end */
+			p+=2;
+			break;
+		}
+		/* normal label */
+		p+=*p+1;
+	}
+	return (p>end)?0:p;
+}
+
+
+
+/* parses the srv record into a srv_rdata structure
+ *   msg   - pointer to the dns message
+ *   end   - pointer to the end of the message
+ *   rdata - pointer  to the rdata part of the srv answer
+ * returns 0 on error, or a dyn. alloc'ed srv_rdata structure */
+/* SRV rdata format:
+ *            111111
+ *  0123456789012345
+ * +----------------+
+ * |     priority   |
+ * |----------------|
+ * |     weight     |
+ * |----------------|
+ * |   port number  |
+ * |----------------|
+ * |                |
+ * ~      name      ~
+ * |                |
+ * +----------------+
+ */
+struct srv_rdata* dns_srv_parser( unsigned char* msg, unsigned char* end,
+								  unsigned char* rdata)
+{
+	struct srv_rdata* srv;
+	int len;
+	
+	srv=0;
+	if ((rdata+6)>end) goto error;
+	srv=(struct srv_rdata*)local_malloc(sizeof(struct srv_rdata));
+	if (srv==0){
+		LOG(L_ERR, "ERROR: dns_srv_parser: outof memory\n");
+		goto error;
+	}
+	
+	memcpy((void*)&srv->priority, rdata, 2);
+	memcpy((void*)&srv->weight,   rdata+2, 2);
+	memcpy((void*)&srv->port,     rdata+4, 2);
+	rdata+=6;
+	srv->priority=ntohs(srv->priority);
+	srv->weight=ntohs(srv->weight);
+	srv->port=ntohs(srv->port);
+	if ((len=dn_expand(msg, end, rdata, srv->name, MAX_DNS_NAME))==-1)
+		goto error;
+	/* add terminatng 0 ? */
+	return srv;
+error:
+	if (srv) local_free(srv);
+	return 0;
+}
+
+
+
+/* parses an A record rdata into an a_rdata structure
+ * returns 0 on error or a dyn. alloc'ed a_rdata struct
+ */
+struct a_rdata* dns_a_parser(unsigned char* rdata, unsigned char* end)
+{
+	struct a_rdata* a;
+	
+	if (rdata+4>end) goto error;
+	a=(struct a_rdata*)local_malloc(sizeof(struct a_rdata));
+	if (a==0){
+		LOG(L_ERR, "ERROR: dns_a_parser: out of memory\n");
+		goto error;
+	}
+	memcpy(a->ip, rdata, 4);
+	return a;
+error:
+	return 0;
+}
+
+
+
+/* parses an AAAA (ipv6) record rdata into an aaaa_rdata structure
+ * returns 0 on error or a dyn. alloc'ed aaaa_rdata struct */
+struct aaaa_rdata* dns_aaaa_parser(unsigned char* rdata, unsigned char* end)
+{
+	struct aaaa_rdata* aaaa;
+	
+	if (rdata+16>end) goto error;
+	aaaa=(struct aaaa_rdata*)local_malloc(sizeof(struct aaaa_rdata));
+	if (aaaa==0){
+		LOG(L_ERR, "ERROR: dns_aaaa_parser: out of memory\n");
+		goto error;
+	}
+	memcpy(aaaa->ip6, rdata, 16);
+	return aaaa;
+error:
+	return 0;
+}
+
+
+
+/* frees completely a struct rdata list */
+void free_rdata_list(struct rdata* head)
+{
+	struct rdata* l;
+	for(l=head; l; l=l->next){
+		/* free the parsed rdata*/
+		if (l->rdata) local_free(l->rdata);
+		local_free(l);
+	}
+}
+
+
+
+/* gets the DNS records for name:type
+ * returns a dyn. alloc'ed struct rdata linked list with the parsed responses
+ * or 0 on error
+ * see rfc1035 for the query/response format */
+struct rdata* get_record(char* name, int type)
+{
+	int size;
+	int qno, answers_no;
+	int r,i;
+	int ans_len;
+	static union dns_query buff;
+	unsigned char* p;
+	unsigned char* t;
+	unsigned char* end;
+	static unsigned char answer[ANS_SIZE];
+	unsigned short rtype, class, rdlength;
+	unsigned int ttl;
+	struct rdata* head;
+	struct rdata** crt;
+	struct rdata* rd;
+	struct srv_rdata* srv_rd;
+	struct srv_rdata* crt_srv;
+	
+	
+	
+	head=rd=0;
+	crt=&head;
+	size=res_search(name, C_IN, type, buff.buff, sizeof(buff));
+	if (size<0) goto error;
+	else if (size > sizeof(buff)) size=sizeof(buff);
+	
+	p=buff.buff+DNS_HDR_SIZE;
+	end=buff.buff+size;
+	if (p>end) goto error_boundary;
+	qno=ntohs((unsigned short)buff.hdr.qdcount);
+
+	for (r=0; r<qno; r++){
+		/* skip the name of the question */
+		if ((p=dns_skipname(p, end))==0) goto error;
+		p+=2+2; /* skip QCODE & QCLASS */
+	#if 0
+		for (;(p<end && (*p)); p++);
+		p+=1+2+2; /* skip the ending  '\0, QCODE and QCLASS */
+	#endif
+		if (p>end) goto error;
+	};
+	answers_no=ntohs((unsigned short)buff.hdr.ancount);
+	ans_len=ANS_SIZE;
+	t=answer;
+	for (r=0; (r<answers_no) && (p<end); r++){
+		/*  ignore it the default domain name */
+		if ((p=dns_skipname(p, end))==0) goto error;
+		/*
+		skip=dn_expand(buff.buff, end, p, t, ans_len);
+		p+=skip;
+		*/
+		/* check if enough space is left fot type, class, ttl & size */
+		if ((p+2+2+4+2)>end) goto error_boundary;
+		/* get type */
+		memcpy((void*) &rtype, (void*)p, 2);
+		rtype=ntohs(rtype);
+		p+=2;
+		/* get  class */
+		memcpy((void*) &class, (void*)p, 2);
+		class=ntohs(class);
+		p+=2;
+		/* get ttl*/
+		memcpy((void*) &ttl, (void*)p, 4);
+		ttl=ntohl(ttl);
+		p+=4;
+		/* get size */
+		memcpy((void*)&rdlength, (void*)p, 2);
+		rdlength=ntohs(rdlength);
+		p+=2;
+		/* check for type */
+		if (rtype!=type){
+			LOG(L_WARN, "WARNING: get_record: wrong type in answer\n");
+			p+=rdlength;
+			continue;
+		}
+		/* expand the "type" record  (rdata)*/
+		/* print it */
+		/*
+		printf("\ntype=%d class= %d, ttl= %d, rdlength= %d\n",
+				rtype, class, ttl, rdlength);
+		for (i=0;i<rdlength;i++){
+			printf("%x ", *(p+i));
+		}
+		printf("\n");
+		*/
+		rd=(struct rdata*) local_malloc(sizeof(struct rdata));
+		if (rd==0){
+			LOG(L_ERR, "ERROR: get_record: out of memory\n");
+			goto error;
+		}
+		rd->type=rtype;
+		rd->class=class;
+		rd->ttl=ttl;
+		switch(type){
+			case T_SRV:
+				srv_rd= dns_srv_parser(buff.buff, end, p);
+				rd->rdata=(void*)srv_rd;
+				if (srv_rd==0) goto error_parse;
+				
+				/* insert sorted into the list
+				 * crt reused */
+				for (crt=&head; *crt; crt= &((*crt)->next)){
+					crt_srv=(struct srv_rdata*)(*crt)->rdata;
+					if ((srv_rd->priority <  crt_srv->priority) ||
+					   ( (srv_rd->priority == crt_srv->priority) && 
+							 (srv_rd->weight > crt_srv->weight) ) ){
+						/* insert here */
+						break;
+					}
+				}
+				/* insert here */
+				rd->next=*crt;
+				*crt=rd;
+				
+				break;
+			case T_A:
+				rd->rdata=(void*) dns_a_parser(p,end);
+				if (rd->rdata==0) goto error_parse;
+				*crt=rd; /* crt points to the last "next" or the list head*/
+				crt=&(rd->next);
+				break;
+			case T_AAAA:
+				rd->rdata=(void*) dns_aaaa_parser(p,end);
+				if (rd->rdata==0) goto error_parse;
+				*crt=rd;
+				crt=&(rd->next);
+				break;
+			default:
+				LOG(L_ERR, "BUG: get_record: unknown type %d\n", type);
+				rd->rdata=0;
+				goto error;
+		}
+		
+		p+=rdlength;
+		
+	}
+	return head;
+error_boundary:
+		LOG(L_ERR, "ERROR: get_record: end of query buff reached\n");
+		return 0;
+error_parse:
+		LOG(L_ERR, "ERROR: get_record: rdata parse error \n");
+		if (rd) local_free(rd); /* rd->rdata=0 & rd is not linked yet into
+								   the list */
+error:
+		LOG(L_ERR, "ERROR: get_record \n");
+		if (head) free_rdata_list(head);
+	return 0;
+}
+

+ 49 - 3
resolve.h

@@ -9,14 +9,60 @@
 #define resolve_h
 
 #include <netdb.h>
+#include <arpa/nameser.h>
 
-#include "ip_addr.h"
 
+#define MAX_QUERY_SIZE 8192
+#define ANS_SIZE       8192
+#define DNS_HDR_SIZE     12
+#define MAX_DNS_NAME 256
 
-/* gethostbyname wrappers
- * use this, someday htey will use a local cache */
 
 
+/* query union*/
+union dns_query{
+	HEADER hdr;
+	unsigned char buff[MAX_QUERY_SIZE];
+};
+
+
+/* rdata struct*/
+struct rdata {
+	unsigned short type;
+	unsigned short class;
+	unsigned int   ttl;
+	void* rdata;
+	struct rdata* next;
+};
+
+
+/* srv rec. struct*/
+struct srv_rdata {
+	unsigned short priority;
+	unsigned short weight;
+	unsigned short port;
+	unsigned int name_len;
+	char name[MAX_DNS_NAME];
+};
+
+
+/* A rec. struct */
+struct a_rdata {
+	unsigned char ip[4];
+};
+
+struct aaaa_rdata {
+	unsigned char ip6[16];
+};
+
+
+
+struct rdata* get_record(char* name, int type);
+void free_rdata_list(struct rdata* head);
+
+
+/* gethostbyname wrappers
+ * use this, someday htey will use a local cache */
 
 static inline struct hostent* resolvehost(const char* name)
 {

+ 150 - 0
test/dns_query.c

@@ -0,0 +1,150 @@
+/*
+ * $Id$
+ *
+ * tests for ../resolver.c
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../resolve.h"
+
+/* symbols needed by dprint */
+int log_stderr=1;
+int debug=0;
+int pids[1];
+int process_no=0;
+
+
+static char* id="$Id$";
+static char* version="dns_query 0.1";
+static char* help_msg="\
+Usage: dns_query  [-t type] [-hV] -n host\n\
+Options:\n\
+    -n host       host name\n\
+    -t type       query type (default A)\n\
+    -V            version number\n\
+    -h            this help message\n\
+";
+
+
+int main(int argc, char** argv)
+{
+	char c;
+	char* name;
+	char* type_str;
+	int type;
+	int r;
+	struct rdata* head;
+	struct rdata* l;
+	struct srv_rdata* srv;
+	struct a_rdata* ip;
+
+	name=type_str=0;
+	
+	opterr=0;
+	while ((c=getopt(argc, argv, "n:t:hV"))!=-1){
+		switch(c){
+			case 'n':
+				name=optarg;
+				break;
+			case 't':
+				type_str=optarg;
+				break;
+			case 'V':
+				printf("version: %s\n", version);
+				printf("%s\n", id);
+				exit(0);
+				break;
+			case 'h':
+				printf("version: %s\n", version);
+				printf("%s", help_msg);
+				exit(0);
+				break;
+			case '?':
+				if (isprint(optopt))
+					fprintf(stderr, "Unknown option `-%c´\n", optopt);
+				else
+					fprintf(stderr, "Unknown character `\\x%x´\n", optopt);
+				goto error;
+			case ':':
+				fprintf(stderr, "Option `-%c´ requires an argument.\n",
+						optopt);
+				goto error;
+				break;
+			default:
+				abort();
+		}
+	}
+	
+	if (name==0){
+		fprintf(stderr, "Missing domain name (-n name)\n");
+		goto error;
+	}
+	type=T_A;
+	if (type_str){
+		if (strcasecmp(type_str, "A")==0) type=T_A;
+		else if (strcasecmp(type_str, "NS")==0) type=T_NS;
+		else if (strcasecmp(type_str, "MD")==0) type=T_MD;
+		else if (strcasecmp(type_str, "MF")==0) type=T_MF;
+		else if (strcasecmp(type_str, "CNAME")==0) type=T_CNAME;
+		else if (strcasecmp(type_str, "SOA")==0) type=T_SOA;
+		else if (strcasecmp(type_str, "PTR")==0) type=T_PTR;
+		else if (strcasecmp(type_str, "HINFO")==0) type=T_HINFO;
+		else if (strcasecmp(type_str, "MINFO")==0) type=T_MINFO;
+		else if (strcasecmp(type_str, "MX")==0) type=T_MX;
+		else if (strcasecmp(type_str, "TXT")==0) type=T_TXT;
+		else if (strcasecmp(type_str, "AAAA")==0) type=T_AAAA;
+		else if (strcasecmp(type_str, "SRV")==0) type=T_SRV;
+		else if (strcasecmp(type_str, "NAPTR")==0) type=T_NAPTR;
+		else if (strcasecmp(type_str, "AXFR")==0) type=T_AXFR;
+		else{
+			fprintf(stderr, "Unknown query type %s\n", type_str);
+			goto error;
+		}
+	}
+	printf("calling get_record...\n");
+	head=get_record(name, type);
+	if (head==0) printf("no answer\n");
+	else{
+		printf("records:\n");
+		for(l=head; l; l=l->next){
+			switch(l->type){
+				case T_SRV:
+					srv=(struct srv_rdata*)l->rdata;
+					printf("SRV  type= %d class=%d  ttl=%d\n",
+							l->type, l->class, l->ttl);
+					printf("     prio= %d weight=%d port=%d\n",
+								srv->priority, srv->weight, srv->port);
+					printf("     name= [%s]\n", srv->name);
+					break;
+				case T_A:
+					ip=(struct a_rdata*)l->rdata;
+					printf("A    type= %d class=%d  ttl=%d\n",
+								l->type, l->class, l->ttl);
+					printf("     ip= %d.%d.%d.%d\n",
+								ip->ip[0], ip->ip[1], ip->ip[2], ip->ip[3]);
+					break;
+				case T_AAAA:
+					printf("AAAA  type= %d class=%d  ttl=%d\n",
+							l->type, l->class, l->ttl);
+					printf("      ip6= ");
+					for(r=0;r<16;r++) 
+						printf("%x ", ((struct aaaa_rdata*)l->rdata)->ip6[r]);
+					printf("\n");
+					break;
+				default:
+					printf("UNKN  type= %d class=%d  ttl=%d\n",
+								l->type, l->class, l->ttl);
+					printf("       rdata=%p\n", l->rdata);
+			}
+		}
+	}
+	printf("done\n");
+	exit(0);
+error:
+	exit(-1);
+}

+ 23 - 0
test/inv_srv.sip

@@ -0,0 +1,23 @@
+INVITE sip:[email protected] SIP/2.0
+Via: SIP/2.0/UDP dorian.fokus.gmd.de 
+From: "GMD FOKUS iptlab" <sip:[email protected]>;tag=b96b0300ed30f1286-2f5d
+Call-ID: [email protected]
+CSeq: 101 INVITE
+Expires: 180
+Max-Forwards:  0
+User-Agent: Cisco-SIP-IP-Phone/2
+Accept: application/sdp
+Contact: sip:[email protected]:5060
+Content-Type: application/sdp
+Content-Length: 225
+
+
+v=0
+o=CiscoSystemsSIP-IPPhone-UserAgent 14474 8233 IN IP4 195.37.78.190
+s=SIP Call
+c=IN IP4 195.37.78.190
+t=0 0
+m=audio 18456 RTP/AVP 0 8 18 101
+a=rtpmap:0 pcmu/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-11 

+ 1 - 2
test/stateless.cfg

@@ -8,7 +8,7 @@
 debug=9          # debug level (cmd line: -dddddddddd)
 #fork=yes          # (cmd. line: -D)
 fork=yes
-#fork=no
+fork=no
 log_stderror=yes # (cmd line: -E)
 #log_stderror=no	# (cmd line: -E)
 
@@ -26,7 +26,6 @@ loop_checks=0
 # for more info: sip_router -h
 
 #modules
-loadmodule "modules/tm/tm.so"
 
 
 route{