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

version» Context lines:

pike.git/lib/modules/Protocols.pmod/DNS.pmod:139:    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:188:    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:214:    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||({})),    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||({})),    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:314:    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=({});    while(pos < sizeof(msg))    {    switch(int len=msg[pos])    {    case 0:    if(next==-1) next=pos+1;    n[0]=next;
pike.git/lib/modules/Protocols.pmod/DNS.pmod:346:    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:483:    //! @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:546:    {    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:586:    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++) {    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);
pike.git/lib/modules/Protocols.pmod/DNS.pmod:693:    }    }    }    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
pike.git/lib/modules/Protocols.pmod/DNS.pmod:789:    // 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:889:    } 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 destroy() +  + //! 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.   class client   {    inherit protocol;      #ifdef __NT__    array(string) get_tcpip_param(string val, void|string fallbackvalue)