Table of Contents

Getting started

Install

Ubiety.Dns.Core targets .NET 10.

dotnet package add Ubiety.Dns.Core

Your first query

A resolver is built with ResolverBuilder, then queried:

using Ubiety.Dns.Core;
using Ubiety.Dns.Core.Common;
using Ubiety.Dns.Core.Records.General;

var resolver = ResolverBuilder.Begin()
    .AddDnsServer("1.1.1.1")
    .SetTimeout(5000)
    .SetRetries(2)
    .UseRecursion()
    .EnableCache()
    .Build();

var response = resolver.Query("example.com", QuestionType.A);

foreach (var record in response.GetRecords<RecordA>())
{
    Console.WriteLine(record.Address);
}

If no server is added the builder falls back to the system resolvers, so ResolverBuilder.Begin().Build() is a working resolver.

Asynchronously

QueryAsync is the asynchronous equivalent and takes a cancellation token:

var response = await resolver.QueryAsync("example.com", QuestionType.A, cancellationToken);

The async path is implemented independently down to the socket rather than wrapping the synchronous one, so it does not block a thread.

Cancellation and timeout mean different things. A cancelled token abandons the query and throws OperationCanceledException. An elapsed timeout is a transport failure: the resolver moves on to the next server, and once every server and retry is exhausted it returns a response with TimedOut set.

Reading the answer

A Response carries the four sections of a DNS message.

response.Header.ResponseCode   // NoError, NXDomain, ServFail, ...
response.Questions             // what was asked
response.Answers               // the answer section
response.Authorities           // the authority section
response.Additional            // the additional section
response.ResourceRecords       // answers, authorities and additional together

GetRecords<T> filters the answer section by record type. To reach glue records in the additional section, read Additional directly.

var addresses = response.GetRecords<RecordA>();          // answers only
var everything = response.ResourceRecords;               // all three sections

Handling failure

A query that no server answers does not throw. It returns a response with TimedOut set and no answers, so check that before reading records:

var response = resolver.Query("example.com", QuestionType.A);

if (response.TimedOut)
{
    // no server replied
}
else if (response.Header.ResponseCode != ResponseCode.NoError)
{
    // the server replied with an error, for example NXDomain
}

Query throws only when the resolver is misconfigured, such as having no servers at all.