bb02cc1997-09-15Fredrik Hübinette (Hubbe) // Not yet finished -- Fredrik Hubinette
2253ed2002-12-01Martin Nilsson 
f7616f2010-01-05H. William Welliver III  //inherit Stdio.UDP : udp;
89a02a2012-01-31Henrik Grubbström (Grubba)  //! Support for the Domain Name System protocol. //!
8630372008-07-23Henrik Grubbström (Grubba) //! RFC 1034, RFC 1035 and RFC 2308
89a02a2012-01-31Henrik Grubbström (Grubba) 
a0122a2013-09-18Martin Nilsson protected void send_reply(mapping r, mapping q, mapping m, Stdio.UDP udp);
a20af62000-09-26Fredrik Hübinette (Hubbe) 
a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
189c792000-09-11Leif Stensson 
555c1f2012-11-14Chris Angelico // documentation taken from RFC 2136
9354632012-12-24Tobias S. Josefowitz 
555c1f2012-11-14Chris Angelico //! No error condition.
f53d9c2008-07-13Marcus Comstedt final constant NOERROR=0;
9354632012-12-24Tobias S. Josefowitz 
a0122a2013-09-18Martin Nilsson //! The name server was unable to interpret the request due to a //! format error.
f53d9c2008-07-13Marcus Comstedt final constant FORMERR=1;
9354632012-12-24Tobias S. Josefowitz 
a0122a2013-09-18Martin Nilsson //! The name server encountered an internal failure while processing //! this request, for example an operating system error or a //! forwarding timeout.
f53d9c2008-07-13Marcus Comstedt final constant SERVFAIL=2;
9354632012-12-24Tobias S. Josefowitz 
555c1f2012-11-14Chris Angelico //! Some name that ought to exist, does not exist.
f53d9c2008-07-13Marcus Comstedt final constant NXDOMAIN=3;
9354632012-12-24Tobias S. Josefowitz 
555c1f2012-11-14Chris Angelico //! The name server does not support the specified Opcode.
f53d9c2008-07-13Marcus Comstedt final constant NOTIMPL=4;
9354632012-12-24Tobias S. Josefowitz 
a0122a2013-09-18Martin Nilsson //! The name server refuses to perform the specified operation for //! policy or security reasons.
8630372008-07-23Henrik Grubbström (Grubba) final constant REFUSED=5;
9354632012-12-24Tobias S. Josefowitz 
555c1f2012-11-14Chris Angelico //! Some RRset that ought to exist, does not exist.
f53d9c2008-07-13Marcus Comstedt final constant NXRRSET=8;
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
f53d9c2008-07-13Marcus Comstedt final constant QUERY=0;
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
c534f62002-12-23Henrik Grubbström (Grubba) //! Resource classes enum ResourceClass { //! Class Internet C_IN=1,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Class CSNET (Obsolete) C_CS=2,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Class CHAOS C_CH=3,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Class Hesiod C_HS=4,
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Class ANY C_ANY=255, };
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba) //! Entry types enum EntryType { //! Type - host address T_A=1,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - authoritative name server T_NS=2,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - mail destination (Obsolete - use MX) T_MD=3,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - mail forwarder (Obsolete - use MX) T_MF=4,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - canonical name for an alias T_CNAME=5,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - start of a zone of authority T_SOA=6,
27e9f92002-12-01H. William Welliver III 
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - mailbox domain name (Obsolete)
c534f62002-12-23Henrik Grubbström (Grubba)  T_MB=7,
27e9f92002-12-01H. William Welliver III 
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - mail group member (Obsolete)
c534f62002-12-23Henrik Grubbström (Grubba)  T_MG=8,
27e9f92002-12-01H. William Welliver III 
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - mail rename domain name (Obsolete)
c534f62002-12-23Henrik Grubbström (Grubba)  T_MR=9,
27e9f92002-12-01H. William Welliver III 
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - null RR (Obsolete RFC 1035)
c534f62002-12-23Henrik Grubbström (Grubba)  T_NULL=10,
27e9f92002-12-01H. William Welliver III 
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - well known service description (Obsolete RFC 1123, RFC 1127)
c534f62002-12-23Henrik Grubbström (Grubba)  T_WKS=11,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - domain name pointer T_PTR=12,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - host information T_HINFO=13,
27e9f92002-12-01H. William Welliver III 
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - mailbox or mail list information (Obsolete)
c534f62002-12-23Henrik Grubbström (Grubba)  T_MINFO=14,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - mail exchange T_MX=15,
27e9f92002-12-01H. William Welliver III 
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - text strings T_TXT=16,
27e9f92002-12-01H. William Welliver III 
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - Responsible Person T_RP=17, //! Type - AFC database record (RFC 1183) T_AFSDB=18, //! Type - X25 PSDN address (RFC 1183) T_X25=19, //! Type - ISDN address (RFC 1183) T_ISDN=20, //! Type - Route Through (RFC 1183) T_RT=21, //! Type - OSI Network Service Access Protocol (RFC 1348, RFC 1637, //! RFC 1706) T_NSAP=22, //! Type - OSI NSAP Pointer (RFC 1348, Obsolete RFC 1637) T_NSAP_PTR=23, //! Type - Signature (RFC 2535) T_SIG=24, //! Type - Key record (RFC 2535 and RFC 2930) T_KEY=25, //! Type - Pointer to X.400 mapping information (RFC 1664) T_PX=26, //! Type - Global Position (RFC 1712 Obsolete use LOC). T_GPOS=27,
555c1f2012-11-14Chris Angelico  //! Type - IPv6 address record (RFC 1886)
c534f62002-12-23Henrik Grubbström (Grubba)  T_AAAA=28,
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
79d3032004-04-13Jeff Hungerford  //! Type - Location Record (RFC 1876) T_LOC=29,
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - Next (RFC 2065, Obsolete RFC 3755) T_NXT=30, //! Type - Nimrod Endpoint IDentifier (draft) T_EID=31, //! Type - Nimrod Locator (draft) T_NIMLOC=32,
c534f62002-12-23Henrik Grubbström (Grubba)  //! Type - Service location record (RFC 2782) T_SRV=33,
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - ATM End System Address (af-saa-0069.000) T_ATMA=34,
ddcaf02007-03-19Ignasi Cavero  //! Type - NAPTR (RFC 3403) T_NAPTR=35,
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - Key eXchanger record (RFC 2230) T_KX=36, //! Type - Certificate Record (RFC 4398) T_CERT=37, //! Type - IPv6 address record (RFC 2874 Obsolete RFC 6563)
c534f62002-12-23Henrik Grubbström (Grubba)  T_A6=38,
5241a02008-07-24Henrik Grubbström (Grubba) 
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - Delegation Name (RFC 2672) T_DNAME=39, //! Type - Kitchen Sink (draft) T_SINK=40, //! Type - Option (RFC 2671) T_OPT=41, //! Type - Address Prefix List (RFC 3123) T_APL=42, //! Type - Delegation Signer (RFC 4034) T_DS=43, //! Type - SSH Public Key Fingerprint (RFC 4255) T_SSHFP=44, //! Type - IPsec Key (RFC 4025) T_IPSECKEY=45, //! Type - DNSSEC signature (RFC 4034) T_RRSIG=46, //! Type - Next-Secure record (RFC 4034) T_NSEC=47, //! Type - DNS Key record (RFC 4034) T_DNSKEY=48, //! Type - DHCP identifier (RFC 4701) T_DHCID=49, //! Type - NSEC record version 3 (RFC 5155) T_NSEC3=50, //! Type - NSEC3 parameters (RFC 5155) T_NSEC3PARAM=51, //! Type - TLSA certificate association (RFC 6698) T_TLSA=52, //! Type - Host Identity Protocol (RFC 5205) T_HIP=55,
5241a02008-07-24Henrik Grubbström (Grubba)  //! Type - SPF - Sender Policy Framework (RFC 4408) T_SPF=99,
02f1b32013-08-09Andreas Petersson 
6b1c882014-01-09Henrik Grubbström (Grubba)  // UserDB via DNS? T_UINFO=100, T_UID=101, T_GID=102, T_UNSPEC=103, //! Type - Secret key record (RFC 2930) T_TKEY=249, //! Type - Transaction Signature (RFC 2845) T_TSIG=250, //! Type - Incremental Zone Transfer (RFC 1996) T_IXFR=251, //! Type - Authoritative Zone Transfer (RFC 1035) T_AXFR=252, //! Type - Mail Box (MB, MG or MR) (Obsolete - use MX) T_MAILB=253, //! Type - Mail Agent (both MD and MF) (Obsolete - use MX) T_MAILA=254,
02f1b32013-08-09Andreas Petersson  //! Type - ANY - A request for all records T_ANY=255,
6b1c882014-01-09Henrik Grubbström (Grubba)  //! Type - Certificate Authority Authorization (RFC 6844) T_CAA=257, //! Type - DNSSEC Trust Authorities (draft) T_TA=32768, //! Type - DNSSEC Lookaside Validation Record (RFC 4431) T_DLV=32769,
c534f62002-12-23Henrik Grubbström (Grubba) };
27e9f92002-12-01H. William Welliver III 
f5dc042013-09-03Tobias S. Josefowitz int safe_bind(Stdio.UDP udp, string|int port, string|void device)
4ef71a2012-01-25Henrik Grubbström (Grubba) { mixed err = catch {
f5dc042013-09-03Tobias S. Josefowitz  udp->bind(port, device, 1);
4ef71a2012-01-25Henrik Grubbström (Grubba)  return 1; };
2fdd642012-01-30Henrik Grubbström (Grubba) #if constant(System.EADDRINUSE) if (errno() == System.EADDRINUSE) return 0; #endif
138e4a2019-05-15Henrik Grubbström (Grubba) #if constant(System.EACCES) if (errno() == System.EACCES) return 0; // Privileged port. #endif
51633c2014-11-17Pontus Rodling #if constant(System.WSAEACCES) if (errno() == System.WSAEACCES) return 0; #endif
2fdd642012-01-30Henrik Grubbström (Grubba)  werror("Protocols.DNS: Binding of UDP port failed with errno %d: %s\n", errno(), strerror(errno()));
4ef71a2012-01-25Henrik Grubbström (Grubba)  master()->handle_error(err); return 0; }
27e9f92002-12-01H. William Welliver III //! Low level DNS protocol
bb02cc1997-09-15Fredrik Hübinette (Hubbe) class protocol { string mklabel(string s) {
ead9722003-01-20Martin Nilsson  if(sizeof(s)>63)
484a742002-03-09Martin Nilsson  error("Too long component in domain name.\n");
76d8a62013-05-18Martin Nilsson  return sprintf("%1H",s);
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
9eaf1d2008-06-28Martin Nilsson  protected private string mkname(string|array(string) labels, int pos,
8be18a2000-04-12Marcus Comstedt  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]);
ead9722003-01-20Martin Nilsson  return l + mkname(labels[1..], pos+sizeof(l), comp);
8be18a2000-04-12Marcus Comstedt  } }
9eaf1d2008-06-28Martin Nilsson  protected string make_raw_addr6(string addr6)
e94d1a2003-04-23Marcus Comstedt  { if(!addr6) return "\0"*16;
1118462009-04-25Martin Stjernholm  return sprintf ("%@2c", Protocols.IPv6.parse_addr (addr6));
e94d1a2003-04-23Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected private string mkrdata(mapping entry, int pos, mapping(string:int) c)
8be18a2000-04-12Marcus Comstedt  { 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:
76d8a62013-05-18Martin Nilsson  return sprintf("%1H%1H", entry->cpu||"", entry->os||"");
8be18a2000-04-12Marcus Comstedt  case T_MINFO: string rmailbx = mkname(entry->rmailbx, pos, c);
ead9722003-01-20Martin Nilsson  return rmailbx + mkname(entry->emailbx, pos+sizeof(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: return sprintf("%@1c", (array(int))((entry->a||"0.0.0.0")/".")[0..3]);
d004002003-04-22Martin Nilsson  case T_AAAA:
e94d1a2003-04-23Marcus Comstedt  return make_raw_addr6(entry->aaaa);
1a58a12011-12-09Per Hedbor  case T_A6: if( stringp( entry->a6 ) || !entry->a6 ) return "\0"+make_raw_addr6(entry->a6); return sprintf( "%c%s%s", entry->a6->prefixlen, make_raw_addr6(entry->a6->address)[entry->a6->prefixlen/8..], entry->a6->prefixname||"");
8be18a2000-04-12Marcus Comstedt  case T_SOA: string mname = mkname(entry->mname, pos, c);
ead9722003-01-20Martin Nilsson  return mname + mkname(entry->rname, pos+sizeof(mname), c) +
8be18a2000-04-12Marcus Comstedt  sprintf("%4c%4c%4c%4c%4c", entry->serial, entry->refresh, entry->retry, entry->expire, entry->minimum);
ddcaf02007-03-19Ignasi Cavero  case T_NAPTR: string rnaptr = sprintf("%2c%2c", entry->order, entry->preference);
76d8a62013-05-18Martin Nilsson  rnaptr += sprintf("%1H%1H%1H%s", entry->flags || "", entry->service || "", entry->regexp || "",
ddcaf02007-03-19Ignasi Cavero  mkname(entry->replacement, pos, c)); return rnaptr;
8be18a2000-04-12Marcus Comstedt  case T_TXT:
a0122a2013-09-18Martin Nilsson  return map(stringp(entry->txt)? ({entry->txt}):(entry->txt||({})), lambda(string t) { return sprintf("%1H", t); })*"";
5241a02008-07-24Henrik Grubbström (Grubba)  case T_SPF:
a0122a2013-09-18Martin Nilsson  return map(stringp(entry->spf)? ({entry->spf}):(entry->spf||({})), lambda(string t) { return sprintf("%1H", t); })*"";
79d3032004-04-13Jeff Hungerford  case T_LOC: // FIXME: Not implemented yet.
8be18a2000-04-12Marcus Comstedt  default: return ""; } }
9eaf1d2008-06-28Martin Nilsson  protected private string encode_entries(array(mapping) entries, int pos,
8be18a2000-04-12Marcus Comstedt  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);
ead9722003-01-20Martin Nilsson  pos += sizeof(e)+2;
8be18a2000-04-12Marcus Comstedt  string rd = entry->rdata || mkrdata(entry, pos, comp);
76d8a62013-05-18Martin Nilsson  res += e + sprintf("%2H", rd);
ead9722003-01-20Martin Nilsson  pos += sizeof(rd);
8be18a2000-04-12Marcus Comstedt  } 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)
ead9722003-01-20Martin Nilsson  r += mkname(_qd->name, sizeof(r), c) +
8be18a2000-04-12Marcus Comstedt  sprintf("%2c%2c", _qd->type, _qd->cl); if(an)
ead9722003-01-20Martin Nilsson  r+=encode_entries(an, sizeof(r), c);
8be18a2000-04-12Marcus Comstedt  if(ns)
ead9722003-01-20Martin Nilsson  r+=encode_entries(ns, sizeof(r), c);
8be18a2000-04-12Marcus Comstedt  if(ar)
ead9722003-01-20Martin Nilsson  r+=encode_entries(ar, sizeof(r), c);
8be18a2000-04-12Marcus Comstedt  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)  }
a0122a2013-09-18Martin Nilsson  //! 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] //! //! @example //! // generate a query PDU for a address lookup on the hostname pike.lysator.liu.se //! string q=Protocols.DNS.protocol()->mkquery("pike.lysator.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)  { int pos=n[0]; int next=-1;
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) ret=({});
e6175a2013-08-09Andreas Petersson  int labels = 0;
b956561998-05-20Henrik Grubbström (Grubba)  while(pos < sizeof(msg))
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
e6175a2013-08-09Andreas Petersson  labels++; if (labels > 255) error("Bad domain name. Too many labels.\n");
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)  {
9939f12013-05-18Martin Nilsson  int pos = next[0]; int len=s[pos];
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  next[0]+=len+1;
9939f12013-05-18Martin Nilsson  return s[pos+1..pos+len];
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
79d3032004-04-13Jeff Hungerford  int decode_byte(string s, array(int) next) { return s[next[0]++]; }
4755b72000-02-18Henrik Grubbström (Grubba)  int decode_short(string s, array(int) next)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
9939f12013-05-18Martin Nilsson  return s[next[0]++]<<8 | s[next[0]++];
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
4755b72000-02-18Henrik Grubbström (Grubba)  int decode_int(string s, array(int) next)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
9939f12013-05-18Martin Nilsson  int pos = next[0]; next[0] += 4; return s[pos++]<<24 | s[pos++]<<16 | s[pos++]<<8 | s[pos++];
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
8630372008-07-23Henrik Grubbström (Grubba)  //! Decode a set of entries from an answer. //! //! @param s //! Encoded entries. //! //! @param num //! Number of entires in @[s]. //! //! @param next //! Array with a single element containing the start position in @[s] //! on entry and the continuation position on return. //! //! @returns //! Returns an array of mappings describing the decoded entires: //! @array //! @elem mapping 0.. //! Mapping describing a single entry: //! @mapping //! @member string "name" //! Name the entry concerns. //! @member EntryType "type" //! Type of entry. //! @member ResourceClass "cl" //! Resource class. Typically @[C_IN]. //! @member int "ttl" //! Time to live for the entry in seconds. //! @member int "len" //! Length in bytes of the encoded data section. //! @endmapping //! Depending on the type of entry the mapping may contain //! different additional fields: //! @int //! @value T_CNAME //! @mapping //! @member string "cname" //! @endmapping //! @value T_PTR //! @mapping //! @member string "ptr" //! @endmapping //! @value T_NS //! @mapping //! @member string "ns" //! @endmapping //! @value T_MX //! @mapping //! @member int "preference" //! @member string "mx" //! @endmapping //! @value T_HINFO //! @mapping //! @member string "cpu" //! @member string "os" //! @endmapping //! @value T_SRV //! RFC 2052 and RFC 2782. //! @mapping //! @member int "priority" //! @member int "weight" //! @member int "port" //! @member string "target" //! @member string "service" //! @member string "proto" //! @member string "name" //! @endmapping //! @value T_A //! @mapping //! @member string "a" //! IPv4-address in dotted-decimal format. //! @endmapping //! @value T_AAAA //! @mapping //! @member string "aaaa" //! IPv6-address in colon-separated hexadecimal format. //! @endmapping //! @value T_LOC //! @mapping //! @member int "version" //! Version, currently only version @expr{0@} (zero) is //! supported. //! @member int "size" //! @member int "h_perc" //! @member int "v_perc" //! @member int "lat" //! @member int "long" //! @member int "alt" //! @endmapping //! @value T_SOA //! @mapping //! @member string "mname" //! @member string "rname" //! @member int "serial" //! @member int "refresh" //! @member int "retry" //! @member int "expire" //! //! @member int "minimum" //! Note: For historical reasons this entry is named //! @expr{"minimum"@}, but it contains the TTL for //! negative answers (RFC 2308). //! @endmapping //! @value T_NAPTR //! @mapping //! @member int "order" //! @member int "preference" //! @member string "flags" //! @member string "service" //! @member string "regexp" //! @member string "replacement" //! @endmapping //! @value T_TXT //! @mapping //! @member string "txt"
78b2c82013-01-08Tobias S. Josefowitz  //! Note: For historical reasons, when receiving decoded //! DNS entries from a client, this will be the first string //! in the TXT record only. //! @member string "txta" //! When receiving decoded DNS data from a client, txta is //! the array of all strings in the record. When sending //! multiple strings in a TXT record in a server, please //! supply an array as "txt" containing the strings, txta //! will be ignored.
8630372008-07-23Henrik Grubbström (Grubba)  //! @endmapping
5241a02008-07-24Henrik Grubbström (Grubba)  //! @value T_SPF //! @mapping //! @member string "spf" //! @endmapping
8630372008-07-23Henrik Grubbström (Grubba)  //! @endint //! @endarray
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=({});
ead9722003-01-20Martin Nilsson  for(int e=0;e<num && next[0]<sizeof(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:
8630372008-07-23Henrik Grubbström (Grubba)  // RFC 2052 and RFC 2782.
b6e3442002-11-26H. William Welliver III  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/".";
8630372008-07-23Henrik Grubbström (Grubba)  if(x[0][0..0]=="_") m->service=x[0][1..]; else m->service=x[0]; if(sizeof(x) > 1)
b6e3442002-11-26H. William Welliver III  { if(x[1][0..0]=="_") m->proto=x[1][1..]; else m->proto=x[1]; }
8630372008-07-23Henrik Grubbström (Grubba)  m->name=x[2..]*".";
b6e3442002-11-26H. William Welliver III  break;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  case T_A:
ff31872013-05-19Martin Nilsson  m->a=(array(string))values(s[next[0]..next[0]+m->len-1])*".";
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  break;
d004002003-04-22Martin Nilsson  case T_AAAA:
ff31872013-05-19Martin Nilsson  m->aaaa=sprintf("%{%02X%}", (values(s[next[0]..next[0]+m->len-1])/2)[*])*":";
d004002003-04-22Martin Nilsson  break;
79d3032004-04-13Jeff Hungerford  case T_LOC: m->version = decode_byte(s,next); if (m->version == 0) { int aByte; aByte = decode_byte(s,next); m->size = pow((aByte>>4)&0xf , aByte&0xf)/100.0; aByte = decode_byte(s,next); m->h_perc = pow((aByte>>4)&0xf , aByte&0xf)/100.0; aByte = decode_byte(s,next); m->v_perc = pow((aByte>>4)&0xf , aByte&0xf)/100.0; m->lat = ((decode_int(s,next)-(2<<30))/3600000.0); m->long = ((decode_int(s,next)-(2<<30))/3600000.0); m->alt = ((decode_int(s,next)/100.0)-100000.0); } break;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  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;
ddcaf02007-03-19Ignasi Cavero  case T_NAPTR: m->order = decode_short (s, next); m->preference = decode_short (s, next); m->flags = decode_string (s, next); m->service = decode_string (s, next); m->regexp = decode_string (s, next); m->replacement = decode_domain (s, next); break;
f96a6f2005-01-03Jeff Hungerford  case T_TXT:
78b2c82013-01-08Tobias S. Josefowitz  { int tlen; m->txta = ({ }); while (tlen < m->len) { m->txta += ({ decode_string(s, next) }); tlen += sizeof(m->txta[-1]) + 1; } m->txt = m->txta[0]; }
5241a02008-07-24Henrik Grubbström (Grubba)  break; case T_SPF: m->spf = decode_string(s, next);
f96a6f2005-01-03Jeff Hungerford  break;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  } next[0]=tmp+m->len; ret+=({m}); } return ret; } mapping decode_res(string s) { mapping m=([]);
7a5f9d2013-04-16Per Hedbor  if (sscanf(s,"%2c%c%c%2c%2c%2c%2c", m->id, m->c1, m->c2, m->qdcount, m->ancount, m->nscount, m->arcount) != 7) error("Bad DNS request, failed to parse header\n");
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) 
ead9722003-01-20Martin Nilsson  m->length=sizeof(s);
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
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++) {
d02efa2013-08-09Andreas Petersson  if (next[0] > sizeof(s)) error("Bad DNS request, not enough data\n");
5b31d62000-04-11Marcus Comstedt  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; } };
4ef71a2012-01-25Henrik Grubbström (Grubba) protected string ANY; protected void create() {
89a02a2012-01-31Henrik Grubbström (Grubba)  // Check if IPv6 support is available, and that mapped IPv4 is enabled.
4ef71a2012-01-25Henrik Grubbström (Grubba)  catch {
89a02a2012-01-31Henrik Grubbström (Grubba)  // Note: Attempt to bind on the IPv6 loopback (::1)
4ef71a2012-01-25Henrik Grubbström (Grubba)  // rather than on IPv6 any (::), to make sure some // IPv6 support is actually configured. This is needed
89a02a2012-01-31Henrik Grubbström (Grubba)  // since eg Solaris happily binds on :: even
4ef71a2012-01-25Henrik Grubbström (Grubba)  // if no IPv6 interfaces are configured. // Try IPv6 any (::) too for paranoia reasons.
89a02a2012-01-31Henrik Grubbström (Grubba)  // For even more paranoia, try sending some data // from :: to the drain services on 127.0.0.1 and ::1. // // If the tests fail, we regard the IPv6 support as broken, // and use just IPv4. Stdio.UDP udp = Stdio.UDP(); if (udp->bind(0, "::1") && udp->bind(0, "::") && (udp->send("127.0.0.1", 9, "/dev/null") == 9) && (udp->send("::1", 9, "/dev/null") == 9)) {
461bb32012-04-02Henrik Grubbström (Grubba)  // Note: The above tests are apparently not sufficient, since // WIN32 happily pretends to send stuff to ::ffff:0:0/96... Stdio.UDP udp2 = Stdio.UDP(); if (udp2->bind(0, "127.0.0.1")) { // IPv4 is present. array(string) a = udp2->query_address()/" "; int port = (int)a[1]; string key = Crypto.Random.random_string(16); udp2->set_nonblocking(); // We shouldn't get any lost packets, since we're on the loop-back, // but for paranoia reasons we perform a couple of retries. retry: for (int i = 0; i < 16; i++) { if (!udp->send("127.0.0.1", port, key)) continue; // udp2->wait() throws errors on WIN32. catch(udp2->wait(1)); mapping res; while ((res = udp2->read())) { if (res->data == key) { // Mapped IPv4 seems to work. ANY = "::"; break retry; } } } destruct(udp2); }
4ef71a2012-01-25Henrik Grubbström (Grubba)  }
89a02a2012-01-31Henrik Grubbström (Grubba)  destruct(udp);
4ef71a2012-01-25Henrik Grubbström (Grubba)  }; }
8c37972013-04-05Tobias S. Josefowitz //! Base class for @[server], @[tcp_server]. class server_base
8be18a2000-04-12Marcus Comstedt { inherit protocol;
8630372008-07-23Henrik Grubbström (Grubba) 
8c37972013-04-05Tobias S. Josefowitz  array(Stdio.UDP|object) ports = ({ });
8be18a2000-04-12Marcus Comstedt 
8c37972013-04-05Tobias S. Josefowitz  protected string low_send_reply(mapping r, mapping q, mapping m)
8be18a2000-04-12Marcus Comstedt  { 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);
8c37972013-04-05Tobias S. Josefowitz  return s;
8be18a2000-04-12Marcus Comstedt  }
c534f62002-12-23Henrik Grubbström (Grubba)  //! Reply to a query (stub). //! //! @param query //! Parsed query. //! //! @param udp_data
8c37972013-04-05Tobias S. Josefowitz  //! Raw UDP data. If the server operates in TCP mode (@[tcp_server]), //! it will contain an additional tcp_con entry. In that case, //! @expr{udp_data->tcp_con->con@} will contain the TCP connection the //! request was received on as @[Stdio.File] object.
c534f62002-12-23Henrik Grubbström (Grubba)  //!
f7616f2010-01-05H. William Welliver III  //! @param cb //! Callback you can call with the result instead of returning it. //! In that case, return @expr{0@} (zero). //! //!
c534f62002-12-23Henrik Grubbström (Grubba)  //! Overload this function to implement the proper lookup. //!
16b3f22010-12-23Henrik Grubbström (Grubba)  //! @note //! To indicate the default failure @[cb] must be called with an //! argument of @expr{0@} (zero), and @expr{0@} (zero) be returned. //!
c534f62002-12-23Henrik Grubbström (Grubba)  //! @returns
16b3f22010-12-23Henrik Grubbström (Grubba)  //! Returns @expr{0@} (zero) when the @[cb] callback will be used, //! or a result mapping if not:
c534f62002-12-23Henrik Grubbström (Grubba)  //! @mapping //! @member int "rcode"
555c1f2012-11-14Chris Angelico  //! 0 (or omit) for success, otherwise one of the Protocols.DNS.* constants
8630372008-07-23Henrik Grubbström (Grubba)  //! @member array(mapping(string:string|int))|void "an"
555c1f2012-11-14Chris Angelico  //! Answer section:
c534f62002-12-23Henrik Grubbström (Grubba)  //! @array //! @elem mapping(string:string|int) entry //! @mapping //! @member string|array(string) "name" //! @member int "type" //! @member int "cl" //! @endmapping //! @endarray
8630372008-07-23Henrik Grubbström (Grubba)  //! @member array|void "qd"
555c1f2012-11-14Chris Angelico  //! Question section, same format as @[an]; omit to return the original question
c534f62002-12-23Henrik Grubbström (Grubba)  //! @member array|void "ns"
555c1f2012-11-14Chris Angelico  //! Authority section (usually NS records), same format as @[an]
c534f62002-12-23Henrik Grubbström (Grubba)  //! @member array|void "ar"
555c1f2012-11-14Chris Angelico  //! Additional section, same format as @[an] //! @member int "aa" //! Set to 1 to include the Authoritative Answer bit in the response //! @member int "tc" //! Set to 1 to include the TrunCated bit in the response //! @member int "rd" //! Set to 1 to include the Recursion Desired bit in the response //! @member int "ra" //! Set to 1 to include the Recursion Available bit in the response //! @member int "cd" //! Set to 1 to include the Checking Disabled bit in the response //! @member int "ad" //! Set to 1 to include the Authenticated Data bit in the response
c534f62002-12-23Henrik Grubbström (Grubba)  //! @endmapping
f7616f2010-01-05H. William Welliver III  protected mapping reply_query(mapping query, mapping udp_data, function(mapping:void) cb)
8be18a2000-04-12Marcus Comstedt  { // Override this function. // // Return mapping may contain: // aa, ra, {ad, cd,} rcode, an, ns, ar return 0; }
16b3f22010-12-23Henrik Grubbström (Grubba)  //! Handle a query. //! //! This function calls @[reply_query()], //! and dispatches the result to @[send_reply()].
8c37972013-04-05Tobias S. Josefowitz  protected void handle_query(mapping q, mapping m, Stdio.UDP|object udp)
8be18a2000-04-12Marcus Comstedt  {
f7616f2010-01-05H. William Welliver III  int(0..1) result_available = 0; void _cb(mapping r) { if(result_available) error("DNS: you can either use the callback OR return the reply, not both.\n"); result_available = 1; send_reply(r, q, m, udp); }; mapping r = reply_query(q, m, _cb); if(r && result_available) error("DNS: you can either use the callback OR return the reply, not both.\n"); if(r) { result_available = 1; send_reply(r, q, m, udp); }
8be18a2000-04-12Marcus Comstedt  }
16b3f22010-12-23Henrik Grubbström (Grubba)  //! Handle a query response (stub). //! //! Overload this function to handle responses to possible recursive queries.
8c37972013-04-05Tobias S. Josefowitz  protected void handle_response(mapping r, mapping m, Stdio.UDP|object udp)
8be18a2000-04-12Marcus Comstedt  { // This is a stub intended to simplify servers which allow recursion }
16b3f22010-12-23Henrik Grubbström (Grubba)  //! Low-level DNS-data receiver. //! //! This function receives the raw DNS-data from the @[Stdio.UDP] socket
8c37972013-04-05Tobias S. Josefowitz  //! or TCP connection object @[udp], decodes it, and dispatches the decoded //! DNS request to @[handle_query()] and @[handle_response()]. protected void rec_data(mapping m, Stdio.UDP|object udp)
8be18a2000-04-12Marcus Comstedt  { mixed err; mapping q; if (err = catch { q=decode_res(m->data); }) {
8c37972013-04-05Tobias S. Josefowitz  werror("DNS: Failed to read %s packet.\n%s\n", udp->tcp_connection ? "TCP" : "UDP",
0bd6f52003-10-21Martin Nilsson  describe_backtrace(err));
8be18a2000-04-12Marcus Comstedt  if(m && m->data && sizeof(m->data)>=2) send_reply((["rcode":1]),
f7616f2010-01-05H. William Welliver III  mkmapping(({"id"}), array_sscanf(m->data, "%2c")), m, udp);
8be18a2000-04-12Marcus Comstedt  } else if(q->qr)
f7616f2010-01-05H. William Welliver III  handle_response(q, m, udp);
8be18a2000-04-12Marcus Comstedt  else
f7616f2010-01-05H. William Welliver III  handle_query(q, m, udp);
8be18a2000-04-12Marcus Comstedt  }
8c37972013-04-05Tobias S. Josefowitz  protected void send_reply(mapping r, mapping q, mapping m, Stdio.UDP|object con); protected void destroy() { if(sizeof(ports)) { foreach(ports;; object port) destruct(port); } } } //! Base class for implementing a Domain Name Service (DNS) server operating //! over UDP. //! //! This class is typically used by inheriting it, //! and overloading @[reply_query()] and @[handle_response()].
0a51722013-04-05Tobias S. Josefowitz //! //! @seealso //! @[dual_server]
8c37972013-04-05Tobias S. Josefowitz class server { //! inherit server_base; //inherit Stdio.UDP : udp; protected void send_reply(mapping r, mapping q, mapping m, Stdio.UDP udp) { udp->send(m->ip, m->port, low_send_reply(r, q, m)); }
16b3f22010-12-23Henrik Grubbström (Grubba)  //! @decl void create() //! @decl void create(int port) //! @decl void create(string ip) //! @decl void create(string ip, int port) //! @decl void create(string ip, int port, string|int ... more) //! //! Open one or more new DNS server ports. //! //! @param ip
4ef71a2012-01-25Henrik Grubbström (Grubba)  //! The IP to bind to. Defaults to @expr{"::"@} or @expr{0@} (ie ANY) //! depending on whether IPv6 support is present or not.
16b3f22010-12-23Henrik Grubbström (Grubba)  //! //! @param port //! The port number to bind to. Defaults to @expr{53@}. //! //! @param more //! Optional further DNS server ports to open. //! Must be a set of @[ip], @[port] argument pairs. protected void create(int|string|void arg1, string|int ... args)
8be18a2000-04-12Marcus Comstedt  {
f7616f2010-01-05H. William Welliver III  if(!arg1 && !sizeof(args)) arg1 = 53; if(!sizeof(args)) { if(stringp(arg1)) args = ({ arg1, 53 }); else
4ef71a2012-01-25Henrik Grubbström (Grubba)  args = ({ ANY, arg1 });
f7616f2010-01-05H. William Welliver III  } else args = ({ arg1 }) + args; if(sizeof(args)&1) error("DNS: if you specify more than one argument, the number of " "arguments needs to be even (server(ip1, port1, ip2, port2, " "...)).\n"); for(int i;i<sizeof(args);i+=2) { Stdio.UDP udp = Stdio.UDP(); if(args[i]) {
4ef71a2012-01-25Henrik Grubbström (Grubba)  if (!safe_bind(udp, args[i+1], args[i]))
f7616f2010-01-05H. William Welliver III  error("DNS: failed to bind host:port %s:%d.\n", args[i],args[i+1]); } else {
4ef71a2012-01-25Henrik Grubbström (Grubba)  if(!safe_bind(udp, args[i+1]))
f7616f2010-01-05H. William Welliver III  error("DNS: failed to bind port %d.\n", args[i+1]); } udp->set_read_callback(rec_data, udp); // port objects are stored for destruction when the server object is destroyed. ports += ({udp}); } }
8c37972013-04-05Tobias S. Josefowitz }
f7616f2010-01-05H. William Welliver III 
8c37972013-04-05Tobias S. Josefowitz  //! Base class for implementing a Domain Name Service (DNS) server operating //! over TCP. //! //! This class is typically used by inheriting it, //! and overloading @[reply_query()] and @[handle_response()]. class tcp_server { inherit server_base; mapping(Connection:int(1..1)) connections = ([ ]); protected class Connection { constant tcp_connection = 1; protected int(0..1) write_ready; protected string read_buffer = "", out_buffer = ""; protected array c_id; Stdio.File con; protected void create(Stdio.File con) {
8e06a32014-09-30Martin Nilsson  this::con = con;
8c37972013-04-05Tobias S. Josefowitz  con->set_nonblocking(rcb, wcb, ccb); c_id = call_out(destruct, 120, this); } protected void ccb(mixed id) { destruct(con); m_delete(connections, this); } protected void wcb(mixed id) { if (sizeof(out_buffer)) { int written = con->write(out_buffer); out_buffer = out_buffer[written..]; } else write_ready = 1; } protected void rcb(mixed id, string data) { int len; read_buffer += data; if (sscanf(read_buffer, "%2c", len)) { if (sizeof(read_buffer) > len - 2) { string data = read_buffer[2..len+1]; string ip, port; mapping m; read_buffer = read_buffer[len+2..]; remove_call_out(c_id); c_id = call_out(destruct, 120, this); [ip, port] = con->query_address() / " "; m = ([ "data" : data, "ip" : ip, "port" : (int)port, "tcp_con" : this ]); rec_data(m, this); } } } void send(string s) { if (sizeof(s) > 65535) error("DNS: Cannot send packets > 65535 bytes (%d here).\n", sizeof(s));
76d8a62013-05-18Martin Nilsson  out_buffer += sprintf("%2H", s);
8c37972013-04-05Tobias S. Josefowitz  if (write_ready) { int written = con->write(out_buffer); out_buffer = out_buffer[written..]; write_ready = 0; } remove_call_out(c_id); c_id = call_out(destruct, 120, this); } void destroy() { if (con) con->close(); destruct(con); m_delete(connections, this); } } protected int accept(Stdio.Port port) { connections[Connection(port->accept())] = 1; } protected void send_reply(mapping r, mapping q, mapping m, Connection con) { con->send(low_send_reply(r, q, m)); } //! @decl void create() //! @decl void create(int port) //! @decl void create(string ip) //! @decl void create(string ip, int port) //! @decl void create(string ip, int port, string|int ... more) //! //! Open one or more new DNS server ports. //! //! @param ip //! The IP to bind to. Defaults to @expr{"::"@} or @expr{0@} (ie ANY) //! depending on whether IPv6 support is present or not. //! //! @param port //! The port number to bind to. Defaults to @expr{53@}. //! //! @param more //! Optional further DNS server ports to open. //! Must be a set of @[ip], @[port] argument pairs. protected void create(int|string|void arg1, string|int ... args)
f7616f2010-01-05H. William Welliver III  {
8c37972013-04-05Tobias S. Josefowitz  if(!arg1 && !sizeof(args)) arg1 = 53; if(!sizeof(args))
f7616f2010-01-05H. William Welliver III  {
8c37972013-04-05Tobias S. Josefowitz  if(stringp(arg1)) args = ({ arg1, 53 }); else args = ({ ANY, arg1 });
f7616f2010-01-05H. William Welliver III  }
8c37972013-04-05Tobias S. Josefowitz  else args = ({ arg1 }) + args; if(sizeof(args)&1) error("DNS: if you specify more than one argument, the number of " "arguments needs to be even (server(ip1, port1, ip2, port2, " "...)).\n"); for(int i;i<sizeof(args);i+=2) { Stdio.Port port; if(args[i]) { port = Stdio.Port(args[i+1], accept, args[i]); } else { port = Stdio.Port(args[i+1], accept); } port->set_id(port);
a0122a2013-09-18Martin Nilsson  // Port objects are stored for destruction when the server // object is destroyed.
8c37972013-04-05Tobias S. Josefowitz  ports += ({ port }); } } protected void destroy() { foreach (connections; Connection con;) { destruct(con); } ::destroy();
8be18a2000-04-12Marcus Comstedt  }
0a51722013-04-05Tobias S. Josefowitz } //! This is both a @[server] and @[tcp_server]. class dual_server { inherit server : UDP; inherit tcp_server : TCP; protected void send_reply(mapping r, mapping q, mapping m, Connection|Stdio.UDP con) { string rpl = low_send_reply(r, q, m); if (!con->tcp_connection) { if (sizeof(rpl) > 512) {
a0122a2013-09-18Martin Nilsson  rpl = sprintf("%s%8c", rpl[..3], 0); // Truncate after header and
0a51722013-04-05Tobias S. Josefowitz  // send empty response // ("dnscache strategy")
a0122a2013-09-18Martin Nilsson  rpl[2] |= 2; // Set TC bit
0a51722013-04-05Tobias S. Josefowitz  } con->send(m->ip, m->port, rpl); } else con->send(rpl); } protected void create(int|string|void arg1, string|int ... args) { ::create(arg1, @args); }
8be18a2000-04-12Marcus Comstedt 
8c37972013-04-05Tobias S. Josefowitz  protected void destroy() { ::destroy(); }
8be18a2000-04-12Marcus Comstedt }
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
eabcd51999-07-25Henrik Grubbström (Grubba) #define RETRIES 12 #define RETRY_DELAY 5
a0122a2013-09-18Martin Nilsson //! Synchronous DNS client.
a7c1441999-10-30Mirar (Pontus Hagland) class client {
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  inherit protocol;
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",
461bb32012-04-02Henrik Grubbström (Grubba)  "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters",
a7c1441999-10-30Mirar (Pontus Hagland)  "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)  }; }
461bb32012-04-02Henrik Grubbström (Grubba)  foreach(RegGetKeyNames(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\" "Parameters\\Interfaces"), string key) { catch { res += ({ RegGetValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\" "Parameters\\Interfaces\\" + key, val) }); }; }
b3e4402001-03-14Henrik Grubbström (Grubba)  };
83e1902000-06-28Fredrik Hübinette (Hubbe) #endif
7f55912009-03-16Henrik Grubbström (Grubba)  res -= ({ UNDEFINED });
e177732000-12-07Henrik Grubbström (Grubba)  return sizeof(res) ? res : ({ fallbackvalue });
a7c1441999-10-30Mirar (Pontus Hagland)  }
2880382008-01-31Henrik Grubbström (Grubba)  #else /* !__NT__ */
df6e592005-03-01Henrik Grubbström (Grubba) 
9eaf1d2008-06-28Martin Nilsson  protected private mapping(string:string) etc_hosts;
df6e592005-03-01Henrik Grubbström (Grubba) 
9eaf1d2008-06-28Martin Nilsson  protected private int is_ip(string ip)
2880382008-01-31Henrik Grubbström (Grubba)  {
72e5f82011-02-10Per Hedbor  if( has_value( ip, ":") ) return (replace(ip, "0123456789abcdefABCDEF:"/1, allocate(23,"")) == "");
2880382008-01-31Henrik Grubbström (Grubba)  return (replace(ip, "0123456789."/1, allocate(11,"")) == ""); }
9eaf1d2008-06-28Martin Nilsson  protected private string read_etc_file(string fname)
a7c1441999-10-30Mirar (Pontus Hagland)  {
df6e592005-03-01Henrik Grubbström (Grubba)  array(string) paths; string res;
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #ifdef __NT__
df6e592005-03-01Henrik Grubbström (Grubba)  paths = get_tcpip_param("DataBasePath");
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #else
df6e592005-03-01Henrik Grubbström (Grubba)  paths = ({ "/etc", "/amitcp/db" });
9f6f4d1998-02-27Fredrik Hübinette (Hubbe) #endif
df6e592005-03-01Henrik Grubbström (Grubba)  foreach(paths, string path) { string raw = Stdio.read_file(path + "/" + fname); if (raw && sizeof(raw = replace(raw, "\r", "\n"))) { if (res) { if (sizeof(res) && (res[-1] != '\n')) { // Avoid confusion... res += "\n"; } res += raw; } else { res = raw; } } } return res; }
e177732000-12-07Henrik Grubbström (Grubba) 
9eaf1d2008-06-28Martin Nilsson  protected private string match_etc_hosts(string host)
df6e592005-03-01Henrik Grubbström (Grubba)  { if (!etc_hosts) {
a7c1441999-10-30Mirar (Pontus Hagland)  etc_hosts = ([ "localhost":"127.0.0.1" ]);
37ffea1998-04-06Fredrik Hübinette (Hubbe) 
df6e592005-03-01Henrik Grubbström (Grubba)  string raw = read_etc_file("hosts");
e177732000-12-07Henrik Grubbström (Grubba) 
df6e592005-03-01Henrik Grubbström (Grubba)  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) 
df6e592005-03-01Henrik Grubbström (Grubba)  if (sizeof(arr) > 1) { if (is_ip(arr[0])) { foreach(arr[1..], string name) { etc_hosts[name] = arr[0];
a7c1441999-10-30Mirar (Pontus Hagland)  }
df6e592005-03-01Henrik Grubbström (Grubba)  } else { // Bad /etc/hosts entry ignored.
6676441998-01-25Henrik Grubbström (Grubba)  } } }
df6e592005-03-01Henrik Grubbström (Grubba)  } else { // Couldn't read or no /etc/hosts.
6676441998-01-25Henrik Grubbström (Grubba)  } }
18096f2003-08-22Martin Nilsson  return etc_hosts[lower_case(host)];
a7c1441999-10-30Mirar (Pontus Hagland)  }
2863542008-01-31Henrik Grubbström (Grubba) #endif /* !__NT__ */
a7c1441999-10-30Mirar (Pontus Hagland) 
df6e592005-03-01Henrik Grubbström (Grubba)  // FIXME: Read hosts entry in /etc/nswitch.conf?
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 = ({});
892fe72010-12-26Henrik Grubbström (Grubba)  protected 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");
eb04632004-09-15 Marian  } else { nameservers = ({});
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");
eb04632004-09-15 Marian  } else { domains = ({});
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))) {
0bd6f52003-10-21Martin Nilsson  werror("Protocols.DNS.client(): " "Can't resolv nameserver \"%s\"\n", host);
d334772001-04-09Jonas Wallden  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 -= ({ "" });
a0122a2013-09-18Martin Nilsson  domains = map(domains, lambda(string d) { if (d[-1] == '.') { return d[..<1]; } 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)  } }
a0122a2013-09-18Martin Nilsson  //! Perform a synchronous DNS query. //! //! @param s //! Result of @[Protocols.DNS.protocol.mkquery] //! @returns //! mapping containing query result or 0 on failure/timeout //! //! @example //! @code //! // Perform a hostname lookup, results stored in r->an //! object d=Protocols.DNS.client(); //! mapping r=d->do_sync_query(d->mkquery("pike.lysator.liu.se", C_IN, T_A)); //! @endcode
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  mapping do_sync_query(string s) {
3347a82008-07-22Henrik Grubbström (Grubba)  int i;
eabcd51999-07-25Henrik Grubbström (Grubba)  object udp = Stdio.UDP();
3347a82008-07-22Henrik Grubbström (Grubba)  // Attempt to randomize the source port. for (i = 0; i < RETRIES; i++) {
4ef71a2012-01-25Henrik Grubbström (Grubba)  if (!safe_bind(udp, 1024 + random(65536-1024), ANY)) continue;
3347a82008-07-22Henrik Grubbström (Grubba)  }
4ef71a2012-01-25Henrik Grubbström (Grubba)  if (i >= RETRIES) safe_bind(udp, 0, ANY) || udp->bind(0);
355bbe2005-01-03Henrik Grubbström (Grubba) #if 0
f96a6f2005-01-03Jeff Hungerford  werror("Protocols.DNS.client()->do_sync_query(%O)\n" "UDP Address: %s\n" "%s\n", s, udp->query_address(), describe_backtrace(backtrace()));
355bbe2005-01-03Henrik Grubbström (Grubba) #endif /* 0 */
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  mapping m;
eabcd51999-07-25Henrik Grubbström (Grubba)  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.
461bb32012-04-02Henrik Grubbström (Grubba)  // // One common case is on WIN32 where select complains // about it not being a socket. catch { udp->wait(RETRY_DELAY); }; // Make sure that udp->read() doesn't block. udp->set_nonblocking(); // udp->read() can throw an error on connection refused. catch { m = udp->read(); if (m && (m->port == 53) && (m->data[0..1] == s[0..1]) && has_value(nameservers, m->ip)) { // Success. return decode_res(m->data); }
d692ea2000-09-07Leif Stensson  };
461bb32012-04-02Henrik Grubbström (Grubba)  // Restore blocking state for udp->send() on retry. udp->set_blocking();
eabcd51999-07-25Henrik Grubbström (Grubba)  } // Failure. return 0;
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  }
df6e592005-03-01Henrik Grubbström (Grubba) 
9eaf1d2008-06-28Martin Nilsson  protected mapping low_gethostbyname(string s, int type)
df6e592005-03-01Henrik Grubbström (Grubba)  { mapping m; if(sizeof(domains) && s[-1] != '.' && sizeof(s/".") < 3) { mapping m = do_sync_query(mkquery(s, C_IN, type)); if(!m || !m->an || !sizeof(m->an)) foreach(domains, string domain) { m = do_sync_query(mkquery(s+"."+domain, C_IN, type)); if(m && m->an && sizeof(m->an)) break; } return m; } else { return do_sync_query(mkquery(s, C_IN, type)); } }
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)  //!
df6e592005-03-01Henrik Grubbström (Grubba)  //! @note //! Prior to Pike 7.7 this function only returned IPv4 addresses. //!
4755b72000-02-18Henrik Grubbström (Grubba)  array gethostbyname(string s)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
df6e592005-03-01Henrik Grubbström (Grubba)  mapping a_records = low_gethostbyname(s, T_A); mapping aaaa_records = low_gethostbyname(s, T_AAAA);
24e8852005-03-01Martin Nilsson #if 0
df6e592005-03-01Henrik Grubbström (Grubba)  werror("a_records: %O\n" "aaaa_records: %O\n",
17b6b72013-10-08Per Hedbor  a_records, aaaa_records);
df6e592005-03-01Henrik Grubbström (Grubba) #endif /* 0 */
eabcd51999-07-25Henrik Grubbström (Grubba) 
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) names=({}); array(string) ips=({});
df6e592005-03-01Henrik Grubbström (Grubba)  if (a_records) { foreach(a_records->an, mapping x) { if(x->name) names+=({x->name}); if(x->a) ips+=({x->a}); }
27c6851997-12-10David Hedbor  }
df6e592005-03-01Henrik Grubbström (Grubba)  if (aaaa_records) { foreach(aaaa_records->an, mapping x) { if(x->name) names+=({x->name}); if(x->aaaa) ips+=({x->aaaa}); } }
17b6b72013-10-08Per Hedbor 
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) {
e94d1a2003-04-23Marcus Comstedt  if(has_value(ip,':')) { string raw_ipv6 = make_raw_addr6(ip); return reverse(sprintf("%@02X", values(raw_ipv6)))/1*"."+".IP6.ARPA"; } else return reverse(ip/".")*"."+".IN-ADDR.ARPA";
f971bd1997-09-15Fredrik Hübinette (Hubbe)  } string ip_from_arpa(string arpa) {
e94d1a2003-04-23Marcus Comstedt  array(string) parts = reverse(arpa/"."); if(sizeof(parts)<2) return ""; if(lower_case(parts[1]) == "ip6") return map(parts[2..]/4, `*, "")*":"; else return parts[2..]*".";
f971bd1997-09-15Fredrik Hübinette (Hubbe)  }
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.
18096f2003-08-22Martin Nilsson  return ({ 0, ({}), ({}) });
eabcd51999-07-25Henrik Grubbström (Grubba)  }
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  {
b031b92008-03-10Henrik Grubbström (Grubba)  if(m2->mx && 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) 
892fe72010-12-26Henrik 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; }
b031b92008-03-10Henrik Grubbström (Grubba)  array a = filter(m->an, `[], "mx");
3833921999-12-22Henrik Grubbström (Grubba)  array(string) b = column( a, "mx"); sort( column( a, "preference"), b); return b; }
bb02cc1997-09-15Fredrik Hübinette (Hubbe) 
f980322014-01-24Henrik Grubbström (Grubba)  //!
21375c2014-01-10Henrik Grubbström (Grubba)  class Request(string domain, string req, function(string,mapping,mixed...:void) callback, array(mixed) args)
bb02cc1997-09-15Fredrik Hübinette (Hubbe)  {
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  int retries;
21375c2014-01-10Henrik Grubbström (Grubba)  int timestamp = time();
f980322014-01-24Henrik Grubbström (Grubba)  //! Cancel the current request.
21375c2014-01-10Henrik Grubbström (Grubba)  void cancel() { remove(this_object()); }
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) 
f980322014-01-24Henrik Grubbström (Grubba)  protected void remove(object(Request) r)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  { if(!r) return; sscanf(r->req,"%2c",int id); m_delete(requests,id);
f980322014-01-24Henrik Grubbström (Grubba)  r->callback && r->callback(r->domain,0,@r->args);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  destruct(r); }
f980322014-01-24Henrik Grubbström (Grubba) } #define REMOVE_DELAY 120 #define GIVE_UP_DELAY (RETRIES * RETRY_DELAY + REMOVE_DELAY)*2 // FIXME: Randomized source port! //! Asynchronous DNS client. class async_client { inherit client; inherit Stdio.UDP : udp; async_client next_client;
3d3ed71997-09-15Fredrik Hübinette (Hubbe) 
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)  }
f980322014-01-24Henrik Grubbström (Grubba) 
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)  }
f980322014-01-24Henrik Grubbström (Grubba)  //! Enqueue a new raw DNS request.
beb88d2013-09-18Chris Angelico  //!
f980322014-01-24Henrik Grubbström (Grubba)  //! @returns //! Returns a @[Request] object. //! //! @note //! Pike versions prior to 8.0 did not return the @[Request] object. Request do_query(string domain, int cl, int type, function(string,mapping,mixed...:void) callback, mixed ... args)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  {
8ba41f2003-07-30Anders Johansson  for(int e=next_client ? 100 : 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);
21375c2014-01-10Henrik Grubbström (Grubba)  object r = Request(domain, req, callback, args);
75c66d1998-07-17Fredrik Hübinette (Hubbe)  requests[lid]=r; udp::send(nameservers[0],53,r->req); call_out(retry,RETRY_DELAY,r,1);
f980322014-01-24Henrik Grubbström (Grubba)  return r;
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)
c834d52013-10-07Markus Ottensmann  next_client=this_program(nameservers,domains);
75c66d1998-07-17Fredrik Hübinette (Hubbe) 
f980322014-01-24Henrik Grubbström (Grubba)  return next_client->do_query(domain, cl, type, callback, @args);
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  }
9eaf1d2008-06-28Martin Nilsson  protected private void rec_data(mapping m)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  {
6aa4b71998-06-13Henrik Grubbström (Grubba)  mixed err; if (err = catch {
7b69642003-08-07Martin Nilsson  if(m->port != 53 || !has_value(nameservers, m->ip)) return;
6aa4b71998-06-13Henrik Grubbström (Grubba)  sscanf(m->data,"%2c",int id); object r=requests[id];
5154072010-11-19Henrik Grubbström (Grubba)  if(!r) { // Invalid request id. Spoofed answer? // FIXME: Consider black- or greylisting the answer. return; }
6aa4b71998-06-13Henrik Grubbström (Grubba)  m_delete(requests,id); r->callback(r->domain,decode_res(m->data),@r->args); destruct(r); }) {
0bd6f52003-10-21Martin Nilsson  werror("DNS: Failed to read UDP packet. Connection refused?\n%s\n", describe_backtrace(err));
6aa4b71998-06-13Henrik Grubbström (Grubba)  }
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  }
f971bd1997-09-15Fredrik Hübinette (Hubbe) 
f980322014-01-24Henrik Grubbström (Grubba)  protected private Request generic_get(string d, mapping answer, int multi, int all, int type, string field, string domain, function callback, mixed ... args)
f971bd1997-09-15Fredrik Hübinette (Hubbe)  { 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...
f980322014-01-24Henrik Grubbström (Grubba)  return 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);
f980322014-01-24Henrik Grubbström (Grubba)  return UNDEFINED;
901dde1998-09-13Henrik Grubbström (Grubba)  } callback(domain,0,@args); }
f971bd1997-09-15Fredrik Hübinette (Hubbe)  }
f980322014-01-24Henrik Grubbström (Grubba)  return UNDEFINED;
f971bd1997-09-15Fredrik Hübinette (Hubbe)  }
7248292012-02-29Henrik Grubbström (Grubba)  //!
f980322014-01-24Henrik Grubbström (Grubba)  Request host_to_ip(string host, function callback, mixed ... args)
f971bd1997-09-15Fredrik Hübinette (Hubbe)  {
7f82921997-12-10David Hedbor  if(sizeof(domains) && host[-1] != '.' && sizeof(host/".") < 3) {
f980322014-01-24Henrik Grubbström (Grubba)  return do_query(host, C_IN, T_A, generic_get, 0, 0, T_A, "a", host, callback, @args );
27c6851997-12-10David Hedbor  } else {
f980322014-01-24Henrik Grubbström (Grubba)  return do_query(host, C_IN, T_A, generic_get, -1, 0, T_A, "a", host, callback, @args);
27c6851997-12-10David Hedbor  }
f971bd1997-09-15Fredrik Hübinette (Hubbe)  }
7248292012-02-29Henrik Grubbström (Grubba)  //!
f980322014-01-24Henrik Grubbström (Grubba)  Request ip_to_host(string ip, function callback, mixed ... args)
3d3ed71997-09-15Fredrik Hübinette (Hubbe)  {
f980322014-01-24Henrik Grubbström (Grubba)  return do_query(arpa_from_ip(ip), C_IN, T_PTR, generic_get, -1, 0, T_PTR, "ptr", ip, callback, @args);
f971bd1997-09-15Fredrik Hübinette (Hubbe)  }
7248292012-02-29Henrik Grubbström (Grubba)  //!
f980322014-01-24Henrik Grubbström (Grubba)  Request get_mx_all(string host, function callback, mixed ... args)
901dde1998-09-13Henrik Grubbström (Grubba)  { if(sizeof(domains) && host[-1] != '.' && sizeof(host/".") < 3) {
f980322014-01-24Henrik Grubbström (Grubba)  return do_query(host, C_IN, T_MX, generic_get, 0, 1, T_MX, "mx", host, callback, @args);
901dde1998-09-13Henrik Grubbström (Grubba)  } else {
f980322014-01-24Henrik Grubbström (Grubba)  return do_query(host, C_IN, T_MX, generic_get, -1, 1, T_MX, "mx", host, callback, @args);
901dde1998-09-13Henrik Grubbström (Grubba)  } }
7248292012-02-29Henrik Grubbström (Grubba)  //!
f980322014-01-24Henrik Grubbström (Grubba)  Request get_mx(string host, function callback, mixed ... args)
901dde1998-09-13Henrik Grubbström (Grubba)  {
f980322014-01-24Henrik Grubbström (Grubba)  return 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); }, callback, @args);
901dde1998-09-13Henrik Grubbström (Grubba)  }
9bffb12012-02-29Henrik Grubbström (Grubba)  //! Close the client. //! //! @note //! All active requests are aborted.
d9f18f2011-01-21Martin Jonsson  void close() {
9bffb12012-02-29Henrik Grubbström (Grubba)  foreach(requests; ; Request r) { remove(r); }
d9f18f2011-01-21Martin Jonsson  udp::close(); udp::set_read_callback(0); }
892fe72010-12-26Henrik Grubbström (Grubba)  //! protected void create(void|string|array(string) server, void|string|array(string) domain)
f971bd1997-09-15Fredrik Hübinette (Hubbe)  {
4ef71a2012-01-25Henrik Grubbström (Grubba)  int i; // Attempt to randomize the source port. for (i = 0; i < RETRIES; i++) { if (safe_bind(udp::this, 1024 + random(65536-1024), ANY)) break; } if((i >= RETRIES) && !safe_bind(udp::this, 0, ANY) && !safe_bind(udp::this, 0))
484a742002-03-09Martin Nilsson  error( "DNS: failed to bind a port.\n" );
355bbe2005-01-03Henrik Grubbström (Grubba) #if 0
f96a6f2005-01-03Jeff Hungerford  werror("Protocols.DNS.async_client(%O, %O)\n" "UDP Address: %s\n" "%s\n", server, domain, udp::query_address(), describe_backtrace(backtrace()));
355bbe2005-01-03Henrik Grubbström (Grubba) #endif /* 0 */
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 
8ba41f2003-07-30Anders Johansson 
00fd452013-09-18Chris Angelico // FIXME: Reuse sockets where possible? //! Synchronous DNS client using TCP //! Can handle larger responses than @[client] can. class tcp_client { inherit client; //! Perform a synchronous DNS query. //! //! @param s //! Result of @[Protocols.DNS.protocol.mkquery] //! @returns //! mapping containing query result or 0 on failure/timeout //! //! @example //! @code //! // Perform a hostname lookup, results stored in r->an //! object d=Protocols.DNS.tcp_client(); //! mapping r=d->do_sync_query(d->mkquery("pike.lysator.liu.se", C_IN, T_A)); //! @endcode mapping do_sync_query(string s) { for (int i=0; i < RETRIES; i++) { object tcp = Stdio.File(); if (tcp->connect(nameservers[i % sizeof(nameservers)], 53)) { tcp->write("%2H",s); sscanf(tcp->read(2),"%2c",int len); return decode_res(tcp->read(len)); } } // Failure. return 0; } } // FIXME: Reuse sockets? Acknowledge RETRIES? //! Asynchronous DNS client using TCP class async_tcp_client {
1dc4702013-12-19Henrik Grubbström (Grubba)  inherit async_client;
00fd452013-09-18Chris Angelico 
f980322014-01-24Henrik Grubbström (Grubba)  //! class Request
00fd452013-09-18Chris Angelico  {
f980322014-01-24Henrik Grubbström (Grubba)  inherit ::this_program; protected Stdio.File sock; protected string writebuf="",readbuf="";
00fd452013-09-18Chris Angelico 
f980322014-01-24Henrik Grubbström (Grubba)  protected void create(string domain, string req, function(string,mapping,mixed...:void) callback, array(mixed) args)
00fd452013-09-18Chris Angelico  {
f980322014-01-24Henrik Grubbström (Grubba)  ::create(domain, req, callback, args);
00fd452013-09-18Chris Angelico  sock=Stdio.File(); sock->async_connect(nameservers[0], 53, connectedcb); }
f980322014-01-24Henrik Grubbström (Grubba)  protected void close() { sock && sock->close(); sock = UNDEFINED; } protected void connectedcb(int ok)
00fd452013-09-18Chris Angelico  { if (!ok) {callback(domain, 0, @args); return;} sock->set_nonblocking(readcb, writecb, closecb); writebuf=sprintf("%2H",req); writecb(); }
f980322014-01-24Henrik Grubbström (Grubba)  protected void readcb(mixed id,string data)
00fd452013-09-18Chris Angelico  { readbuf+=data; if (sscanf(readbuf,"%2H",string ret)) { if (callback) callback(domain, decode_res(ret), @args); callback=0;
f980322014-01-24Henrik Grubbström (Grubba)  close();
00fd452013-09-18Chris Angelico  } }
f980322014-01-24Henrik Grubbström (Grubba)  protected void writecb()
00fd452013-09-18Chris Angelico  { if (writebuf!="") writebuf=writebuf[sock->write(writebuf)..]; }
f980322014-01-24Henrik Grubbström (Grubba)  protected void closecb()
00fd452013-09-18Chris Angelico  {
f980322014-01-24Henrik Grubbström (Grubba)  cancel(); } void cancel() { close(); ::cancel();
00fd452013-09-18Chris Angelico  } } //!
f980322014-01-24Henrik Grubbström (Grubba)  Request do_query(string domain, int cl, int type, function(string,mapping,mixed...:void) callback, mixed ... args)
00fd452013-09-18Chris Angelico  { string req=low_mkquery(random(65536),domain,cl,type);
f980322014-01-24Henrik Grubbström (Grubba)  return Request(domain, req, callback, args);
00fd452013-09-18Chris Angelico  } } //! Both a @[client] and a @[tcp_client]. class dual_client { inherit client : UDP; inherit tcp_client : TCP; //! mapping do_sync_query(string s) { mapping ret = UDP::do_sync_query(s); if (!ret->tc) return ret; return TCP::do_sync_query(s); } void create(mixed ... args) {::create(@args);} } //! Both an @[async_client] and an @[async_tcp_client]. class async_dual_client { inherit async_client : UDP; inherit async_tcp_client : TCP; void check_truncation(string domain, mapping result, int cl, int type, function(string,mapping,mixed...:void) callback, mixed ... args) { if (!result || !result->tc) callback(domain,result,@args); else TCP::do_query(domain,cl,type,callback,@args); } //!
f980322014-01-24Henrik Grubbström (Grubba)  Request do_query(string domain, int cl, int type, function(string,mapping,mixed...:void) callback, mixed ... args)
00fd452013-09-18Chris Angelico  {
f980322014-01-24Henrik Grubbström (Grubba)  return UDP::do_query(domain,cl,type,check_truncation, cl,type,callback,@args);
00fd452013-09-18Chris Angelico  } void create(mixed ... args) {::create(@args);} }
8ba41f2003-07-30Anders Johansson async_client global_async_client; #define GAC(X) \
f980322014-01-24Henrik Grubbström (Grubba) async_client.Request async_##X( string host, function callback, mixed ... args ) \
8ba41f2003-07-30Anders Johansson { \ if( !global_async_client ) \ global_async_client = async_client(); \
f980322014-01-24Henrik Grubbström (Grubba)  return global_async_client->X(host,callback,@args); \
8ba41f2003-07-30Anders Johansson }
f3b89c2003-08-05Martin Nilsson //! @ignore
8ba41f2003-07-30Anders Johansson GAC(ip_to_host);
f3b89c2003-08-05Martin Nilsson //! @endignore //! @decl void async_ip_to_host(string ip, function cb, mixed ... cba)
8ba41f2003-07-30Anders Johansson 
f3b89c2003-08-05Martin Nilsson //! @ignore
8ba41f2003-07-30Anders Johansson GAC(host_to_ip);
f3b89c2003-08-05Martin Nilsson //! @endignore //! @decl void async_host_to_ip(string host, function cb, mixed ... cba)
8ba41f2003-07-30Anders Johansson 
f3b89c2003-08-05Martin Nilsson //! @ignore
8ba41f2003-07-30Anders Johansson GAC(get_mx_all);
f3b89c2003-08-05Martin Nilsson //! @endignore //! @decl void async_get_mx_all(string host, function cb, mixed ... cba)
8ba41f2003-07-30Anders Johansson 
f3b89c2003-08-05Martin Nilsson //! @ignore
8ba41f2003-07-30Anders Johansson GAC(get_mx);
f3b89c2003-08-05Martin Nilsson //! @endignore //! @decl void async_get_mx(string host, function cb, mixed ... cba)
8ba41f2003-07-30Anders Johansson  client global_client; #define GC(X) \ mixed X( string host ) \ { \ if( !global_client ) \ global_client = client(); \ return global_client->X(host); \ }
f3b89c2003-08-05Martin Nilsson //! @ignore
8ba41f2003-07-30Anders Johansson GC(gethostbyname);
f3b89c2003-08-05Martin Nilsson //! @endignore //! @decl array gethostbyname(string host)
8ba41f2003-07-30Anders Johansson 
f3b89c2003-08-05Martin Nilsson //! @ignore
8ba41f2003-07-30Anders Johansson GC(gethostbyaddr);
f3b89c2003-08-05Martin Nilsson //! @endignore //! @decl array gethostbyaddr(string host)
8ba41f2003-07-30Anders Johansson 
f3b89c2003-08-05Martin Nilsson //! @ignore
8ba41f2003-07-30Anders Johansson GC(get_mx);
f3b89c2003-08-05Martin Nilsson //! @endignore //! @decl string get_mx(string host)
8ba41f2003-07-30Anders Johansson 
f3b89c2003-08-05Martin Nilsson //! @ignore
8ba41f2003-07-30Anders Johansson GC(get_primary_mx);
f3b89c2003-08-05Martin Nilsson //! @endignore //! @decl string get_primary_mx(string host)