Add missing srv.c and srv.h files
[asterisk/asterisk.git] / srv.c
1 /*
2  * ENUM Support for Asterisk
3  *
4  * Copyright (C) 2003 Digium
5  *
6  * Written by Mark Spencer <markster@digium.com>
7  *
8  * Funding provided by nic.at
9  *
10  * Distributed under the terms of the GNU GPL
11  *
12  */
13
14 #include <string.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/nameser.h>
22 #include <resolv.h>
23 #include <errno.h>
24
25 #include <asterisk/logger.h>
26 #include <asterisk/options.h>
27 #include <asterisk/srv.h>
28 #include <asterisk/channel.h>
29 #include <asterisk/config.h>
30
31 #define MAX_SIZE 4096
32
33 typedef struct {
34         unsigned        id :16;         /* query identification number */
35 #if BYTE_ORDER == BIG_ENDIAN
36                         /* fields in third byte */
37         unsigned        qr: 1;          /* response flag */
38         unsigned        opcode: 4;      /* purpose of message */
39         unsigned        aa: 1;          /* authoritive answer */
40         unsigned        tc: 1;          /* truncated message */
41         unsigned        rd: 1;          /* recursion desired */
42                         /* fields in fourth byte */
43         unsigned        ra: 1;          /* recursion available */
44         unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
45         unsigned        ad: 1;          /* authentic data from named */
46         unsigned        cd: 1;          /* checking disabled by resolver */
47         unsigned        rcode :4;       /* response code */
48 #endif
49 #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
50                         /* fields in third byte */
51         unsigned        rd :1;          /* recursion desired */
52         unsigned        tc :1;          /* truncated message */
53         unsigned        aa :1;          /* authoritive answer */
54         unsigned        opcode :4;      /* purpose of message */
55         unsigned        qr :1;          /* response flag */
56                         /* fields in fourth byte */
57         unsigned        rcode :4;       /* response code */
58         unsigned        cd: 1;          /* checking disabled by resolver */
59         unsigned        ad: 1;          /* authentic data from named */
60         unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
61         unsigned        ra :1;          /* recursion available */
62 #endif
63                         /* remaining bytes */
64         unsigned        qdcount :16;    /* number of question entries */
65         unsigned        ancount :16;    /* number of answer entries */
66         unsigned        nscount :16;    /* number of authority entries */
67         unsigned        arcount :16;    /* number of resource entries */
68 } dns_HEADER;
69
70 struct dn_answer {
71         unsigned short rtype;
72         unsigned short class;
73         unsigned int ttl;
74         unsigned short size;
75 } __attribute__ ((__packed__));
76
77 struct srv {
78         unsigned short priority;
79         unsigned short weight;
80         unsigned short portnum;
81 } __attribute__ ((__packed__));
82
83 static int skip_name(unsigned char *s, int len)
84 {
85         /* Shamelessly take from SER */
86         int x = 0;
87         while(x < len) {
88                 if (!*s) {
89                         s++;
90                         x++;
91                         break;
92                 }
93                 if (((*s) & 0xc0) == 0xc0) {
94                         s += 2;
95                         x += 2;
96                         break;
97                 }
98                 x += *s + 1;
99                 s += *s + 1;
100         }
101         if (x >= len)
102                 return -1;
103         return x;
104 }
105
106 static int parse_ie(unsigned char *data, int maxdatalen, unsigned char *src, int srclen)
107 {
108         int len, olen;
109         len = olen = (int)src[0];
110         src++;
111         srclen--;
112         if (len > srclen) {
113                 ast_log(LOG_WARNING, "Want %d, got %d\n", len, srclen);
114                 return -1;
115         }
116         if (len > maxdatalen)
117                 len = maxdatalen;
118         memcpy(data, src, len);
119         return olen + 1;
120 }
121
122 static int parse_srv(unsigned char *host, int hostlen, int *portno, unsigned char *answer, int len)
123 {
124         int res = 0;
125         struct srv *srv = (struct srv *)answer;
126         char repl[256] = "";
127         char *oanswer = answer;
128         
129         if (len < sizeof(struct srv)) {
130                 printf("Length too short\n");
131                 return -1;
132         }
133         answer += sizeof(struct srv);
134         len -= sizeof(struct srv);
135
136         if ((res = dn_expand(oanswer,answer + len,answer, repl, sizeof(repl) - 1)) < 0) {
137                 ast_log(LOG_WARNING, "Failed to expand hostname\n");
138                 return -1;
139         }
140         if (res && strcmp(repl, ".")) {
141                 if (host) {
142                         strncpy(host, repl, hostlen - 1);
143                         host[hostlen] = '\0';
144                 }
145                 if (portno)
146                         *portno = ntohs(srv->portnum);
147                 res = 1;
148         }
149                 
150 #if 0
151         if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
152                 ast_log(LOG_WARNING, "Failed to get flags\n");
153                 return -1; 
154         } else { answer += res; len -= res; }
155         if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
156                 ast_log(LOG_WARNING, "Failed to get services\n");
157                 return -1; 
158         } else { answer += res; len -= res; }
159         if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0)
160                 return -1; else { answer += res; len -= res; }
161 #if 0
162         printf("Flags: %s\n", flags);
163         printf("Services: %s\n", services);
164         printf("Regexp: %s\n", regexp);
165         printf("Repl: %s\n", repl);
166 #endif
167         if (!strncmp(regexp, "!^.*$!", 6)) {
168                 if (!strncmp(services, "E2U+voice:", 10)) {
169                         if (regexp[strlen(regexp) - 1] == '!')
170                                 regexp[strlen(regexp) - 1] = '\0';
171 #if 0
172                         printf("Technology: %s\n", services + 10);
173                         printf("Destination: %s\n", regexp + 6);
174 #endif
175                         strncpy(dst, regexp + 6, dstsize);
176                         strncpy(tech, services + 10, techsize);
177                 }
178         } else
179                 ast_log(LOG_WARNING, "Non-total substitution not yet supported\n");
180 #endif   
181         return 0;
182 }
183
184 static int parse_answer(unsigned char *host, int hostlen, int *port, char *answer, int len)
185 {
186         /*
187          * This function is influenced by "ser" the SIP router.
188          */
189         int x;
190         int res;
191         dns_HEADER *h;
192         struct dn_answer *ans;
193         host[0] = '\0';
194         *port = 0;
195 #if 0
196         for (x=0;x<len;x++) {
197                 if ((answer[x] < 32) || (answer[x] > 127)) {
198                         if (lastlit)
199                                 printf("\"");
200                         printf(" 0x%02x", answer[x]);
201                         lastlit = 0;
202                 } else {
203                         if (!lastlit) 
204                                 printf(" \"");
205                         printf("%c", answer[x]);
206                         lastlit = 1;
207                 }
208         }
209         printf("\n");
210 #endif  
211         h = (dns_HEADER *)answer;
212         /* Skip over DNS header */
213         answer += sizeof(dns_HEADER);
214         len -= sizeof(dns_HEADER);
215 #if 0
216         printf("Query count: %d\n", ntohs(h->qdcount));
217 #endif
218         for (x=0;x<ntohs(h->qdcount);x++) {
219                 if ((res = skip_name(answer, len)) < 0) {
220                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
221                         return -1;
222                 }
223                 answer += res;
224                 len -= res;
225                 answer += 4;    /* Skip QCODE / QCLASS */
226                 len -= 4;
227                 if (len < 0) {
228                         ast_log(LOG_WARNING, "Strange query size\n");
229                         return -1;
230                 }
231         }
232 #if 0
233         printf("Length remaining: %d, already at %04x\n", len, 0x2a + (answer - oanswer));
234         printf("Answer count: %d\n", ntohs(h->ancount));
235         printf("Looking for %d/%d\n", C_IN, T_SRV);
236         printf("Answer so far: %02x %02x %02x %02x %02x %02x\n", answer[0], answer[1], answer[2], answer[3], answer[4], answer[5]);
237 #endif
238         for (x=0;x<ntohs(h->ancount);x++) {
239                 if ((res = skip_name(answer, len)) < 0) {
240                         ast_log(LOG_WARNING, "Failed to skip name :(\n");
241                         return -1;
242                 }
243                 answer += res;
244                 len -= res;
245                 ans = (struct dn_answer *)answer;
246                 answer += sizeof(struct dn_answer);
247                 len -= sizeof(struct dn_answer);
248                 if (len < 0)
249                         return -1;
250 #if 0
251                 printf("Type: %d (%04x), class: %d (%04x), ttl: %d (%08x), length: %d (%04x)\n", ntohs(ans->rtype), ntohs(ans->rtype), ntohs(ans->class), ntohs(ans->class),
252                         ntohl(ans->ttl), ntohl(ans->ttl), ntohs(ans->size), ntohs(ans->size));
253 #endif                  
254                 len -= ntohs(ans->size);
255                 if (len < 0) {
256                         ast_log(LOG_WARNING, "Length exceeds frame by %d\n", -len);
257                         return -1;
258                 }
259                 if ((ntohs(ans->class) == C_IN) && (ntohs(ans->rtype) == T_SRV)) {
260                         if (parse_srv(host, hostlen, port, answer, ntohs(ans->size)))
261                                 ast_log(LOG_WARNING, "Failed to parse srv :(\n");
262                         if (strlen(host))
263                                 return 0;
264                 }
265                 answer += ntohs(ans->size);
266         }
267         return 0;
268 }
269
270 int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
271 {
272         int res;
273         int ret = -1;
274         struct __res_state srvstate;
275         char answer[MAX_SIZE];
276
277         if (*port)
278                 *port = 0;
279         res_ninit(&srvstate);   
280         if (chan && ast_autoservice_start(chan) < 0)
281                 return -1;
282         res = res_nsearch(&srvstate, service, C_IN, T_SRV, answer, sizeof(answer));
283         if (res > 0) {
284                 if ((res = parse_answer(host, hostlen, port, answer, res))) {
285                         ast_log(LOG_WARNING, "Parse error returned %d\n", res);
286                         ret = 0;
287                 } else {
288                         ast_log(LOG_DEBUG, "Found host '%s', port '%d'\n", host, *port);
289                         ret = 1;
290                 }
291         } else {
292                 ast_log(LOG_DEBUG, "No such service found: %s (%s)\n", service, strerror(errno));
293                 ret = 0;
294         }
295         if (chan)
296                 ret |= ast_autoservice_stop(chan);
297         res_nclose(&srvstate);
298         return ret;
299 }