GPLization banner introduced to *.[hc] files
[sip-router] / resolve.c
1 /* $Id$*/
2 /*
3  *
4  * Copyright (C) 2001-2003 Fhg Fokus
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License 
24  * along with this program; if not, write to the Free Software 
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27
28
29 #include <sys/types.h>
30 #include <netinet/in.h>
31 #include <arpa/nameser.h>
32 #include <resolv.h>
33 #include <string.h>
34
35 #include "resolve.h"
36 #include "dprint.h"
37 #include "mem/mem.h"
38 #include "ip_addr.h"
39
40
41
42 /* mallocs for local stuff */
43 #define local_malloc pkg_malloc
44 #define local_free   pkg_free
45
46
47
48  /*  skips over a domain name in a dns message
49  *  (it can be  a sequence of labels ending in \0, a pointer or
50  *   a sequence of labels ending in a pointer -- see rfc1035
51  *   returns pointer after the domain name or null on error*/
52 unsigned char* dns_skipname(unsigned char* p, unsigned char* end)
53 {
54         while(p<end){
55                 /* check if \0 (root label length) */
56                 if (*p==0){
57                         p+=1;
58                         break;
59                 }
60                 /* check if we found a pointer */
61                 if (((*p)&0xc0)==0xc0){
62                         /* if pointer skip over it (2 bytes) & we found the end */
63                         p+=2;
64                         break;
65                 }
66                 /* normal label */
67                 p+=*p+1;
68         }
69         return (p>end)?0:p;
70 }
71
72
73
74 /* parses the srv record into a srv_rdata structure
75  *   msg   - pointer to the dns message
76  *   end   - pointer to the end of the message
77  *   rdata - pointer  to the rdata part of the srv answer
78  * returns 0 on error, or a dyn. alloc'ed srv_rdata structure */
79 /* SRV rdata format:
80  *            111111
81  *  0123456789012345
82  * +----------------+
83  * |     priority   |
84  * |----------------|
85  * |     weight     |
86  * |----------------|
87  * |   port number  |
88  * |----------------|
89  * |                |
90  * ~      name      ~
91  * |                |
92  * +----------------+
93  */
94 struct srv_rdata* dns_srv_parser( unsigned char* msg, unsigned char* end,
95                                                                   unsigned char* rdata)
96 {
97         struct srv_rdata* srv;
98         int len;
99         
100         srv=0;
101         if ((rdata+6)>end) goto error;
102         srv=(struct srv_rdata*)local_malloc(sizeof(struct srv_rdata));
103         if (srv==0){
104                 LOG(L_ERR, "ERROR: dns_srv_parser: out of memory\n");
105                 goto error;
106         }
107         
108         memcpy((void*)&srv->priority, rdata, 2);
109         memcpy((void*)&srv->weight,   rdata+2, 2);
110         memcpy((void*)&srv->port,     rdata+4, 2);
111         rdata+=6;
112         srv->priority=ntohs(srv->priority);
113         srv->weight=ntohs(srv->weight);
114         srv->port=ntohs(srv->port);
115         if ((len=dn_expand(msg, end, rdata, srv->name, MAX_DNS_NAME-1))==-1)
116                 goto error;
117         /* add terminating 0 ? (warning: len=compressed name len) */
118         return srv;
119 error:
120         if (srv) local_free(srv);
121         return 0;
122 }
123
124
125
126 /* parses a CNAME record into a cname_rdata structure */
127 struct cname_rdata* dns_cname_parser( unsigned char* msg, unsigned char* end,
128                                                                           unsigned char* rdata)
129 {
130         struct cname_rdata* cname;
131         int len;
132         
133         cname=0;
134         cname=(struct cname_rdata*)local_malloc(sizeof(struct cname_rdata));
135         if(cname==0){
136                 LOG(L_ERR, "ERROR: dns_cname_parser: out of memory\n");
137                 goto error;
138         }
139         if ((len=dn_expand(msg, end, rdata, cname->name, MAX_DNS_NAME-1))==-1)
140                 goto error;
141         return cname;
142 error:
143         if (cname) local_free(cname);
144         return 0;
145 }
146
147
148
149 /* parses an A record rdata into an a_rdata structure
150  * returns 0 on error or a dyn. alloc'ed a_rdata struct
151  */
152 struct a_rdata* dns_a_parser(unsigned char* rdata, unsigned char* end)
153 {
154         struct a_rdata* a;
155         
156         if (rdata+4>end) goto error;
157         a=(struct a_rdata*)local_malloc(sizeof(struct a_rdata));
158         if (a==0){
159                 LOG(L_ERR, "ERROR: dns_a_parser: out of memory\n");
160                 goto error;
161         }
162         memcpy(a->ip, rdata, 4);
163         return a;
164 error:
165         return 0;
166 }
167
168
169
170 /* parses an AAAA (ipv6) record rdata into an aaaa_rdata structure
171  * returns 0 on error or a dyn. alloc'ed aaaa_rdata struct */
172 struct aaaa_rdata* dns_aaaa_parser(unsigned char* rdata, unsigned char* end)
173 {
174         struct aaaa_rdata* aaaa;
175         
176         if (rdata+16>end) goto error;
177         aaaa=(struct aaaa_rdata*)local_malloc(sizeof(struct aaaa_rdata));
178         if (aaaa==0){
179                 LOG(L_ERR, "ERROR: dns_aaaa_parser: out of memory\n");
180                 goto error;
181         }
182         memcpy(aaaa->ip6, rdata, 16);
183         return aaaa;
184 error:
185         return 0;
186 }
187
188
189
190 /* frees completely a struct rdata list */
191 void free_rdata_list(struct rdata* head)
192 {
193         struct rdata* l;
194         for(l=head; l; l=l->next){
195                 /* free the parsed rdata*/
196                 if (l->rdata) local_free(l->rdata);
197                 local_free(l);
198         }
199 }
200
201
202
203 /* gets the DNS records for name:type
204  * returns a dyn. alloc'ed struct rdata linked list with the parsed responses
205  * or 0 on error
206  * see rfc1035 for the query/response format */
207 struct rdata* get_record(char* name, int type)
208 {
209         int size;
210         int qno, answers_no;
211         int r;
212         int ans_len;
213         static union dns_query buff;
214         unsigned char* p;
215         unsigned char* t;
216         unsigned char* end;
217         static unsigned char answer[ANS_SIZE];
218         unsigned short rtype, class, rdlength;
219         unsigned int ttl;
220         struct rdata* head;
221         struct rdata** crt;
222         struct rdata** last;
223         struct rdata* rd;
224         struct srv_rdata* srv_rd;
225         struct srv_rdata* crt_srv;
226         
227         
228         
229         size=res_search(name, C_IN, type, buff.buff, sizeof(buff));
230         if (size<0) {
231                 DBG("get_record: lookup(%s, %d) failed\n", name, type);
232                 goto not_found;
233         }
234         else if (size > sizeof(buff)) size=sizeof(buff);
235         head=rd=0;
236         last=crt=&head;
237         
238         p=buff.buff+DNS_HDR_SIZE;
239         end=buff.buff+size;
240         if (p>end) goto error_boundary;
241         qno=ntohs((unsigned short)buff.hdr.qdcount);
242
243         for (r=0; r<qno; r++){
244                 /* skip the name of the question */
245                 if ((p=dns_skipname(p, end))==0) {
246                         LOG(L_ERR, "ERROR: get_record: skipname==0\n");
247                         goto error;
248                 }
249                 p+=2+2; /* skip QCODE & QCLASS */
250         #if 0
251                 for (;(p<end && (*p)); p++);
252                 p+=1+2+2; /* skip the ending  '\0, QCODE and QCLASS */
253         #endif
254                 if (p>end) {
255                         LOG(L_ERR, "ERROR: get_record: p>end\n");
256                         goto error;
257                 }
258         };
259         answers_no=ntohs((unsigned short)buff.hdr.ancount);
260         ans_len=ANS_SIZE;
261         t=answer;
262         for (r=0; (r<answers_no) && (p<end); r++){
263                 /*  ignore it the default domain name */
264                 if ((p=dns_skipname(p, end))==0) {
265                         LOG(L_ERR, "ERROR: get_record: skip_name=0 (#2)\n");
266                         goto error;
267                 }
268                 /*
269                 skip=dn_expand(buff.buff, end, p, t, ans_len);
270                 p+=skip;
271                 */
272                 /* check if enough space is left fot type, class, ttl & size */
273                 if ((p+2+2+4+2)>end) goto error_boundary;
274                 /* get type */
275                 memcpy((void*) &rtype, (void*)p, 2);
276                 rtype=ntohs(rtype);
277                 p+=2;
278                 /* get  class */
279                 memcpy((void*) &class, (void*)p, 2);
280                 class=ntohs(class);
281                 p+=2;
282                 /* get ttl*/
283                 memcpy((void*) &ttl, (void*)p, 4);
284                 ttl=ntohl(ttl);
285                 p+=4;
286                 /* get size */
287                 memcpy((void*)&rdlength, (void*)p, 2);
288                 rdlength=ntohs(rdlength);
289                 p+=2;
290                 /* check for type */
291                 /*
292                 if (rtype!=type){
293                         LOG(L_ERR, "WARNING: get_record: wrong type in answer (%d!=%d)\n",
294                                         rtype, type);
295                         p+=rdlength;
296                         continue;
297                 }
298                 */
299                 /* expand the "type" record  (rdata)*/
300                 
301                 rd=(struct rdata*) local_malloc(sizeof(struct rdata));
302                 if (rd==0){
303                         LOG(L_ERR, "ERROR: get_record: out of memory\n");
304                         goto error;
305                 }
306                 rd->type=rtype;
307                 rd->class=class;
308                 rd->ttl=ttl;
309                 rd->next=0;
310                 switch(rtype){
311                         case T_SRV:
312                                 srv_rd= dns_srv_parser(buff.buff, end, p);
313                                 rd->rdata=(void*)srv_rd;
314                                 if (srv_rd==0) goto error_parse;
315                                 
316                                 /* insert sorted into the list */
317                                 for (crt=&head; *crt; crt= &((*crt)->next)){
318                                         crt_srv=(struct srv_rdata*)(*crt)->rdata;
319                                         if ((srv_rd->priority <  crt_srv->priority) ||
320                                            ( (srv_rd->priority == crt_srv->priority) && 
321                                                          (srv_rd->weight > crt_srv->weight) ) ){
322                                                 /* insert here */
323                                                 goto skip;
324                                         }
325                                 }
326                                 last=&(rd->next); /*end of for => this will be the last elem*/
327                         skip:
328                                 /* insert here */
329                                 rd->next=*crt;
330                                 *crt=rd;
331                                 
332                                 break;
333                         case T_A:
334                                 rd->rdata=(void*) dns_a_parser(p,end);
335                                 if (rd->rdata==0) goto error_parse;
336                                 *last=rd; /* last points to the last "next" or the list head*/
337                                 last=&(rd->next);
338                                 break;
339                         case T_AAAA:
340                                 rd->rdata=(void*) dns_aaaa_parser(p,end);
341                                 if (rd->rdata==0) goto error_parse;
342                                 *last=rd;
343                                 last=&(rd->next);
344                                 break;
345                         case T_CNAME:
346                                 rd->rdata=(void*) dns_cname_parser(buff.buff, end, p);
347                                 if(rd->rdata==0) goto error_parse;
348                                 *last=rd;
349                                 last=&(rd->next);
350                                 break;
351                         default:
352                                 LOG(L_ERR, "WARNING: get_record: unknown type %d\n", rtype);
353                                 rd->rdata=0;
354                                 *last=rd;
355                                 last=&(rd->next);
356                 }
357                 
358                 p+=rdlength;
359                 
360         }
361         return head;
362 error_boundary:
363                 LOG(L_ERR, "ERROR: get_record: end of query buff reached\n");
364                 return 0;
365 error_parse:
366                 LOG(L_ERR, "ERROR: get_record: rdata parse error \n");
367                 if (rd) local_free(rd); /* rd->rdata=0 & rd is not linked yet into
368                                                                    the list */
369 error:
370                 LOG(L_ERR, "ERROR: get_record \n");
371                 if (head) free_rdata_list(head);
372 not_found:
373         return 0;
374 }
375
376
377
378 /* resolves a host name trying SRV lookup if *port==0 or normal A/AAAA lookup
379  * if *port!=0.
380  * returns: hostent struct & *port filled with the port from the SRV record;
381  *  0 on error
382  */
383 struct hostent* sip_resolvehost(char* name, unsigned short* port)
384 {
385         struct hostent* he;
386         struct rdata* head;
387         struct rdata* l;
388         struct srv_rdata* srv;
389         struct ip_addr* ip;
390         static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups */
391         int len;
392
393         /* try SRV if no port specified (draft-ietf-sip-srv-06) */
394         if ((port)&&(*port==0)){
395                 *port=SIP_PORT; /* just in case we don't find another */
396                 len=strlen(name);
397                 if ((len+SRV_PREFIX_LEN+1)>MAX_DNS_NAME){
398                         LOG(L_WARN, "WARNING: sip_resolvehost: domain name too long (%d),"
399                                                 " unable to perform SRV lookup\n", len);
400                 }else{
401                         /* check if it's an ip address */
402                         if ( ((ip=str2ip(name, len))!=0)
403 #ifdef  USE_IPV6
404                                   || ((ip=str2ip6(name, len))!=0)
405 #endif
406                                 ){
407                                 /* we are lucky, this is an ip address */
408                                 return ip_addr2he(name,len,ip);
409                         }
410                         
411                         memcpy(tmp, SRV_PREFIX, SRV_PREFIX_LEN);
412                         memcpy(tmp+SRV_PREFIX_LEN, name, len+1); /*include the ending 0*/
413                         
414                         head=get_record(tmp, T_SRV);
415                         for(l=head; l; l=l->next){
416                                 if (l->type!=T_SRV) continue; /*should never happen*/
417                                 srv=(struct srv_rdata*) l->rdata;
418                                 if (srv==0){
419                                         LOG(L_CRIT, "sip_resolvehost: BUG: null rdata\n");
420                                         free_rdata_list(head);
421                                         break;
422                                 }
423                                 he=resolvehost(srv->name);
424                                 if (he!=0){
425                                         /* we found it*/
426                                         DBG("sip_resolvehost: SRV(%s) = %s:%d\n",
427                                                         tmp, srv->name, srv->port);
428                                         *port=srv->port;
429                                         free_rdata_list(head); /*clean up*/
430                                         return he;
431                                 }
432                         }
433                         DBG("sip_resolvehost: not SRV record found for %s," 
434                                         " trying 'normal' lookup...\n", name);
435                 }
436         }
437         he=resolvehost(name);
438         return he;
439 }
440
441