static int dblchk(str *domain, str *dbltxt) { str dblstr; char *dbl = getenv("DBLLOOKUP"); int l, i; unsigned char ansbuf[512]; if(!dbl || domain->len == 0) return 0; if(session_getnum("sump",0)) return 0; /* no point */ str_init(&dblstr); str_copy(&dblstr, domain); str_catc(&dblstr, '.'); str_cats(&dblstr, dbl); l = res_query(dblstr.s, C_IN, T_TXT, ansbuf, sizeof(ansbuf)); if(l > 0 && ((HEADER *)ansbuf)->ancount != 0) { /* something in the answer */ unsigned char *recbuf = ansbuf+NS_HFIXEDSZ; /* skip over questions, why am I still writing stuff * like this? */ for(i = ns_get16(ansbuf+4); i != 0; --i) recbuf += dn_skipname(recbuf, ansbuf+l)+4; for(i = ns_get16(ansbuf+6); i != 0; --i) { recbuf += dn_skipname(recbuf, ansbuf+l); if(ns_get16(recbuf) != T_TXT) { /* CNAME or something */ recbuf += 10 + ns_get16(recbuf+8); continue; } /* it's a TXT record, wow */ str_init(dbltxt); str_copyb(dbltxt, (char*)recbuf+11, recbuf[10]); str_free(&dblstr); return 1; } } /* didn't find anything */ str_free(&dblstr); return 0; }
static int app_parse_srv(radiodns_app_t *app, ns_msg handle, ns_rr rr, char *dnbuf, radiodns_srv_t *srv) { const unsigned char *rdata; (void) app; rdata = ns_rr_rdata(rr); srv->priority = ns_get16(rdata); rdata += NS_INT16SZ; srv->weight = ns_get16(rdata); rdata += NS_INT16SZ; srv->port = ns_get16(rdata); rdata += NS_INT16SZ; dn_expand(ns_msg_base(handle), ns_msg_base(handle) + ns_msg_size(handle), rdata, dnbuf, MAXDNAME); if(!(srv->target = strdup(dnbuf))) { return -2; } return 0; }
u_int16_t _getshort(const u_char *src) { return (ns_get16(src)); }
static void afsdb_hosts_to_addrs(char *vllist[], int *vlsnum, ns_msg handle, ns_sect section, unsigned mask, unsigned long *_ttl) { int rrnum; ns_rr rr; int subtype, i, ret; unsigned int ttl = UINT_MAX, rr_ttl; debug("AFSDB RR count is %d", ns_msg_count(handle, section)); /* Look at all the resource records in this section. */ for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) { /* Expand the resource record number rrnum into rr. */ if (ns_parserr(&handle, section, rrnum, &rr)) { _error("ns_parserr failed : %m"); continue; } /* We're only interested in AFSDB records */ if (ns_rr_type(rr) == ns_t_afsdb) { vllist[*vlsnum] = malloc(MAXDNAME); if (!vllist[*vlsnum]) error("Out of memory"); subtype = ns_get16(ns_rr_rdata(rr)); /* Expand the name server's domain name */ if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), ns_rr_rdata(rr) + 2, vllist[*vlsnum], MAXDNAME) < 0) error("ns_name_uncompress failed"); rr_ttl = ns_rr_ttl(rr); if (ttl > rr_ttl) ttl = rr_ttl; /* Check the domain name we've just unpacked and add it to * the list of VL servers if it is not a duplicate. * If it is a duplicate, just ignore it. */ for (i = 0; i < *vlsnum; i++) if (strcasecmp(vllist[i], vllist[*vlsnum]) == 0) goto next_one; /* Turn the hostname into IP addresses */ ret = dns_resolver(vllist[*vlsnum], mask); if (ret) { debug("AFSDB RR can't resolve." "subtype:%d, server name:%s, netmask:%u", subtype, vllist[*vlsnum], mask); goto next_one; } info("AFSDB RR subtype:%d, server name:%s, ip:%*.*s, ttl:%u", subtype, vllist[*vlsnum], (int)payload[payload_index - 1].iov_len, (int)payload[payload_index - 1].iov_len, (char *)payload[payload_index - 1].iov_base, ttl); /* prepare for the next record */ *vlsnum += 1; continue; next_one: free(vllist[*vlsnum]); } } *_ttl = ttl; info("ttl: %u", ttl); }
int DnsResolver_lookupMx(DnsResolver *self, const char *domain, DnsMxResponse **resp) { int query_stat = DnsResolver_query(self, domain, ns_t_mx); if (NETDB_SUCCESS != query_stat) { return query_stat; } // end if size_t msg_count = ns_msg_count(self->msghanlde, ns_s_an); DnsMxResponse *respobj = (DnsMxResponse *) malloc(sizeof(DnsMxResponse) + msg_count * sizeof(struct mxentry *)); if (NULL == respobj) { return DnsResolver_setError(self, NETDB_INTERNAL); } // end if memset(respobj, 0, sizeof(DnsMxResponse) + msg_count * sizeof(struct mxentry *)); respobj->num = 0; for (size_t n = 0; n < msg_count; ++n) { ns_rr rr; int parse_stat = ns_parserr(&self->msghanlde, ns_s_an, n, &rr); if (0 != parse_stat) { goto formerr; } // end if if (ns_t_mx != ns_rr_type(rr)) { continue; } // end if const unsigned char *rdata = ns_rr_rdata(rr); if (ns_rr_rdlen(rr) < NS_INT16SZ) { goto formerr; } // end if int preference = ns_get16(rdata); rdata += NS_INT16SZ; // NOTE: NS_MAXDNAME で十分なのかイマイチ確証が持てないが, bind8 の dig コマンドの実装でもこの値を使っていたのでいいのではないか. char dnamebuf[NS_MAXDNAME]; int dnamelen = ns_name_uncompress(self->msgbuf, self->msgbuf + self->msglen, rdata, dnamebuf, sizeof(dnamebuf)); if (NS_INT16SZ + dnamelen != ns_rr_rdlen(rr)) { goto formerr; } // end if // TODO: dnamebuf は NULL 終端が保証されているのか? size_t domainlen = strlen(dnamebuf); respobj->exchange[respobj->num] = (struct mxentry *) malloc(sizeof(struct mxentry) + sizeof(char[domainlen + 1])); if (NULL == respobj->exchange[respobj->num]) { goto noresource; } // end if respobj->exchange[respobj->num]->preference = preference; if (domainlen + 1 <= strlcpy(respobj->exchange[respobj->num]->domain, dnamebuf, domainlen + 1)) { abort(); } // end if ++(respobj->num); } // end for if (0 == respobj->num) { goto formerr; } // end if *resp = respobj; return NETDB_SUCCESS; formerr: DnsMxResponse_free(respobj); return DnsResolver_setError(self, NO_RECOVERY); noresource: DnsMxResponse_free(respobj); return DnsResolver_setError(self, NETDB_INTERNAL); } // end function : DnsResolver_lookupMx
static int printZone(ns_type xfr, const char *zone, const struct sockaddr_in *sin, ns_tsig_key *key) { static u_char *answer = NULL; static int answerLen = 0; querybuf buf; int msglen, amtToRead, numRead, result, sockFD, len; int count, type, rlen, done, n; int numAnswers, numRecords, soacnt; u_char *cp, tmp[NS_INT16SZ]; char dname[2][NS_MAXDNAME]; enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING } error; pid_t zpid = -1; u_char *newmsg; int newmsglen; ns_tcp_tsig_state tsig_state; int tsig_ret, tsig_required, tsig_present; switch (xfr) { case ns_t_axfr: case ns_t_zxfr: break; default: fprintf(stderr, ";; %s - transfer type not supported\n", p_type(xfr)); return (ERROR); } /* * Create a query packet for the requested zone name. */ msglen = res_nmkquery(&res, ns_o_query, zone, queryClass, ns_t_axfr, NULL, 0, 0, buf.qb2, sizeof buf); if (msglen < 0) { if (res.options & RES_DEBUG) fprintf(stderr, ";; res_nmkquery failed\n"); return (ERROR); } /* * Sign the message if a key was sent */ if (key == NULL) { newmsg = (u_char *)&buf; newmsglen = msglen; } else { DST_KEY *dstkey; int bufsize, siglen; u_char sig[64]; int ret; /* ns_sign() also calls dst_init(), but there is no harm * doing it twice */ dst_init(); bufsize = msglen + 1024; newmsg = (u_char *) malloc(bufsize); if (newmsg == NULL) { errno = ENOMEM; return (-1); } memcpy(newmsg, (u_char *)&buf, msglen); newmsglen = msglen; if (strcmp(key->alg, NS_TSIG_ALG_HMAC_MD5) != 0) dstkey = NULL; else dstkey = dst_buffer_to_key(key->name, KEY_HMAC_MD5, NS_KEY_TYPE_AUTH_ONLY, NS_KEY_PROT_ANY, key->data, key->len); if (dstkey == NULL) { errno = EINVAL; if (key) free(newmsg); return (-1); } siglen = sizeof(sig); /* newmsglen++; */ ret = ns_sign(newmsg, &newmsglen, bufsize, NOERROR, dstkey, NULL, 0, sig, &siglen, 0); if (ret < 0) { if (key) free (newmsg); if (ret == NS_TSIG_ERROR_NO_SPACE) errno = EMSGSIZE; else if (ret == -1) errno = EINVAL; return (ret); } ns_verify_tcp_init(dstkey, sig, siglen, &tsig_state); } /* * Set up a virtual circuit to the server. */ if ((sockFD = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { int e = errno; perror(";; socket"); return (e); } switch (sin->sin_family) { case AF_INET: if (bind(sockFD, (struct sockaddr *)&myaddress, sizeof myaddress) < 0){ int e = errno; fprintf(stderr, ";; bind(%s port %u): %s\n", inet_ntoa(myaddress.sin_addr), ntohs(myaddress.sin_port), strerror(e)); (void) close(sockFD); sockFD = -1; return (e); } if (connect(sockFD, (const struct sockaddr *)sin, sizeof *sin) < 0) { int e = errno; perror(";; connect"); (void) close(sockFD); sockFD = -1; return (e); } break; case AF_INET6: if (bind(sockFD, (struct sockaddr *)&myaddress6, sizeof myaddress6) < 0){ int e = errno; char buf[80]; fprintf(stderr, ";; bind(%s port %u): %s\n", inet_ntop(AF_INET6, &myaddress6.sin6_addr, buf, sizeof(buf)), ntohs(myaddress6.sin6_port), strerror(e)); (void) close(sockFD); sockFD = -1; return (e); } if (connect(sockFD, (const struct sockaddr *)sin, sizeof(struct sockaddr_in6)) < 0) { int e = errno; perror(";; connect"); (void) close(sockFD); sockFD = -1; return (e); } break; } /* * Send length & message for zone transfer */ ns_put16(newmsglen, tmp); if (write(sockFD, (char *)tmp, NS_INT16SZ) != NS_INT16SZ || write(sockFD, (char *)newmsg, newmsglen) != newmsglen) { int e = errno; if (key) free (newmsg); perror(";; write"); (void) close(sockFD); sockFD = -1; return (e); } else if (key) free (newmsg); /* * If we're compressing, push a gzip into the pipeline. */ if (xfr == ns_t_zxfr) { enum { rd = 0, wr = 1 }; int z[2]; if (pipe(z) < 0) { int e = errno; perror(";; pipe"); (void) close(sockFD); sockFD = -1; return (e); } zpid = vfork(); if (zpid < 0) { int e = errno; perror(";; fork"); (void) close(sockFD); sockFD = -1; return (e); } else if (zpid == 0) { /* Child. */ (void) close(z[rd]); (void) dup2(sockFD, STDIN_FILENO); (void) close(sockFD); (void) dup2(z[wr], STDOUT_FILENO); (void) close(z[wr]); execlp("gzip", "gzip", "-d", "-v", NULL); perror(";; child: execlp(gunzip)"); _exit(1); } /* Parent. */ (void) close(z[wr]); (void) dup2(z[rd], sockFD); (void) close(z[rd]); } result = 0; numAnswers = 0; numRecords = 0; soacnt = 0; error = NO_ERRORS; numRead = 0; dname[0][0] = '\0'; for (done = 0; !done; (void)NULL) { /* * Read the length of the response. */ cp = tmp; amtToRead = INT16SZ; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_LEN; break; } len = ns_get16(tmp); if (len == 0) break; /* nothing left to read */ /* * The server sent too much data to fit the existing buffer -- * allocate a new one. */ if (len > answerLen) { if (answerLen != 0) free(answer); answerLen = len; answer = (u_char *)malloc(answerLen); } /* * Read the response. */ amtToRead = len; cp = answer; while (amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0) { cp += numRead; amtToRead -= numRead; } if (numRead <= 0) { error = ERR_READING_MSG; break; } result = print_axfr(stdout, answer, len); if (result != 0) { error = ERR_PRINTING; break; } numRecords += htons(((HEADER *)answer)->ancount); numAnswers++; /* Header. */ cp = answer + HFIXEDSZ; /* Question. */ for (count = ntohs(((HEADER *)answer)->qdcount); count > 0; count--) { n = dn_skipname(cp, answer + len); if (n < 0) { error = ERR_PRINTING; done++; break; } cp += n + QFIXEDSZ; if (cp > answer + len) { error = ERR_PRINTING; done++; break; } } /* Answer. */ for (count = ntohs(((HEADER *)answer)->ancount); count > 0 && !done; count--) { n = dn_expand(answer, answer + len, cp, dname[soacnt], sizeof dname[0]); if (n < 0) { error = ERR_PRINTING; done++; break; } cp += n; if (cp + 3 * INT16SZ + INT32SZ > answer + len) { error = ERR_PRINTING; done++; break; } GETSHORT(type, cp); cp += INT16SZ; cp += INT32SZ; /* ttl */ GETSHORT(rlen, cp); cp += rlen; if (cp > answer + len) { error = ERR_PRINTING; done++; break; } if (type == T_SOA && soacnt++ && ns_samename(dname[0], dname[1]) == 1) { done++; break; } } /* * Verify the TSIG */ if (key) { if (ns_find_tsig(answer, answer + len) != NULL) tsig_present = 1; else tsig_present = 0; if (numAnswers == 1 || soacnt > 1) tsig_required = 1; else tsig_required = 0; tsig_ret = ns_verify_tcp(answer, &len, &tsig_state, tsig_required); if (tsig_ret == 0) { if (tsig_present) printf("; TSIG ok\n"); } else printf("; TSIG invalid\n"); } } printf(";; Received %d answer%s (%d record%s).\n", numAnswers, (numAnswers != 1) ? "s" : "", numRecords, (numRecords != 1) ? "s" : ""); (void) close(sockFD); sockFD = -1; /* * If we were uncompressing, reap the uncompressor. */ if (xfr == ns_t_zxfr) { pid_t pid; int status = 0; pid = wait(&status); if (pid < 0) { int e = errno; perror(";; wait"); return (e); } if (pid != zpid) { fprintf(stderr, ";; wrong pid (%lu != %lu)\n", (u_long)pid, (u_long)zpid); return (ERROR); } printf(";; pid %lu: exit %d, signal %d, core %c\n", (u_long)pid, WEXITSTATUS(status), WIFSIGNALED(status) ? WTERMSIG(status) : 0, WCOREDUMP(status) ? 't' : 'f'); } switch (error) { case NO_ERRORS: return (0); case ERR_READING_LEN: return (EMSGSIZE); case ERR_PRINTING: return (result); case ERR_READING_MSG: return (EMSGSIZE); default: return (EFAULT); } }
DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto, const std::string& domain, const LogSink& logInstance ) { buffer srvbuf; bool error = false; const std::string dname = "_" + service + "._" + proto; if( !domain.empty() ) srvbuf.len = res_querydomain( dname.c_str(), const_cast<char*>( domain.c_str() ), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ ); else srvbuf.len = res_query( dname.c_str(), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ ); if( srvbuf.len < 0 ) return defaultHostMap( domain, logInstance ); HEADER* hdr = (HEADER*)srvbuf.buf; unsigned char* here = srvbuf.buf + NS_HFIXEDSZ; if( ( hdr->tc ) || ( srvbuf.len < NS_HFIXEDSZ ) ) error = true; if( hdr->rcode >= 1 && hdr->rcode <= 5 ) error = true; if( ntohs( hdr->ancount ) == 0 ) error = true; if( ntohs( hdr->ancount ) > NS_PACKETSZ ) error = true; int cnt; for( cnt = ntohs( hdr->qdcount ); cnt>0; --cnt ) { int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len ); here += strlen + NS_QFIXEDSZ; } unsigned char* srv[NS_PACKETSZ]; int srvnum = 0; for( cnt = ntohs( hdr->ancount ); cnt>0; --cnt ) { int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len ); here += strlen; srv[srvnum++] = here; here += SRV_FIXEDSZ; here += dn_skipname( here, srvbuf.buf + srvbuf.len ); } if( error ) { return defaultHostMap( domain, logInstance ); } // (q)sort here HostMap servers; for( cnt=0; cnt<srvnum; ++cnt ) { name srvname; if( ns_name_ntop( srv[cnt] + SRV_SERVER, (char*)srvname, NS_MAXDNAME ) < 0 ) printf( "handle this error!\n" ); servers[(char*)srvname] = ns_get16( srv[cnt] + SRV_PORT ); } return servers; }
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); }
/** * create_naptr_record() */ static NAPTR_record *create_naptr_record(const ns_msg *handle, const ns_rr *rr) { size_t rdchars; const u_char *rdata, *edata; const u_char *message = ns_msg_base(*handle); size_t message_len = ns_msg_size(*handle); char buf[NS_MAXDNAME]; /* defined in nameser.h */ NAPTR_record *naptr_record = malloc(sizeof(NAPTR_record)); memset(naptr_record,0,sizeof(NAPTR_record)); /* ** Domain field */ naptr_record->domain = strdup(ns_rr_name(*rr)); /* ** TTL field */ naptr_record->ttl = ns_rr_ttl(*rr); /* ** Copy from rdata ** (borrowed from BIND-8.2.2 ns_print.c) */ rdata = ns_rr_rdata(*rr); edata = rdata+ns_rr_rdlen(*rr); /* ** Order & preference field */ naptr_record->order = ns_get16(rdata); rdata += NS_INT16SZ; naptr_record->preference = ns_get16(rdata); rdata += NS_INT16SZ; /* ** Flags field */ if((naptr_record->flags = get_rdata_str(rdata,edata,&rdchars))==NULL) { free_naptr_record(naptr_record); return NULL; } rdata += rdchars; /* ** Service field */ if((naptr_record->service = get_rdata_str(rdata,edata,&rdchars))==NULL) { free_naptr_record(naptr_record); return NULL; } rdata += rdchars; /* ** RegExp field */ if((naptr_record->regexp = get_rdata_str(rdata,edata,&rdchars))==NULL) { free_naptr_record(naptr_record); return NULL; } rdata += rdchars; /* ** Replacement field ** Note: dn_expand() sets the first character to '\0' for the root, ** we are going to set it back to '.' */ if(dn_expand(message,message+message_len,rdata,buf,NS_MAXDNAME)==-1) { free_naptr_record(naptr_record); return NULL; } if(buf[0]=='\0') { buf[0]='.'; buf[1]='\0'; } naptr_record->replacement = strdup(buf); return naptr_record; }
/* Setup a client socket for the named service over the given protocol under * the given domain name. */ static int insrv_lookup (int (*mksox) (int,const struct sockaddr *,socklen_t), char *service, char *proto, char *domain, char *cnxhost, size_t cnxhlen, int *cnxport) { // 1. convert service/proto to svcnm // 2. construct SRV query for _service._proto.domain // 3. try connecting to all answers in turn // 4. if no SRV records exist, lookup A record to connect to on stdport // 5. return connection socket or error code iobuf query, names; name svcnm; int error=0; int ctr; int rnd; int sox=0; HEADER *nameshdr; unsigned char *here, *srv[MAXNUM_SRV], *ip; int num_srv=0; // Storage for fallback SRV list, constructed when DNS gives no SRV unsigned char fallbacksrv [2*(MAXCDNAME+SRV_FIXEDSZ+MAXCDNAME)]; srv_flags &= ~SRV_GOT_MASK; srv_flags |= SRV_GOT_SRV; strcpy (svcnm, "_"); strcat (svcnm, service); strcat (svcnm, "._"); strcat (svcnm, proto); // Note that SRV records are only defined for class IN if (domain) { names.len=res_querydomain (svcnm, domain, C_IN, T_SRV, names.buf, PACKETSZ); } else { names.len=res_query (svcnm, C_IN, T_SRV, names.buf, PACKETSZ); } if (names.len < 0) { error = -ENOENT; goto fallback; } nameshdr=(HEADER *) names.buf; here=names.buf + HFIXEDSZ; rnd=nameshdr->id; // Heck, gimme one reason why not! if ((names.len < HFIXEDSZ) || nameshdr->tc) { error = -EMSGSIZE; } switch (nameshdr->rcode) { case 1: error = -EFAULT; goto fallback; case 2: error = -EAGAIN; goto fallback; case 3: error = -ENOENT; goto fallback; case 4: error = -ENOSYS; goto fallback; case 5: error = -EPERM; goto fallback; default: break; } if (ntohs (nameshdr->ancount) == 0) { error = -ENOENT; goto fallback; } if (ntohs (nameshdr->ancount) > MAXNUM_SRV) { error = -ERANGE; goto fallback; } for (ctr=ntohs (nameshdr->qdcount); ctr>0; ctr--) { int strlen=dn_skipname (here, names.buf+names.len); here += strlen + QFIXEDSZ; } for (ctr=ntohs (nameshdr->ancount); ctr>0; ctr--) { int strlen=dn_skipname (here, names.buf+names.len); here += strlen; srv [num_srv++] = here; here += SRV_FIXEDSZ; here += dn_skipname (here, names.buf+names.len); } // In case an error occurred, there are no SRV records. // Fallback strategy now is: construct two. One with the domain name, // the other with the /standard/ service name prefixed. // Note: Assuming a domain without the service name prefixed! fallback: if (error) { struct servent *servent = getservbyname (service, proto); srv_flags &= ~SRV_GOT_MASK; srv_flags |= SRV_GOT_A; num_srv = 2; if (!servent) { return error; // First error returned } srv [0] = here = fallbacksrv; // Only few record fields are really needed: *(unsigned short *)(here + SRV_COST) = htons (0); *(unsigned short *)(here + SRV_WEIGHT) = htons (0); *(unsigned short *)(here + SRV_PORT) = servent->s_port; here += SRV_FIXEDSZ; if (domain) { here += dn_comp (domain, here, MAXCDNAME, NULL, NULL); } // Forget about the name whose SRV IN this is, no need for it srv [1] = here; // Only few record fields are really needed: *(unsigned short *)(here + SRV_COST) = htons (1); *(unsigned short *)(here + SRV_WEIGHT) = htons (0); *(unsigned short *)(here + SRV_PORT) = servent->s_port; here += SRV_FIXEDSZ; here += dn_comp (servent->s_name, here, MAXCDNAME, NULL, NULL); here--; // Go back to overwrite final zero byte if (domain) { here += dn_comp (domain, here, MAXCDNAME, NULL, NULL); } rnd = 1; } // End of fallback construction, making sure that variables are defined // srv[] points to the SRV RR, num_srv counts the number of entries. // Every SRV RR has at least cost, weight, port and servername set. #ifdef DEBUG for (ctr=0; ctr<num_srv; ctr++) { name srvname; if (ns_name_ntop (srv [ctr]+SRV_SERVER, srvname, MAXDNAME) < 0) { return -errno; } printf ("Considering SRV server %d %d %d\t%s\n", ns_get16 (srv [ctr]+SRV_COST), ns_get16 (srv [ctr]+SRV_WEIGHT), ns_get16 (srv [ctr]+SRV_PORT), srvname ); } #endif // Overwrite weight with rnd-spread version to divide load over weights for (ctr=0; ctr<num_srv; ctr++) { *(unsigned short *)(srv [ctr]+SRV_WEIGHT) = rnd % (1+ns_get16 (srv [ctr]+SRV_WEIGHT)); } qsort (srv, num_srv, sizeof (*srv), srvcmp); for (ctr=0; ctr<num_srv; ctr++) { name srvname; struct hostent *host; // Open a socket to connect with int sox = socket (PF_INET, (*proto!='u')? SOCK_STREAM: SOCK_DGRAM, 0); if (sox < 0) { return -errno; } if (ns_name_ntop (srv [ctr]+SRV_SERVER, srvname, MAXDNAME) < 0) { return -errno; } #ifdef DEBUG printf ("Trying SRV server %d %d\t%s\n", ns_get16 (srv [ctr]+SRV_COST), *(unsigned short *)(srv [ctr]+SRV_WEIGHT), srvname ); #endif if ((host=gethostbyname (srvname)) && (host->h_addrtype == AF_INET)) { char **ip=host->h_addr_list; while (*ip) { char *ipnr=*ip; struct sockaddr_in sin; memset (&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; memcpy (&sin.sin_addr, ipnr, sizeof (sin.sin_addr)); sin.sin_port = *(unsigned short *) (srv [ctr]+SRV_PORT); #ifdef DEBUG fprintf (stderr, "\tbind_connect (%d, 0x%08lx, %d)\t", sox, ntohl(*(unsigned long *)ipnr), ntohs (sin.sin_port)); #endif #ifdef DEBUG if (mksox == connect) { printf ("mksox == connect\n"); } if (mksox == bind) { printf ("mksox == bind\n"); } { int i; printf ("SIN ="); for (i=0; i<sizeof (sin); i++) printf (" %02x", (int) (((unsigned char *) &sin) [i])); printf ("\n"); } #endif if (mksox (sox, (struct sockaddr *) &sin, sizeof (sin)) == 0) { #ifdef DEBUG fprintf (stderr, "Connected or bound to %s:%d\n", srvname, ntohs (sin.sin_port)); #endif if (cnxhost) { if (strlen (cnxhost) > cnxhlen-1) { *cnxhost = '\0'; } else { strncpy (cnxhost,srvname,cnxhlen); } } if (cnxport) { *cnxport = ntohs (sin.sin_port); } return sox; } else { if (!error) { error = -errno; } } ip++; } } #ifdef DEBUG printf ("Closing socket %d\n", sox); #endif close (sox); } if (!error) { error = -ENOENT; } return error; }
void PlatformDomainNameServiceQuery::runBlocking() { if (!serviceValid) { emitError(); return; } SWIFT_LOG(debug) << "Querying " << service << std::endl; std::vector<DomainNameServiceQuery::Result> records; #if defined(SWIFTEN_PLATFORM_WINDOWS) DNS_RECORD* responses; // FIXME: This conversion doesn't work if unicode is deffed above if (DnsQuery(service.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &responses, NULL) != ERROR_SUCCESS) { emitError(); return; } DNS_RECORD* currentEntry = responses; while (currentEntry) { if (currentEntry->wType == DNS_TYPE_SRV) { DomainNameServiceQuery::Result record; record.priority = currentEntry->Data.SRV.wPriority; record.weight = currentEntry->Data.SRV.wWeight; record.port = currentEntry->Data.SRV.wPort; // The pNameTarget is actually a PCWSTR, so I would have expected this // conversion to not work at all, but it does. // Actually, it doesn't. Fix this and remove explicit cast // Remove unicode undef above as well record.hostname = std::string((const char*) currentEntry->Data.SRV.pNameTarget); records.push_back(record); } currentEntry = currentEntry->pNext; } DnsRecordListFree(responses, DnsFreeRecordList); #else // Make sure we reinitialize the domain list every time res_init(); ByteArray response; response.resize(NS_PACKETSZ); int responseLength = res_query(const_cast<char*>(service.c_str()), ns_c_in, ns_t_srv, reinterpret_cast<u_char*>(vecptr(response)), response.size()); if (responseLength == -1) { SWIFT_LOG(debug) << "Error" << std::endl; emitError(); return; } // Parse header HEADER* header = reinterpret_cast<HEADER*>(vecptr(response)); unsigned char* messageStart = vecptr(response); unsigned char* messageEnd = messageStart + responseLength; unsigned char* currentEntry = messageStart + NS_HFIXEDSZ; // Skip over the queries int queriesCount = ntohs(header->qdcount); while (queriesCount > 0) { int entryLength = dn_skipname(currentEntry, messageEnd); if (entryLength < 0) { emitError(); return; } currentEntry += entryLength + NS_QFIXEDSZ; queriesCount--; } // Process the SRV answers int answersCount = ntohs(header->ancount); while (answersCount > 0) { DomainNameServiceQuery::Result record; int entryLength = dn_skipname(currentEntry, messageEnd); currentEntry += entryLength; currentEntry += NS_RRFIXEDSZ; // Priority if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.priority = boost::numeric_cast<int>(ns_get16(currentEntry)); currentEntry += 2; // Weight if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.weight = boost::numeric_cast<int>(ns_get16(currentEntry)); currentEntry += 2; // Port if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.port = boost::numeric_cast<int>(ns_get16(currentEntry)); currentEntry += 2; // Hostname if (currentEntry >= messageEnd) { emitError(); return; } ByteArray entry; entry.resize(NS_MAXDNAME); entryLength = dn_expand(messageStart, messageEnd, currentEntry, reinterpret_cast<char*>(vecptr(entry)), entry.size()); if (entryLength < 0) { emitError(); return; } record.hostname = std::string(reinterpret_cast<const char*>(vecptr(entry))); records.push_back(record); currentEntry += entryLength; answersCount--; } #endif BoostRandomGenerator generator; DomainNameServiceQuery::sortResults(records, generator); //std::cout << "Sending out " << records.size() << " SRV results " << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onResult), records), shared_from_this()); }
int SendRequest(union res_sockaddr_union *nsAddrPtr, const u_char *buf, int buflen, u_char *answer, u_int anslen, int *trueLenPtr) { int n, try, v_circuit, resplen; ISC_SOCKLEN_T salen; int gotsomewhere = 0, connected = 0; int connreset = 0; u_short id, len; u_char *cp; fd_set dsmask; struct timeval timeout; const HEADER *hp = (const HEADER *) buf; HEADER *anhp = (HEADER *) answer; struct iovec iov[2]; int terrno = ETIMEDOUT; char junk[512]; struct sockaddr_storage sa; int family = nsAddrPtr->sin.sin_family; int clen = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); if (res.options & RES_DEBUG2) { printf("------------\nSendRequest(), len %d\n", buflen); Print_query(buf, buf + buflen, 1); } v_circuit = (res.options & RES_USEVC) || buflen > PACKETSZ; id = hp->id; /* * Send request, RETRY times, or until successful */ for (try = 0; try < res.retry; try++) { usevc: if (v_circuit) { int truncated = 0; /* * Use virtual circuit; * at most one attempt per server. */ try = res.retry; if (s < 0) { s = socket(family, SOCK_STREAM, 0); if (s < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("socket (vc) failed"); continue; } if (connect(s, (struct sockaddr *)nsAddrPtr, clen) < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("connect failed"); (void) close(s); s = -1; continue; } } /* * Send length & message */ __putshort(buflen, (u_char *)&len); iov[0].iov_base = (caddr_t)&len; iov[0].iov_len = INT16SZ; DE_CONST(buf, iov[1].iov_base); iov[1].iov_len = buflen; if (writev(s, iov, 2) != INT16SZ + buflen) { terrno = errno; if (res.options & RES_DEBUG) perror("write failed"); (void) close(s); s = -1; continue; } /* * Receive length & response */ cp = answer; len = INT16SZ; while ((n = read(s, (char *)cp, (int)len)) > 0) { cp += n; if ((len -= n) <= 0) break; } if (n <= 0) { terrno = errno; if (res.options & RES_DEBUG) perror("read failed"); (void) close(s); s = -1; /* * A long running process might get its TCP * connection reset if the remote server was * restarted. Requery the server instead of * trying a new one. When there is only one * server, this means that a query might work * instead of failing. We only allow one reset * per query to prevent looping. */ if (terrno == ECONNRESET && !connreset) { connreset = 1; } continue; } cp = answer; if ((resplen = ns_get16((u_char*)cp)) > (int)anslen) { if (res.options & RES_DEBUG) fprintf(stderr, "response truncated\n"); len = anslen; truncated = 1; } else len = resplen; while (len != 0 && (n = read(s, (char *)cp, (int)len)) > 0) { cp += n; len -= n; } if (n <= 0) { terrno = errno; if (res.options & RES_DEBUG) perror("read failed"); (void) close(s); s = -1; continue; } if (truncated) { /* * Flush rest of answer * so connection stays in synch. */ anhp->tc = 1; len = resplen - anslen; while (len != 0) { n = (len > sizeof(junk) ? sizeof(junk) : len); if ((n = read(s, junk, n)) > 0) len -= n; else break; } } } else { /* * Use datagrams. */ if (s < 0) { s = socket(family, SOCK_DGRAM, 0); if (s < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("socket (dg) failed"); continue; } } #if BSD >= 43 if (connected == 0) { if (connect(s, (struct sockaddr *)nsAddrPtr, clen) < 0) { if (res.options & RES_DEBUG) perror("connect"); continue; } connected = 1; } if (send(s, buf, buflen, 0) != buflen) { if (res.options & RES_DEBUG) perror("send"); continue; } #else /* BSD */ if (sendto(s, (const char *)buf, buflen, 0, (struct sockaddr *) nsAddrPtr, clen) != buflen) { if (res.options & RES_DEBUG) perror("sendto"); continue; } #endif /* * Wait for reply */ timeout.tv_sec = (res.retrans << try); if (timeout.tv_sec <= 0) timeout.tv_sec = 1; timeout.tv_usec = 0; wait: FD_ZERO(&dsmask); FD_SET(s, &dsmask); n = select(s+1, &dsmask, (fd_set *)NULL, (fd_set *)NULL, &timeout); if (n < 0) { if (res.options & RES_DEBUG) perror("select"); continue; } if (n == 0) { /* * timeout */ if (res.options & RES_DEBUG) printf("timeout\n"); #if BSD >= 43 gotsomewhere = 1; #endif continue; } salen = sizeof sa; resplen = recvfrom(s, (char *)answer, anslen, 0, (struct sockaddr *)&sa, &salen); if (resplen <= 0) { if (res.options & RES_DEBUG) perror("recvfrom"); continue; } gotsomewhere = 1; if (id != anhp->id) { /* * response from old query, ignore it */ if (res.options & RES_DEBUG2) { printf("------------\nOld answer:\n"); Print_query(answer, answer+resplen, 1); } goto wait; } if (!(res.options & RES_IGNTC) && anhp->tc) { /* * get rest of answer; * use TCP with same server. */ if (res.options & RES_DEBUG) printf("truncated answer\n"); (void) close(s); s = -1; v_circuit = 1; goto usevc; } } if (res.options & RES_DEBUG) { if (res.options & RES_DEBUG2) printf("------------\nGot answer (%d bytes):\n", resplen); else printf("------------\nGot answer:\n"); Print_query(answer, answer+resplen, 1); } (void) close(s); s = -1; *trueLenPtr = resplen; return (SUCCESS); } if (s >= 0) { (void) close(s); s = -1; } if (v_circuit == 0) if (gotsomewhere == 0) return NO_RESPONSE; /* no nameservers found */ else return TIME_OUT; /* no answer obtained */ else if (errno == ECONNREFUSED) return NO_RESPONSE; else return ERROR; }
/* * 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; }
/* Setup a client socket for the named service over the given protocol under * the given domain name. */ int insrv_lookup (const char *service, const char *proto, const char *domain, list<t_dns_result> &result) { // 1. convert service/proto to svcnm // 2. construct SRV query for _service._proto.domain iobuf names; name svcnm; int ctr; int rnd; HEADER *nameshdr; unsigned char *here, *srv[MAXNUM_SRV]; int num_srv=0; // Storage for fallback SRV list, constructed when DNS gives no SRV //unsigned char fallbacksrv [2*(MAXCDNAME+SRV_FIXEDSZ+MAXCDNAME)]; // srv_flags &= ~SRV_GOT_MASK; // srv_flags |= SRV_GOT_SRV; strcpy (svcnm, "_"); strcat (svcnm, service); strcat (svcnm, "._"); strcat (svcnm, proto); // Note that SRV records are only defined for class IN if (domain) { names.len=res_querydomain (svcnm, domain, C_IN, T_SRV, names.buf, PACKETSZ); } else { names.len=res_query (svcnm, C_IN, T_SRV, names.buf, PACKETSZ); } if (names.len < 0) { return -ENOENT; } nameshdr=(HEADER *) names.buf; here=names.buf + HFIXEDSZ; rnd=nameshdr->id; // Heck, gimme one reason why not! if ((names.len < HFIXEDSZ) || nameshdr->tc) { return -EMSGSIZE; } switch (nameshdr->rcode) { case 1: return -EFAULT; case 2: return -EAGAIN; case 3: return -ENOENT; case 4: return -ENOSYS; case 5: return -EPERM; default: break; } if (ntohs (nameshdr->ancount) == 0) { return -ENOENT; } if (ntohs (nameshdr->ancount) > MAXNUM_SRV) { return -ERANGE; } for (ctr=ntohs (nameshdr->qdcount); ctr>0; ctr--) { int strlen=dn_skipname (here, names.buf+names.len); here += strlen + QFIXEDSZ; } for (ctr=ntohs (nameshdr->ancount); ctr>0; ctr--) { int strlen=dn_skipname (here, names.buf+names.len); here += strlen; srv [num_srv++] = here; here += SRV_FIXEDSZ; here += dn_skipname (here, names.buf+names.len); } // Overwrite weight with rnd-spread version to divide load over weights for (ctr=0; ctr<num_srv; ctr++) { *(unsigned short *) (srv [ctr]+SRV_WEIGHT) = htons(rnd % (1+ns_get16 (srv [ctr]+SRV_WEIGHT))); } qsort (srv, num_srv, sizeof (*srv), srvcmp); result.clear(); for (ctr=0; ctr<num_srv; ctr++) { name srvname; if (ns_name_ntop (srv [ctr]+SRV_SERVER, srvname, MAXDNAME) < 0) { return -errno; } t_dns_result r; r.hostname = srvname; r.port = ns_get16(srv [ctr]+SRV_PORT); result.push_back(r); } return 0; }