static int check_host(char *address, size_t len, char *host, int dflag, int uflag, int Eflag) { union { HEADER hdr; u_char buf[PACKETSZ]; } answer; int status; if ((dflag && memchr(address, '@', len)) || (uflag && memchr(address, '!', len))) return(0); if ((_res.options & RES_INIT) == 0) res_init(); status = res_search(host, C_IN, T_AAAA, answer.buf, sizeof(answer.buf)); if (status == -1) status = res_search(host, C_IN, T_A, answer.buf, sizeof(answer.buf)); if (status == -1 && Eflag) status = res_search(host, C_IN, T_MX, answer.buf, sizeof(answer.buf)); return(status == -1); }
/** * @brief Send DNS SRV request using search domain list */ std::string BaseDns::querySrvRrSearch() { std::cerr << "Sending DNS query for SRV record for _ndn._udp" << std::endl; QueryAnswer queryAnswer; res_init(); _res.retrans = 1; _res.retry = 2; _res.ndots = 10; int answerSize = res_search("_ndn._udp", ns_c_in, ns_t_srv, queryAnswer.buf, sizeof(queryAnswer)); if (answerSize == 0) { throw Error("No DNS SRV records found for _ndn._udp"); } return parseSrvRr(queryAnswer, answerSize); }
int php_checkdnsrr(char *host, char *typestr) { #ifdef PCC_MINGW return -1; #else unsigned char answer[MAXPACKET]; int type = T_MX; if (strcasecmp(typestr, "MX") == 0) type = T_MX; else if (strcasecmp(typestr, "A") == 0) type = T_A; else if (strcasecmp(typestr, "NS") == 0) type = T_NS; else if (strcasecmp(typestr, "PTR") == 0) type = T_PTR; else if (strcasecmp(typestr, "ANY") == 0) type = T_ANY; else if (strcasecmp(typestr, "SOA") == 0) type = T_SOA; else if (strcasecmp(typestr, "CNAME") == 0) type = T_CNAME; else return -1; return res_search(host, C_IN, type, answer, MAXPACKET); #endif /* PCC_MINGW */ }
static int check_host(char *address, char *host, int dflag, int uflag, int Eflag) { u_char answer[PACKETSZ]; int status; if ((dflag && strchr(address, '@')) || (uflag && strchr(address, '!'))) return(0); if ((_res.options & RES_INIT) == 0) res_init(); status = res_search(host, C_IN, T_AAAA, answer, sizeof(answer)); if (status == -1) status = res_search(host, C_IN, T_A, answer, sizeof(answer)); if ((status == -1) && Eflag) status = res_search(host, C_IN, T_MX, answer, sizeof(answer)); return(status == -1); }
void* fn(void* arg) { char* dn = (char*)arg; unsigned char buff[8000]; if(-1 == res_search(dn, 1, 1, buff, 8000)) { printf("Error: res_search()\n"); } else { printf("Success!\n"); } return 0; }
int php_getmxrr(char *hostname, char *mx_list, char *weight_list) { #ifdef PCC_MINGW return 0; #else char *mx_list_ptr = (char *)(mx_list + sprintf(mx_list, "")); char *weight_list_ptr = (char *)(weight_list + sprintf(weight_list, "")); unsigned char answer[MAXPACKET]; unsigned char expand_buffer[MAXHOSTNAMELEN]; int ans_len = res_search(hostname, C_IN, T_MX, answer, sizeof(answer)); HEADER *header_ptr = (HEADER *)&answer; unsigned char *body_ptr = (unsigned char *)&answer + NS_HFIXEDSZ; unsigned char *eom_ptr = (unsigned char *)&answer + sizeof(answer); int n, ancount, qdcount, type, weight; for (qdcount = ntohs((unsigned short)header_ptr->qdcount); qdcount--; body_ptr += (n + NS_QFIXEDSZ)) if ((n = dn_skipname(body_ptr, eom_ptr)) < 0) return -1; ancount = ntohs((unsigned short)header_ptr->ancount); while (--ancount >= 0 && body_ptr < eom_ptr) { if ((n = dn_skipname(body_ptr, eom_ptr)) < 0) return -1; body_ptr += n; NS_GET16(type, body_ptr); body_ptr += (NS_INT16SZ + NS_INT32SZ); NS_GET16(n, body_ptr); if (type != T_MX) { body_ptr += n; continue; } NS_GET16(weight, body_ptr); if ((n = dn_expand(answer, eom_ptr, body_ptr, expand_buffer, sizeof(expand_buffer) - 1)) < 0) return -1; body_ptr += n; mx_list_ptr += sprintf(mx_list_ptr - 1, " %s ", expand_buffer); weight_list_ptr += sprintf(weight_list_ptr - 1, " %d ", weight); } return 0; #endif /* PCC_MINGW */ }
int main(int argc, char** argv) { pthread_t pid; if(2 != argc) { printf("Usage: %s <domain>\n", argv[0]); return 1; } _res.options |= RES_DEBUG; if(0 != res_init()) { printf("Error: res_init()\n"); return(1); } #if 1 if(0 != pthread_create(&pid, 0, fn, (void*)argv[1])) { printf("Failed to create thread.\n"); return 1; } pthread_join(pid, 0); #else { unsigned char buff[8000]; if(-1 == res_search(argv[1], 1, 1, buff, 8000)) { printf("Error: res_search()\n"); } else { printf("Success!\n"); } } #endif return 0; }
/* 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 flags) { int size; int skip; int qno, answers_no; int i, r; static union dns_query buff; unsigned char* p; unsigned char* end; unsigned char* rd_end; static char rec_name[MAX_DNS_NAME]; /* placeholder for the record name */ int rec_name_len; unsigned short rtype, class, rdlength; unsigned int ttl; struct rdata* head; struct rdata** crt; struct rdata** last; struct rdata* rd; struct srv_rdata* srv_rd; struct srv_rdata* crt_srv; int search_list_used; int name_len; struct rdata* fullname_rd; char c; #ifdef USE_DNSSEC val_status_t val_status; #endif name_len=strlen(name); for (i = 0; i < name_len; i++) { c = name[i]; if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || (name[i] == '.') || (name[i] == '-') || (name[i] == '_')) continue; LM_DBG("'%s' is not domain name\n", name); return 0; } if (cfg_get(core, core_cfg, dns_search_list)==0) { search_list_used=0; name_len=0; } else { search_list_used=1; } fullname_rd=0; #ifndef USE_DNSSEC size=res_search(name, C_IN, type, buff.buff, sizeof(buff)); #else size=val_res_query((val_context_t *) NULL, (char *) name, (int) C_IN, (int) type, (unsigned char *) buff.buff, (int) sizeof(buff), &val_status); if(!val_istrusted(val_status)){ LOG(L_INFO, "INFO: got not trusted record when resolving %s\n",name); } #endif if (unlikely(size<0)) { DBG("get_record: lookup(%s, %d) failed\n", name, type); goto not_found; } else if (unlikely(size > sizeof(buff))) size=sizeof(buff); head=rd=0; last=crt=&head; p=buff.buff+DNS_HDR_SIZE; end=buff.buff+size; if (unlikely(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 (unlikely((p=dns_skipname(p, end))==0)) { LOG(L_ERR, "ERROR: get_record: skipname==0\n"); 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 (unlikely(p>end)) { LOG(L_ERR, "ERROR: get_record: p>=end\n"); goto error; } }; answers_no=ntohs((unsigned short)buff.hdr.ancount); again: for (r=0; (r<answers_no) && (p<end); r++){ #if 0 /* ignore it the default domain name */ if ((p=dns_skipname(p, end))==0) { LOG(L_ERR, "ERROR: get_record: skip_name=0 (#2)\n"); goto error; } #else if (unlikely((skip=dn_expand(buff.buff, end, p, rec_name, MAX_DNS_NAME-1))==-1)){ LOG(L_ERR, "ERROR: get_record: dn_expand(rec_name) failed\n"); goto error; } #endif p+=skip; rec_name_len=strlen(rec_name); if (unlikely(rec_name_len>255)){ LOG(L_ERR, "ERROR: get_record: dn_expand(rec_name): name too" " long (%d)\n", rec_name_len); goto error; } /* check if enough space is left for type, class, ttl & size */ if (unlikely((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; rd_end=p+rdlength; if (unlikely((rd_end)>end)) goto error_boundary; if ((flags & RES_ONLY_TYPE) && (rtype!=type)){ /* skip */ p=rd_end; continue; } /* expand the "type" record (rdata)*/ rd=(struct rdata*) local_malloc(sizeof(struct rdata)+rec_name_len+ 1-1); if (rd==0){ LOG(L_ERR, "ERROR: get_record: out of memory\n"); goto error; } rd->type=rtype; rd->pclass=class; rd->ttl=ttl; rd->next=0; memcpy(rd->name, rec_name, rec_name_len); rd->name[rec_name_len]=0; rd->name_len=rec_name_len; /* check if full name matches */ if ((search_list_used==1)&&(fullname_rd==0)&& (rec_name_len>=name_len)&& (strncasecmp(rec_name, name, name_len)==0)) { /* now we have record whose name is the same (up-to the * name_len with the searched one): * if the length is the same - we found full match, no fake * cname needed, just clear the flag * if the length of the name differs - it has matched using * search list remember the rd, so we can create fake CNAME * record when all answers are used and no better match found */ if (rec_name_len==name_len) search_list_used=0; /* this is safe.... here was rec_name_len > name_len */ else if (rec_name[name_len]=='.') { #ifdef HAVE_RESOLV_RES if ((cfg_get(core, core_cfg, dns_search_fmatch)==0) || (match_search_list(&_res, rec_name+name_len+1)!=0)) #endif fullname_rd=rd; } } switch(rtype){ case T_SRV: srv_rd= dns_srv_parser(buff.buff, end, rd_end, p); rd->rdata=(void*)srv_rd; if (unlikely(srv_rd==0)) goto error_parse; /* insert sorted into the list */ for (crt=&head; *crt; crt= &((*crt)->next)){ if ((*crt)->type!=T_SRV) continue; 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 */ goto skip; } } last=&(rd->next); /*end of for => this will be the last element*/ skip: /* insert here */ rd->next=*crt; *crt=rd; break; case T_A: rd->rdata=(void*) dns_a_parser(p, rd_end); if (unlikely(rd->rdata==0)) goto error_parse; *last=rd; /* last points to the last "next" or the list head*/ last=&(rd->next); break; case T_AAAA: rd->rdata=(void*) dns_aaaa_parser(p, rd_end); if (unlikely(rd->rdata==0)) goto error_parse; *last=rd; last=&(rd->next); break; case T_CNAME: rd->rdata=(void*) dns_cname_parser(buff.buff, end, p); if(unlikely(rd->rdata==0)) goto error_parse; *last=rd; last=&(rd->next); break; case T_NAPTR: rd->rdata=(void*)dns_naptr_parser(buff.buff, end, rd_end, p); if(unlikely(rd->rdata==0)) goto error_parse; *last=rd; last=&(rd->next); break; case T_TXT: rd->rdata= dns_txt_parser(buff.buff, rd_end, p); if (rd->rdata==0) goto error_parse; *last=rd; last=&(rd->next); break; case T_EBL: rd->rdata= dns_ebl_parser(buff.buff, end, rd_end, p); if (rd->rdata==0) goto error_parse; *last=rd; last=&(rd->next); break; case T_PTR: rd->rdata=(void*) dns_ptr_parser(buff.buff, end, p); if(unlikely(rd->rdata==0)) goto error_parse; *last=rd; last=&(rd->next); break; default: LOG(L_ERR, "WARNING: get_record: unknown type %d\n", rtype); rd->rdata=0; *last=rd; last=&(rd->next); } p+=rdlength; } if (flags & RES_AR){ flags&=~RES_AR; answers_no=ntohs((unsigned short)buff.hdr.nscount); #ifdef RESOLVE_DBG DBG("get_record: skipping %d NS (p=%p, end=%p)\n", answers_no, p, end); #endif for (r=0; (r<answers_no) && (p<end); r++){ /* skip over the ns records */ if ((p=dns_skipname(p, end))==0) { LOG(L_ERR, "ERROR: get_record: skip_name=0 (#3)\n"); goto error; } /* check if enough space is left for type, class, ttl & size */ if (unlikely((p+2+2+4+2)>end)) goto error_boundary; memcpy((void*)&rdlength, (void*)p+2+2+4, 2); p+=2+2+4+2+ntohs(rdlength); } answers_no=ntohs((unsigned short)buff.hdr.arcount); #ifdef RESOLVE_DBG DBG("get_record: parsing %d ARs (p=%p, end=%p)\n", answers_no, p, end); #endif goto again; /* add also the additional records */ } /* if the name was expanded using DNS search list * create fake CNAME record to convert the short name * (queried) to long name (answered) */ if ((search_list_used==1)&&(fullname_rd!=0)) { rd=(struct rdata*) local_malloc(sizeof(struct rdata)+name_len+1-1); if (unlikely(rd==0)){ LOG(L_ERR, "ERROR: get_record: out of memory\n"); goto error; } rd->type=T_CNAME; rd->pclass=fullname_rd->pclass; rd->ttl=fullname_rd->ttl; rd->next=head; memcpy(rd->name, name, name_len); rd->name[name_len]=0; rd->name_len=name_len; /* alloc sizeof struct + space for the null terminated name */ rd->rdata=(void*)local_malloc(sizeof(struct cname_rdata)-1+ head->name_len+1); if(unlikely(rd->rdata==0)){ LOG(L_ERR, "ERROR: get_record: out of memory\n"); goto error_rd; } ((struct cname_rdata*)(rd->rdata))->name_len=fullname_rd->name_len; memcpy(((struct cname_rdata*)(rd->rdata))->name, fullname_rd->name, fullname_rd->name_len); ((struct cname_rdata*)(rd->rdata))->name[head->name_len]=0; head=rd; } return head; error_boundary: LOG(L_ERR, "ERROR: get_record: end of query buff reached\n"); if (head) free_rdata_list(head); return 0; error_parse: LOG(L_ERR, "ERROR: get_record: rdata parse error (%s, %d), %p-%p" " rtype=%d, class=%d, ttl=%d, rdlength=%d \n", name, type, p, end, rtype, class, ttl, rdlength); error_rd: 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); not_found: /* increment error counter */ counter_inc(dns_cnts_h.failed_dns_req); return 0; }
int dns_get_mx_list(const char *host, int port, struct mx_hostentry **he, int no_mx) { char outname[MAXDNAME]; ns_msg msg; ns_rr rr; const char *searchhost; const unsigned char *cp; unsigned char *ans; struct mx_hostentry *hosts = NULL; size_t nhosts = 0; size_t anssz; int pref; int cname_recurse; int err; int i; res_init(); searchhost = host; cname_recurse = 0; anssz = 65536; ans = malloc(anssz); if (ans == NULL) return (1); if (no_mx) goto out; repeat: err = res_search(searchhost, ns_c_in, ns_t_mx, ans, anssz); if (err < 0) { switch (h_errno) { case NO_DATA: /* * Host exists, but no MX (or CNAME) entry. * Not an error, use host name instead. */ goto out; case TRY_AGAIN: /* transient error */ goto transerr; case NO_RECOVERY: case HOST_NOT_FOUND: default: errno = ENOENT; goto err; } } if (!ns_initparse(ans, anssz, &msg)) goto transerr; switch (ns_msg_getflag(msg, ns_f_rcode)) { case ns_r_noerror: break; case ns_r_nxdomain: goto err; default: goto transerr; } for (i = 0; i < ns_msg_count(msg, ns_s_an); i++) { if (ns_parserr(&msg, ns_s_an, i, &rr)) goto transerr; cp = ns_rr_rdata(rr); switch (ns_rr_type(rr)) { case ns_t_mx: pref = ns_get16(cp); cp += 2; err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), cp, outname, sizeof(outname)); if (err < 0) goto transerr; add_host(pref, outname, port, &hosts, &nhosts); break; case ns_t_cname: err = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), cp, outname, sizeof(outname)); if (err < 0) goto transerr; /* Prevent a CNAME loop */ if (cname_recurse++ > 10) goto err; searchhost = outname; goto repeat; default: break; } } out: err = 0; if (0) { transerr: if (nhosts == 0) err = 1; } if (0) { err: err = -1; } free(ans); if (!err) { /* * If we didn't find any MX, use the hostname instead. */ if (nhosts == 0) add_host(0, searchhost, port, &hosts, &nhosts); qsort(hosts, nhosts, sizeof(*hosts), sort_pref); } if (nhosts > 0) { /* terminate list */ *hosts[nhosts].host = 0; } else { if (hosts != NULL) free(hosts); hosts = NULL; } *he = hosts; return (err); free(ans); if (hosts != NULL) free(hosts); return (err); }
int dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type) { #ifndef STAND_ALONE int rc = -1; const uschar *save_domain; #endif res_state resp = os_get_dns_resolver_res(); tree_node *previous; uschar node_name[290]; /* DNS lookup failures of any kind are cached in a tree. This is mainly so that a timeout on one domain doesn't happen time and time again for messages that have many addresses in the same domain. We rely on the resolver and name server caching for successful lookups. */ sprintf(CS node_name, "%.255s-%s-%lx", name, dns_text_type(type), resp->options); previous = tree_search(tree_dns_fails, node_name); if (previous != NULL) { DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: using cached value %s\n", name, dns_text_type(type), (previous->data.val == DNS_NOMATCH)? "DNS_NOMATCH" : (previous->data.val == DNS_NODATA)? "DNS_NODATA" : (previous->data.val == DNS_AGAIN)? "DNS_AGAIN" : (previous->data.val == DNS_FAIL)? "DNS_FAIL" : "??"); return previous->data.val; } #ifdef EXPERIMENTAL_INTERNATIONAL /* Convert all names to a-label form before doing lookup */ { uschar * alabel; uschar * errstr = NULL; DEBUG(D_dns) if (string_is_utf8(name)) debug_printf("convert utf8 '%s' to alabel for for lookup\n", name); if ((alabel = string_domain_utf8_to_alabel(name, &errstr)), errstr) { DEBUG(D_dns) debug_printf("DNS name '%s' utf8 conversion to alabel failed: %s\n", name, errstr); host_find_failed_syntax = TRUE; return DNS_NOMATCH; } name = alabel; } #endif /* If configured, check the hygene of the name passed to lookup. Otherwise, although DNS lookups may give REFUSED at the lower level, some resolvers turn this into TRY_AGAIN, which is silly. Give a NOMATCH return, since such domains cannot be in the DNS. The check is now done by a regular expression; give it space for substring storage to save it having to get its own if the regex has substrings that are used - the default uses a conditional. This test is omitted for PTR records. These occur only in calls from the dnsdb lookup, which constructs the names itself, so they should be OK. Besides, bitstring labels don't conform to normal name syntax. (But the aren't used any more.) For SRV records, we omit the initial _smtp._tcp. components at the start. */ #ifndef STAND_ALONE /* Omit this for stand-alone tests */ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) { const uschar *checkname = name; int ovector[3*(EXPAND_MAXN+1)]; dns_pattern_init(); /* For an SRV lookup, skip over the first two components (the service and protocol names, which both start with an underscore). */ if (type == T_SRV || type == T_TLSA) { while (*checkname++ != '.'); while (*checkname++ != '.'); } if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname), 0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0) { DEBUG(D_dns) debug_printf("DNS name syntax check failed: %s (%s)\n", name, dns_text_type(type)); host_find_failed_syntax = TRUE; return DNS_NOMATCH; } } #endif /* STAND_ALONE */ /* Call the resolver; for an overlong response, res_search() will return the number of bytes the message would need, so we need to check for this case. The effect is to truncate overlong data. On some systems, res_search() will recognize "A-for-A" queries and return the IP address instead of returning -1 with h_error=HOST_NOT_FOUND. Some nameservers are also believed to do this. It is, of course, contrary to the specification of the DNS, so we lock it out. */ if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0) return DNS_NOMATCH; /* If we are running in the test harness, instead of calling the normal resolver (res_search), we call fakens_search(), which recognizes certain special domains, and interfaces to a fake nameserver for certain special zones. */ dnsa->answerlen = running_in_test_harness ? fakens_search(name, type, dnsa->answer, MAXPACKET) : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET); if (dnsa->answerlen > MAXPACKET) { DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet (size %d), truncating to %d.\n", name, dns_text_type(type), dnsa->answerlen, MAXPACKET); dnsa->answerlen = MAXPACKET; } if (dnsa->answerlen < 0) switch (h_errno) { case HOST_NOT_FOUND: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave HOST_NOT_FOUND\n" "returning DNS_NOMATCH\n", name, dns_text_type(type)); return dns_return(name, type, DNS_NOMATCH); case TRY_AGAIN: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n", name, dns_text_type(type)); /* Cut this out for various test programs */ #ifndef STAND_ALONE save_domain = deliver_domain; deliver_domain = string_copy(name); /* set $domain */ rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save_domain; if (rc != OK) { DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); return dns_return(name, type, DNS_AGAIN); } DEBUG(D_dns) debug_printf("%s is in dns_again_means_nonexist: returning " "DNS_NOMATCH\n", name); return dns_return(name, type, DNS_NOMATCH); #else /* For stand-alone tests */ return dns_return(name, type, DNS_AGAIN); #endif case NO_RECOVERY: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n" "returning DNS_FAIL\n", name, dns_text_type(type)); return dns_return(name, type, DNS_FAIL); case NO_DATA: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_DATA\n" "returning DNS_NODATA\n", name, dns_text_type(type)); return dns_return(name, type, DNS_NODATA); default: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave unknown DNS error %d\n" "returning DNS_FAIL\n", name, dns_text_type(type), h_errno); return dns_return(name, type, DNS_FAIL); } DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) succeeded\n", name, dns_text_type(type)); return DNS_SUCCEED; }
/* * krb5int_dns_init() * * Initialize an opaque handle. Do name lookup and initial parsing of * reply, skipping question section. Prepare to iterate over answer * section. Returns -1 on error, 0 on success. */ int krb5int_dns_init(struct krb5int_dns_state **dsp, char *host, int nclass, int ntype) { #if USE_RES_NINIT struct __res_state statbuf; #endif struct krb5int_dns_state *ds; int len, ret; size_t nextincr, maxincr; unsigned char *p; *dsp = ds = malloc(sizeof(*ds)); if (ds == NULL) return -1; ret = -1; ds->nclass = nclass; ds->ntype = ntype; ds->ansp = NULL; ds->anslen = 0; ds->ansmax = 0; nextincr = 2048; maxincr = INT_MAX; #if HAVE_NS_INITPARSE ds->cur_ans = 0; #endif #if USE_RES_NINIT memset(&statbuf, 0, sizeof(statbuf)); ret = res_ninit(&statbuf); #else ret = res_init(); #endif if (ret < 0) return -1; do { p = (ds->ansp == NULL) ? malloc(nextincr) : realloc(ds->ansp, nextincr); if (p == NULL) { ret = -1; goto errout; } ds->ansp = p; ds->ansmax = nextincr; #if USE_RES_NINIT len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype, ds->ansp, ds->ansmax); #else len = res_search(host, ds->nclass, ds->ntype, ds->ansp, ds->ansmax); #endif if ((size_t) len > maxincr) { ret = -1; goto errout; } while (nextincr < (size_t) len) nextincr *= 2; if (len < 0 || nextincr > maxincr) { ret = -1; goto errout; } } while (len > ds->ansmax); ds->anslen = len; #if HAVE_NS_INITPARSE ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg); #else ret = initparse(ds); #endif if (ret < 0) goto errout; ret = 0; errout: #if USE_RES_NINIT res_ndestroy(&statbuf); #endif if (ret < 0) { if (ds->ansp != NULL) { free(ds->ansp); ds->ansp = NULL; } } return ret; }
static int dns_lookup(nsp_state *N, obj_t *tobj, const char *domain) { #define __FN__ __FILE__ ":dns_lookup()" #ifdef WIN32 DNS_FREE_TYPE freetype; DNS_STATUS status; PDNS_RECORD pDnsRecord; PDNS_RECORD pDnsCur; obj_t *stobj; IN_ADDR ipaddr; char namebuf[8]; register int i; freetype = DnsFreeRecordListDeep; /* status=DnsQuery_A(domain, DNS_TYPE_ANY, DNS_QUERY_STANDARD, NULL, &pDnsRecord, NULL); */ status = DnsQuery_A(domain, DNS_TYPE_ANY, DNS_QUERY_BYPASS_CACHE, NULL, &pDnsRecord, NULL); if (status) { if (N->debug) n_warn(N, __FN__, "res_search failed"); return -1; } /* Loop through the answer buffer and extract records. */ i = 0; pDnsCur = pDnsRecord; while (pDnsCur != NULL) { stobj = nsp_settable(N, tobj, n_ntoa(N, namebuf, i, 10, 0)); switch (pDnsCur->wType) { case DNS_TYPE_A: { ipaddr.S_un.S_addr = (pDnsCur->Data.A.IpAddress); nsp_setstr(N, stobj, "type", "A", -1); nsp_setstr(N, stobj, "host", pDnsCur->pName, -1); nsp_setstr(N, stobj, "addr", inet_ntoa(ipaddr), -1); if (N->debug) n_warn(N, __FN__, "A [%s]", inet_ntoa(ipaddr)); break; } case DNS_TYPE_NS: { nsp_setstr(N, stobj, "type", "NS", -1); /* is PTR the right struct? */ nsp_setstr(N, stobj, "host", pDnsCur->Data.PTR.pNameHost, -1); if (N->debug) n_warn(N, __FN__, "NS [%s]", pDnsCur->Data.PTR.pNameHost); break; } case DNS_TYPE_SOA: { nsp_setstr(N, stobj, "type", "SOA", -1); nsp_setstr(N, stobj, "host", pDnsCur->Data.SOA.pNamePrimaryServer, -1); nsp_setstr(N, stobj, "mail", pDnsCur->Data.SOA.pNameAdministrator, -1); nsp_setnum(N, stobj, "serial", pDnsCur->Data.SOA.dwSerialNo); nsp_setnum(N, stobj, "refresh", pDnsCur->Data.SOA.dwRefresh); nsp_setnum(N, stobj, "retry", pDnsCur->Data.SOA.dwRetry); nsp_setnum(N, stobj, "expire", pDnsCur->Data.SOA.dwExpire); nsp_setnum(N, stobj, "minttl", pDnsCur->Data.SOA.dwDefaultTtl); break; } case DNS_TYPE_PTR: { nsp_setstr(N, stobj, "type", "PTR", -1); nsp_setstr(N, stobj, "host", pDnsCur->Data.PTR.pNameHost, -1); if (N->debug) n_warn(N, __FN__, "PTR [%s]", pDnsCur->Data.PTR.pNameHost); break; } case DNS_TYPE_MX: { nsp_setstr(N, stobj, "type", "MX", -1); nsp_setnum(N, stobj, "pref", pDnsCur->Data.MX.wPreference); nsp_setstr(N, stobj, "host", pDnsCur->Data.MX.pNameExchange, -1); if (N->debug) n_warn(N, __FN__, "MX [%s]", pDnsCur->Data.MX.pNameExchange); break; } default: { /* skip data we're too dumb to parse */ nsp_setnum(N, stobj, "type", pDnsCur->wType); /* if (N->debug) */ n_warn(N, __FN__, "xx[%d]", pDnsCur->wType); break; } } i++; pDnsCur = pDnsCur->pNext; } DnsRecordListFree(pDnsRecord, freetype); return 0; #else char hostbuf[HOSTBUF + 1]; char abuf[20]; char namebuf[8]; querybuf answer; HEADER *hp; int ancount, qdcount; uchar *msg, *eom, *cp; //int type, class, ttl, dlen; int type, dlen; unsigned short pref; register int i; register int n; uchar *e; obj_t *stobj; /* Query the nameserver to retrieve mx records for the given domain. */ n = res_search(domain, C_ANY, T_ANY, (u_char *)&answer, sizeof(answer)); if (n < 0) { if (N->debug) n_warn(N, __FN__, "res_search failed"); return -1; } if (n < HFIXEDSZ) return -1; /* avoid problems after truncation in tcp packets */ if (n > (int)sizeof(answer)) n = (int)sizeof(answer); /* Valid answer received. Skip the query record. */ hp = (HEADER *)&answer; qdcount = ntohs((u_short)hp->qdcount); ancount = ntohs((u_short)hp->ancount); msg = (u_char *)&answer; eom = (u_char *)&answer + n; cp = (u_char *)&answer + HFIXEDSZ; while (qdcount-->0 && cp < eom) { n = dn_skipname(cp, eom); if (n < 0) return -1; cp += n; cp += QFIXEDSZ; } /* Loop through the answer buffer and extract records. */ i = 0; memset(hostbuf, 0, sizeof(hostbuf)); while (ancount-->0 && cp < eom) { stobj = nsp_settable(N, tobj, n_ntoa(N, namebuf, i, 10, 0)); if ((n = dn_expand(msg, eom, cp, hostbuf, HOSTBUF)) < 0) break; if (N->debug) n_warn(N, __FN__, "?[%s]", hostbuf); cp += n; GETSHORT(type, cp); // GETSHORT(class, cp); // GETLONG(ttl, cp); GETSHORT(dlen, cp); e = cp + dlen; switch (type) { case T_A: { snprintf(abuf, sizeof(abuf), "%d.%d.%d.%d", (int)cp[0], (int)cp[1], (int)cp[2], (int)cp[3]); nsp_setstr(N, stobj, "type", "A", -1); nsp_setstr(N, stobj, "host", hostbuf, -1); nsp_setstr(N, stobj, "addr", abuf, -1); if (N->debug) n_warn(N, __FN__, "A [%s]", abuf); cp += dlen; break; } case T_NS: { if ((n = dn_expand(msg, eom, cp, hostbuf, HOSTBUF)) < 0) return -1; cp += n; n = strlen(hostbuf); nsp_setstr(N, stobj, "type", "NS", -1); nsp_setstr(N, stobj, "host", hostbuf, n); if (N->debug) n_warn(N, __FN__, "NS [%s]", hostbuf); break; } case T_SOA: { unsigned int n; nsp_setstr(N, stobj, "type", "SOA", -1); if ((n = dn_expand(msg, eom, cp, hostbuf, HOSTBUF)) < 0) return -1; cp += n; nsp_setstr(N, stobj, "host", hostbuf, -1); if ((n = dn_expand(msg, eom, cp, hostbuf, HOSTBUF)) < 0) return -1; cp += n; nsp_setstr(N, stobj, "mail", hostbuf, -1); GETLONG(n, cp); nsp_setnum(N, stobj, "serial", n); GETLONG(n, cp); nsp_setnum(N, stobj, "refresh", n); GETLONG(n, cp); nsp_setnum(N, stobj, "retry", n); GETLONG(n, cp); nsp_setnum(N, stobj, "expire", n); GETLONG(n, cp); nsp_setnum(N, stobj, "minttl", n); break; } case T_PTR: { if ((n = dn_expand(msg, eom, cp, hostbuf, HOSTBUF)) < 0) return -1; cp += n; n = strlen(hostbuf); nsp_setstr(N, stobj, "type", "PTR", -1); nsp_setstr(N, stobj, "host", hostbuf, n); if (N->debug) n_warn(N, __FN__, "PTR [%s]", hostbuf); break; } case T_MX: { GETSHORT(pref, cp); if ((n = dn_expand(msg, eom, cp, hostbuf, HOSTBUF)) < 0) return -1; cp += n; n = strlen(hostbuf); nsp_setstr(N, stobj, "type", "MX", -1); nsp_setnum(N, stobj, "pref", pref); nsp_setstr(N, stobj, "host", hostbuf, n); if (N->debug) n_warn(N, __FN__, "MX [%s]", hostbuf); break; } default: { /* skip data we're too dumb to parse */ nsp_setnum(N, stobj, "type", type); /* if (N->debug) */ n_warn(N, __FN__, "xx[%d]", type); cp += dlen; break; } } i++; if (cp != e) n_warn(N, __FN__, "SOA[%d %d]", cp, e); cp = e; } #endif return 0; #undef __FN__ }
int srv_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { char *srv_name; size_t srv_name_size; char tgt_port[6]; #ifdef WIN32 PDNS_RECORD resp, entry; char *tgt_name; #else char tgt_name[BINKD_FQDNLEN + 1]; unsigned char resp[SRVGAI_DNSRESPLEN]; ns_msg nsb; ns_rr rrb; int rlen, i, rrlen; const unsigned char *p; struct in_addr dummy_addr; #endif int rc; struct addrinfo *ai, **ai_last = res; /* we need sensible information for all parameters */ if (!node || (node && !*node) || !service || (service && !*service) || !hints || !res) return getaddrinfo(node, service, hints, res); /* only domain names are supported */ if (hints->ai_flags & AI_NUMERICHOST) return getaddrinfo(node, service, hints, res); /* detect IP addresses */ if ((hints->ai_family == AF_INET || hints->ai_family == AF_UNSPEC) && #ifdef WIN32 inet_addr(node) != INADDR_NONE #else inet_aton(node, &dummy_addr) != 0 #endif ) return getaddrinfo(node, service, hints, res); #ifdef AF_INET6 if ((hints->ai_family == AF_INET6 || hints->ai_family == AF_UNSPEC) && strchr(node, ':')) return getaddrinfo(node, service, hints, res); #endif /* only named services are supported */ if ((hints->ai_flags & AI_NUMERICSERV) || *service == '0' || atoi(service) > 0) return getaddrinfo(node, service, hints, res); /* only TCP and UDP are supported */ if (hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != SOCK_DGRAM) return getaddrinfo(node, service, hints, res); /* _ <service> ._ tcp . <node> \0 */ srv_name_size = 1 + strlen(service) + 2 + 3 + 1 + strlen(node) + 1; srv_name = malloc(srv_name_size); if (!srv_name) return EAI_MEMORY; /* construct domain name for SRV query */ snprintf(srv_name, srv_name_size, "_%s._%s.%s", service, hints->ai_socktype == SOCK_STREAM ? "tcp" : "udp", node); #ifdef WIN32 rc = DnsQuery(srv_name, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &resp, NULL); #else rlen = res_search(srv_name, ns_c_in, ns_t_srv, resp, sizeof(resp)); #endif free(srv_name); /* fallback if DNS query does not return a single entry */ #ifdef WIN32 if (rc != ERROR_SUCCESS) #else if (rlen < 1) #endif return getaddrinfo(node, service, hints, res); #ifndef WIN32 /* fallback in case we cannot parse the response */ if (ns_initparse(resp, rlen, &nsb) < 0) return getaddrinfo(node, service, hints, res); #endif /* iterate through result set -- might be incomplete */ #ifdef WIN32 for (entry = resp; entry != NULL; entry = entry->pNext) { switch (entry->wType) { case DNS_TYPE_SRV: snprintf(tgt_port, sizeof(tgt_port), "%d", entry->Data.SRV.wPort); tgt_name = entry->Data.SRV.pNameTarget; #else for (i = 0; i < ns_msg_count(nsb, ns_s_an); i++) { rc = ns_parserr(&nsb, ns_s_an, i, &rrb); if (rc < 0) continue; if (ns_rr_class(rrb) != ns_c_in) continue; switch (ns_rr_type(rrb)) { case ns_t_srv: rrlen = ns_rr_rdlen(rrb); if (rrlen < 8) /* 2+2+2 and at least 2 for host */ break; /* extract host and port */ p = ns_rr_rdata(rrb); rc = dn_expand(resp, resp+rlen, p+6, tgt_name, sizeof(tgt_name)); if (rc < 2) break; snprintf(tgt_port, sizeof(tgt_port), "%u", (unsigned int)p[4] << 8 | (unsigned int)p[5]); #endif /* resolve and add to end of list */ if (getaddrinfo(tgt_name, tgt_port, hints, ai_last) != 0) break; /* get end of list */ for (ai=*ai_last; ai != NULL; ai = ai->ai_next) ai_last = &(ai->ai_next); break; default: /* someone stupid might use CNAME */ break; } } #ifdef WIN32 /* free result set */ DnsRecordListFree(resp, DnsFreeRecordList); #endif /* fallback in case no resolution via SRV is possible */ if (ai_last == res) return getaddrinfo(node, service, hints, res); return 0; }
/*! \brief Returns a MEM STACK with the requested DNS records */ PKI_MEM_STACK *URL_get_data_dns_url(URL *url, ssize_t size) { PKI_MEM_STACK *ret = NULL; #ifdef HAVE_LIBRESOLV int type = T_A; PKI_MEM *obj = NULL; if( (!url) || (!url->addr)) { return NULL; } unsigned char response[NS_PACKETSZ]; ns_msg dnsMessage; ns_rr dnsRecord; int dnsRecordSection = ns_s_an; int dns_msgCount = 0; int len = 0; // Check the Type of record if ((type = URL_get_dns_type(url->attrs)) == -1) return NULL; PKI_log_debug("DNS URI: Searching for %s (%s/%d)", url->addr, url->attrs, type); if ((len = res_search(url->addr, C_IN, type, response, sizeof(response))) < 0) { // An Error Occurred PKI_log_err("DNS URI: search failed\n"); return NULL; } if (ns_initparse(response, len, &dnsMessage) < 0) { // This should not happen if the record is correct PKI_log_err("DNS URI: can not init DNS parsing of the dnsMessage\n"); return NULL; } len = ns_msg_count(dnsMessage, dnsRecordSection); PKI_log_debug("DNS_URI: msg count ==> %d\n", len); if (len <= 0) return NULL; if((ret = PKI_STACK_MEM_new()) == NULL ) { PKI_log_debug ("DNS URI: Memory Failure"); return NULL; } for (dns_msgCount = 0; dns_msgCount < len; dns_msgCount++) { PKI_log_debug("DNS URI: Retrieving DNS record #%d",dns_msgCount); if (ns_parserr(&dnsMessage, dnsRecordSection, dns_msgCount, &dnsRecord)) { // ERROR: ns_parserr failed, let's continue to the next record PKI_log_err("DNS URI: Can not parse record %d of %d", dns_msgCount, len); continue; } PKI_log_debug("DNS URI: type = %d (req: %d)\n", ns_rr_type(dnsRecord), type); if (type == pki_ns_t_address) { switch (ns_rr_type(dnsRecord)) { case T_A: case T_AAAA: case T_CNAME: break; default: continue; } } else if (type != ns_rr_type(dnsRecord)) { PKI_log_debug("DNS URI: recived type %d is different from requested (%d)", type, ns_rr_type(dnsRecord)); // continue; } // int i = 0; int rv = 0; // int len = 0; int offset = 0; char dnsRecordName[MAXDNAME]; memset(dnsRecordName, '\x0', MAXDNAME); // Parse the different types of DNS records if ((ns_rr_type(dnsRecord) == T_A) || (ns_rr_type(dnsRecord) == T_AAAA)) { // These require Translation using IPv4/IPv6 functions int family = AF_INET; if (ns_rr_type(dnsRecord) == T_A) family = AF_INET; else family = AF_INET6; if(inet_ntop(family, ns_rr_rdata(dnsRecord), dnsRecordName, sizeof(dnsRecordName)) == NULL) { // Can not convert continue; } } else if ((ns_rr_type(dnsRecord) == T_CNAME) || (ns_rr_type(dnsRecord) == T_MX) || (ns_rr_type(dnsRecord) == T_NS)) { if (ns_rr_type(dnsRecord) == T_MX) offset = NS_INT16SZ; rv = ns_name_uncompress(ns_msg_base(dnsMessage), ns_msg_end(dnsMessage), ns_rr_rdata(dnsRecord) + offset, dnsRecordName, MAXDNAME); // ERROR, can not uncompress the names if (rv < 0) continue; } else if (ns_rr_type(dnsRecord) == T_SRV) { // This requires special handling, the format is [SHORT][SHORT][SHORT][DATA] // unsigned short *pri = (unsigned short *) ns_rr_rdata(dnsRecord); // unsigned short *weight = (unsigned short *) &(ns_rr_rdata(dnsRecord)[2]); // unsigned short *port = (unsigned short *) &(ns_rr_rdata(dnsRecord)[4]); // Shall we return the additional data too ? // printf("PRI : %d\n", *pri); // printf("WEIGHT : %d\n", ntohs(*weight)); // printf("PORT : %d\n", ntohs(*port)); offset = 6; rv = ns_name_uncompress(ns_msg_base(dnsMessage), ns_msg_end(dnsMessage), ns_rr_rdata(dnsRecord) + offset, dnsRecordName, MAXDNAME); if (rv < 0) continue; } else if (ns_rr_type(dnsRecord) == T_TXT) { // Special handling required. Format is [BYTE][DATA] unsigned char *p = (unsigned char *)ns_rr_rdata(dnsRecord); snprintf(dnsRecordName, (size_t) *p+1, "%s", &ns_rr_rdata(dnsRecord)[1]); } else { PKI_log_debug("DNS URI: record type not supported [%d]", ns_rr_type(dnsRecord)); continue; } if((obj = PKI_MEM_new_null()) == NULL ) { // Memory Allocation Error, we abort break; } if (strlen(dnsRecordName) > 0) { // If it is a printable value, we add the parsed version of the // record if(PKI_MEM_add(obj, (char *) dnsRecordName, strlen(dnsRecordName)) == PKI_ERR) { /* ERROR in memory growth */; PKI_log_err("DNS URI: Memory Allocation Error"); break; } } else { // The value is not parsed/parsable, we return the raw data // the application should know what to do with the data! if(PKI_MEM_add(obj, (char *) ns_rr_rdata(dnsRecord), ns_rr_rdlen(dnsRecord)) == PKI_ERR) { /* ERROR in memory growth */; PKI_log_err("DNS URI: Memory Allocation Error"); break; } } /* printf("MSG Data [%d]:\n", ns_rr_rdlen(dnsRecord)); for (i=0; i < ns_rr_rdlen(dnsRecord); i++) { unsigned char *kk; kk = (unsigned char *) &ns_rr_rdata(dnsRecord)[i]; printf("%x:", *kk); }; printf("\n"); fprintf(stderr, "DEBUG: RV => %d (err: %d, %s)\n", rv, h_errno, hstrerror(h_errno)); fprintf(stderr, "DEBUG: name => %s (%s)\n", ns_rr_name(dnsRecord), url->addr); fprintf(stderr, "DEBUG: value => %s\n", dnsRecordName); fprintf(stderr, "DEBUG: type => %d\n", ns_rr_type(dnsRecord)); fprintf(stderr, "DEBUG: class => %d\n", ns_rr_class(dnsRecord)); */ PKI_STACK_MEM_push(ret, obj); // PKI_log_debug("DNS URI: Added object #%d to stack", PKI_STACK_MEM_elements(ret)); } #endif return ret; };
/* * Fill out host list, based on DNS lookups. * This is not reentrant. Better be called before any other threads * are started. */ int cldc_getaddr(GList **host_list, const char *thishost, struct hail_log *log) { enum { hostsz = 64 }; char cldb[hostsz]; unsigned char resp[512]; int rlen; ns_msg nsb; ns_rr rrb; int rrlen; char hostb[hostsz]; int i; struct cldc_host hp; const unsigned char *p; int rc; int search_retries = 10; /* * We must create FQDN or else the first thing the resolver does * is a lookup in the DNS root (probably the standard-compliant * dot between "_cld" and "_udp" hurts us here). */ if (cldc_make_fqdn(cldb, hostsz, "_cld._udp", thishost) != 0) { HAIL_INFO(log, "internal error in cldc_make_fqdn(%s)", thishost); return -1; } do_try_again: rc = res_search(cldb, ns_c_in, ns_t_srv, resp, 512); if (rc < 0) { switch (h_errno) { case HOST_NOT_FOUND: HAIL_INFO(log, "%s: No _cld._udp SRV record", __func__); return -1; case NO_DATA: HAIL_INFO(log, "%s: Cannot find _cld._udp" " SRV record", __func__); return -1; case TRY_AGAIN: if (search_retries-- > 0) goto do_try_again; /* fall through */ case NO_RECOVERY: default: HAIL_ERR(log, "%s: res_search error (%d): %s", __func__, h_errno, hstrerror(h_errno)); return -1; } } rlen = rc; if (rlen == 0) { HAIL_INFO(log, "%s: res_search returned empty reply", __func__); return -1; } if (ns_initparse(resp, rlen, &nsb) < 0) { HAIL_ERR(log, "%s: ns_initparse error", __func__); return -1; } for (i = 0; i < ns_msg_count(nsb, ns_s_an); i++) { rc = ns_parserr(&nsb, ns_s_an, i, &rrb); if (rc < 0) continue; if (ns_rr_class(rrb) != ns_c_in) continue; memset(&hp, 0, sizeof(hp)); switch (ns_rr_type(rrb)) { case ns_t_srv: rrlen = ns_rr_rdlen(rrb); if (rrlen < 8) { /* 2+2+2 and 2 for host */ HAIL_DEBUG(log, "%s: SRV len %d", __func__, rrlen); break; } p = ns_rr_rdata(rrb); rc = dn_expand(resp, resp+rlen, p+6, hostb, hostsz); if (rc < 0) { HAIL_DEBUG(log, "%s: dn_expand error %d", __func__, rc); break; } if (rc < 2) { HAIL_DEBUG(log, "%s: dn_expand short %d", __func__, rc); break; } if (cldc_saveaddr(&hp, ns_get16(p+0), ns_get16(p+2), ns_get16(p+4), rc, hostb, NULL)) break; HAIL_DEBUG(log, "%s: found CLD host %s port %u" " prio %d weight %d", __func__, hp.host, hp.port, hp.prio, hp.weight); push_host(host_list, &hp); break; case ns_t_cname: /* impossible, but */ HAIL_DEBUG(log, "%s: CNAME in SRV request, ignored", __func__); break; default: ; } } return 0; }
static int fakens_search(const uschar *domain, int type, uschar *answerptr, int size) { int len = Ustrlen(domain); int asize = size; /* Locally modified */ uschar *endname; uschar name[256]; uschar utilname[256]; uschar *aptr = answerptr; /* Locally modified */ struct stat statbuf; /* Remove terminating dot. */ if (domain[len - 1] == '.') len--; Ustrncpy(name, domain, len); name[len] = 0; endname = name + len; /* This code, for forcing TRY_AGAIN and NO_RECOVERY, is here so that it works for the old test suite that uses a real nameserver. When the old test suite is eventually abandoned, this code could be moved into the fakens utility. */ if (len >= 14 && Ustrcmp(endname - 14, "test.again.dns") == 0) { int delay = Uatoi(name); /* digits at the start of the name */ DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n", name, dns_text_type(type)); if (delay > 0) { DEBUG(D_dns) debug_printf("delaying %d seconds\n", delay); sleep(delay); } h_errno = TRY_AGAIN; return -1; } if (len >= 13 && Ustrcmp(endname - 13, "test.fail.dns") == 0) { DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n", name, dns_text_type(type)); h_errno = NO_RECOVERY; return -1; } /* Look for the fakens utility, and if it exists, call it. */ (void)string_format(utilname, sizeof(utilname), "%s/../bin/fakens", spool_directory); if (stat(CS utilname, &statbuf) >= 0) { pid_t pid; int infd, outfd, rc; uschar *argv[5]; DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", name, dns_text_type(type)); argv[0] = utilname; argv[1] = spool_directory; argv[2] = name; argv[3] = dns_text_type(type); argv[4] = NULL; pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE); if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s", strerror(errno)); len = 0; rc = -1; while (asize > 0 && (rc = read(outfd, aptr, asize)) > 0) { len += rc; aptr += rc; /* Don't modify the actual arguments, because they */ asize -= rc; /* may need to be passed on to res_search(). */ } if (rc < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "read from fakens failed: %s", strerror(errno)); switch(child_close(pid, 0)) { case 0: return len; case 1: h_errno = HOST_NOT_FOUND; return -1; case 2: h_errno = TRY_AGAIN; return -1; default: case 3: h_errno = NO_RECOVERY; return -1; case 4: h_errno = NO_DATA; return -1; case 5: /* Pass on to res_search() */ DEBUG(D_dns) debug_printf("fakens returned PASS_ON\n"); } } /* fakens utility not found, or it returned "pass on" */ DEBUG(D_dns) debug_printf("passing %s on to res_search()\n", domain); return res_search(CS domain, C_IN, type, answerptr, size); }
static int fakens_search(const uschar *domain, int type, uschar *answerptr, int size) { int len = Ustrlen(domain); int asize = size; /* Locally modified */ uschar *endname; uschar name[256]; uschar utilname[256]; uschar *aptr = answerptr; /* Locally modified */ struct stat statbuf; /* Remove terminating dot. */ if (domain[len - 1] == '.') len--; Ustrncpy(name, domain, len); name[len] = 0; endname = name + len; /* Look for the fakens utility, and if it exists, call it. */ (void)string_format(utilname, sizeof(utilname), "%s/bin/fakens", config_main_directory); if (stat(CS utilname, &statbuf) >= 0) { pid_t pid; int infd, outfd, rc; uschar *argv[5]; DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", name, dns_text_type(type)); argv[0] = utilname; argv[1] = config_main_directory; argv[2] = name; argv[3] = dns_text_type(type); argv[4] = NULL; pid = child_open(argv, NULL, 0000, &infd, &outfd, FALSE); if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to run fakens: %s", strerror(errno)); len = 0; rc = -1; while (asize > 0 && (rc = read(outfd, aptr, asize)) > 0) { len += rc; aptr += rc; /* Don't modify the actual arguments, because they */ asize -= rc; /* may need to be passed on to res_search(). */ } /* If we ran out of output buffer before exhasting the return, carry on reading and counting it. */ if (asize == 0) while ((rc = read(outfd, name, sizeof(name))) > 0) len += rc; if (rc < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "read from fakens failed: %s", strerror(errno)); switch(child_close(pid, 0)) { case 0: return len; case 1: h_errno = HOST_NOT_FOUND; return -1; case 2: h_errno = TRY_AGAIN; return -1; default: case 3: h_errno = NO_RECOVERY; return -1; case 4: h_errno = NO_DATA; return -1; case 5: /* Pass on to res_search() */ DEBUG(D_dns) debug_printf("fakens returned PASS_ON\n"); } } else { DEBUG(D_dns) debug_printf("fakens (%s) not found\n", utilname); } /* fakens utility not found, or it returned "pass on" */ DEBUG(D_dns) debug_printf("passing %s on to res_search()\n", domain); return res_search(CS domain, C_IN, type, answerptr, size); }
/* This function expects the bep034_lock to be held by caller, releases it while working and returns with the lock held */ static bep034_status bep034_fill_hostrecord( const char * hostname, bep034_hostrecord ** hostrecord, uintptr_t dns_handle ) { uint8_t answer[NS_PACKETSZ]; bep034_hostrecord * hr = 0; int answer_len, num_msgs, max_entries, i; ns_msg msg; ns_rr rr; /* Reset hostrecord pointer */ * hostrecord = 0; /* If we find a record in cache, return it */ hr = bep034_find_hostrecord( hostname, &i /* dummy */ ); if( hr ) { *hostrecord = hr; return BEP_034_INPROGRESS; } /* Return mutex, we'll be blocking now and do not hold any resources in need of guarding */ bep034_dounlock(); /* Query resolver for TXT records for the trackers domain */ #ifdef __MACH__ { struct sockaddr tmp; uint32_t tmplen; answer_len = dns_search( (dns_handle_t)dns_handle, hostname, ns_c_in, ns_t_txt, answer, sizeof(answer), &tmp, &tmplen ); } #else (void)dns_handle; answer_len = res_search(hostname, ns_c_in, ns_t_txt, answer, sizeof(answer)); #endif if( answer_len < 0 ) { /* Here we enter race condition land */ switch( h_errno ) { case NO_RECOVERY: case HOST_NOT_FOUND: return BEP_034_NXDOMAIN; case NO_DATA: return BEP_034_NORECORD; case NETDB_INTERNAL: case TRY_AGAIN: default: return BEP_034_TIMEOUT; } } ns_initparse (answer, answer_len, &msg); num_msgs = ns_msg_count (msg, ns_s_an); for( i=0; i<num_msgs; ++i) { ns_parserr (&msg, ns_s_an, i, &rr); if (ns_rr_class(rr) == ns_c_in && ns_rr_type(rr) == ns_t_txt ) { uint32_t record_ttl = ns_rr_ttl(rr); uint16_t record_len = ns_rr_rdlen(rr); const uint8_t *record_ptr = ns_rr_rdata(rr); const char * string_ptr, * string_end; /* First octet is length of (first) string in the txt record. Since BEP034 does not say anything about multiple strings, we ignore all but the first string in each TXT record. */ uint8_t string_len = *record_ptr; /* If we would read beyond buffer end, ignore record */ if( record_ptr + 1 + string_len > answer + answer_len ) continue; /* Sanitize string length against record length */ if( string_len + 1 > record_len ) string_len = record_len - 1; /* Test if we are interested in the record */ if( string_len < 10 /* strlen( "BITTORRENT" ) */ ) continue; /* Although the BEP is not very specific, we interpret the wording "This should always be the first word in the record" as a requirment to not start the record with a space */ if( memcmp( record_ptr + 1, "BITTORRENT", 10 ) ) continue; /* We found a BITTORRENT TXT record. Now start parsing: Given entries of the form "UDP:\d+\s" i.e. 6 bytes, in a record of length N we have an upper bound of ( N - 11 ) / 6 records. */ max_entries = 1 + ( string_len - 11 ) / 6; /* Allocate memory for host record */ hr = (bep034_hostrecord*) malloc( sizeof(bep034_hostrecord) + sizeof( uint32_t ) * max_entries ); if( !hr ) return BEP_034_TIMEOUT; /* Init host record */ hr->hostname = strdup( hostname ); hr->entries = 0; hr->expiry = NOW() + record_ttl; /* Look for "\s(TCP|UDP):\d+" */ string_ptr = record_ptr + 1; string_end = string_ptr + string_len; string_ptr += 10 /* strlen( "BITTORRENT" ) */; while( string_ptr + 6 < string_end ) { /* We need at least 6 bytes for a word */ int found; uint32_t port = 0; ++string_ptr; if( string_ptr[-1] != ' ' || string_ptr[2] != 'P' || string_ptr[3] != ':' ) continue; if( string_ptr[0] == 'T' && string_ptr[1] == 'C' ) found = 0; else if( string_ptr[0] == 'U' && string_ptr[1] == 'D' ) found = 1; else continue; /* Now we're sure, we've found UDP: or TCP: and assume, from string_ptr + 4 there's a port number*/ string_ptr += 4; while( string_ptr < string_end && (*string_ptr >= '0' && *string_ptr <= '9' ) ) port = port * 10 + *(string_ptr++) - '0'; /* If no digit was found, word is invalid */ if( string_ptr[-1] == ':' ) continue; /* If number did not terminate on end of string or with a space, word is invalid */ if( string_ptr != string_end && *string_ptr != ' ' ) continue; /* If we have an invalid port number, word is invalid */ if( port > 65335 ) continue; /* Valid word found, add it to tracker list */ hr->trackers[ hr->entries++ ] = port | ( found ? 0x10000 : 0 ); } /* Ensure exclusive access to the host record list, lock will be held on return so that the caller can work with hr */ bep034_dolock(); /* Hand over record to cache, from now the cache has to release memory */ if( bep034_save_record( &hr ) ) return BEP_034_TIMEOUT; /* Dump what we found */ bep034_dump_record( hr ); /* Once the first line in the first record has been parsed, return host record */ *hostrecord = hr; return BEP_034_INPROGRESS; } } }
static int dnsSD_Request(HSP *sp, char *dname, uint16_t rtype, HSPDnsCB callback, HSPSFlowSettings *settings) { u_char buf[PACKETSZ]; // We could have a config option to set the DNS servers that we will ask. If so // they should be written into this array of sockaddrs... // myDebug(1,"_res_nsaddr=%p", &_res.nsaddr); myDebug(1,"=== res_search(%s, C_IN, %u) ===", dname, rtype); int anslen = res_search(dname, C_IN, rtype, buf, PACKETSZ); if(anslen == -1) { if(errno == 0 && (h_errno == HOST_NOT_FOUND || h_errno == NO_DATA)) { // although res_search returned -1, the request did actually get an answer, // it's just that there was no SRV record configured, or the response was // not authoritative. Interpret this the same way as answer_count==0. myDebug(1,"res_search(%s, C_IN, %u) came up blank (h_errno=%d)", dname, rtype, h_errno); return 0; } else { myLog(LOG_ERR,"res_search(%s, C_IN, %u) failed : %s (h_errno=%d)", dname, rtype, strerror(errno), h_errno); return -1; } } if(anslen < sizeof(HEADER)) { myLog(LOG_ERR,"res_search(%s) returned %d (too short)", dname, anslen); return -1; } HEADER *ans = (HEADER *)buf; if(ans->rcode != NOERROR) { myLog(LOG_ERR,"res_search(%s) returned response code %d", dname, ans->rcode); return -1; } uint32_t answer_count = (ntohs(ans->ancount)); if(answer_count == 0) { myLog(LOG_INFO,"res_search(%s) returned no answer", dname); return 0; } myDebug(1, "dnsSD: answer_count = %d", answer_count); u_char *p = buf + sizeof(HEADER); u_char *endp = buf + anslen; // consume query int query_name_len = dn_skipname(p, endp); if(query_name_len == -1) { myLog(LOG_ERR,"dn_skipname() <query> failed"); return -1; } myDebug(1, "dnsSD: (compressed) query_name_len = %d", query_name_len); p += (query_name_len); p += QFIXEDSZ; // collect array of results for(int entry = 0; entry < answer_count; entry++) { myDebug(1, "dnsSD: entry %d, bytes_left=%d", entry, (endp - p)); // consume name (again) query_name_len = dn_skipname(p, endp); if(query_name_len == -1) { myLog(LOG_ERR,"dn_skipname() <ans> failed"); return -1; } p += (query_name_len); // now p should be looking at: // [type:16][class:16][ttl:32][len:16][record] if((endp - p) <= 16) { myLog(LOG_ERR,"ans %d of %d: ran off end -- only %d bytes left", entry, answer_count, (endp-p)); return -1; } uint16_t res_typ = (p[0] << 8) | p[1]; uint16_t res_cls = (p[2] << 8) | p[3]; uint32_t res_ttl = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; uint16_t res_len = (p[8] << 8) | p[9]; p += 10; // use another pointer to walk the payload and move p to the next answer u_char *x = p; p += res_len; uint16_t res_payload = res_len; // sanity check if(res_typ != rtype || res_cls != C_IN) { myLog(LOG_ERR,"expected t=%d,c=%d, got t=%d,c=%d", rtype, C_IN, res_typ, res_cls); return -1; } switch(rtype) { case T_SRV: { // now x should see // [priority:2][weight:2][port:2][FQDN:res_len-6] uint16_t res_pri = (x[0] << 8) | x[1]; uint16_t res_wgt = (x[2] << 8) | x[3]; uint32_t res_prt = (x[4] << 8) | x[5]; x += 6; res_payload -= 6; // still got room for an FQDN? if((endp - x) < HSP_MIN_DNAME) { myLog(LOG_ERR,"no room for target name -- only %d bytes left", (endp - x)); return -1; } char fqdn[MAXDNAME]; int ans_len = dn_expand(buf, endp, x, fqdn, MAXDNAME); if(ans_len == -1) { myLog(LOG_ERR,"dn_expand() failed"); return -1; } // cross-check if(ans_len != res_payload) { myLog(LOG_ERR,"target name len cross-check failed"); return -1; } if(ans_len < HSP_MIN_DNAME) { // just ignore this one -- e.g. might just be "." } else { // fqdn[ans_len] = '\0'; myDebug(1, "answer %d is <%s>:<%u> (wgt=%d; pri=%d; ttl=%d; ans_len=%d; res_len=%d)", entry, fqdn, res_prt, res_wgt, res_pri, res_ttl, ans_len, res_len); if(callback) { char fqdn_port[PACKETSZ]; sprintf(fqdn_port, "%s/%u", fqdn, res_prt); // use key == NULL to indicate that the value is host:port (*callback)(sp, rtype, res_ttl, NULL, 0, (u_char *)fqdn_port, strlen(fqdn_port), settings); } } } break; case T_TXT: { // now x should see // [TXT:res_len] // still got room for a text record? if((endp - x) < HSP_MIN_TXT) { myLog(LOG_ERR,"no room for text record -- only %d bytes left", (endp - x)); return -1; } if(getDebug()) { printf("dsnSD TXT Record: "); for(int i = 0; i < res_len; i++) { int ch = x[i]; if(isalnum(ch)) printf("%c", ch); else printf("{%02x}", ch); } printf("\n"); } // format is [len][<key>=<val>][len][<key>=<val>]... // so we can pull out the settings and give them directly // to the callback fn without copying u_char *txtend = x + res_len; // need at least 3 chars for a var=val setting while((txtend - x) >= 3) { int pairlen = *x++; int klen = strcspn((char *)x, "="); if(klen < 0) { myLog(LOG_ERR, "dsnSD TXT record not in var=val format: %s", x); } else { if(callback) (*callback)(sp, rtype, res_ttl, x, klen, (x+klen+1), (pairlen - klen - 1), settings); } x += pairlen; } } break; default: myLog(LOG_ERR, "unsupported query type: %u" , rtype); return -1; break; } } return answer_count; }
int main(int argc, char *argv[]) { struct timeval start, end; time_t when; int ch, i, qflag, dflag, r; uint16_t type = T_A; char buf[1024], *host; dflag = 0; qflag = 0; while((ch = getopt(argc, argv, "deqt:")) != -1) { switch(ch) { case 'd': dflag = 1; break; case 'e': long_err += 1; break; case 'q': qflag = 1; break; case 't': if ((type = strtotype(optarg)) == 0) usage(); break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; for (i = 0; i < argc; i++) { if (i) printf("\n"); printf("===> \"%s\"\n", argv[i]); host = gethostarg(argv[i]); errno = 0; h_errno = 0; gai_errno = 0; rrset_errno = 0; if (gettimeofday(&start, NULL) != 0) err(1, "gettimeofday"); if (qflag) r = res_query(host, C_IN, type, buf, sizeof(buf)); else r = res_search(host, C_IN, type, buf, sizeof(buf)); if (gettimeofday(&end, NULL) != 0) err(1, "gettimeofday"); if (r != -1) { dump_packet(buf, r); printf("\n"); if (dflag) { printf(";; Query time: %d msec\n", msec(start, end)); when = time(NULL); printf(";; WHEN: %s", ctime(&when)); } printf(";; MSG SIZE rcvd: %i\n", r); } print_errors(); } return (0); }
int afsconf_LookupServer(const char *service, const char *protocol, const char *cellName, unsigned short afsdbPort, afs_uint32 *cellHostAddrs, char cellHostNames[][MAXHOSTCHARS], unsigned short ports[], unsigned short ipRanks[], int *numServers, int *ttl, char **arealCellName) { int code = 0; int r; int len; unsigned char answer[4096]; unsigned char *p; char *dotcellname = NULL; char *realCellName; char host[256]; int server_num = 0; int minttl = 0; int try_init = 0; int dnstype = 0; int pass = 0; char *IANAname = (char *) afsconf_FindIANAName(service); int tservice = afsconf_FindService(service); realCellName = NULL; *numServers = 0; *ttl = 0; if (tservice <= 0 || !IANAname) return AFSCONF_NOTFOUND; /* service not found */ if (strchr(cellName,'.')) pass += 2; #ifdef HAVE_RES_RETRANSRETRY if ((_res.options & RES_INIT) == 0 && res_init() == -1) return (0); /* * Rx timeout is typically 56 seconds; limit user experience to * similar timeout */ _res.retrans = 18; _res.retry = 3; #endif retryafsdb: r = -1; switch (pass) { case 0: dnstype = T_SRV; r = asprintf(&dotcellname, "_%s._%s.%s.", IANAname, protocol, cellName); break; case 1: dnstype = T_AFSDB; r = asprintf(&dotcellname, "%s.", cellName); break; case 2: dnstype = T_SRV; r = asprintf(&dotcellname, "_%s._%s.%s", IANAname, protocol, cellName); break; case 3: dnstype = T_AFSDB; r = asprintf(&dotcellname, "%s", cellName); break; } if (r < 0 || dotcellname == NULL) goto findservererror; LOCK_GLOBAL_MUTEX; len = res_search(dotcellname, C_IN, dnstype, answer, sizeof(answer)); UNLOCK_GLOBAL_MUTEX; if (dotcellname != NULL) { free(dotcellname); dotcellname = NULL; } if (len < 0) { if (try_init < 1) { try_init++; res_init(); goto retryafsdb; } if (pass < 3) { pass++; goto retryafsdb; } else { code = AFSCONF_NOTFOUND; goto findservererror; } } p = answer + sizeof(HEADER); /* Skip header */ code = dn_expand(answer, answer + len, p, host, sizeof(host)); if (code < 0) { code = AFSCONF_NOTFOUND; goto findservererror; } p += code + QFIXEDSZ; /* Skip name */ while (p < answer + len) { int type, ttl, size; code = dn_expand(answer, answer + len, p, host, sizeof(host)); if (code < 0) { code = AFSCONF_NOTFOUND; goto findservererror; } p += code; /* Skip the name */ type = (p[0] << 8) | p[1]; p += 4; /* Skip type and class */ ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; p += 4; /* Skip the TTL */ size = (p[0] << 8) | p[1]; p += 2; /* Skip the size */ if (type == T_AFSDB) { struct hostent *he; short afsdb_type; afsdb_type = (p[0] << 8) | p[1]; if (afsdb_type == 1) { /* * We know this is an AFSDB record for our cell, of the * right AFSDB type. Write down the true cell name that * the resolver gave us above. */ if (!realCellName) realCellName = strdup(host); } code = dn_expand(answer, answer + len, p + 2, host, sizeof(host)); if (code < 0) { code = AFSCONF_NOTFOUND; goto findservererror; } if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) && /* Do we want to get TTL data for the A record as well? */ (he = gethostbyname(host))) { if (he->h_addrtype == AF_INET) { afs_uint32 ipaddr; memcpy(&ipaddr, he->h_addr, sizeof(ipaddr)); cellHostAddrs[server_num] = ipaddr; ports[server_num] = afsdbPort; ipRanks[server_num] = 0; strncpy(cellHostNames[server_num], host, sizeof(cellHostNames[server_num])); server_num++; if (!minttl || ttl < minttl) minttl = ttl; } } } if (type == T_SRV) { struct hostent *he; /* math here: _ is 1, _ ._ is 3, _ ._ . is 4. then the domain. */ if ((strncmp(host + 1, IANAname, strlen(IANAname)) == 0) && (strncmp(host + strlen(IANAname) + 3, protocol, strlen(protocol)) == 0)) { if (!realCellName) realCellName = strdup(host + strlen(IANAname) + strlen(protocol) + 4); } code = dn_expand(answer, answer + len, p + 6, host, sizeof(host)); if (code < 0) { code = AFSCONF_NOTFOUND; goto findservererror; } if ((server_num < MAXHOSTSPERCELL) && /* Do we want to get TTL data for the A record as well? */ (he = gethostbyname(host))) { if (he->h_addrtype == AF_INET) { afs_uint32 ipaddr; memcpy(&ipaddr, he->h_addr, sizeof(ipaddr)); cellHostAddrs[server_num] = ipaddr; ipRanks[server_num] = (p[0] << 8) | p[1]; ports[server_num] = htons((p[4] << 8) | p[5]); /* weight = (p[2] << 8) | p[3]; */ strncpy(cellHostNames[server_num], host, sizeof(cellHostNames[server_num])); server_num++; if (!minttl || ttl < minttl) minttl = ttl; } } } p += size; } if (server_num == 0) { /* No AFSDB or SRV records */ code = AFSCONF_NOTFOUND; goto findservererror; } if (realCellName) { /* Convert the real cell name to lowercase */ for (p = (unsigned char *)realCellName; *p; p++) *p = tolower(*p); } *numServers = server_num; *ttl = minttl ? (time(0) + minttl) : 0; if ( *numServers > 0 ) { code = 0; *arealCellName = realCellName; } else code = AFSCONF_NOTFOUND; findservererror: if (code && realCellName) free(realCellName); return code; }
static uintptr_t * apdns_getFQDN_MX_1 (Region rAddrLPairs, Region rAddrEPairs, Region rAddrString, char *str, uintptr_t *list, int depth, request_data *rd) { if (depth < 0) return list; // i,j are used as loop counters // the dnspackage returned from the resolver is scanned from top to buttom // next is the package pointer relative to the start of the package int j, next; char ans[NS_PACKETSZ + 1]; char dnsnamesa[NS_MAXDNAME]; char *dnsnames = dnsnamesa; char *input = str; uintptr_t *pair, *listpair; String rs; dnshead *head = (dnshead *) ans; // get the dns package int n = res_search (input, C_IN, T_MX, (unsigned char *) ans, NS_PACKETSZ + 1); input = 0; if (n == -1) return list; if (n < sizeof (dnshead)) return list; ntohhead (ans, head); if ((head->flags & 0xF) != 0) return list; if (head->anscount < 1) return list; // skip questions next = NS_HFIXEDSZ; for (j = 0; j < head->questcount; j++) { next = skipname (ans, next, n); next = iflessthan (next + 4, n); if (next < 0) return list; } // The answers int rv; for (j = 0; j < head->anscount; j++) { // int a_name = next; if (next >= n) return list; next = skipname (ans, next, n); if (next + NS_RRFIXEDSZ >= n || next < 0) return list; uint16_t a_type = twocharto16 (ans[next], ans[next + 1]); next += 4; uint32_t a_ttl = fourcharto32 (ans[next], ans[next + 1], ans[next + 2], ans[next + 3]); next += 4; uint16_t a_rdlength = twocharto16 (ans[next], ans[next + 1]); next += 2; if (a_type == T_MX) { // We got a mx record if (next + a_rdlength >= n || a_rdlength < 3) return list; uint16_t a_mx_pref = twocharto16 (ans[next], ans[next + 1]); rv = dumpname (dnsnames, NS_MAXDNAME, ans, next + 2, n); rs = convertStringToML (rAddrString, dnsnames); allocRecordML (rAddrEPairs, 3, pair); elemRecordML (pair, 0) = (uintptr_t) a_mx_pref; elemRecordML (pair, 1) = (uintptr_t) a_ttl; elemRecordML (pair, 2) = (uintptr_t) rs; allocRecordML (rAddrLPairs, 2, listpair); first (listpair) = (uintptr_t) pair; second (listpair) = (uintptr_t) list; makeCONS (listpair, list); ap_log_error (APLOG_MARK, LOG_DEBUG, 0, rd->server, "apdns_getFQDN_MX: pref %i, ttl %i, %s", a_mx_pref, a_ttl, dnsnames); } else if (a_type == T_CNAME) { // we got an alias (cononical name) rv = dumpname (dnsnames, NS_MAXDNAME, ans, next, n); input = dnsnames; break; } else { // we got something we did not ask for // or cannot handle at the momnet return list; } next += a_rdlength; } if (input) return apdns_getFQDN_MX_1 (rAddrLPairs, rAddrEPairs, rAddrString, input, list, depth - 1, rd); return list; }