diff --git a/dns.c b/dns.c new file mode 100755 index 0000000000000000000000000000000000000000..74a3f320d7e8f24a066da7db86866e2a055f0a50 --- /dev/null +++ b/dns.c @@ -0,0 +1,187 @@ +/* + * DNS Support for Asterisk + * + * Written by Thorsten Lockert <tholo@trollphone.org> + * + * Funding provided by Troll Phone Networks AS + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <unistd.h> + +#include <asterisk/logger.h> +#include <asterisk/channel.h> +#include <asterisk/dns.h> + +#define MAX_SIZE 4096 + +typedef struct { + unsigned id :16; /* query identification number */ +#if BYTE_ORDER == BIG_ENDIAN + /* fields in third byte */ + unsigned qr: 1; /* response flag */ + unsigned opcode: 4; /* purpose of message */ + unsigned aa: 1; /* authoritive answer */ + unsigned tc: 1; /* truncated message */ + unsigned rd: 1; /* recursion desired */ + /* fields in fourth byte */ + unsigned ra: 1; /* recursion available */ + unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ + unsigned ad: 1; /* authentic data from named */ + unsigned cd: 1; /* checking disabled by resolver */ + unsigned rcode :4; /* response code */ +#endif +#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN + /* fields in third byte */ + unsigned rd :1; /* recursion desired */ + unsigned tc :1; /* truncated message */ + unsigned aa :1; /* authoritive answer */ + unsigned opcode :4; /* purpose of message */ + unsigned qr :1; /* response flag */ + /* fields in fourth byte */ + unsigned rcode :4; /* response code */ + unsigned cd: 1; /* checking disabled by resolver */ + unsigned ad: 1; /* authentic data from named */ + unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ + unsigned ra :1; /* recursion available */ +#endif + /* remaining bytes */ + unsigned qdcount :16; /* number of question entries */ + unsigned ancount :16; /* number of answer entries */ + unsigned nscount :16; /* number of authority entries */ + unsigned arcount :16; /* number of resource entries */ +} dns_HEADER; + +struct dn_answer { + unsigned short rtype; + unsigned short class; + unsigned int ttl; + unsigned short size; +} __attribute__ ((__packed__)); + +static int skip_name(u_char *s, int len) +{ + int x = 0; + + while (x < len) { + if (*s == '\0') { + s++; + x++; + break; + } + if ((*s & 0xc0) == 0xc0) { + s += 2; + x += 2; + break; + } + x += *s + 1; + s += *s + 1; + } + if (x >= len) + return -1; + return x; +} + +static int dns_parse_answer(void *context, + int class, int type, u_char *answer, int len, + int (*callback)(void *context, u_char *answer, int len, u_char *fullanswer)) +{ + u_char *fullanswer = answer; + struct dn_answer *ans; + dns_HEADER *h; + int res; + int x; + + h = (dns_HEADER *)answer; + answer += sizeof(dns_HEADER); + len -= sizeof(dns_HEADER); + + for (x = 0; x < ntohs(h->qdcount); x++) { + if ((res = skip_name(answer, len)) < 0) { + ast_log(LOG_WARNING, "Couldn't skip over name\n"); + return -1; + } + answer += res + 4; /* Skip name and QCODE / QCLASS */ + len -= res + 4; + if (len < 0) { + ast_log(LOG_WARNING, "Strange query size\n"); + return -1; + } + } + + for (x = 0; x < ntohs(h->ancount); x++) { + if ((res = skip_name(answer, len)) < 0) { + ast_log(LOG_WARNING, "Failed skipping name\n"); + return -1; + } + answer += res; + len -= res; + ans = (struct dn_answer *)answer; + answer += sizeof(struct dn_answer); + len -= sizeof(struct dn_answer); + if (len < 0) { + ast_log(LOG_WARNING, "Strange result size\n"); + return -1; + } + if (len < 0) { + ast_log(LOG_WARNING, "Length exceeds frame\n"); + return -1; + } + + if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) { + if (callback) { + if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) + ast_log(LOG_WARNING, "Failed to parse result\n"); + if (res > 0) + return 1; + } + } + answer += ntohs(ans->size); + len -= ntohs(ans->size); + } + return 0; +} + +int ast_search_dns(void *context, + const char *dname, int class, int type, + int (*callback)(void *context, u_char *answer, int len, u_char *fullanswer)) +{ +#ifdef __Linux__ + struct __res_state dnsstate; +#endif + char answer[MAX_SIZE]; + int res, ret = -1; + +#ifdef __Linux__ + res_ninit(&dnsstate); + res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer)); +#else + res_init(); + res = res_search(dname, class, type, answer, sizeof(answer)); +#endif + if (res > 0) { + if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) { + ast_log(LOG_WARNING, "Parse error\n"); + ret = -1; + } + else if (ret == 0) { + ast_log(LOG_DEBUG, "No matches found\n"); + ret = 0; + } + else + ret = 1; + } +#ifdef __Linux__ + res_nclose(&srvstate); +#else + res_close(); +#endif + return ret; +} diff --git a/include/asterisk/dns.h b/include/asterisk/dns.h new file mode 100755 index 0000000000000000000000000000000000000000..a0a64e30f6e6fff9479a8ed7746c7a45161ed0fb --- /dev/null +++ b/include/asterisk/dns.h @@ -0,0 +1,20 @@ +/* + * DNS support + * + * Written by Thorsten Lockert <tholo@trollphone.org> + * + * Funding provided by Troll Phone Networks AS + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#ifndef _ASTERISK_DNS_H +#define _ASTERISK_DNS_H + +struct ast_channel; + +extern int ast_search_dns(void *context, const char *dname, int class, int type, + int (*callback)(void *context, u_char *answer, int len, u_char *fullanswer)); + +#endif /* _ASTERISK_DNS_H */