Name Resolution
The resolver class performs asynchronous DNS lookups, converting hostnames
to IP addresses. It wraps the system’s getaddrinfo() function with an
asynchronous interface.
| Code snippets assume: |
#include <boost/corosio/resolver.hpp>
namespace corosio = boost::corosio;
Overview
corosio::resolver r(ioc);
auto [ec, results] = co_await r.resolve("www.example.com", "https");
for (auto const& entry : results)
{
auto ep = entry.get_endpoint();
std::cout << ep.v4_address().to_string() << ":" << ep.port() << "\n";
}
Resolving Names
Basic Resolution
auto [ec, results] = co_await r.resolve("www.example.com", "80");
The host can be:
-
A hostname:
"www.example.com" -
An IPv4 address string:
"192.168.1.1" -
An IPv6 address string:
"::1"or"2001:db8::1"
The service can be:
-
A service name:
"http","https","ssh" -
A port number string:
"80","443","22" -
An empty string:
""(returns port 0)
Resolve Flags
| Flag | Description |
|---|---|
|
No special behavior (default) |
|
Return addresses suitable for |
|
Host is a numeric address string, don’t perform DNS lookup |
|
Service is a port number string, don’t look up service name |
|
Only return IPv4 if the system has IPv4 configured, same for IPv6 |
|
If no IPv6 addresses found, return IPv4-mapped IPv6 addresses |
|
With |
Flags can be combined:
auto flags =
corosio::resolve_flags::numeric_host |
corosio::resolve_flags::numeric_service;
auto [ec, results] = co_await r.resolve("127.0.0.1", "8080", flags);
Working with Results
The resolver_results class is a range of resolver_entry objects:
for (auto const& entry : results)
{
corosio::endpoint ep = entry.get_endpoint();
if (ep.is_v4())
std::cout << "IPv4: " << ep.v4_address().to_string();
else
std::cout << "IPv6: " << ep.v6_address().to_string();
std::cout << ":" << ep.port() << "\n";
}
Connecting to Resolved Addresses
Try each address until one works:
capy::task<void> connect_to_service(
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);
if (results.empty())
throw std::runtime_error("No addresses found");
corosio::socket sock(ioc);
sock.open();
boost::system::error_code last_error;
for (auto const& entry : results)
{
auto [ec] = co_await sock.connect(entry.get_endpoint());
if (!ec)
co_return; // Connected successfully
last_error = ec;
sock.close();
sock.open();
}
throw boost::system::system_error(last_error);
}
Error Handling
Common resolution errors:
| Error | Meaning |
|---|---|
|
Hostname doesn’t exist |
|
Hostname exists but has no addresses |
|
Unknown service name |
|
Resolution was cancelled |
Move Semantics
Resolvers are move-only:
corosio::resolver r1(ioc);
corosio::resolver r2 = std::move(r1); // OK
corosio::resolver r3 = r2; // Error: deleted copy constructor
| Source and destination must share the same execution context. |
Thread Safety
| Operation | Thread Safety |
|---|---|
Distinct resolvers |
Safe from different threads |
Same resolver |
NOT safe for concurrent operations |
Don’t start multiple resolve operations on the same resolver concurrently.
Example: HTTP Client with Resolution
capy::task<void> http_get(
corosio::io_context& ioc,
std::string_view hostname)
{
// Resolve hostname
corosio::resolver r(ioc);
auto [resolve_ec, results] = co_await r.resolve(hostname, "80");
if (resolve_ec)
{
std::cerr << "Resolution failed: " << resolve_ec.message() << "\n";
co_return;
}
// Connect to first address
corosio::socket sock(ioc);
sock.open();
for (auto const& entry : results)
{
auto [ec] = co_await sock.connect(entry);
if (!ec)
break;
}
if (!sock.is_open())
{
std::cerr << "Failed to connect\n";
co_return;
}
// Send HTTP request
std::string request =
"GET / HTTP/1.1\r\n"
"Host: " + std::string(hostname) + "\r\n"
"Connection: close\r\n"
"\r\n";
(co_await corosio::write(
sock, capy::const_buffer(request.data(), request.size()))).value();
// Read response
std::string response;
co_await corosio::read(sock, response);
std::cout << response << "\n";
}
Platform Notes
The resolver uses the system’s getaddrinfo() function. On most platforms,
this is a blocking call executed on a thread pool to avoid blocking the
I/O context.
Next Steps
-
Endpoints — Working with resolved addresses
-
Sockets — Connecting to endpoints
-
DNS Lookup Tutorial — Complete example