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:1:
// Not yet finished -- Fredrik Hubinette
+
// RFC 1035
//! module Protocols //! submodule DNS constant NOERROR=0; constant FORMERR=1; constant SERVFAIL=2; constant NXDOMAIN=3; constant NOTIMPL=4; constant NXRRSET=8;
pike.git/lib/modules/Protocols.pmod/DNS.pmod:15:
constant C_IN=1; constant C_ANY=255; constant T_A=1; constant T_NS=2; constant T_MD=3; constant T_MF=4; constant T_CNAME=5; constant T_SOA=6; constant T_MB=7;
+
constant T_MG=8;
+
constant T_MR=9;
+
constant T_NULL=10;
constant T_PTR=12; constant T_HINFO=13; constant T_MINFO=14; constant T_MX=15; constant T_TXT=16; constant T_AAAA=28; class protocol { string mklabel(string s) { if(strlen(s)>63) throw(({"Too long component in domain name\n",backtrace()})); return sprintf("%c%s",strlen(s),s); }
-
+
static 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*".";
+
if(comp[n])
+
return sprintf("%2c", comp[n]|0xc000);
+
else {
+
if(pos<0x4000)
+
comp[n]=pos;
+
string l = mklabel(labels[0]);
+
return l + mkname(labels[1..], pos+strlen(l), comp);
+
}
+
}
+
+
static private string mkrdata(mapping entry, int pos, mapping(string:int) c)
+
{
+
switch(entry->type) {
+
case T_CNAME:
+
return mkname(entry->cname, pos, c);
+
case T_PTR:
+
return mkname(entry->ptr, pos, c);
+
case T_NS:
+
return mkname(entry->ns, pos, c);
+
case T_MD:
+
return mkname(entry->md, pos, c);
+
case T_MF:
+
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", strlen(entry->cpu||""), entry->cpu||"",
+
strlen(entry->os||""), entry->os||"");
+
case T_MINFO:
+
string rmailbx = mkname(entry->rmailbx, pos, c);
+
return rmailbx + mkname(entry->emailbx, pos+strlen(rmailbx), c);
+
case T_A:
+
case T_AAAA:
+
return sprintf("%@1c", (array(int))((entry->a||"0.0.0.0")/".")[0..3]);
+
case T_SOA:
+
string mname = mkname(entry->mname, pos, c);
+
return mname + mkname(entry->rname, pos+strlen(mname), c) +
+
sprintf("%4c%4c%4c%4c%4c", entry->serial, entry->refresh,
+
entry->retry, entry->expire, entry->minimum);
+
case T_TXT:
+
return Array.map(stringp(entry->txt)? ({entry->txt}):(entry->txt||({})),
+
lambda(string t) {
+
return sprintf("%1c%s", strlen(t), t);
+
})*"";
+
default:
+
return "";
+
}
+
}
+
+
static 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 += strlen(e)+2;
+
string rd = entry->rdata || mkrdata(entry, pos, comp);
+
res += e + sprintf("%2c", strlen(rd)) + rd;
+
pos += strlen(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}));
+
array ar = q->ar && (arrayp(q->ar)? q->ar : ({q->ar}));
+
string r = sprintf("%2c%c%c%2c%2c%2c%2c", q->id,
+
((q->rd)&1)|(((q->tc)&1)<<1)|(((q->aa)&1)<<2)|
+
(((q->opcode)&15)<<3)|(((q->qr)&1)<<7),
+
((q->rcode)&15)|(((q->cd)&1)<<4)|(((q->ad)&1)<<5)|
+
(((q->ra)&1)<<7),
+
qd && sizeof(qd), an && sizeof(an),
+
ns && sizeof(ns), ar && sizeof(ar));
+
mapping(string:int) c = ([]);
+
if(qd)
+
foreach(qd, mapping _qd)
+
r += mkname(_qd->name, strlen(r), c) +
+
sprintf("%2c%2c", _qd->type, _qd->cl);
+
if(an)
+
r+=encode_entries(an, strlen(r), c);
+
if(ns)
+
r+=encode_entries(ns, strlen(r), c);
+
if(ar)
+
r+=encode_entries(ar, strlen(r), c);
+
return r;
+
}
+
string low_mkquery(int id, string dname, int cl, int type) {
-
if ( dname[-1] == '.') dname = dname[..sizeof(dname)-2];
-
return
sprintf
("
%2c%c%c%2c%2c%2c%2c%s\000%2c%2c
"
,
-
id,
-
1,
0,
-
1,
-
0,
-
0,
-
0,
-
Array.map
(
dname/
"
.
",
mklabel)*
"",
-
type
,cl
);
-
+
return
low_low_mkquery
(
(
["
id
"
:
id,
"rd":
1,
+
"qd":
(
[
"
name
"
:dname
,
"
cl
"
:cl
,
"
type
":type]
)
]))
;
}
-
// This will have to be generalized for
-
// the server part...
-
string mkquery(string
dname
,
-
int cl,
-
int type)
+
string mkquery(string
|mapping
dnameorquery
, int
|void
cl, int
|void
type)
{
-
return low_mkquery(random(65536),
dname
,cl,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=({}); while(pos < sizeof(msg))
pike.git/lib/modules/Protocols.pmod/DNS.pmod:206:
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; } };
+
class server
+
{
+
inherit protocol;
+
inherit Stdio.UDP : udp;
-
+
static void 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);
+
}
+
+
static mapping reply_query(mapping q, mapping m)
+
{
+
// Override this function.
+
//
+
// Return mapping may contain:
+
// aa, ra, {ad, cd,} rcode, an, ns, ar
+
+
return 0;
+
}
+
+
static void handle_query(mapping q, mapping m)
+
{
+
mapping r = reply_query(q, m);
+
send_reply(r, q, m);
+
}
+
+
static void handle_response(mapping r, mapping m)
+
{
+
// This is a stub intended to simplify servers which allow recursion
+
}
+
+
static private void rec_data(mapping m)
+
{
+
mixed err;
+
mapping q;
+
if (err = catch {
+
q=decode_res(m->data);
+
}) {
+
werror(sprintf("DNS: Failed to read UDP packet.\n"
+
"%s\n", describe_backtrace(err)));
+
if(m && m->data && sizeof(m->data)>=2)
+
send_reply((["rcode":1]),
+
mkmapping(({"id"}), array_sscanf(m->data, "%2c")), m);
+
}
+
else if(q->qr)
+
handle_response(q, m);
+
else
+
handle_query(q, m);
+
}
+
+
void create(int|void port)
+
{
+
if(!port)
+
port = 53;
+
if(!udp::bind(port))
+
throw(({"DNS: failed to bind port "+port+".\n",backtrace()}));
+
udp::set_read_callback(rec_data);
+
}
+
+
}
+
+
#define RETRIES 12 #define RETRY_DELAY 5 class client { //! //! class client //! Synchronous DNS client. //!