From 8be18a15ec980776c4dac294133282341d15bf77 Mon Sep 17 00:00:00 2001 From: Marcus Comstedt <marcus@mc.pp.se> Date: Wed, 12 Apr 2000 00:34:57 +0200 Subject: [PATCH] Server side of protocol implemented. Rev: lib/modules/Protocols.pmod/DNS.pmod:1.40 --- lib/modules/Protocols.pmod/DNS.pmod | 203 +++++++++++++++++++++++++--- 1 file changed, 186 insertions(+), 17 deletions(-) diff --git a/lib/modules/Protocols.pmod/DNS.pmod b/lib/modules/Protocols.pmod/DNS.pmod index b83a2b57fe..36d7e70e31 100644 --- a/lib/modules/Protocols.pmod/DNS.pmod +++ b/lib/modules/Protocols.pmod/DNS.pmod @@ -1,4 +1,5 @@ // Not yet finished -- Fredrik Hubinette +// RFC 1035 //! module Protocols //! submodule DNS @@ -22,6 +23,9 @@ 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; @@ -39,31 +43,126 @@ class protocol 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) @@ -213,6 +312,76 @@ class protocol } }; +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 -- GitLab