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=({});
b956561998-05-20Henrik Grubbström (Grubba)  while(pos < sizeof(msg))
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  { 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=({});
a87b6b1998-04-29Fredrik Hübinette (Hubbe)  for(int e=0;e<num && next[0]<strlen(s);e++)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  { 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;
22f1771998-01-16Fredrik Hübinette (Hubbe)  m->aa=(m->c1>>5)&1;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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;
6676441998-01-25Henrik Grubbström (Grubba)  static private int is_ip(string ip) {
b9b6ec1998-01-25Henrik Grubbström (Grubba)  return(replace(ip,
6676441998-01-25Henrik Grubbström (Grubba)  ({ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "." }),
b9b6ec1998-01-25Henrik Grubbström (Grubba)  ({ "", "", "", "", "", "", "", "", "", "", "" })) == "");
6676441998-01-25Henrik Grubbström (Grubba)  } static private mapping etc_hosts;
37ffea1998-04-06Fredrik Hübinette (Hubbe) #ifdef __NT__ string get_tcpip_param(string val) { foreach(({ "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP" }),string key) { catch { return RegGetValue(HKEY_LOCAL_MACHINE, key, value); }; } } #endif
6676441998-01-25Henrik Grubbström (Grubba)  static private string match_etc_hosts(string host)
37ffea1998-04-06Fredrik Hübinette (Hubbe)  { if (!etc_hosts) { string raw;
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #ifdef __NT__
37ffea1998-04-06Fredrik Hübinette (Hubbe)  raw=get_tcpip_param("DataBasePath")+"\\hosts";
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #else
37ffea1998-04-06Fredrik Hübinette (Hubbe)  raw="/etc/hosts";
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #endif
37ffea1998-04-06Fredrik Hübinette (Hubbe)  raw = Stdio.read_file(raw); etc_hosts = ([ "localhost":"127.0.0.1" ]);
59c50b1998-05-06Johan Schön  if (raw && sizeof(raw)) { foreach(raw/"\n"-({""}), string line) {
37ffea1998-04-06Fredrik Hübinette (Hubbe)  // Handle comments, and split the line on white-space line = lower_case(replace((line/"#")[0], "\t", " ")); array arr = (line/" ") - ({ "" }); if (sizeof(arr) > 1) { if (is_ip(arr[0])) { foreach(arr[1..], string name) { etc_hosts[name] = arr[0]; } } else { // Bad /etc/hosts entry ignored. }
6676441998-01-25Henrik Grubbström (Grubba)  } }
37ffea1998-04-06Fredrik Hübinette (Hubbe)  } else { // Couldn't read /etc/hosts.
6676441998-01-25Henrik Grubbström (Grubba)  } }
37ffea1998-04-06Fredrik Hübinette (Hubbe)  return(etc_hosts[lower_case(host)]);
6676441998-01-25Henrik Grubbström (Grubba)  }
6256271998-01-11Henrik Grubbström (Grubba)  array(string) nameservers = ({});
7f82921997-12-10David Hedbor  array domains = ({});
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  void create(void|string server) { if(!server) {
27c6851997-12-10David Hedbor  string domain;
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #if __NT__
37ffea1998-04-06Fredrik Hübinette (Hubbe)  domain=get_tcpip_param("Domain"); nameservers = ({ get_tcpip_param("NameServer") }); domains=get_tcpip_param("SearchList") / " "- ({""});
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #else
6256271998-01-11Henrik Grubbström (Grubba)  string resolv_conf = Stdio.read_file("/etc/resolv.conf"); if (!resolv_conf) { throw(({ "Protocols.DNS.client(): No /etc/resolv.conf!\n", backtrace() })); } foreach(resolv_conf/"\n", string line)
27c6851997-12-10David Hedbor  { 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)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
6676441998-01-25Henrik Grubbström (Grubba)  case "domain":
27c6851997-12-10David Hedbor  // Save domain for later. domain = rest; break;
6676441998-01-25Henrik Grubbström (Grubba)  case "search":
27c6851997-12-10David Hedbor  rest = replace(rest, "\t", " "); foreach(rest / " " - ({""}), string dom)
7f82921997-12-10David Hedbor  domains += ({dom});
27c6851997-12-10David Hedbor  break;
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
6676441998-01-25Henrik Grubbström (Grubba)  case "nameserver": if (!is_ip(rest)) { // Not an IP-number! string host = rest; if (!(rest = match_etc_hosts(host))) { werror(sprintf("Protocols.DNS.client(): " "Can't resolv nameserver \"%s\"\n", host)); break; } } if (sizeof(rest)) {
b956561998-05-20Henrik Grubbström (Grubba)  nameservers = `+(nameservers, ({ rest }));
6676441998-01-25Henrik Grubbström (Grubba)  }
27c6851997-12-10David Hedbor  break;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
27c6851997-12-10David Hedbor  }
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #endif
6256271998-01-11Henrik Grubbström (Grubba)  if (!sizeof(nameservers)) { nameservers = ({ "127.0.0.1" }); }
27c6851997-12-10David Hedbor  if(domain)
7f82921997-12-10David Hedbor  domains = ({ domain }) + domains;
b9b6ec1998-01-25Henrik Grubbström (Grubba)  domains = Array.map(domains, lambda(string d) { if (d[-1] == '.') { return d[..sizeof(d)-2]; } return d; });
27c6851997-12-10David Hedbor  } else {
6256271998-01-11Henrik Grubbström (Grubba)  nameservers= ({ server });
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  } } // Warning: NO TIMEOUT mapping do_sync_query(string s) { object udp=spider.dumUDP(); udp->bind(0);
6256271998-01-11Henrik Grubbström (Grubba)  udp->send(nameservers[0],53,s);
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  mapping m; do { m=udp->read(); } while (m->port != 53 ||
6256271998-01-11Henrik Grubbström (Grubba)  m->ip != nameservers[0] ||
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  m->data[0..1]!=s[0..1]); return decode_res(m->data); } // Warning: NO TIMEOUT mixed *gethostbyname(string s) {
27c6851997-12-10David Hedbor  mapping m;
7f82921997-12-10David Hedbor  if(sizeof(domains) && s[-1] != '.' && sizeof(s/".") < 3) {
27c6851997-12-10David Hedbor  m=do_sync_query(mkquery(s, C_IN, T_A)); if(!m || !m->an || !sizeof(m->an))
7f82921997-12-10David Hedbor  foreach(domains, string domain)
27c6851997-12-10David Hedbor  { m=do_sync_query(mkquery(s+"."+domain, C_IN, T_A)); if(m && m->an && sizeof(m->an)) break; } } else { m=do_sync_query(mkquery(s, C_IN, T_A)); }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  string *names=({}); string *ips=({}); foreach(m->an, mapping x)
27c6851997-12-10David Hedbor  { if(x->name) names+=({x->name}); if(x->a) ips+=({x->a}); }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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)
27c6851997-12-10David Hedbor  { if(x->ptr) names+=({x->ptr}); if(x->name)
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
27c6851997-12-10David Hedbor  { ips+=({ip_from_arpa(x->name)});
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
27c6851997-12-10David Hedbor  }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  return ({ sizeof(names)?names[0]:0, ips, names, }); } string get_primary_mx(string host) {
27c6851997-12-10David Hedbor  mapping m;
7f82921997-12-10David Hedbor  if(sizeof(domains) && host[-1] != '.' && sizeof(host/".") < 3) {
27c6851997-12-10David Hedbor  m=do_sync_query(mkquery(host, C_IN, T_MX)); if(!m || !m->an || !sizeof(m->an))
7f82921997-12-10David Hedbor  foreach(domains, string domain)
27c6851997-12-10David Hedbor  { m=do_sync_query(mkquery(host+"."+domain, C_IN, T_MX)); if(m && m->an && sizeof(m->an)) break; } } else { m=do_sync_query(mkquery(host, C_IN, T_MX)); }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  int minpref=29372974; string ret; foreach(m->an, mapping m2)
27c6851997-12-10David Hedbor  { if(m2->preference<minpref)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
27c6851997-12-10David Hedbor  ret=m2->mx; minpref=m2->preference;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
27c6851997-12-10David Hedbor  }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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) 
366e6d1998-07-17Henrik Grubbström (Grubba) #if constant(thread_create) object lock = Thread.Mutex(); #endif /* constant(thread_create) */
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); }
6256271998-01-11Henrik Grubbström (Grubba)  void retry(object(Request) r, void|int nsno)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  { if(!r) return;
6256271998-01-11Henrik Grubbström (Grubba)  if (nsno >= sizeof(nameservers)) { if(r->retries++ > 12) { call_out(remove,120,r); return; } else { nsno = 0; }
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  }
6256271998-01-11Henrik Grubbström (Grubba)  send(nameservers[nsno],53,r->req); call_out(retry,5,r,nsno+1);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  } 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) {
366e6d1998-07-17Henrik Grubbström (Grubba) #if constant(thread_create) object key = lock->lock(); #endif /* constant(thread_create) */
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  id++; id&=65535;
366e6d1998-07-17Henrik Grubbström (Grubba)  int lid = id; #if constant(thread_create) key = 0; #endif /* constant(thread_create) */
3d3ed71997-09-15Fredrik Hübinette (Hubbe) 
366e6d1998-07-17Henrik Grubbström (Grubba)  if(requests[lid])
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  throw(({"Cannot find an empty request slot.\n",backtrace()}));
366e6d1998-07-17Henrik Grubbström (Grubba)  string req=low_mkquery(lid,domain,cl,type);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  object r=Request(); r->req=req;
f971bd1997-09-15Fredrik Hübinette (Hubbe)  r->domain=domain; r->callback=callback; r->args=args;
366e6d1998-07-17Henrik Grubbström (Grubba)  requests[lid]=r;
6256271998-01-11Henrik Grubbström (Grubba)  udp::send(nameservers[0],53,r->req); call_out(retry,5,r,1);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  } static private void rec_data() {
6aa4b71998-06-13Henrik Grubbström (Grubba)  mixed err; if (err = catch { mapping m=udp::read(); if(m->port != 53 || search(nameservers, m->ip) == -1) return; sscanf(m->data,"%2c",int id); object r=requests[id]; if(!r) return; m_delete(requests,id); r->callback(r->domain,decode_res(m->data),@r->args); destruct(r); }) { werror(sprintf("DNS: Failed to read UDP packet. Connection refused?\n" "%s\n", describe_backtrace(err))); }
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  }
f971bd1997-09-15Fredrik Hübinette (Hubbe)  static private void generic_get(string d, mapping answer,
27c6851997-12-10David Hedbor  int multi,
f971bd1997-09-15Fredrik Hübinette (Hubbe)  string field, string domain, function callback, mixed ... args) { if(!answer || !answer->an || !sizeof(answer->an)) {
7f82921997-12-10David Hedbor  if(multi == -1 || multi >= sizeof(domains)) {
27c6851997-12-10David Hedbor  // Either a request without multi (ip, or FQDN) or we have tried all // domains. callback(domain,0,@args); } else { // Multiple domain request. Try the next one...
7f82921997-12-10David Hedbor  do_query(domain+"."+domains[multi], C_IN, T_A,
27c6851997-12-10David Hedbor  generic_get, ++multi, "a", domain, callback, @args); } } else {
b7cc991997-12-04Per Hedbor  foreach(answer->an, array an) if(an[field]) { callback(domain,an[field],@args); return; } callback(domain,0,@args); return;
f971bd1997-09-15Fredrik Hübinette (Hubbe)  } } void host_to_ip(string host, function callback, mixed ... args) {
7f82921997-12-10David Hedbor  if(sizeof(domains) && host[-1] != '.' && sizeof(host/".") < 3) {
27c6851997-12-10David Hedbor  do_query(host, C_IN, T_A, generic_get, 0, "a", host, callback, @args ); } else { do_query(host, C_IN, T_A, generic_get, -1, "a", host, callback, @args); }
f971bd1997-09-15Fredrik Hübinette (Hubbe)  } 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,
27c6851997-12-10David Hedbor  generic_get, -1, "ptr",
f971bd1997-09-15Fredrik Hübinette (Hubbe)  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) };