Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2020-09

Buffer size for TCP DNS queries in netdb

5 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Noel Duffy

Hi all,

I've spent some time this past couple of weeks reading through the DNS resolver code in netdb and poring over RFCs that specify the protocol with a view to adding support for additional resource record queries. Things like TXT, SOA, and so forth. I'm using netdb from fpc 3.2.0 as the starting point. That code only supports querying DNS over UDP. Because TXT records can be up to 64k in size, they frequently get too big to fit inside a UDP packet. The RFCs mandate that resolvers must fall back to TCP in that case. That's the part I'm currently looking at.

The maximum size for a DNS response over TCP is 65,535 octets. The netdb code defines a type TPayload that's a 512 octet buffer for receiving DNS responses. That's the largest response possible over UDP. The simplest path forward for me is to extend that buffer from 512 octets to 65,535. While that increase in size would hardly register on most machines, I wonder whether it might be too much on some of the more memory-restricted platforms that FPC supports. DOS, for instance. I also see IFDEFs for Android in this code.

The alternative is to use different types for TCP queries, or to have the buffer allocated dynamically.

To restate the question, is 64k too much overhead for DNS queries? That overhead would be present on all queries, regardless of whether UDP or TCP was used.

Reply by email

Michael Van Canneyt
to Noel Duffy
Noel Duffy wrote 25 lines
Hi all, I've spent some time this past couple of weeks reading through the DNS resolver code in netdb and poring over RFCs that specify the protocol with a view to adding support for additional resource record queries. Things like TXT, SOA, and so forth. I'm using netdb from fpc 3.2.0 as the starting point. That code only supports querying DNS over UDP. Because TXT records can be up to 64k in size, they frequently get too big to fit inside a UDP packet. The RFCs mandate that resolvers must fall back to TCP in that case. That's the part I'm currently looking at. The maximum size for a DNS response over TCP is 65,535 octets. The netdb code defines a type TPayload that's a 512 octet buffer for receiving DNS responses. That's the largest response possible over UDP. The simplest path forward for me is to extend that buffer from 512 octets to 65,535. While that increase in size would hardly register on most machines, I wonder whether it might be too much on some of the more memory-restricted platforms that FPC supports. DOS, for instance. I also see IFDEFs for Android in this code. The alternative is to use different types for TCP queries, or to have the buffer allocated dynamically. To restate the question, is 64k too much overhead for DNS queries? That overhead would be present on all queries, regardless of whether UDP or TCP was used.

I would then prefer to allocate a dynamic buffer. A simple "Array of Byte" can easily be managed with SetLength() and it is automatically disposed of when not needed.

Michael.

Reply by email

Noel Duffy
to Michael Van Canneyt
Michael Van Canneyt wrote 8 lines
On Sat, 26 Sep 2020, Noel Duffy via fpc-pascal wrote: > To restate the question, is 64k too much overhead for DNS queries? That overhead would be present on all queries, regardless of whether UDP or TCP was used. I would then prefer to allocate a dynamic buffer. A simple "Array of Byte" can easily be managed with SetLength() and it is automatically disposed of when not needed.

Fair enough. Dynamic it is. Thanks Michael.

Reply by email

Noel Duffy
to Michael Van Canneyt: I would then prefer to allocate a dynamic buffer. A simple "Array of Byte" can easily be…
Michael Van Canneyt wrote 7 lines
On Sat, 26 Sep 2020, Noel Duffy via fpc-pascal wrote: > To restate the question, is 64k too much overhead for DNS queries? That overhead would be present on all queries, regardless of whether UDP or TCP was used. I would then prefer to allocate a dynamic buffer. A simple "Array of Byte" can easily be managed with SetLength() and it is automatically disposed of when not needed.

An unintended consequence of attempting to replace the TPayLoad static array with a dynamic one is that the TQueryData record can no longer be passed to fpSendTo as a stream of octets by passing a pointer to it.

I.e,

   Function Query(Resolver : Integer; Var Qry,Ans : TQueryData; QryLen : Integer;
	Var AnsLen : Integer) : Boolean;
   begin
   [...]
     fpsendto(sock,@qry,qrylen+12,0,@SA,SizeOf(SA));

When TPayLoad is made dynamic, the final member of TQueryData changes from an array of octets to a pointer. Now passing a pointer to Qry results in the pointer to the dynamic array being serialized instead of the query payload. Naturally the DNS server will turn up its nose at that and return a sternly-worded error code in protest.

The payload data will have to be manually serialized to octets, I think. At least I don't know of any way to serialize a record that contains pointers using any feature of the language or api in the RTL. Doing manual serialization will also require more memory, of course. The quantities aren't huge, though, so I don't think it matters hugely. But more experienced FPC developers may be able to recommend a better way, of course!

Reply by email

Michael Van Canneyt
to Noel Duffy
Noel Duffy wrote 37 lines
On 26/09/20 9:07 pm, Michael Van Canneyt via fpc-pascal wrote: > > On Sat, 26 Sep 2020, Noel Duffy via fpc-pascal wrote: >> To restate the question, is 64k too much overhead for DNS queries? That overhead would be present on all queries, regardless of whether UDP or TCP was used. > > I would then prefer to allocate a dynamic buffer. A simple "Array of Byte" can > easily be managed with SetLength() and it is automatically disposed of when > not needed. An unintended consequence of attempting to replace the TPayLoad static array with a dynamic one is that the TQueryData record can no longer be passed to fpSendTo as a stream of octets by passing a pointer to it. I.e, Function Query(Resolver : Integer; Var Qry,Ans : TQueryData; QryLen : Integer; Var AnsLen : Integer) : Boolean; begin [...] fpsendto(sock,@qry,qrylen+12,0,@SA,SizeOf(SA)); When TPayLoad is made dynamic, the final member of TQueryData changes from an array of octets to a pointer. Now passing a pointer to Qry results in the pointer to the dynamic array being serialized instead of the query payload. Naturally the DNS server will turn up its nose at that and return a sternly-worded error code in protest. The payload data will have to be manually serialized to octets, I think. At least I don't know of any way to serialize a record that contains pointers using any feature of the language or api in the RTL. Doing manual serialization will also require more memory, of course. The quantities aren't huge, though, so I don't think it matters hugely. But more experienced FPC developers may be able to recommend a better way, of course!

Hm. I don't think this is a good way then. Seems like a little too much overhead: allocating a buffer for TPayload, and then allocating a second one for sending everything in 1 block to the server. This will impact speed significantly.

Probably it is then better to go for 2 kinds of records, one for UPD, one for TCP ?

Michael.

Reply by email