2253ed2002-12-01Martin Nilsson // $Id: DNS.pmod,v 1.69 2002/12/01 22:54:25 nilsson Exp $
bb02cc1997-09-15Fredrik Hübinette (Hubbe) // Not yet finished -- Fredrik Hubinette
2253ed2002-12-01Martin Nilsson 
27e9f92002-12-01H. William Welliver III //! Domain Name System //! RFC 1035
a20af62000-09-26Fredrik Hübinette (Hubbe) 
a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
189c792000-09-11Leif Stensson 
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant NOERROR=0; constant FORMERR=1; constant SERVFAIL=2; constant NXDOMAIN=3; constant NOTIMPL=4; constant NXRRSET=8; constant QUERY=0;
27e9f92002-12-01H. William Welliver III //! Class Internet
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant C_IN=1;
27e9f92002-12-01H. William Welliver III  //! Class CHAOS constant C_CH=3; //! Class Hesiod constant C_HS=4; //! Class ANY
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant C_ANY=255;
27e9f92002-12-01H. William Welliver III //! Type - host address
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_A=1;
27e9f92002-12-01H. William Welliver III  //! Type - authoritative name server
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_NS=2;
27e9f92002-12-01H. William Welliver III  //! Type - mail destination (Obsolete - use MX)
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_MD=3;
27e9f92002-12-01H. William Welliver III  //! Type - mail forwarder (Obsolete - use MX)
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_MF=4;
27e9f92002-12-01H. William Welliver III  //! Type - canonical name for an alias
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_CNAME=5;
27e9f92002-12-01H. William Welliver III  //! Type - start of a zone of authority
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_SOA=6;
27e9f92002-12-01H. William Welliver III  //! Type - mailbox domain name (EXPERIMENTAL)
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_MB=7;
27e9f92002-12-01H. William Welliver III  //! Type - mail group member (EXPERIMENTAL)
8be18a2000-04-12Marcus Comstedt constant T_MG=8;
27e9f92002-12-01H. William Welliver III  //! Type - mail rename domain name (EXPERIMENTAL)
8be18a2000-04-12Marcus Comstedt constant T_MR=9;
27e9f92002-12-01H. William Welliver III  //! Type - null RR (EXPERIMENTAL)
8be18a2000-04-12Marcus Comstedt constant T_NULL=10;
27e9f92002-12-01H. William Welliver III  //! Type - well known service description constant T_WKS=11; //! Type - domain name pointer
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_PTR=12;
27e9f92002-12-01H. William Welliver III  //! Type - host information
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_HINFO=13;
27e9f92002-12-01H. William Welliver III  //! Type - mailbox or mail list information
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_MINFO=14;
27e9f92002-12-01H. William Welliver III  //! Type - mail exchange
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_MX=15;
27e9f92002-12-01H. William Welliver III  //! Type - text strings
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_TXT=16;
27e9f92002-12-01H. William Welliver III  //! Type - IPv6 address record (RFC 1886, deprecated)
bb02cc1997-09-15Fredrik Hübinette (Hubbe) constant T_AAAA=28;
27e9f92002-12-01H. William Welliver III  //! Type - Service location record (RFC 2782)
b6e3442002-11-26H. William Welliver III constant T_SRV=33;
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
27e9f92002-12-01H. William Welliver III //! Type - IPv6 address record (RFC 2874, incomplete support) constant T_A6=38; //! Low level DNS protocol
bb02cc1997-09-15Fredrik Hübinette (Hubbe) class protocol { string mklabel(string s) { if(strlen(s)>63)
484a742002-03-09Martin Nilsson  error("Too long component in domain name.\n");
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  return sprintf("%c%s",strlen(s),s); }
8be18a2000-04-12Marcus Comstedt  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);
b6e3442002-11-26H. William Welliver III  case T_SRV: return sprintf("%2c%2c%2c", entry->priority, entry->weight, entry->port) + mkname(entry->target||"", pos+6, c);
8be18a2000-04-12Marcus Comstedt  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; }
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  string low_mkquery(int id, string dname, int cl, int type)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
8be18a2000-04-12Marcus Comstedt  return low_low_mkquery((["id":id, "rd":1, "qd":(["name":dname, "cl":cl, "type":type])]));
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
27e9f92002-12-01H. William Welliver III //! create a DNS query PDU //! //! @param dnameorquery //! @param cl //! record class such as Protocols.DNS.C_IN //! @param type //! query type such Protocols.DNS.T_A //! //! @returns //! data suitable for use with //! @[Protocols.DNS.client.do_sync_query]
2789992002-12-01H. William Welliver III //! //! @example //! // generate a query PDU for a address lookup on the hostname pike.ida.liu.se //! string q=Protocols.DNS.protocol()->mkquery("pike.ida.liu.se", Protocols.DNS.C_IN, Protocols.DNS.T_A);
8be18a2000-04-12Marcus Comstedt  string mkquery(string|mapping dnameorquery, int|void cl, int|void type)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  {
8be18a2000-04-12Marcus Comstedt  if(mappingp(dnameorquery)) return low_low_mkquery(dnameorquery); else return low_mkquery(random(65536),dnameorquery,cl,type);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  }
4755b72000-02-18Henrik Grubbström (Grubba)  string decode_domain(string msg, array(int) n)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) domains=({});
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  int pos=n[0]; int next=-1;
4755b72000-02-18Henrik Grubbström (Grubba)  array(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:
5b31d62000-04-11Marcus Comstedt  if((~len)&0xc0)
484a742002-03-09Martin Nilsson  error("Invalid message compression mode.\n");
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  if(next==-1) next=pos+2; pos=((len&63)<<8) + msg[pos+1]; continue; } break; } }
4755b72000-02-18Henrik Grubbström (Grubba)  string decode_string(string s, array(int) next)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  { int len=s[next[0]]; next[0]+=len+1; return s[next[0]-len..next[0]-1]; }
4755b72000-02-18Henrik Grubbström (Grubba)  int decode_short(string s, array(int) next)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  { sscanf(s[next[0]..next[0]+1],"%2c",int ret); next[0]+=2; return ret; }
4755b72000-02-18Henrik Grubbström (Grubba)  int decode_int(string s, array(int) next)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
a8c3f22002-01-13Marcus Comstedt  sscanf(s[next[0]..next[0]+3],"%4c",int ret); next[0]+=4;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  return ret; }
4755b72000-02-18Henrik Grubbström (Grubba)  array decode_entries(string s,int num, array(int) next)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
4755b72000-02-18Henrik Grubbström (Grubba)  array(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;
b6e3442002-11-26H. William Welliver III  case T_SRV: m->priority=decode_short(s,next); m->weight=decode_short(s,next); m->port=decode_short(s,next); m->target=decode_domain(s,next); array x=m->name/"."; if(x[0]) { if(x[0][0..0]=="_") m->service=x[0][1..]; else m->service=x[0]; } if(x[1]) { if(x[1][0..0]=="_") m->proto=x[1][1..]; else m->proto=x[1]; } if(x[2]) { if(x[2][0..0]=="_") x[2]=x[2][1..]; m->name=x[2..]*"."; } m->ttl=decode_int(s,next); break;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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);
5b31d62000-04-11Marcus Comstedt  m->rd=m->c1&1; m->tc=(m->c1>>1)&1; m->aa=(m->c1>>2)&1; m->opcode=(m->c1>>3)&15; m->qr=(m->c1>>7)&1;
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
5b31d62000-04-11Marcus Comstedt  m->rcode=m->c2&15; m->cd=(m->c2>>4)&1; m->ad=(m->c2>>5)&1; m->ra=(m->c2>>7)&1;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  m->length=strlen(s);
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) tmp=({});
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  int e;
4755b72000-02-18Henrik Grubbström (Grubba)  array(int) next=({12});
5b31d62000-04-11Marcus Comstedt  m->qd = allocate(m->qdcount); for(int i=0; i<m->qdcount; i++) { m->qd[i]=(["name":decode_domain(s,next)]); sscanf(s[next[0]..next[0]+3],"%2c%2c",m->qd[i]->type, m->qd[i]->cl); next[0]+=4; }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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; } };
8be18a2000-04-12Marcus Comstedt 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))
484a742002-03-09Martin Nilsson  error("DNS: failed to bind port "+port+".\n");
8be18a2000-04-12Marcus Comstedt  udp::set_read_callback(rec_data); } }
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
eabcd51999-07-25Henrik Grubbström (Grubba) #define RETRIES 12 #define RETRY_DELAY 5
7dc3162001-04-27Henrik Grubbström (Grubba) //! Synchronous DNS client.
a7c1441999-10-30Mirar (Pontus Hagland) class client {
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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__
e177732000-12-07Henrik Grubbström (Grubba)  array(string) get_tcpip_param(string val, void|string fallbackvalue)
a7c1441999-10-30Mirar (Pontus Hagland)  {
e177732000-12-07Henrik Grubbström (Grubba)  array(string) res = ({});
a7c1441999-10-30Mirar (Pontus Hagland)  foreach(({ "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP" }),string key)
37ffea1998-04-06Fredrik Hübinette (Hubbe)  {
a7c1441999-10-30Mirar (Pontus Hagland)  catch {
e177732000-12-07Henrik Grubbström (Grubba)  res += ({ RegGetValue(HKEY_LOCAL_MACHINE, key, val) });
a7c1441999-10-30Mirar (Pontus Hagland)  };
37ffea1998-04-06Fredrik Hübinette (Hubbe)  }
63ecf62000-06-28Fredrik Hübinette (Hubbe)  #if constant(RegGetKeyNames)
b3e4402001-03-14Henrik Grubbström (Grubba)  /* Catch if RegGetKeyNames() doesn't find the directory. */ catch { foreach(RegGetKeyNames(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\" "Parameters\\Interfaces"), string key)
63ecf62000-06-28Fredrik Hübinette (Hubbe)  { catch {
e177732000-12-07Henrik Grubbström (Grubba)  res += ({ RegGetValue(HKEY_LOCAL_MACHINE,
3097d92001-02-05Tomas Nilsson  "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\"
e177732000-12-07Henrik Grubbström (Grubba)  "Parameters\\Interfaces\\" + key, val) });
63ecf62000-06-28Fredrik Hübinette (Hubbe)  }; }
b3e4402001-03-14Henrik Grubbström (Grubba)  };
83e1902000-06-28Fredrik Hübinette (Hubbe) #endif
e177732000-12-07Henrik Grubbström (Grubba)  return sizeof(res) ? res : ({ fallbackvalue });
a7c1441999-10-30Mirar (Pontus Hagland)  }
37ffea1998-04-06Fredrik Hübinette (Hubbe) #endif
6676441998-01-25Henrik Grubbström (Grubba)  static private string match_etc_hosts(string host)
a7c1441999-10-30Mirar (Pontus Hagland)  { if (!etc_hosts) {
e177732000-12-07Henrik Grubbström (Grubba)  array(string) paths;
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #ifdef __NT__
e177732000-12-07Henrik Grubbström (Grubba)  paths = get_tcpip_param("DataBasePath");
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #else
e177732000-12-07Henrik Grubbström (Grubba)  paths = ({ "/etc", "/amitcp/db" });
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #endif
e177732000-12-07Henrik Grubbström (Grubba) 
a7c1441999-10-30Mirar (Pontus Hagland)  etc_hosts = ([ "localhost":"127.0.0.1" ]);
37ffea1998-04-06Fredrik Hübinette (Hubbe) 
e177732000-12-07Henrik Grubbström (Grubba)  foreach(paths, string path) { string raw = Stdio.read_file(path + "/hosts"); if (raw && sizeof(raw)) { foreach(raw/"\n"-({""}), string line) { // Handle comments, and split the line on white-space line = lower_case(replace((line/"#")[0], "\t", " ")); array arr = (line/" ") - ({ "" });
37ffea1998-04-06Fredrik Hübinette (Hubbe) 
e177732000-12-07Henrik Grubbström (Grubba)  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.
a7c1441999-10-30Mirar (Pontus Hagland)  }
6676441998-01-25Henrik Grubbström (Grubba)  } }
e177732000-12-07Henrik Grubbström (Grubba)  } else { // Couldn't read /etc/hosts.
6676441998-01-25Henrik Grubbström (Grubba)  } } }
a7c1441999-10-30Mirar (Pontus Hagland)  return(etc_hosts[lower_case(host)]); }
7dc3162001-04-27Henrik Grubbström (Grubba)  //! @decl void create() //! @decl void create(void|string|array server, void|int|array domain)
6676441998-01-25Henrik Grubbström (Grubba) 
6256271998-01-11Henrik Grubbström (Grubba)  array(string) nameservers = ({});
7f82921997-12-10David Hedbor  array domains = ({});
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  void create(void|string|array(string) server, void|int|array(string) domain)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  { if(!server) {
d357791999-04-14Henrik Grubbström (Grubba) #if __NT__
e177732000-12-07Henrik Grubbström (Grubba)  domains = get_tcpip_param("Domain", "") + get_tcpip_param("DhcpDomain", "") +
3c287a2000-12-08Henrik Grubbström (Grubba)  map(get_tcpip_param("SearchList", ""), lambda(string s) { return replace(s, " ", ",")/","; }) * ({});
89249d2000-08-22Johan Schön 
e177732000-12-07Henrik Grubbström (Grubba)  nameservers = map(get_tcpip_param("NameServer", "") +
3c287a2000-12-08Henrik Grubbström (Grubba)  get_tcpip_param("DhcpNameServer", ""), lambda(string s) { return replace(s, " ", ",")/","; }) * ({});
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #else
e177732000-12-07Henrik Grubbström (Grubba)  string domain;
9f70401998-11-13Marcus Comstedt  string resolv_conf;
8fe0142000-10-17Henrik Grubbström (Grubba)  foreach(({"/etc/resolv.conf", "/amitcp/db/resolv.conf"}), string resolv_loc)
9f70401998-11-13Marcus Comstedt  if ((resolv_conf = Stdio.read_file(resolv_loc))) break;
6256271998-01-11Henrik Grubbström (Grubba)  if (!resolv_conf) {
7b80842002-09-17Martin Nilsson  if (System->get_netinfo_property) {
d334772001-04-09Jonas Wallden  // Mac OS X / Darwin (and possibly other systems) that use // NetInfo may have these values in the database. if (nameservers =
7b80842002-09-17Martin Nilsson  System->get_netinfo_property(".",
d334772001-04-09Jonas Wallden  "/locations/resolver", "nameserver")) {
6241d62001-09-17Henrik Grubbström (Grubba)  nameservers = map(nameservers, `-, "\n");
d334772001-04-09Jonas Wallden  }
7b80842002-09-17Martin Nilsson  if (domains = System->get_netinfo_property(".",
d334772001-04-09Jonas Wallden  "/locations/resolver", "domain")) {
6241d62001-09-17Henrik Grubbström (Grubba)  domains = map(domains, `-, "\n");
d334772001-04-09Jonas Wallden  } } else { /* FIXME: Is this a good idea? * Why not just try the fallback? * /grubba 1999-04-14 * * Now uses 127.0.0.1 as fallback. * /grubba 2000-10-17 */ resolv_conf = "nameserver 127.0.0.1"; }
8fe0142000-10-17Henrik Grubbström (Grubba) #if 0
484a742002-03-09Martin Nilsson  error( "Protocols.DNS.client(): No /etc/resolv.conf!\n" );
8fe0142000-10-17Henrik Grubbström (Grubba) #endif /* 0 */
6256271998-01-11Henrik Grubbström (Grubba)  }
d334772001-04-09Jonas Wallden  if (resolv_conf) foreach(resolv_conf/"\n", string line)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
d334772001-04-09Jonas Wallden  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": // Save domain for later. domain = sizeof(rest) && rest; break; case "search": rest = replace(rest, "\t", " "); domains += ((rest/" ") - ({""})); break;
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
d334772001-04-09Jonas Wallden  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; }
a7c1441999-10-30Mirar (Pontus Hagland)  }
d334772001-04-09Jonas Wallden  if (sizeof(rest)) { nameservers += ({ rest }); } break; }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
9381ae2001-04-09Marcus Comstedt 
e177732000-12-07Henrik Grubbström (Grubba)  if(domain) domains = ({ domain }) + domains;
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #endif
e177732000-12-07Henrik Grubbström (Grubba)  nameservers -= ({ "" });
6256271998-01-11Henrik Grubbström (Grubba)  if (!sizeof(nameservers)) {
d357791999-04-14Henrik Grubbström (Grubba)  /* Try localhost... */
6256271998-01-11Henrik Grubbström (Grubba)  nameservers = ({ "127.0.0.1" }); }
dc01b51999-06-01Henrik Grubbström (Grubba)  domains -= ({ "" });
b9b6ec1998-01-25Henrik Grubbström (Grubba)  domains = Array.map(domains, lambda(string d) { if (d[-1] == '.') { return d[..sizeof(d)-2]; } return d; });
a7c1441999-10-30Mirar (Pontus Hagland)  } else {
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  if(arrayp(server))
940cb71998-07-17Henrik Grubbström (Grubba)  nameservers = server;
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  else
940cb71998-07-17Henrik Grubbström (Grubba)  nameservers = ({ server });
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  if(arrayp(domain)) domains = domain; else
522de11998-07-17Fredrik Hübinette (Hubbe)  if(stringp(domain))
4f3ea41998-07-17Fredrik Hübinette (Hubbe)  domains = ({ domain });
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  } }
27e9f92002-12-01H. William Welliver III //! perform a syncronous query //! //! @param s //! result of @[Protocols.DNS.protocol.mkquery] //! @returns //! mapping containing query result or 0 on failure/timeout
2789992002-12-01H. William Welliver III //! //! @example //! // perform a hostname lookup, results stored in r->an //! object d=Protocols.DNS.client(); //! mapping r=d->do_sync_query(d->mkquery("pike.ida.liu.se", C_IN, T_A));
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  mapping do_sync_query(string s) {
eabcd51999-07-25Henrik Grubbström (Grubba)  object udp = Stdio.UDP();
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  udp->bind(0); mapping m;
eabcd51999-07-25Henrik Grubbström (Grubba)  int i; for (i=0; i < RETRIES; i++) { udp->send(nameservers[i % sizeof(nameservers)], 53, s);
d692ea2000-09-07Leif Stensson  // upd->wait() can throw an error sometimes. catch { while (udp->wait(RETRY_DELAY)) { // udp->read() can throw an error on connection refused. catch { m = udp->read(); if ((m->port == 53) && (m->data[0..1] == s[0..1]) && (search(nameservers, m->ip) != -1)) { // Success. return decode_res(m->data); } }; } };
eabcd51999-07-25Henrik Grubbström (Grubba)  } // Failure. return 0;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
7dc3162001-04-27Henrik Grubbström (Grubba)  //! @decl array gethostbyname(string hostname)
bf895c2002-11-27Marek Habersack  //! Queries the host name from the default or given
7dc3162001-04-27Henrik Grubbström (Grubba)  //! DNS server. The result is an array with three elements,
bf895c2002-11-27Marek Habersack  //! //! @returns //! An array with the requested information about the specified //! host. //!
7dc3162001-04-27Henrik Grubbström (Grubba)  //! @array //! @elem string hostname //! Hostname. //! @elem array(string) ip //! IP number(s). //! @elem array(string) aliases //! DNS name(s). //! @endarray
a7c1441999-10-30Mirar (Pontus Hagland)  //!
4755b72000-02-18Henrik Grubbström (Grubba)  array gethostbyname(string s)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
27c6851997-12-10David Hedbor  mapping m;
7f82921997-12-10David Hedbor  if(sizeof(domains) && s[-1] != '.' && sizeof(s/".") < 3) {
eabcd51999-07-25Henrik Grubbström (Grubba)  m = do_sync_query(mkquery(s, C_IN, T_A));
27c6851997-12-10David Hedbor  if(!m || !m->an || !sizeof(m->an))
7f82921997-12-10David Hedbor  foreach(domains, string domain)
27c6851997-12-10David Hedbor  {
eabcd51999-07-25Henrik Grubbström (Grubba)  m = do_sync_query(mkquery(s+"."+domain, C_IN, T_A));
27c6851997-12-10David Hedbor  if(m && m->an && sizeof(m->an)) break; } } else {
eabcd51999-07-25Henrik Grubbström (Grubba)  m = do_sync_query(mkquery(s, C_IN, T_A));
27c6851997-12-10David Hedbor  }
eabcd51999-07-25Henrik Grubbström (Grubba)  if (!m) { return ({ 0, ({}), ({}) }); }
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) names=({}); array(string) ips=({});
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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,
eabcd51999-07-25Henrik Grubbström (Grubba)  ips, names, });
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
f971bd1997-09-15Fredrik Hübinette (Hubbe) 
27e9f92002-12-01H. William Welliver III  //! Queries the service record (RFC 2782) from the default or given
b6e3442002-11-26H. William Welliver III  //! DNS server. The result is an array of arrays with the //! following six elements for each record. The array is //! sorted according to the priority of each record. //! //! Each element of the array returned represents a service //! record. Each service record contains the following:
bf895c2002-11-27Marek Habersack  //! //! @returns //! An array with the requested information about the specified //! service. //!
b6e3442002-11-26H. William Welliver III  //! @array //! @elem int priority //! Priority //! @elem int weight //! Weight in event of multiple records with same priority. //! @elem int port //! port number //! @elem string target //! target dns name //! @endarray //! array getsrvbyname(string service, string protocol, string|void name) { mapping m; if(!service) error("no service name specified."); if(!protocol) error("no service name specified."); if(sizeof(domains) && !name) { // we haven't provided a target domain to search on. // we probably shouldn't do a searchdomains search, // as it might return ambiguous results. m = do_sync_query( mkquery("_" + service +"._"+ protocol + "." + name, C_IN, T_SRV)); if(!m || !m->an || !sizeof(m->an)) foreach(domains, string domain) { m = do_sync_query( mkquery("_" + service +"._"+ protocol + "." + domain, C_IN, T_SRV)); if(m && m->an && sizeof(m->an)) break; } } else { m = do_sync_query( mkquery("_" + service +"._"+ protocol + "." + name, C_IN, T_SRV)); } if (!m) { // no entries. return ({}); } array res=({}); foreach(m->an, mapping x) { res+=({({x->priority, x->weight, x->port, x->target})}); } // now we sort the array by priority as a convenience array y=({}); foreach(res, array t) y+=({t[0]}); sort(y, res); return res; }
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..]*"."; }
a7c1441999-10-30Mirar (Pontus Hagland) 
bf895c2002-11-27Marek Habersack  //! @decl array gethostbyaddr(string hostip) //! Queries the host name or ip from the default or given //! DNS server. The result is an array with three elements, //! //! @returns //! The requested data about the specified host. //! //! @array //! @elem string hostip //! The host IP. //! @elem array(string) ip //! IP number(s). //! @elem array(string) aliases //! DNS name(s). //! @endarray //!
1f9e122002-11-27Marek Habersack  array gethostbyaddr(string s)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
f971bd1997-09-15Fredrik Hübinette (Hubbe)  mapping m=do_sync_query(mkquery(arpa_from_ip(s), C_IN, T_PTR));
eabcd51999-07-25Henrik Grubbström (Grubba)  if (m) {
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) names=({}); array(string) ips=({});
eabcd51999-07-25Henrik Grubbström (Grubba)  foreach(m->an, mapping x)
27c6851997-12-10David Hedbor  {
eabcd51999-07-25Henrik Grubbström (Grubba)  if(x->ptr) names+=({x->ptr}); if(x->name) { ips+=({ip_from_arpa(x->name)}); }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
eabcd51999-07-25Henrik Grubbström (Grubba)  return ({ sizeof(names)?names[0]:0,
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  ips, names,
eabcd51999-07-25Henrik Grubbström (Grubba)  }); } else { // Lookup failed. return({ 0, ({}), ({}) }); }
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
7dc3162001-04-27Henrik Grubbström (Grubba)  //! @decl string get_primary_mx(string hostname)
bf895c2002-11-27Marek Habersack  //! Queries the primary mx for the host.
7dc3162001-04-27Henrik Grubbström (Grubba)  //! @returns //! Returns the hostname of the primary mail exchanger.
a7c1441999-10-30Mirar (Pontus Hagland)  //!
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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)); }
eabcd51999-07-25Henrik Grubbström (Grubba)  if (!m) { return 0; }
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; }
3833921999-12-22Henrik Grubbström (Grubba) 
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) get_mx(string host)
3833921999-12-22Henrik Grubbström (Grubba)  { mapping m; if(sizeof(domains) && host[-1] != '.' && sizeof(host/".") < 3) { m = do_sync_query(mkquery(host, C_IN, T_MX)); if(!m || !m->an || !sizeof(m->an)) { foreach(domains, string domain) { 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)); } if (!m) { return 0; } array a = m->an; array(string) b = column( a, "mx"); sort( column( a, "preference"), b); return b; }
bb02cc1997-09-15Fredrik Hübinette (Hubbe) }
cd6ed01998-07-17Fredrik Hübinette (Hubbe) #define REMOVE_DELAY 120 #define GIVE_UP_DELAY (RETRIES * RETRY_DELAY + REMOVE_DELAY)*2
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  class async_client { inherit client;
8540911999-07-21David Hedbor  inherit Stdio.UDP : udp;
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  async_client next_client;
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;
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  int timestamp;
4755b72000-02-18Henrik Grubbström (Grubba)  array 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)) {
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  if(r->retries++ > RETRIES)
6256271998-01-11Henrik Grubbström (Grubba)  {
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  call_out(remove,REMOVE_DELAY,r);
6256271998-01-11Henrik Grubbström (Grubba)  return; } else { nsno = 0; }
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  }
6256271998-01-11Henrik Grubbström (Grubba)  send(nameservers[nsno],53,r->req);
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  call_out(retry,RETRY_DELAY,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) {
75c66d1998-07-17Fredrik Hübinette (Hubbe)  for(int e=next_client ? 5 : 256;e>=0;e--)
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  {
75c66d1998-07-17Fredrik Hübinette (Hubbe)  int lid = random(65536); if(!catch { requests[lid]++; })
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  {
75c66d1998-07-17Fredrik Hübinette (Hubbe)  string req=low_mkquery(lid,domain,cl,type); object r=Request(); r->req=req; r->domain=domain; r->callback=callback; r->args=args; r->timestamp=time(); requests[lid]=r; udp::send(nameservers[0],53,r->req); call_out(retry,RETRY_DELAY,r,1); return;
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  } }
75c66d1998-07-17Fredrik Hübinette (Hubbe)  /* We failed miserably to find a request id to use, * so we create a second UDP port to be able to have more * requests 'in the air'. /Hubbe */ if(!next_client) next_client=async_client(nameservers,domains); next_client->do_query(domain, cl, type, callback, @args);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  }
529b301999-07-25David Hedbor  static private void rec_data(mapping m)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  {
6aa4b71998-06-13Henrik Grubbström (Grubba)  mixed err; if (err = catch { 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,
901dde1998-09-13Henrik Grubbström (Grubba)  int all, int type,
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...
901dde1998-09-13Henrik Grubbström (Grubba)  do_query(domain+"."+domains[multi], C_IN, type, generic_get, ++multi, all, type, field, domain, callback, @args);
27c6851997-12-10David Hedbor  } } else {
901dde1998-09-13Henrik Grubbström (Grubba)  if (all) { callback(domain, answer->an, @args); } else { 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,
901dde1998-09-13Henrik Grubbström (Grubba)  generic_get, 0, 0, T_A, "a", host, callback, @args );
27c6851997-12-10David Hedbor  } else { do_query(host, C_IN, T_A,
c9d48d1998-09-13Henrik Grubbström (Grubba)  generic_get, -1, 0, T_A, "a",
27c6851997-12-10David Hedbor  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,
901dde1998-09-13Henrik Grubbström (Grubba)  generic_get, -1, 0, T_PTR, "ptr",
f971bd1997-09-15Fredrik Hübinette (Hubbe)  ip, callback, @args); }
901dde1998-09-13Henrik Grubbström (Grubba)  void get_mx_all(string host, function callback, mixed ... args) { mapping m; if(sizeof(domains) && host[-1] != '.' && sizeof(host/".") < 3) { do_query(host, C_IN, T_MX, generic_get, 0, 1, T_MX, "mx", host, callback, @args); } else { do_query(host, C_IN, T_MX, generic_get, -1, 1, T_MX, "mx", host, callback, @args); } } void get_mx(string host, function callback, mixed ... args) { get_mx_all(host, lambda(string domain, array(mapping) mx, function callback, mixed ... args) { array a; if (mx) { a = column(mx, "mx"); sort(column(mx, "preference"), a); } callback(a, @args);
79ee221998-09-18Henrik Grubbström (Grubba)  }, callback, @args);
901dde1998-09-13Henrik Grubbström (Grubba)  }
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  void create(void|string|array(string) server, void|string|array(string) domain)
f971bd1997-09-15Fredrik Hübinette (Hubbe)  { if(!udp::bind(0))
484a742002-03-09Martin Nilsson  error( "DNS: failed to bind a port.\n" );
f971bd1997-09-15Fredrik Hübinette (Hubbe)  udp::set_read_callback(rec_data);
cd6ed01998-07-17Fredrik Hübinette (Hubbe)  ::create(server,domain);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  }
bb02cc1997-09-15Fredrik Hübinette (Hubbe) };
b6e3442002-11-26H. William Welliver III