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, }); } // Warning: NO TIMEOUT mixed *gethostbyaddr(string s) { mapping m=do_sync_query(mkquery(reverse(s/".")*"."+".IN-ADDR.ARPA", C_IN, T_PTR)); string *names=({}); string *ips=({}); foreach(m->an, mapping x) { if(x->ptr) names+=({x->ptr}); if(x->name) { ips+=({reverse(x->name/".")[2..]*"."}); } } 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;
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  inherit spider.dumUDP; 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; function success; function fail; int retries; mixed *args;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  };
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  static private mapping requests=([]); static private void remove(object(Request) r) { if(!r) return; sscanf(r->req,"%2c",int id); function f=r->fail; mixed *args=r->args; m_delete(requests,id); destruct(r); f(@args); } void retry(object(Request) r) { if(!r) return; if(r->retries > 6) { call_out(remove,120,r); }else{ send(nameserver,53,r->req); } } void do_query(string domain, int cl, int type, function callback, function fail_callback, 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; r->success=callback; r->fail=fail_callback; requests[id]=r; call_out(retry,5,r); send(nameserver,53,r->req); } static private void rec_data() { mapping m=read(); if(m->port != 53 || m->ip != nameserver) return; sscanf(m->data,"%2c",int id); object r=requests[id]; if(!r) return; function f=r->success; mixed *args=r->args; m_delete(requests,id); destruct(r); f(decode_res(m->data),@args); } void creat(string server) { bind(0); set_read_callback(rec_data); ::create(server); }
bb02cc1997-09-15Fredrik Hübinette (Hubbe) };