I'm writing a generic HTTP server and will probably be writing a full-fledged SMTP server as well (queueing messages in a database, automatic DKIM signage, full MX-DNS support with retries and automatic fallback/backoff for recipient servers that don't allow many recipients or many connections or many messages per second).
For the HTTP server I need multiple IP addresses per domainquery (I need to bind to all interfaces), and for the SMTP server I need to query multiple TXT records (SPF validation) and need to get back preference-sorted MX records, of which I then need multiple IP addresses again to try and connect to all IP addresses. For the both servers I need to know the difference between a (possibly temporary) failure of the query and a definitive result that says the record does not exist.
For this purpose I created a small generic dnsquery() function (see below). Is it something worth including in the Protocols.DNS module, or is it better left outside? The problem was/is, none of the existing convenience functions seems to allow me to do this.
Protocols.DNS.async_client dnsclient = Protocols.DNS.async_client();
private void dnscb(string domain, mapping res, function(array, array:void) cb, array restargs) { array an = res->an; switch (res->rcode) { case Protocols.DNS.SERVFAIL: case Protocols.DNS.NOTIMP: case Protocols.DNS.REFUSED: case Protocols.DNS.YXDOMAIN: case Protocols.DNS.YXRRSET: case Protocols.DNS.NXRRSET: case Protocols.DNS.NOTZONE: if (!sizeof(an)) { cb(0, restargs); return; } } sort(an->preference, an); cb(an->aaaa + an->a + an->mx + an->txt - ({ 0 }), restargs); }
private mapping dnstypetonum = ([ "A": Protocols.DNS.T_A, "MX": Protocols.DNS.T_MX, "TXT": Protocols.DNS.T_TXT, "AAAA": Protocols.DNS.T_AAAA, ]);
private void collect2aaaa(array|zero results, array restargs) { array|zero prevresults = restargs[1]; if (results) { if (prevresults) results -= prevresults; else prevresults = ({}); prevresults += results; } if (--restargs[0]) restargs[1] = prevresults; else restargs[2](prevresults, restargs[3..]); }
//! Asynchronous DNS query with multiple results and a distinction //! between failure and empty results. //! //! @param type //! DNS query type (case sensitive). Currently supported: A, MX, TXT, AAAA. //! Querying for AAAA gives both IPv4 and IPv6 results. //! //! @param domain //! The domain name we are querying. //! //! @param result_cb //! The callback function that receives the result of the DNS query. //! It is called as follows: //! @expr{void result_cb(array(string)|zero results,array restargs);@} //! If the request fails it will return @expr{zero@} for @expr{results@}. //! @expr{restargs@} is passed unaltered to the callback function. //! //! @note //! There is a notable difference between @expr{results@} equal //! to @expr{zero@} (= request failed and can be retried) and //! @expr{({})@} (= request definitively answered the record //! does not exist; retries are pointless). void dnsquery(string type, string domain, function(array(string)|zero, array:void) result_cb, array restargs) { int itype = dnstypetonum[type]; if (!itype) { result_cb(0, restargs); return; } if (type == "AAAA") { restargs = ({ 2, 0, result_cb }) + restargs; dnsclient->do_query(domain, Protocols.DNS.C_IN, dnstypetonum["A"], dnscb, collect2aaaa, restargs); result_cb = collect2aaaa; } dnsclient->do_query(domain, Protocols.DNS.C_IN, itype, dnscb, result_cb, restargs); }
pike-devel@lists.lysator.liu.se