DNS Lookup Tutorial
This tutorial builds a command-line DNS lookup tool similar to nslookup.
You’ll learn to use the asynchronous resolver to convert hostnames to IP
addresses.
| Code snippets assume: |
#include <boost/corosio.hpp>
#include <boost/capy/task.hpp>
#include <boost/capy/ex/run_async.hpp>
namespace corosio = boost::corosio;
namespace capy = boost::capy;
Overview
DNS resolution converts a hostname like www.example.com to one or more IP
addresses. The resolver class performs this asynchronously:
corosio::resolver r(ioc);
auto [ec, results] = co_await r.resolve("www.example.com", "https");
The second argument is the service name (or port number as a string). It determines the port in the returned endpoints.
The Lookup Coroutine
capy::task<void> do_lookup(
corosio::io_context& ioc,
std::string_view host,
std::string_view service)
{
corosio::resolver r(ioc);
auto [ec, results] = co_await r.resolve(host, service);
if (ec)
{
std::cerr << "Resolve failed: " << ec.message() << "\n";
co_return;
}
std::cout << "Results for " << host;
if (!service.empty())
std::cout << ":" << service;
std::cout << "\n";
for (auto const& entry : results)
{
auto ep = entry.get_endpoint();
if (ep.is_v4())
{
std::cout << " IPv4: " << ep.v4_address().to_string()
<< ":" << ep.port() << "\n";
}
else
{
std::cout << " IPv6: " << ep.v6_address().to_string()
<< ":" << ep.port() << "\n";
}
}
std::cout << "\nTotal: " << results.size() << " addresses\n";
}
Understanding Results
The resolver returns a resolver_results object containing resolver_entry
elements. Each entry provides:
-
get_endpoint()— The resolved endpoint (address + port) -
host_name()— The queried hostname -
service_name()— The queried service
The endpoint class supports both IPv4 and IPv6:
auto ep = entry.get_endpoint();
if (ep.is_v4())
{
// IPv4 address
boost::urls::ipv4_address addr = ep.v4_address();
}
else
{
// IPv6 address
boost::urls::ipv6_address addr = ep.v6_address();
}
std::uint16_t port = ep.port();
Main Function
int main(int argc, char* argv[])
{
if (argc < 2 || argc > 3)
{
std::cerr << "Usage: nslookup <hostname> [service]\n"
<< "Examples:\n"
<< " nslookup www.google.com\n"
<< " nslookup www.google.com https\n"
<< " nslookup localhost 8080\n";
return 1;
}
std::string_view host = argv[1];
std::string_view service = (argc == 3) ? argv[2] : "";
corosio::io_context ioc;
capy::run_async(ioc.get_executor())(do_lookup(ioc, host, service));
ioc.run();
}
Resolver Flags
The resolver accepts optional flags to control behavior:
auto [ec, results] = co_await r.resolve(
host, service,
corosio::resolve_flags::numeric_host |
corosio::resolve_flags::numeric_service);
Available flags:
| Flag | Description |
|---|---|
|
Return endpoints suitable for binding (server use) |
|
Host is a numeric address string, skip DNS |
|
Service is a port number string |
|
Only return addresses if configured on the system |
|
Return IPv4-mapped IPv6 addresses if no IPv6 found |
|
With |
Connecting to Resolved Addresses
After resolving, iterate through results to find a working connection:
capy::task<void> connect_to_host(
corosio::io_context& ioc,
std::string_view host,
std::string_view service)
{
corosio::resolver r(ioc);
auto [resolve_ec, results] = co_await r.resolve(host, service);
if (resolve_ec)
throw boost::system::system_error(resolve_ec);
corosio::socket sock(ioc);
sock.open();
// Try each address until one works
boost::system::error_code last_ec;
for (auto const& entry : results)
{
auto [ec] = co_await sock.connect(entry.get_endpoint());
if (!ec)
{
std::cout << "Connected to " << host << "\n";
co_return;
}
last_ec = ec;
}
throw boost::system::system_error(last_ec, "all addresses failed");
}
Running the Lookup Tool
$ ./nslookup www.google.com https
Results for www.google.com:https
IPv4: 142.250.189.68:443
IPv6: 2607:f8b0:4004:800::2004:443
Total: 2 addresses
$ ./nslookup localhost 8080
Results for localhost:8080
IPv4: 127.0.0.1:8080
Total: 1 addresses
Cancellation
Resolver operations support cancellation via std::stop_token:
r.cancel(); // Cancel pending operation
Or through the affine awaitable protocol when using capy::jcancellable_task.
Next Steps
-
Resolver Guide — Full resolver reference
-
Endpoints Guide — Working with addresses
-
HTTP Client — Use resolved addresses for connections