This did already exist in various forms, a couple of years ago I made a newer version, they can all be found in http://www.smalltalkhub.com/#!/~BenComan/DNS/ – including unit tests (but some of the older code in there is a bit stale).
It covers most record types, but most of them are not used a lot.
NeoSimplifiedDNSClient default addressForName: ‘pharo.org‘. “18.104.22.168”
One of my goals was to use it as a more reliable, non-blocking ‘do we have internet access’ test:
NeoNetworkState default hasInternetConnection. “true”
From the class comments:
I am NeoSimplifiedDNSClient.
I resolve fully qualified hostnames into low level IP addresses.
NeoSimplifiedDNSClient default addressForName: ‘stfx.eu‘.
I use the UDP DNS protocol.
I handle localhost and dot-decimal notation.
I can be used to resolve Multicast DNS addresses too.
NeoSimplifiedDNSClient new useMulticastDNS; addressForName: ‘zappy.local’.
I execute requests sequentially and do not cache results.
This means that only one request can be active at any single moment.
It is technically not really necessary to use my default instance as I do not hold state.
I am NeoDNSClient.
I am a NeoSimplifiedDNSClient.
NeoDNSClient default addressForName: ‘stfx.eu‘.
I add caching respecting ttl to DNS requests.
I allow for multiple outstanding requests to be handled concurrently.
UDP requests are asynchroneous and unreliable by definition. Since DNS requests can take some time, it should be possible to have multiple in flight at the same time, thus concurrently. Replies will arrive out of order and need to be matched to their outstanding request by id.
If a request has been seen before and its response is not expired, it will be answered from the cache.
Each incoming request is handled by creating a NeoDNSRequest object and adding that to the request queue. This triggers the start up of the backend process, if necessary. The client then waits on the semaphore inside the request object, limited by the timeout.
The backend process loops while there are still outstanding requests that have not expired. It sends all unsent requests at once, and then listens briefly for incoming replies. It cleans up expired requests. When a reply comes in, it is connected to its request by id. The semaphore in the request object is then signalled so that the waiting client can continue and the request is removed from the queue. The process then loops. If the queue is empty, the backend process stops.