/* * DNS Support for Asterisk * * Written by Thorsten Lockert * * Funding provided by Troll Phone Networks AS * * This program is free software, distributed under the terms of * the GNU General Public License */ #include #include #include #include #include #include #include #include #include #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"); return -1; } if (res > 0) return 1; } } answer += ntohs(ans->size); len -= ntohs(ans->size); } return 0; } #if defined(res_ninit) #define HAS_RES_NINIT #else AST_MUTEX_DEFINE_STATIC(res_lock); #if 0 #warning "Warning, res_ninit is missing... Could have reentrancy issues" #endif #endif 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 HAS_RES_NINIT struct __res_state dnsstate; #endif char answer[MAX_SIZE]; int res, ret = -1; #ifdef HAS_RES_NINIT res_ninit(&dnsstate); res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer)); #else ast_mutex_lock(&res_lock); 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 HAS_RES_NINIT res_nclose(&dnsstate); #else #ifndef __APPLE__ res_close(); #endif ast_mutex_unlock(&res_lock); #endif return ret; }