pike.git / lib / modules / Protocols.pmod / DNS.pmod

version» Context lines:

pike.git/lib/modules/Protocols.pmod/DNS.pmod:3:    //inherit Stdio.UDP : udp;      //! Support for the Domain Name System protocol.   //!   //! RFC 1034, RFC 1035 and RFC 2308       protected void send_reply(mapping r, mapping q, mapping m, Stdio.UDP udp);      #pike __REAL_VERSION__    + // documentation taken from RFC 2136 +  + //! No error condition.   final constant NOERROR=0; -  +  + //! The name server was unable to interpret the request due to a + //! format error.   final constant FORMERR=1; -  +  + //! The name server encountered an internal failure while processing + //! this request, for example an operating system error or a + //! forwarding timeout.   final constant SERVFAIL=2; -  +  + //! Some name that ought to exist, does not exist.   final constant NXDOMAIN=3; -  +  + //! The name server does not support the specified Opcode.   final constant NOTIMPL=4; -  +  + //! The name server refuses to perform the specified operation for + //! policy or security reasons.   final constant REFUSED=5; -  +  + //! Some RRset that ought to exist, does not exist.   final constant NXRRSET=8;      final constant QUERY=0;      //! Resource classes   enum ResourceClass   {    //! Class Internet    C_IN=1,   
pike.git/lib/modules/Protocols.pmod/DNS.pmod:83:       //! Type - mailbox or mail list information    T_MINFO=14,       //! Type - mail exchange    T_MX=15,       //! Type - text strings    T_TXT=16,    -  //! Type - IPv6 address record (RFC 1886, deprecated) +  //! Type - IPv6 address record (RFC 1886)    T_AAAA=28,       //! Type - Location Record (RFC 1876)    T_LOC=29,       //! Type - Service location record (RFC 2782)    T_SRV=33,       //! Type - NAPTR (RFC 3403)    T_NAPTR=35,       //! Type - IPv6 address record (RFC 2874, incomplete support)    T_A6=38,       //! Type - SPF - Sender Policy Framework (RFC 4408)    T_SPF=99, -  +  +  //! Type - ANY - A request for all records +  T_ANY=255,   };    - int safe_bind(Stdio.UDP udp, mixed ... args) + int safe_bind(Stdio.UDP udp, string|int port, string|void device)   {    mixed err = catch { -  udp->bind(@args); +  udp->bind(port, device, 1);    return 1;    };   #if constant(System.EADDRINUSE)    if (errno() == System.EADDRINUSE) return 0;   #endif    werror("Protocols.DNS: Binding of UDP port failed with errno %d: %s\n",    errno(), strerror(errno()));    master()->handle_error(err);    return 0;   }      //! Low level DNS protocol   class protocol   {    string mklabel(string s)    {    if(sizeof(s)>63)    error("Too long component in domain name.\n"); -  return sprintf("%c%s",sizeof(s),s); +  return sprintf("%1H",s);    }       protected 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*".";
pike.git/lib/modules/Protocols.pmod/DNS.pmod:173:    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", sizeof(entry->cpu||""), entry->cpu||"", -  sizeof(entry->os||""), entry->os||""); +  return sprintf("%1H%1H", entry->cpu||"", entry->os||"");    case T_MINFO:    string rmailbx = mkname(entry->rmailbx, pos, c);    return rmailbx + mkname(entry->emailbx, pos+sizeof(rmailbx), c);    case T_SRV:    return sprintf("%2c%2c%2c", entry->priority, entry->weight, entry->port) +    mkname(entry->target||"", pos+6, c);    case T_A:    return sprintf("%@1c", (array(int))((entry->a||"0.0.0.0")/".")[0..3]);    case T_AAAA:    return make_raw_addr6(entry->aaaa);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:199:    entry->a6->prefixlen,    make_raw_addr6(entry->a6->address)[entry->a6->prefixlen/8..],    entry->a6->prefixname||"");    case T_SOA:    string mname = mkname(entry->mname, pos, c);    return mname + mkname(entry->rname, pos+sizeof(mname), c) +    sprintf("%4c%4c%4c%4c%4c", entry->serial, entry->refresh,    entry->retry, entry->expire, entry->minimum);    case T_NAPTR:    string rnaptr = sprintf("%2c%2c", entry->order, entry->preference); -  rnaptr += sprintf("%1c%s%1c%s%1c%s%s", -  sizeof(entry->flags || ""), entry->flags || "", -  sizeof(entry->service || ""), entry->service || "", -  sizeof(entry->regexp || ""), entry->regexp || "", +  rnaptr += sprintf("%1H%1H%1H%s", +  entry->flags || "", +  entry->service || "", +  entry->regexp || "",    mkname(entry->replacement, pos, c));    return rnaptr;       case T_TXT: -  return Array.map(stringp(entry->txt)? ({entry->txt}):(entry->txt||({})), +  return map(stringp(entry->txt)? ({entry->txt}):(entry->txt||({})),    lambda(string t) { -  return sprintf("%1c%s", sizeof(t), t); +  return sprintf("%1H", t);    })*"";    case T_SPF: -  return Array.map(stringp(entry->spf)? ({entry->spf}):(entry->spf||({})), +  return map(stringp(entry->spf)? ({entry->spf}):(entry->spf||({})),    lambda(string t) { -  return sprintf("%1c%s", sizeof(t), t); +  return sprintf("%1H", t);    })*"";    case T_LOC:    // FIXME: Not implemented yet.    default:    return "";    }    }       protected 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 += sizeof(e)+2;    string rd = entry->rdata || mkrdata(entry, pos, comp); -  res += e + sprintf("%2c", sizeof(rd)) + rd; +  res += e + sprintf("%2H", rd);    pos += sizeof(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}));
pike.git/lib/modules/Protocols.pmod/DNS.pmod:280:    return low_low_mkquery((["id":id, "rd":1,    "qd":(["name":dname, "cl":cl, "type":type])]));    }      //! 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 +  //! 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);    string mkquery(string|mapping dnameorquery, int|void cl, int|void type)    {    if(mappingp(dnameorquery))    return low_low_mkquery(dnameorquery);    else    return low_mkquery(random(65536),dnameorquery,cl,type);    }       string decode_domain(string msg, array(int) n)    { -  array(string) domains=({}); -  +     int pos=n[0];    int next=-1;    array(string) ret=({}); -  +  int labels = 0;    while(pos < sizeof(msg))    { -  +  labels++; +  if (labels > 255) +  error("Bad domain name. Too many labels.\n");    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]});
pike.git/lib/modules/Protocols.pmod/DNS.pmod:331:    if(next==-1) next=pos+2;    pos=((len&63)<<8) + msg[pos+1];    continue;    }    break;    }    }       string decode_string(string s, array(int) next)    { -  int len=s[next[0]]; +  int pos = next[0]; +  int len=s[pos];    next[0]+=len+1; -  return s[next[0]-len..next[0]-1]; +  return s[pos+1..pos+len];    }       int decode_byte(string s, array(int) next)    {    return s[next[0]++];    }       int decode_short(string s, array(int) next)    { -  sscanf(s[next[0]..next[0]+1],"%2c",int ret); -  next[0]+=2; -  return ret; +  return s[next[0]++]<<8 | s[next[0]++];    }       int decode_int(string s, array(int) next)    { -  sscanf(s[next[0]..next[0]+3],"%4c",int ret); -  next[0]+=4; -  return ret; +  int pos = next[0]; +  next[0] += 4; +  return s[pos++]<<24 | s[pos++]<<16 | s[pos++]<<8 | s[pos++];    }       //! Decode a set of entries from an answer.    //!    //! @param s    //! Encoded entries.    //!    //! @param num    //! Number of entires in @[s].    //!
pike.git/lib/modules/Protocols.pmod/DNS.pmod:468:    //! @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" +  //! 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.    //! @endmapping    //! @value T_SPF    //! @mapping    //! @member string "spf"    //! @endmapping    //! @endint    //! @endarray    array decode_entries(string s,int num, array(int) next)    {    array(string) ret=({});
pike.git/lib/modules/Protocols.pmod/DNS.pmod:531:    {    if(x[1][0..0]=="_")    m->proto=x[1][1..];    else    m->proto=x[1];    }       m->name=x[2..]*".";    break;    case T_A: -  m->a=sprintf("%{.%d%}",values(s[next[0]..next[0]+m->len-1]))[1..]; +  m->a=(array(string))values(s[next[0]..next[0]+m->len-1])*".";    break;    case T_AAAA: -  m->aaaa=sprintf("%{:%02X%02X%}", -  values(s[next[0]..next[0]+m->len-1])/2)[1..]; +  m->aaaa=sprintf("%{%02X%}", +  (values(s[next[0]..next[0]+m->len-1])/2)[*])*":";    break;    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;
pike.git/lib/modules/Protocols.pmod/DNS.pmod:571:    break;    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;    case T_TXT: -  m->txt = decode_string(s, next); +  { +  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]; +  }    break;    case T_SPF:    m->spf = decode_string(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", +  if (sscanf(s,"%2c%c%c%2c%2c%2c%2c",    m->id,    m->c1,    m->c2,    m->qdcount,    m->ancount,    m->nscount, -  m->arcount); +  m->arcount) != 7) +  error("Bad DNS request, failed to parse header\n");    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;       m->rcode=m->c2&15;    m->cd=(m->c2>>4)&1;    m->ad=(m->c2>>5)&1;    m->ra=(m->c2>>7)&1;       m->length=sizeof(s);    -  array(string) tmp=({}); -  +     array(int) next=({12});    m->qd = allocate(m->qdcount);    for(int i=0; i<m->qdcount; i++) { -  +  if (next[0] > sizeof(s)) +  error("Bad DNS request, not enough data\n");    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;    }    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;    }   };
pike.git/lib/modules/Protocols.pmod/DNS.pmod:678:    }    }    }    destruct(udp2);    }    }    destruct(udp);    };   }    - //! Base class for implementing a Domain Name Service (DNS) server. - //! - //! This class is typically used by inheriting it, - //! and overloading @[reply_query()] and @[handle_response()]. - class server + //! Base class for @[server], @[tcp_server]. + class server_base   { -  //! +     inherit protocol;    -  //inherit Stdio.UDP : udp; +  array(Stdio.UDP|object) ports = ({ });    -  array(Stdio.UDP) ports = ({}); -  -  protected void send_reply(mapping r, mapping q, mapping m, Stdio.UDP udp) +  protected string low_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); +  return s;    }       //! Reply to a query (stub).    //!    //! @param query    //! Parsed query.    //!    //! @param udp_data -  //! Raw UDP data. +  //! 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.    //!    //! @param cb    //! Callback you can call with the result instead of returning it.    //! In that case, return @expr{0@} (zero).    //!    //!    //! Overload this function to implement the proper lookup.    //!    //! @note    //! To indicate the default failure @[cb] must be called with an    //! argument of @expr{0@} (zero), and @expr{0@} (zero) be returned.    //!    //! @returns    //! Returns @expr{0@} (zero) when the @[cb] callback will be used,    //! or a result mapping if not:    //! @mapping    //! @member int "rcode" -  +  //! 0 (or omit) for success, otherwise one of the Protocols.DNS.* constants    //! @member array(mapping(string:string|int))|void "an" -  +  //! Answer section:    //! @array    //! @elem mapping(string:string|int) entry    //! @mapping    //! @member string|array(string) "name"    //! @member int "type"    //! @member int "cl"    //! @endmapping    //! @endarray    //! @member array|void "qd" -  +  //! Question section, same format as @[an]; omit to return the original question    //! @member array|void "ns" -  +  //! Authority section (usually NS records), same format as @[an]    //! @member array|void "ar" -  +  //! 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    //! @endmapping    protected mapping reply_query(mapping query, mapping udp_data,    function(mapping:void) cb)    {    // Override this function.    //    // Return mapping may contain:    // aa, ra, {ad, cd,} rcode, an, ns, ar       return 0;    }       //! Handle a query.    //!    //! This function calls @[reply_query()],    //! and dispatches the result to @[send_reply()]. -  protected void handle_query(mapping q, mapping m, Stdio.UDP udp) +  protected void handle_query(mapping q, mapping m, Stdio.UDP|object udp)    {    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);    }    }       //! Handle a query response (stub).    //!    //! Overload this function to handle responses to possible recursive queries. -  protected void handle_response(mapping r, mapping m, Stdio.UDP udp) +  protected void handle_response(mapping r, mapping m, Stdio.UDP|object udp)    {    // This is a stub intended to simplify servers which allow recursion    }       //! Low-level DNS-data receiver.    //!    //! This function receives the raw DNS-data from the @[Stdio.UDP] socket -  //! @[udp], decodes it, and dispatches the decoded DNS request to -  //! @[handle_query()] and @[handle_response()]. -  protected private void rec_data(mapping m, Stdio.UDP udp) +  //! 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)    {    mixed err;    mapping q;    if (err = catch {    q=decode_res(m->data);    }) { -  werror("DNS: Failed to read UDP packet.\n%s\n", +  werror("DNS: Failed to read %s packet.\n%s\n", +  udp->tcp_connection ? "TCP" : "UDP",    describe_backtrace(err));    if(m && m->data && sizeof(m->data)>=2)    send_reply((["rcode":1]),    mkmapping(({"id"}), array_sscanf(m->data, "%2c")), m, udp);    }    else if(q->qr)    handle_response(q, m, udp);    else    handle_query(q, m, udp);    }    -  +  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()]. + //! + //! @seealso + //! @[dual_server] + 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)); +  } +     //! @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)
pike.git/lib/modules/Protocols.pmod/DNS.pmod:857:    } else {    if(!safe_bind(udp, args[i+1]))    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});    }       } + }    -  protected void destory() +  + //! 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   { -  if(sizeof(ports)) +  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) { +  this_program::con = con; +  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)); +  out_buffer += sprintf("%2H", s); +  +  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)    { -  foreach(ports;; Stdio.UDP port) -  destruct(port); +  if(!arg1 && !sizeof(args)) +  arg1 = 53; +  if(!sizeof(args)) +  { +  if(stringp(arg1)) +  args = ({ arg1, 53 }); +  else +  args = ({ ANY, arg1 });    } -  +  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); +  // Port objects are stored for destruction when the server +  // object is destroyed. +  ports += ({ port });    } -  +  }    -  +  protected void destroy() +  { +  foreach (connections; Connection con;) { +  destruct(con); +  }    -  +  ::destroy(); +  } + } +  + //! 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) { +  rpl = sprintf("%s%8c", rpl[..3], 0); // Truncate after header and +  // send empty response +  // ("dnscache strategy") +  rpl[2] |= 2; // Set TC bit +  } +  con->send(m->ip, m->port, rpl); +  } else +  con->send(rpl); +  } +  +  protected void create(int|string|void arg1, string|int ... args) +  { +  ::create(arg1, @args); +  } +  +  protected void destroy() +  { +  ::destroy(); +  } + } +  +    #define RETRIES 12   #define RETRY_DELAY 5    - //! Synchronous DNS client. + //! Synchronous DNS client.   class client   {    inherit protocol;      #ifdef __NT__    array(string) get_tcpip_param(string val, void|string fallbackvalue)    {    array(string) res = ({});    foreach(({    "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1105:       if(domain)    domains = ({ domain }) + domains;   #endif    nameservers -= ({ "" });    if (!sizeof(nameservers)) {    /* Try localhost... */    nameservers = ({ "127.0.0.1" });    }    domains -= ({ "" }); -  domains = Array.map(domains, lambda(string d) { +  domains = map(domains, lambda(string d) {    if (d[-1] == '.') {    return d[..<1];    }    return d;    });    }    else    {    if(arrayp(server))    nameservers = server;
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1132:    if(stringp(domain))    domains = ({ domain });    }    }      //! Perform a synchronous DNS query.   //!   //! @param s   //! Result of @[Protocols.DNS.protocol.mkquery]   //! @returns - //! mapping containing query result or 0 on failure/timeout +  //! 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    mapping do_sync_query(string s)    {    int i;
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1225:    //! @elem array(string) aliases    //! DNS name(s).    //! @endarray    //!    //! @note    //! Prior to Pike 7.7 this function only returned IPv4 addresses.    //!    array gethostbyname(string s)    {    mapping a_records = low_gethostbyname(s, T_A); -  mapping a6_records = low_gethostbyname(s, T_A6); +     mapping aaaa_records = low_gethostbyname(s, T_AAAA);      #if 0    werror("a_records: %O\n" -  "a6_records: %O\n" +     "aaaa_records: %O\n", -  a_records, a6_records, aaaa_records); +  a_records, aaaa_records);   #endif /* 0 */       array(string) names=({});    array(string) ips=({});    if (a_records) {    foreach(a_records->an, mapping x)    {    if(x->name)    names+=({x->name});    if(x->a)    ips+=({x->a});    }    } -  // Prefer a6 to aaaa. -  if (a6_records) { -  foreach(a6_records->an, mapping x) -  { -  if(x->name) -  names+=({x->name}); -  if(x->a6) -  ips+=({x->a6}); -  } -  } +     if (aaaa_records) {    foreach(aaaa_records->an, mapping x)    {    if(x->name)    names+=({x->name});    if(x->aaaa)    ips+=({x->aaaa});    }    }   
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1326:    } 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);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1516:    return;    } else {    nsno = 0;    }    }       send(nameservers[nsno],53,r->req);    call_out(retry,RETRY_DELAY,r,nsno+1);    }    +  //!    void do_query(string domain, int cl, int type,    function(string,mapping,mixed...:void) callback,    mixed ... args)    {    for(int e=next_client ? 100 : 256;e>=0;e--)    {    int lid = random(65536);    if(!catch { requests[lid]++; })    {    string req=low_mkquery(lid,domain,cl,type);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1545:    call_out(retry,RETRY_DELAY,r,1);    return;    }    }       /* 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=this_program(nameservers,domains);       next_client->do_query(domain, cl, type, callback, @args);    }       protected private void rec_data(mapping m)    {    mixed err;    if (err = catch {    if(m->port != 53 || !has_value(nameservers, m->ip)) return;    sscanf(m->data,"%2c",int id);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:1697: Inside #if 0
   "%s\n", server, domain, udp::query_address(),    describe_backtrace(backtrace()));   #endif /* 0 */       udp::set_read_callback(rec_data);    ::create(server,domain);    }   };       + // 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 + { +  inherit async_client; +  +  class Request(string domain, string req, +  function(string,mapping,mixed...:void) callback, +  array(mixed) args) +  { +  Stdio.File sock; +  string writebuf="",readbuf=""; +  +  void create() +  { +  sock=Stdio.File(); +  sock->async_connect(nameservers[0], 53, connectedcb); +  } +  +  void connectedcb(int ok) +  { +  if (!ok) {callback(domain, 0, @args); return;} +  sock->set_nonblocking(readcb, writecb, closecb); +  writebuf=sprintf("%2H",req); +  writecb(); +  } +  +  void readcb(mixed id,string data) +  { +  readbuf+=data; +  if (sscanf(readbuf,"%2H",string ret)) +  { +  if (callback) callback(domain, decode_res(ret), @args); +  callback=0; +  sock->close(); +  } +  } +  +  void writecb() +  { +  if (writebuf!="") writebuf=writebuf[sock->write(writebuf)..]; +  } +  +  void closecb() +  { +  sock->close(); +  if (callback) callback(domain, 0, @args); +  callback=0; +  } +  } +  +  //! +  void do_query(string domain, int cl, int type, +  function(string,mapping,mixed...:void) callback, +  mixed ... args) +  { +  string req=low_mkquery(random(65536),domain,cl,type); +  Request(domain, req, callback, args); +  } + } +  + //! 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); +  } +  +  //! +  void do_query(string domain, int cl, int type, +  function(string,mapping,mixed...:void) callback, +  mixed ... args) +  { +  UDP::do_query(domain,cl,type,check_truncation,cl,type,callback,@args); +  } +  +  void create(mixed ... args) {::create(@args);} + } +  +    async_client global_async_client;      #define GAC(X) \   void async_##X( string host, function callback, mixed ... args ) \   { \    if( !global_async_client ) \    global_async_client = async_client(); \    global_async_client->X(host,callback,@args); \   }