client-side DNS TCP

Stefan Metzmacher metze at
Fri Jul 7 05:03:43 UTC 2017

Hi Dimitris,

> I'm trying to put together the standard for the client-side DNS TCP
> calls in libcli/dns/dns.c, libdns.h and dns.h, so that I can then work
> on switching from UDP for packages larger than 512 bytes. I'm using
> source4/dns_server/dns_server.c and dns_server for cross-referencing and
> to figure out how exactly should I make the client implementation.

I'd say for dns updates we should only use tcp.

Do you have some work in progress branch somewhere?

> I've gone through RFC 1035/768/793/2548/7766, but I'm still getting a
> bit overwhelmed from the custom headers and APIs that are used in Samba.
> To my understanding, dns_server.c/.h cover the stream, socket and
> binding over TCP and most functions from dns_server.c that handle the
> above, could be directly implemented to libcli/dns.c, am I correct? Do I
> use the same memory and tevent context that is used for UDP requests?
> Both talloc and tevent have given me a bit of a hard time, information
> overload!

But once you get around it you'll never want to use something else:-)

> I've also looked into PIDL and found the definition for TSIG records,
> along with a "fake" TSIG definition, could you tell me what exactly this
> is, since I didn't come across it in RFC 284
dns_fake_tsig_rec is what RFC 2845 3.4.2. "TSIG Variables" is about:
Source       Field Name       Notes
TSIG RR      NAME             Key name, in canonical wire format
TSIG RR      CLASS            (Always ANY in the current specification)
TSIG RR      TTL              (Always 0 in the current specification)
TSIG RDATA   Algorithm Name   in canonical wire format
TSIG RDATA   Time Signed      in network byte order
TSIG RDATA   Fudge            in network byte order
TSIG RDATA   Error            in network byte order
TSIG RDATA   Other Len        in network byte order
TSIG RDATA   Other Data       exactly as transmitted

While dns_tsig_record is "RDATA" from RFC 2845 2.3. Record Format:
      Field Name       Data Type      Notes
      Algorithm Name   domain-name    Name of the algorithm
                                      in domain name syntax.
      Time Signed      u_int48_t      seconds since 1-Jan-70 UTC.
      Fudge            u_int16_t      seconds of error permitted
                                      in Time Signed.
      MAC Size         u_int16_t      number of octets in MAC.
      MAC              octet stream   defined by Algorithm Name.
      Original ID      u_int16_t      original message ID
      Error            u_int16_t      expanded RCODE covering
                                      TSIG processing.
      Other Len        u_int16_t      length, in octets, of
                                      Other Data.
      Other Data       octet stream   empty unless Error == BADTIME

Basically the signature generation needs to use the original
dns packet and:
- remove the bytes from dns_tsig_record
- count down the arcount field in the buffer
- construct a blob using dns_fake_tsig_rec and append that
- pass the constructed buffer to gensec_sign_packet()
- rebuild the dns packet buffer with the signature (MAC) field of
  dns_tsig_record filled with the signature from gensec_sign_packet().

See dns_verify_tsig() in source4/dns_server/dns_crypto.c
for the checking function that verifies the client request
and dns_sign_tsig() and dns_tsig_compute_mac() for the
MAC generated in the server response, which should be validated
by the client.

> I checked this
> <>
> tutorial as well (it's from a Greek University), though it uses Sockets,
> it helped me put all those RFCs in perspective.

It's good have some low level understanding, but our high level code
should deal with all these nasty details.

That's why we have the tdgram (for udp) and tstream (for tcp) abstractions.

I guess for one DNS "transaction/call" (request and response) you'll
need to have a call chain like:

1. prepare the request buffer:
2. submit the buffer with tstream_writev_send() or tdgram_sendto_send()
3. register a callback, which uses tstream_writev_recv() or
  tdgram_sendto_recv() to get the result.
4. use tstream_read_pdu_blob_send() (similar to dns_tcp_call_loop())
  or tdgram_recvfrom_send() (similar to dns_udp_call_loop()) to
  wait for the servers response.
5. register a callback, which uses tstream_read_pdu_blob_recv() or
  tdgram_recvfrom_recv() to get the response buffer.
6. parse the response buffer.

I guess you want to implement a low level
dns_client_call_send/recv() which takes the
request buffer and returns the response buffer,
it will basically cover steps 2-5.
And then you have multiple wrapper on top
of it, e.g. dns_client_update_send/recv()
or dns_client_query_send/recv(), which hide
the preparation of the request buffer (including a
signature generation).


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <>

More information about the samba-technical mailing list