From 00fd45d673ed2451a6b7dcb7c8cd0af8e79ce4d9 Mon Sep 17 00:00:00 2001 From: Chris Angelico <rosuav@gmail.com> Date: Wed, 18 Sep 2013 15:26:36 +1000 Subject: [PATCH] Protocols.DNS: Implement TCP clients (both synchronous and async) --- lib/modules/Protocols.pmod/DNS.pmod | 140 ++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/lib/modules/Protocols.pmod/DNS.pmod b/lib/modules/Protocols.pmod/DNS.pmod index 7b2daf122d..3865d201a4 100644 --- a/lib/modules/Protocols.pmod/DNS.pmod +++ b/lib/modules/Protocols.pmod/DNS.pmod @@ -1966,6 +1966,146 @@ class async_client }; +// FIXME: Reuse sockets where possible? +//! Synchronous DNS client using TCP +//! Can handle larger responses than @[client] can. +class tcp_client +{ + inherit client; + + //! Perform a synchronous DNS query. + //! + //! @param s + //! Result of @[Protocols.DNS.protocol.mkquery] + //! @returns + //! mapping containing query result or 0 on failure/timeout + //! + //! @example + //! @code + //! // Perform a hostname lookup, results stored in r->an + //! object d=Protocols.DNS.tcp_client(); + //! mapping r=d->do_sync_query(d->mkquery("pike.lysator.liu.se", C_IN, T_A)); + //! @endcode + mapping do_sync_query(string s) + { + for (int i=0; i < RETRIES; i++) { + object tcp = Stdio.File(); + if (tcp->connect(nameservers[i % sizeof(nameservers)], 53)) + { + tcp->write("%2H",s); + sscanf(tcp->read(2),"%2c",int len); + return decode_res(tcp->read(len)); + } + } + // Failure. + return 0; + } +} + +// FIXME: Reuse sockets? Acknowledge RETRIES? +//! Asynchronous DNS client using TCP +class async_tcp_client +{ + inherit client; + + class Request(string domain, string req, + function(string,mapping,mixed...:void) callback, + array(mixed) args) + { + Stdio.File sock; + string writebuf="",readbuf=""; + + void create() + { + sock=Stdio.File(); + sock->async_connect(nameservers[0], 53, connectedcb); + } + + void connectedcb(int ok) + { + if (!ok) {callback(domain, 0, @args); return;} + sock->set_nonblocking(readcb, writecb, closecb); + writebuf=sprintf("%2H",req); + writecb(); + } + + void readcb(mixed id,string data) + { + readbuf+=data; + if (sscanf(readbuf,"%2H",string ret)) + { + if (callback) callback(domain, decode_res(ret), @args); + callback=0; + sock->close(); + } + } + + void writecb() + { + if (writebuf!="") writebuf=writebuf[sock->write(writebuf)..]; + } + + void closecb() + { + sock->close(); + if (callback) callback(domain, 0, @args); + callback=0; + } + } + + //! + void do_query(string domain, int cl, int type, + function(string,mapping,mixed...:void) callback, + mixed ... args) + { + string req=low_mkquery(random(65536),domain,cl,type); + Request(domain, req, callback, args); + } +} + +//! Both a @[client] and a @[tcp_client]. +class dual_client +{ + inherit client : UDP; + inherit tcp_client : TCP; + + //! + mapping do_sync_query(string s) + { + mapping ret = UDP::do_sync_query(s); + if (!ret->tc) return ret; + return TCP::do_sync_query(s); + } + + void create(mixed ... args) {::create(@args);} +} + +//! Both an @[async_client] and an @[async_tcp_client]. +class async_dual_client +{ + inherit async_client : UDP; + inherit async_tcp_client : TCP; + + void check_truncation(string domain, mapping result, int cl, int type, + function(string,mapping,mixed...:void) callback, + mixed ... args) + { + if (!result || !result->tc) callback(domain,result,@args); + else TCP::do_query(domain,cl,type,callback,@args); + } + + //! + void do_query(string domain, int cl, int type, + function(string,mapping,mixed...:void) callback, + mixed ... args) + { + UDP::do_query(domain,cl,type,check_truncation,cl,type,callback,@args); + } + + void create(mixed ... args) {::create(@args);} +} + + async_client global_async_client; #define GAC(X) \ -- GitLab