bb02cc1997-09-15Fredrik Hübinette (Hubbe) // Not yet finished -- Fredrik Hubinette constant NOERROR=0; constant FORMERR=1; constant SERVFAIL=2; constant NXDOMAIN=3; constant NOTIMPL=4; constant NXRRSET=8; constant QUERY=0; 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_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",backtrace()})); return sprintf("%c%s",strlen(s),s); }
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  string low_mkquery(int id, string dname, int cl, int type)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  { 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); }
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  // This will have to be generalized for // the server part... string mkquery(string dname, int cl, int type) { return low_mkquery(random(65536),dname,cl,type); }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  string decode_domain(string msg, int *n) { string *domains=({}); int pos=n[0]; int next=-1; string *ret=({}); while(1) { 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]}); continue; default: if(next==-1) next=pos+2; pos=((len&63)<<8) + msg[pos+1]; continue; } break; } } string decode_string(string s, int *next) { int len=s[next[0]]; next[0]+=len+1; return s[next[0]-len..next[0]-1]; } int decode_short(string s, int *next) { sscanf(s[next[0]..next[0]+1],"%2c",int ret); next[0]+=2; return ret; } int decode_int(string s, int *next) { sscanf(s[next[0]..next[0]+1],"%2c",int ret); next[0]+=2; return ret; } mixed *decode_entries(string s,int num, int *next) { string *ret=({}); for(int e=0;e<num;e++) { mapping m=([]); m->name=decode_domain(s,next); sscanf(s[next[0]..next[0]+10], "%2c%2c%4c%2c", m->type,m->cl,m->ttl,m->len); next[0]+=10; int tmp=next[0]; switch(m->type) { case T_CNAME: m->cname=decode_domain(s,next); break; case T_PTR: m->ptr=decode_domain(s,next); break; case T_NS: m->ns=decode_domain(s,next); break; case T_MX: m->preference=decode_short(s,next); m->mx=decode_domain(s,next); break; case T_HINFO: m->cpu=decode_string(s,next); m->os=decode_string(s,next); break; case T_A: case T_AAAA: m->a=sprintf("%{.%d%}",values(s[next[0]..next[0]+m->len-1]))[1..]; break; case T_SOA: m->mname=decode_domain(s,next); m->rname=decode_domain(s,next); m->serial=decode_int(s,next); m->refresh=decode_int(s,next); m->retry=decode_int(s,next); m->expire=decode_int(s,next); m->minimum=decode_int(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", m->id, m->c1, m->c2, m->qdcount, m->ancount, m->nscount, m->arcount); m->rd=(m->c1>>7)&1; m->tc=(m->c1>>6)&1; m->tc=(m->c1>>5)&1; m->opcode=(m->c1>>1)&15; m->qr=m->c1&1; m->rcode=(m->c2>>4)&15; m->cd=(m->c2>>3)&1; m->ad=(m->c2>>2)&1; m->ra=(m->c2)&1; m->length=strlen(s); string *tmp=({}); int e; if(m->qdcount!=1) return m; int *next=({12}); m->qd=decode_domain(s,next); sscanf(s[next[0]..next[0]+3],"%2c%2c",m->type, m->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 client { inherit protocol; string nameserver; void create(void|string server) { if(!server) { foreach(Stdio.read_file("/etc/resolv.conf")/"\n", string line) { string rest; sscanf(line,"%s#",line); sscanf(line,"%*[\r \t]%s",line); line=reverse(line); sscanf(line,"%*[\r \t]%s",line); line=reverse(line); sscanf(line,"%s%*[ \t]%s",line,rest); switch(line) { case "domain": case "search": break; // Not yet implemented case "nameserver": nameserver=rest; break; } } }else{ nameserver=server; } } // Warning: NO TIMEOUT mapping do_sync_query(string s) { object udp=spider.dumUDP(); udp->bind(0); udp->send(nameserver,53,s); mapping m; do { m=udp->read(); } while (m->port != 53 || m->ip != nameserver || m->data[0..1]!=s[0..1]); return decode_res(m->data); } // Warning: NO TIMEOUT mixed *gethostbyname(string s) { mapping m=do_sync_query(mkquery(s, C_IN, T_A)); string *names=({}); string *ips=({}); foreach(m->an, mapping x) { if(x->name) names+=({x->name}); if(x->a) ips+=({x->a}); } return ({ sizeof(names)?names[0]:0, ips, names, }); }
f971bd1997-09-15Fredrik Hübinette (Hubbe)  string arpa_from_ip(string ip) { return reverse(ip/".")*"."+".IN-ADDR.ARPA"; } string ip_from_arpa(string arpa) { return reverse(arpa/".")[2..]*"."; }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  // Warning: NO TIMEOUT mixed *gethostbyaddr(string s) {
f971bd1997-09-15Fredrik Hübinette (Hubbe)  mapping m=do_sync_query(mkquery(arpa_from_ip(s), C_IN, T_PTR));
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  string *names=({}); string *ips=({}); foreach(m->an, mapping x) { if(x->ptr) names+=({x->ptr}); if(x->name) {
f971bd1997-09-15Fredrik Hübinette (Hubbe)  ips+=({ip_from_arpa(x->name)});
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  } } return ({ sizeof(names)?names[0]:0, ips, names, }); } string get_primary_mx(string host) { mapping m=do_sync_query(mkquery(host,C_IN,T_MX)); int minpref=29372974; string ret; foreach(m->an, mapping m2) { if(m2->preference<minpref) { ret=m2->mx; minpref=m2->preference; } } return ret; } } class async_client { inherit client;
f971bd1997-09-15Fredrik Hübinette (Hubbe)  inherit spider.dumUDP : udp;
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  int id;
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  class Request
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  string req;
f971bd1997-09-15Fredrik Hübinette (Hubbe)  string domain; function callback;
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  int retries; mixed *args;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  };
3d3ed71997-09-15Fredrik Hübinette (Hubbe) 
f971bd1997-09-15Fredrik Hübinette (Hubbe)  mapping requests=([]);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  static private void remove(object(Request) r) { if(!r) return; sscanf(r->req,"%2c",int id); m_delete(requests,id);
f971bd1997-09-15Fredrik Hübinette (Hubbe)  r->callback(r->domain,0,@r->args);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  destruct(r); } void retry(object(Request) r) { if(!r) return;
f971bd1997-09-15Fredrik Hübinette (Hubbe)  if(r->retries++ > 12)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  { call_out(remove,120,r); }else{
f971bd1997-09-15Fredrik Hübinette (Hubbe)  call_out(retry,5,r);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  send(nameserver,53,r->req); } } void do_query(string domain, int cl, int type,
f971bd1997-09-15Fredrik Hübinette (Hubbe)  function(string,mapping,mixed...:void) callback,
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  mixed ... args) { id++; id&=65535; string req=low_mkquery(id,domain,cl,type); if(requests[id]) throw(({"Cannot find an empty request slot.\n",backtrace()})); object r=Request(); r->req=req;
f971bd1997-09-15Fredrik Hübinette (Hubbe)  r->domain=domain; r->callback=callback; r->args=args;
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  requests[id]=r; call_out(retry,5,r);
f971bd1997-09-15Fredrik Hübinette (Hubbe)  udp::send(nameserver,53,r->req);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  } static private void rec_data() {
f971bd1997-09-15Fredrik Hübinette (Hubbe)  mapping m=udp::read();
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  if(m->port != 53 || m->ip != nameserver) return; sscanf(m->data,"%2c",int id); object r=requests[id]; if(!r) return; m_delete(requests,id);
f971bd1997-09-15Fredrik Hübinette (Hubbe)  r->callback(r->domain,decode_res(m->data),@r->args);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  destruct(r); }
f971bd1997-09-15Fredrik Hübinette (Hubbe)  static private void generic_get(string d, mapping answer, string field, string domain, function callback, mixed ... args) { if(!answer || !answer->an || !sizeof(answer->an)) { callback(domain,0,@args); }else{ callback(domain,answer->an[0][field],@args); } } void host_to_ip(string host, function callback, mixed ... args) { do_query(host, C_IN, T_A, generic_get,"a", host, callback, @args ); } void ip_to_host(string ip, function callback, mixed ... args)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  {
f971bd1997-09-15Fredrik Hübinette (Hubbe)  do_query(arpa_from_ip(ip), C_IN, T_PTR, generic_get, "ptr", ip, callback, @args); } void create(void|string server) { if(!udp::bind(0)) throw(({"DNS: failed to bind a port.\n",backtrace()})); udp::set_read_callback(rec_data);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  ::create(server); }
bb02cc1997-09-15Fredrik Hübinette (Hubbe) };