pike.git
/
lib
/
modules
/
Protocols.pmod
/
DNS.pmod
version
»
Context lines:
10
20
40
80
file
none
3
pike.git/lib/modules/Protocols.pmod/DNS.pmod:3:
//inherit Stdio.UDP : udp; //! Support for the Domain Name System protocol. //! //! RFC 1034, RFC 1035 and RFC 2308 protected void send_reply(mapping r, mapping q, mapping m, Stdio.UDP udp); #pike __REAL_VERSION__
+
// documentation taken from RFC 2136
+
+
//! No error condition.
final constant NOERROR=0;
-
+
+
//! The name server was unable to interpret the request due to a
+
//! format error.
final constant FORMERR=1;
-
+
+
//! The name server encountered an internal failure while processing
+
//! this request, for example an operating system error or a
+
//! forwarding timeout.
final constant SERVFAIL=2;
-
+
+
//! Some name that ought to exist, does not exist.
final constant NXDOMAIN=3;
-
+
+
//! The name server does not support the specified Opcode.
final constant NOTIMPL=4;
-
+
+
//! The name server refuses to perform the specified operation for
+
//! policy or security reasons.
final constant REFUSED=5;
-
+
+
//! Some RRset that ought to exist, does not exist.
final constant NXRRSET=8; final constant QUERY=0; //! Resource classes enum ResourceClass { //! Class Internet C_IN=1,
pike.git/lib/modules/Protocols.pmod/DNS.pmod:83:
//! Type - mailbox or mail list information T_MINFO=14, //! Type - mail exchange T_MX=15, //! Type - text strings T_TXT=16,
-
//! Type - IPv6 address record (RFC 1886
, deprecated
)
+
//! Type - IPv6 address record (RFC 1886)
T_AAAA=28, //! Type - Location Record (RFC 1876) T_LOC=29, //! Type - Service location record (RFC 2782) T_SRV=33, //! Type - NAPTR (RFC 3403) T_NAPTR=35, //! Type - IPv6 address record (RFC 2874, incomplete support) T_A6=38, //! Type - SPF - Sender Policy Framework (RFC 4408) T_SPF=99,
-
+
+
//! Type - ANY - A request for all records
+
T_ANY=255,
};
-
int safe_bind(Stdio.UDP udp,
mixed
...
args
)
+
int safe_bind(Stdio.UDP udp,
string|int
port,
string|void device
)
{ mixed err = catch {
-
udp->bind(
@args
);
+
udp->bind(
port, device, 1
);
return 1; }; #if constant(System.EADDRINUSE) if (errno() == System.EADDRINUSE) return 0; #endif werror("Protocols.DNS: Binding of UDP port failed with errno %d: %s\n", errno(), strerror(errno())); master()->handle_error(err); return 0; } //! Low level DNS protocol class protocol { string mklabel(string s) { if(sizeof(s)>63) error("Too long component in domain name.\n");
-
return sprintf("%
c%s
",
sizeof(
s)
,s)
;
+
return sprintf("%
1H
",s);
} protected private string mkname(string|array(string) labels, int pos, mapping(string:int) comp) { if(stringp(labels)) labels = labels/"."-({""}); if(!labels || !sizeof(labels)) return "\0"; string n = labels*".";
pike.git/lib/modules/Protocols.pmod/DNS.pmod:173:
return mkname(entry->mf, pos, c); case T_MB: return mkname(entry->mb, pos, c); case T_MG: return mkname(entry->mg, pos, c); case T_MR: return mkname(entry->mr, pos, c); case T_MX: return sprintf("%2c", entry->preference)+mkname(entry->mx, pos+2, c); case T_HINFO:
-
return sprintf("%
1c
%
s%1c%s
",
sizeof(
entry->cpu||""
)
, entry->
cpu||"",
-
sizeof(entry->
os||"")
, entry->os||"")
;
+
return sprintf("%
1H
%
1H
", entry->cpu||"", entry->os||"");
case T_MINFO: string rmailbx = mkname(entry->rmailbx, pos, c); return rmailbx + mkname(entry->emailbx, pos+sizeof(rmailbx), c); case T_SRV: return sprintf("%2c%2c%2c", entry->priority, entry->weight, entry->port) + mkname(entry->target||"", pos+6, c); case T_A: return sprintf("%@1c", (array(int))((entry->a||"0.0.0.0")/".")[0..3]); case T_AAAA: return make_raw_addr6(entry->aaaa);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:199:
entry->a6->prefixlen, make_raw_addr6(entry->a6->address)[entry->a6->prefixlen/8..], entry->a6->prefixname||""); case T_SOA: string mname = mkname(entry->mname, pos, c); return mname + mkname(entry->rname, pos+sizeof(mname), c) + sprintf("%4c%4c%4c%4c%4c", entry->serial, entry->refresh, entry->retry, entry->expire, entry->minimum); case T_NAPTR: string rnaptr = sprintf("%2c%2c", entry->order, entry->preference);
-
rnaptr += sprintf("%
1c
%
s
%
1c
%s
%1c%s%s
",
-
sizeof(
entry->flags || ""
)
,
entry->flags || "",
-
sizeof(
entry->service || ""
)
,
entry->service || "",
-
sizeof(
entry->regexp || ""
)
,
entry->regexp || "",
+
rnaptr += sprintf("%
1H
%
1H
%
1H
%s",
+
entry->flags || "",
+
entry->service || "",
+
entry->regexp || "",
mkname(entry->replacement, pos, c)); return rnaptr; case T_TXT:
-
return
Array.
map(stringp(entry->txt)? ({entry->txt}):(entry->txt||({})),
+
return map(stringp(entry->txt)? ({entry->txt}):(entry->txt||({})),
lambda(string t) {
-
return sprintf("%
1c%s
",
sizeof(
t)
, t)
;
+
return sprintf("%
1H
", t);
})*""; case T_SPF:
-
return
Array.
map(stringp(entry->spf)? ({entry->spf}):(entry->spf||({})),
+
return map(stringp(entry->spf)? ({entry->spf}):(entry->spf||({})),
lambda(string t) {
-
return sprintf("%
1c%s
",
sizeof(
t)
, t)
;
+
return sprintf("%
1H
", t);
})*""; case T_LOC: // FIXME: Not implemented yet. default: return ""; } } protected private string encode_entries(array(mapping) entries, int pos, mapping(string:int) comp) { string res=""; foreach(entries, mapping entry) { string e = mkname(entry->name, pos, comp)+ sprintf("%2c%2c%4c", entry->type, entry->cl, entry->ttl); pos += sizeof(e)+2; string rd = entry->rdata || mkrdata(entry, pos, comp);
-
res += e + sprintf("%
2c
",
sizeof(
rd)
) + rd
;
+
res += e + sprintf("%
2H
", rd);
pos += sizeof(rd); } return res; } string low_low_mkquery(mapping q) { array qd = q->qd && (arrayp(q->qd)? q->qd : ({q->qd})); array an = q->an && (arrayp(q->an)? q->an : ({q->an})); array ns = q->ns && (arrayp(q->ns)? q->ns : ({q->ns}));
pike.git/lib/modules/Protocols.pmod/DNS.pmod:280:
return low_low_mkquery((["id":id, "rd":1, "qd":(["name":dname, "cl":cl, "type":type])])); } //! create a DNS query PDU //! //! @param dnameorquery //! @param cl //! record class such as Protocols.DNS.C_IN //! @param type
-
//! query type such Protocols.DNS.T_A
+
//!
query type such Protocols.DNS.T_A
//! //! @returns //! data suitable for use with //! @[Protocols.DNS.client.do_sync_query] //! //! @example //! // generate a query PDU for a address lookup on the hostname pike.lysator.liu.se //! string q=Protocols.DNS.protocol()->mkquery("pike.lysator.liu.se", Protocols.DNS.C_IN, Protocols.DNS.T_A); string mkquery(string|mapping dnameorquery, int|void cl, int|void type) { if(mappingp(dnameorquery)) return low_low_mkquery(dnameorquery); else return low_mkquery(random(65536),dnameorquery,cl,type); } string decode_domain(string msg, array(int) n) {
-
array(string) domains=({});
-
+
int pos=n[0]; int next=-1; array(string) ret=({});
-
+
int labels = 0;
while(pos < sizeof(msg)) {
-
+
labels++;
+
if (labels > 255)
+
error("Bad domain name. Too many labels.\n");
switch(int len=msg[pos]) { case 0: if(next==-1) next=pos+1; n[0]=next; return ret*"."; case 1..63: pos+=len+1; ret+=({msg[pos-len..pos-1]});
pike.git/lib/modules/Protocols.pmod/DNS.pmod:331:
if(next==-1) next=pos+2; pos=((len&63)<<8) + msg[pos+1]; continue; } break; } } string decode_string(string s, array(int) next) {
-
int
len
=
s[
next[0]];
+
int
pos
=
next[0]
;
+
int len=s[pos
];
next[0]+=len+1;
-
return s[
next[0]-len
..
next[0
]
-1]
;
+
return s[
pos+1
..
pos+len
];
} int decode_byte(string s, array(int) next) { return s[next[0]++]; } int decode_short(string s, array(int) next) {
-
sscanf(
s[next[0]
..next[0
]
+1],"%2c",int
ret);
-
next[0]
+=2
;
-
return ret;
+
return
s[next[0]
++
]
<<8
|
s[
next[0]
++]
;
} int decode_int(string s, array(int) next) {
-
sscanf(s[
next[0]
..next[0]+3],"%4c",int ret)
;
-
next[0]+=4;
-
return
ret
;
+
int pos =
next[0];
+
next[0]
+=
4;
+
return
s[pos++]<<24 | s[pos++]<<16 | s[pos++]<<8 | s[pos++]
;
} //! Decode a set of entries from an answer. //! //! @param s //! Encoded entries. //! //! @param num //! Number of entires in @[s]. //!
pike.git/lib/modules/Protocols.pmod/DNS.pmod:468:
//! @member int "order" //! @member int "preference" //! @member string "flags" //! @member string "service" //! @member string "regexp" //! @member string "replacement" //! @endmapping //! @value T_TXT //! @mapping //! @member string "txt"
+
//! Note: For historical reasons, when receiving decoded
+
//! DNS entries from a client, this will be the first string
+
//! in the TXT record only.
+
//! @member string "txta"
+
//! When receiving decoded DNS data from a client, txta is
+
//! the array of all strings in the record. When sending
+
//! multiple strings in a TXT record in a server, please
+
//! supply an array as "txt" containing the strings, txta
+
//! will be ignored.
//! @endmapping //! @value T_SPF //! @mapping //! @member string "spf" //! @endmapping //! @endint //! @endarray array decode_entries(string s,int num, array(int) next) { array(string) ret=({});
pike.git/lib/modules/Protocols.pmod/DNS.pmod:531:
{ if(x[1][0..0]=="_") m->proto=x[1][1..]; else m->proto=x[1]; } m->name=x[2..]*"."; break; case T_A:
-
m->a=
sprintf
(
"%{.%d%}",
values(s[next[0]..next[0]+m->len-1])
)[1
.
.]
;
+
m->a=(
array(string))
values(s[next[0]..next[0]+m->len-1])
*"
.
"
;
break; case T_AAAA:
-
m->aaaa=sprintf("%{
:
%02X%
02X%
}",
-
values(s[next[0]..next[0]+m->len-1])/2)[
1..
];
+
m->aaaa=sprintf("%{%02X%}",
+
(
values(s[next[0]..next[0]+m->len-1])/2)[
*
]
)*":"
;
break; case T_LOC: m->version = decode_byte(s,next); if (m->version == 0) { int aByte; aByte = decode_byte(s,next); m->size = pow((aByte>>4)&0xf , aByte&0xf)/100.0; aByte = decode_byte(s,next); m->h_perc = pow((aByte>>4)&0xf , aByte&0xf)/100.0;
pike.git/lib/modules/Protocols.pmod/DNS.pmod:571:
break; case T_NAPTR: m->order = decode_short (s, next); m->preference = decode_short (s, next); m->flags = decode_string (s, next); m->service = decode_string (s, next); m->regexp = decode_string (s, next); m->replacement = decode_domain (s, next); break; case T_TXT:
-
m->
txt
= decode_string(s, next);
+
{
+
int tlen;
+
+
m->
txta
=
({ });
+
while (tlen < m->len) {
+
m->txta += ({
decode_string(s, next)
})
;
+
tlen += sizeof(m->txta[-1]) + 1;
+
}
+
m->txt = m->txta[0];
+
}
break; case T_SPF: m->spf = decode_string(s, next); break; } next[0]=tmp+m->len; ret+=({m}); } return ret; } mapping decode_res(string s) { mapping m=([]);
-
sscanf(s,"%2c%c%c%2c%2c%2c%2c",
+
if (
sscanf(s,"%2c%c%c%2c%2c%2c%2c",
m->id, m->c1, m->c2, m->qdcount, m->ancount, m->nscount,
-
m->arcount);
+
m->arcount)
!= 7)
+
error("Bad DNS request, failed to parse header\n")
;
m->rd=m->c1&1; m->tc=(m->c1>>1)&1; m->aa=(m->c1>>2)&1; m->opcode=(m->c1>>3)&15; m->qr=(m->c1>>7)&1; m->rcode=m->c2&15; m->cd=(m->c2>>4)&1; m->ad=(m->c2>>5)&1; m->ra=(m->c2>>7)&1; m->length=sizeof(s);
-
array(string) tmp=({});
-
+
array(int) next=({12}); m->qd = allocate(m->qdcount); for(int i=0; i<m->qdcount; i++) {
-
+
if (next[0] > sizeof(s))
+
error("Bad DNS request, not enough data\n");
m->qd[i]=(["name":decode_domain(s,next)]); sscanf(s[next[0]..next[0]+3],"%2c%2c",m->qd[i]->type, m->qd[i]->cl); next[0]+=4; } m->an=decode_entries(s,m->ancount,next); m->ns=decode_entries(s,m->nscount,next); m->ar=decode_entries(s,m->arcount,next); return m; } };
pike.git/lib/modules/Protocols.pmod/DNS.pmod:678:
} } } destruct(udp2); } } destruct(udp); }; }
-
//! Base class for
implementing a Domain Name Service (DNS) server.
-
//!
-
//! This class is typically used by inheriting it,
-
//! and overloading
@[
reply_query()
]
and
@[
handle
_
response()
].
-
class server
+
//! Base class for @[
server
]
,
@[
tcp
_
server
].
+
class server
_base
{
-
//!
+
inherit protocol;
-
//inherit
Stdio.UDP
:
udp
;
+
array(
Stdio.UDP
|object)
ports
= ({ })
;
-
array(Stdio.UDP) ports = ({});
-
-
protected
void
send_reply(mapping r, mapping q, mapping m
, Stdio.UDP udp
)
+
protected
string
low_
send_reply(mapping r, mapping q, mapping m)
{
-
// FIXME: Needs to handle truncation somehow.
+
if(!r) r = (["rcode":4]); r->id = q->id; r->qr = 1; r->opcode = q->opcode; r->rd = q->rd; r->qd = r->qd || q->qd; string s = mkquery(r);
-
udp->send(m->ip,
m->port,
s
)
;
+
return
s;
} //! Reply to a query (stub). //! //! @param query //! Parsed query. //! //! @param udp_data
-
//! Raw UDP data.
+
//! Raw UDP data.
If the server operates in TCP mode (@[tcp_server]),
+
//! it will contain an additional tcp_con entry. In that case,
+
//! @expr{udp_data->tcp_con->con@} will contain the TCP connection the
+
//! request was received on as @[Stdio.File] object.
//! //! @param cb //! Callback you can call with the result instead of returning it. //! In that case, return @expr{0@} (zero). //! //! //! Overload this function to implement the proper lookup. //! //! @note //! To indicate the default failure @[cb] must be called with an //! argument of @expr{0@} (zero), and @expr{0@} (zero) be returned. //! //! @returns //! Returns @expr{0@} (zero) when the @[cb] callback will be used, //! or a result mapping if not: //! @mapping //! @member int "rcode"
-
+
//! 0 (or omit) for success, otherwise one of the Protocols.DNS.* constants
//! @member array(mapping(string:string|int))|void "an"
-
+
//! Answer section:
//! @array //! @elem mapping(string:string|int) entry //! @mapping //! @member string|array(string) "name" //! @member int "type" //! @member int "cl" //! @endmapping //! @endarray //! @member array|void "qd"
-
+
//! Question section, same format as @[an]; omit to return the original question
//! @member array|void "ns"
-
+
//! Authority section (usually NS records), same format as @[an]
//! @member array|void "ar"
-
+
//! Additional section, same format as @[an]
+
//! @member int "aa"
+
//! Set to 1 to include the Authoritative Answer bit in the response
+
//! @member int "tc"
+
//! Set to 1 to include the TrunCated bit in the response
+
//! @member int "rd"
+
//! Set to 1 to include the Recursion Desired bit in the response
+
//! @member int "ra"
+
//! Set to 1 to include the Recursion Available bit in the response
+
//! @member int "cd"
+
//! Set to 1 to include the Checking Disabled bit in the response
+
//! @member int "ad"
+
//! Set to 1 to include the Authenticated Data bit in the response
//! @endmapping protected mapping reply_query(mapping query, mapping udp_data, function(mapping:void) cb) { // Override this function. // // Return mapping may contain: // aa, ra, {ad, cd,} rcode, an, ns, ar return 0; } //! Handle a query. //! //! This function calls @[reply_query()], //! and dispatches the result to @[send_reply()].
-
protected void handle_query(mapping q, mapping m, Stdio.UDP udp)
+
protected void handle_query(mapping q, mapping m, Stdio.UDP
|object
udp)
{ int(0..1) result_available = 0; void _cb(mapping r) { if(result_available) error("DNS: you can either use the callback OR return the reply, not both.\n"); result_available = 1; send_reply(r, q, m, udp); }; mapping r = reply_query(q, m, _cb); if(r && result_available) error("DNS: you can either use the callback OR return the reply, not both.\n"); if(r) { result_available = 1; send_reply(r, q, m, udp); } } //! Handle a query response (stub). //! //! Overload this function to handle responses to possible recursive queries.
-
protected void handle_response(mapping r, mapping m, Stdio.UDP udp)
+
protected void handle_response(mapping r, mapping m, Stdio.UDP
|object
udp)
{ // This is a stub intended to simplify servers which allow recursion } //! Low-level DNS-data receiver. //! //! This function receives the raw DNS-data from the @[Stdio.UDP] socket
-
//! @[udp], decodes it, and dispatches the decoded
DNS request to
-
//! @[handle_query()] and @[handle_response()].
-
protected
private
void rec_data(mapping m, Stdio.UDP udp)
+
//!
or TCP connection object
@[udp], decodes it, and dispatches the decoded
+
//!
DNS request to @[handle_query()] and @[handle_response()].
+
protected void rec_data(mapping m, Stdio.UDP
|object
udp)
{ mixed err; mapping q; if (err = catch { q=decode_res(m->data); }) {
-
werror("DNS: Failed to read
UDP
packet.\n%s\n",
+
werror("DNS: Failed to read
%s
packet.\n%s\n",
+
udp->tcp_connection ? "TCP" : "UDP",
describe_backtrace(err)); if(m && m->data && sizeof(m->data)>=2) send_reply((["rcode":1]), mkmapping(({"id"}), array_sscanf(m->data, "%2c")), m, udp); } else if(q->qr) handle_response(q, m, udp); else handle_query(q, m, udp); }
-
+
protected void send_reply(mapping r, mapping q, mapping m,
+
Stdio.UDP|object con);
+
+
protected void destroy()
+
{
+
if(sizeof(ports))
+
{
+
foreach(ports;; object port)
+
destruct(port);
+
}
+
}
+
}
+
+
//! Base class for implementing a Domain Name Service (DNS) server operating
+
//! over UDP.
+
//!
+
//! This class is typically used by inheriting it,
+
//! and overloading @[reply_query()] and @[handle_response()].
+
//!
+
//! @seealso
+
//! @[dual_server]
+
class server
+
{
+
//!
+
inherit server_base;
+
+
//inherit Stdio.UDP : udp;
+
+
protected void send_reply(mapping r, mapping q, mapping m, Stdio.UDP udp) {
+
udp->send(m->ip, m->port, low_send_reply(r, q, m));
+
}
+
//! @decl void create() //! @decl void create(int port) //! @decl void create(string ip) //! @decl void create(string ip, int port) //! @decl void create(string ip, int port, string|int ... more) //! //! Open one or more new DNS server ports. //! //! @param ip //! The IP to bind to. Defaults to @expr{"::"@} or @expr{0@} (ie ANY)
pike.git/lib/modules/Protocols.pmod/DNS.pmod:857:
} else { if(!safe_bind(udp, args[i+1])) error("DNS: failed to bind port %d.\n", args[i+1]); } udp->set_read_callback(rec_data, udp); // port objects are stored for destruction when the server object is destroyed. ports += ({udp}); } }
+
}
-
protected
void
destory
()
+
+
//!
Base
class
for
implementing a Domain Name Service
(
DNS
)
server operating
+
//! over TCP.
+
//!
+
//! This class is typically used by inheriting it,
+
//! and overloading @[reply_query()] and @[handle_response()].
+
class tcp_server
{
-
if(sizeof(ports))
+
inherit
server_base;
+
+
mapping(Connection:int(1..1)) connections = ([ ]);
+
+
protected class Connection {
+
constant tcp_connection = 1;
+
+
protected int(0..1) write_ready;
+
protected string read_buffer = "", out_buffer = "";
+
protected array c_id;
+
Stdio.File con;
+
+
protected void create(Stdio.File con) {
+
this_program::con = con;
+
con->set_nonblocking(rcb, wcb, ccb);
+
c_id = call_out(destruct, 120, this);
+
}
+
+
protected void ccb(mixed id) {
+
destruct(con);
+
m_delete(connections, this);
+
}
+
+
protected void wcb(mixed id) {
+
if
(sizeof(
out_buffer)) {
+
int written = con->write(out_buffer);
+
out_buffer = out_buffer[written..];
+
} else
+
write_ready = 1;
+
}
+
+
protected void rcb(mixed id, string data) {
+
int len;
+
+
read_buffer += data;
+
if (sscanf(read_buffer, "%2c", len)) {
+
if (sizeof(read_buffer) > len - 2) {
+
string data = read_buffer[2..len+1];
+
string ip, port;
+
mapping m;
+
+
read_buffer = read_buffer[len+2..];
+
+
remove_call_out(c_id);
+
c_id = call_out(destruct, 120, this);
+
+
[ip, port] = con->query_address() / " ";
+
m = ([ "data" : data,
+
"ip" : ip,
+
"port" : (int)port,
+
"tcp_con" : this ]);
+
+
+
rec_data(m, this);
+
}
+
}
+
}
+
+
void send(string s) {
+
if (sizeof(s) > 65535)
+
error("DNS: Cannot send packets > 65535 bytes (%d here).\n", sizeof(s));
+
out_buffer += sprintf("%2H", s);
+
+
if (write_ready) {
+
int written = con->write(out_buffer);
+
out_buffer = out_buffer[written..];
+
write_ready = 0;
+
}
+
+
remove_call_out(c_id);
+
c_id = call_out(destruct, 120, this);
+
}
+
+
void destroy() {
+
if (con) con->close();
+
destruct(con);
+
m_delete(connections, this);
+
}
+
}
+
+
protected int accept(Stdio.Port port) {
+
connections[Connection(port->accept())] = 1;
+
}
+
+
protected void send_reply(mapping r, mapping q, mapping m, Connection con) {
+
con->send(low_send_reply(r, q, m));
+
}
+
+
//! @decl void create()
+
//! @decl void create(int port)
+
//! @decl void create(string ip)
+
//! @decl void create(string ip, int port)
+
//! @decl void create(string ip, int port, string|int ... more)
+
//!
+
//! Open one or more new DNS server
ports
.
+
//!
+
//! @param ip
+
//! The IP to bind to. Defaults to @expr{"::"@} or @expr{0@} (ie ANY
)
+
//! depending on whether IPv6 support is present or not.
+
//!
+
//! @param port
+
//! The port number to bind to. Defaults to @expr{53@}.
+
//!
+
//! @param more
+
//! Optional further DNS server ports to open.
+
//! Must be a set of @[ip], @[port] argument pairs.
+
protected void create(int|string|void arg1, string|int ... args
)
{
-
foreach
(
ports
;
;
Stdio.UDP
port
)
-
destruct
(
port
);
+
if(!arg1
&&
!sizeof
(
args))
+
arg1 = 53
;
+
if(!sizeof(args
)
)
+
{
+
if
(
stringp(arg1
)
)
+
args = ({ arg1, 53 })
;
+
else
+
args = ({ ANY, arg1 });
}
-
+
else
+
args = ({ arg1 }) + args;
+
if(sizeof(args)&1)
+
error("DNS: if you specify more than one argument, the number of "
+
"arguments needs to be even (server(ip1, port1, ip2, port2, "
+
"...)).\n");
+
for(int i;i<sizeof(args);i+=2) {
+
Stdio.Port port;
+
+
if(args[i]) {
+
port = Stdio.Port(args[i+1], accept, args[i]);
+
} else {
+
port = Stdio.Port(args[i+1], accept);
}
-
+
port->set_id(port);
+
// Port objects are stored for destruction when the server
+
// object is destroyed.
+
ports += ({ port });
}
-
+
}
-
+
protected void destroy()
+
{
+
foreach (connections; Connection con;) {
+
destruct(con);
+
}
-
+
::destroy();
+
}
+
}
+
+
//! This is both a @[server] and @[tcp_server].
+
class dual_server {
+
inherit server : UDP;
+
inherit tcp_server : TCP;
+
+
protected void send_reply(mapping r, mapping q, mapping m,
+
Connection|Stdio.UDP con) {
+
string rpl = low_send_reply(r, q, m);
+
+
if (!con->tcp_connection) {
+
if (sizeof(rpl) > 512) {
+
rpl = sprintf("%s%8c", rpl[..3], 0); // Truncate after header and
+
// send empty response
+
// ("dnscache strategy")
+
rpl[2] |= 2; // Set TC bit
+
}
+
con->send(m->ip, m->port, rpl);
+
} else
+
con->send(rpl);
+
}
+
+
protected void create(int|string|void arg1, string|int ... args)
+
{
+
::create(arg1, @args);
+
}
+
+
protected void destroy()
+
{
+
::destroy();
+
}
+
}
+
+
#define RETRIES 12 #define RETRY_DELAY 5
-
//!
Synchronous DNS client.
+
//! Synchronous DNS client.
class client { inherit protocol; #ifdef __NT__ array(string) get_tcpip_param(string val, void|string fallbackvalue) { array(string) res = ({}); foreach(({ "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1105:
if(domain) domains = ({ domain }) + domains; #endif nameservers -= ({ "" }); if (!sizeof(nameservers)) { /* Try localhost... */ nameservers = ({ "127.0.0.1" }); } domains -= ({ "" });
-
domains =
Array.
map(domains, lambda(string d) {
+
domains = map(domains, lambda(string d) {
if (d[-1] == '.') { return d[..<1]; } return d; }); } else { if(arrayp(server)) nameservers = server;
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1132:
if(stringp(domain)) domains = ({ domain }); } } //! Perform a synchronous DNS query. //! //! @param s //! Result of @[Protocols.DNS.protocol.mkquery] //! @returns
-
//! mapping containing query result or 0 on failure/timeout
+
//!
mapping containing query result or 0 on failure/timeout
//! //! @example //! @code //! // Perform a hostname lookup, results stored in r->an //! object d=Protocols.DNS.client(); //! mapping r=d->do_sync_query(d->mkquery("pike.lysator.liu.se", C_IN, T_A)); //! @endcode mapping do_sync_query(string s) { int i;
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1225:
//! @elem array(string) aliases //! DNS name(s). //! @endarray //! //! @note //! Prior to Pike 7.7 this function only returned IPv4 addresses. //! array gethostbyname(string s) { mapping a_records = low_gethostbyname(s, T_A);
-
mapping a6_records = low_gethostbyname(s, T_A6);
+
mapping aaaa_records = low_gethostbyname(s, T_AAAA); #if 0 werror("a_records: %O\n"
-
"a6_records: %O\n"
+
"aaaa_records: %O\n",
-
a_records,
a6_records,
aaaa_records);
+
a_records, aaaa_records);
#endif /* 0 */ array(string) names=({}); array(string) ips=({}); if (a_records) { foreach(a_records->an, mapping x) { if(x->name) names+=({x->name}); if(x->a) ips+=({x->a}); } }
-
// Prefer a6 to aaaa.
-
if (a6_records) {
-
foreach(a6_records->an, mapping x)
-
{
-
if(x->name)
-
names+=({x->name});
-
if(x->a6)
-
ips+=({x->a6});
-
}
-
}
+
if (aaaa_records) { foreach(aaaa_records->an, mapping x) { if(x->name) names+=({x->name}); if(x->aaaa) ips+=({x->aaaa}); } }
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1326:
} else { m = do_sync_query( mkquery("_" + service +"._"+ protocol + "." + name, C_IN, T_SRV)); } if (!m) { // no entries. return ({}); } array res=({});
-
+
foreach(m->an, mapping x) { res+=({({x->priority, x->weight, x->port, x->target})}); } // now we sort the array by priority as a convenience array y=({}); foreach(res, array t) y+=({t[0]}); sort(y, res);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1516:
return; } else { nsno = 0; } } send(nameservers[nsno],53,r->req); call_out(retry,RETRY_DELAY,r,nsno+1); }
+
//!
void do_query(string domain, int cl, int type, function(string,mapping,mixed...:void) callback, mixed ... args) { for(int e=next_client ? 100 : 256;e>=0;e--) { int lid = random(65536); if(!catch { requests[lid]++; }) { string req=low_mkquery(lid,domain,cl,type);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1545:
call_out(retry,RETRY_DELAY,r,1); return; } } /* We failed miserably to find a request id to use, * so we create a second UDP port to be able to have more * requests 'in the air'. /Hubbe */ if(!next_client)
-
next_client=
async
_
client
(nameservers,domains);
+
next_client=
this
_
program
(nameservers,domains);
next_client->do_query(domain, cl, type, callback, @args); } protected private void rec_data(mapping m) { mixed err; if (err = catch { if(m->port != 53 || !has_value(nameservers, m->ip)) return; sscanf(m->data,"%2c",int id);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1697:
Inside #if 0
"%s\n", server, domain, udp::query_address(), describe_backtrace(backtrace())); #endif /* 0 */ udp::set_read_callback(rec_data); ::create(server,domain); } };
+
// 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 async_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) \ void async_##X( string host, function callback, mixed ... args ) \ { \ if( !global_async_client ) \ global_async_client = async_client(); \ global_async_client->X(host,callback,@args); \ }