COROIO: coroio/dns/resolver.hpp Source File
COROIO
 
Loading...
Searching...
No Matches
resolver.hpp
1#pragma once
2
3#include <exception>
4#include <unordered_map>
5
6#include <coroio/promises.hpp>
7#include <coroio/socket.hpp>
9
10namespace NNet {
11
20public:
26 TResolvConf(const std::string& fn = "/etc/resolv.conf");
35 TResolvConf(std::istream& input);
41 std::vector<TAddress> Nameservers;
42
43private:
44 void Load(std::istream& input);
45};
46
51enum class EDNSType {
52 DEFAULT = 0,
53 A = 1,
54 AAAA = 28,
55};
56
58 TTypelessSocket() = default;
59
60 template<typename TSocket>
61 TTypelessSocket(TSocket&& sock) {
62 auto socket = std::make_shared<TSocket>(std::move(sock));
63 Connector = [socket](const TAddress& addr, TTime deadline) -> TFuture<void> {
64 co_await socket->Connect(addr, deadline);
65 };
66 Reader = [socket](void* buf, size_t size) -> TFuture<ssize_t> {
67 co_return co_await socket->ReadSome(buf, size);
68 };
69 Writer = [socket](const void* buf, size_t size) -> TFuture<ssize_t> {
70 co_return co_await socket->WriteSome(buf, size);
71 };
72 }
73
74 TFuture<void> Connect(const TAddress& addr, TTime deadline = TTime::max()) {
75 co_await Connector(addr, deadline);
76 }
77
78 TFuture<ssize_t> ReadSome(void* buf, size_t size) {
79 co_return co_await Reader(buf, size);
80 }
81
82 TFuture<ssize_t> WriteSome(const void* buf, size_t size) {
83 co_return co_await Writer(buf, size);
84 }
85
86private:
87 std::function<TFuture<void>(const TAddress& addr, TTime deadline)> Connector;
88 std::function<TFuture<ssize_t>(void* buf, size_t size)> Reader;
89 std::function<TFuture<ssize_t>(const void* buf, size_t size)> Writer;
90};
91
116public:
126 template<typename TPoller>
127 TResolver(TPoller& poller, EDNSType defaultType = EDNSType::A);
138 template<typename TPoller>
139 TResolver(const TResolvConf& conf, TPoller& poller, EDNSType defaultType = EDNSType::A);
149 template<typename TPoller>
150 TResolver(TAddress dnsAddr, TPoller& poller, EDNSType defaultType = EDNSType::A);
151 ~TResolver();
152
166 TFuture<std::vector<TAddress>> Resolve(const std::string& hostname, EDNSType type = EDNSType::DEFAULT);
167
168private:
169 TFuture<void> SenderTask();
170 TFuture<void> ReceiverTask();
171 TFuture<void> TimeoutsTask(std::function<TFuture<void>(TTime until)> timeout);
172
173 void ResumeSender();
174
175 TFuture<void> Sender;
176 TFuture<void> Receiver;
177 TFuture<void> Timeouts;
178 std::coroutine_handle<> SenderSuspended;
179
180 TAddress DnsAddr;
181 TTypelessSocket Socket;
182 EDNSType DefaultType;
183
184 struct TResolveRequest {
185 std::string Name;
186 EDNSType Type;
187
188 bool operator==(const TResolveRequest& other) const {
189 return Name == other.Name && Type == other.Type;
190 }
191
192 size_t hash() const {
193 std::size_t result = 0;
194 result ^= std::hash<std::string>()(Name) + 0x9e3779b9 + (result << 6) + (result >> 2);
195 result ^= std::hash<EDNSType>()(Type) + 0x9e3779b9 + (result << 6) + (result >> 2);
196 return result;
197 }
198 };
199
200 struct TResolveRequestHash
201 {
202 std::size_t operator()(const TResolveRequest& c) const
203 {
204 return c.hash();
205 }
206 };
207
208 std::queue<TResolveRequest> AddResolveQueue;
209 std::queue<std::pair<TTime, TResolveRequest>> TimeoutsQueue;
210
211 struct TResolveResult {
212 std::vector<TAddress> Addresses = {};
213 std::exception_ptr Exception = nullptr;
214 int Retries = 0;
215 };
216
217 void ResumeWaiters(TResolveResult&& result, const TResolveRequest& req);
218
219 std::unordered_map<TResolveRequest, TResolveResult, TResolveRequestHash> Results;
220 std::unordered_map<TResolveRequest, std::vector<std::coroutine_handle<>>, TResolveRequestHash> WaitingAddrs;
221 std::unordered_map<uint64_t, TResolveRequest> Inflight;
222
223 uint16_t Xid = 1;
224};
225
226template<typename TPoller>
227TResolver::TResolver(TPoller& poller, EDNSType defaultType)
228 : TResolver(TResolvConf(), poller, defaultType)
229{ }
230
231template<typename TPoller>
232TResolver::TResolver(const TResolvConf& conf, TPoller& poller, EDNSType defaultType)
233 : TResolver(conf.Nameservers[0], poller, defaultType)
234{ }
235
236template<typename TPoller>
237TResolver::TResolver(TAddress dnsAddr, TPoller& poller, EDNSType defaultType)
238 : DnsAddr(std::move(dnsAddr))
239 , Socket(typename TPoller::TSocket(poller, DnsAddr.Domain(), SOCK_DGRAM))
240 , DefaultType(defaultType)
241{
242 // Start tasks after fields initialization
243 Sender = SenderTask();
244 Receiver = ReceiverTask();
245 Timeouts = TimeoutsTask([&poller](TTime until) -> TFuture<void> {
246 co_await poller.Sleep(until);
247 });
248}
249
262public:
264 THostPort(const std::string& hostPort);
266 THostPort(const std::string& host, int port);
267
275 char buf[16];
276 if (inet_pton(AF_INET, Host.c_str(), buf) == 1 || inet_pton(AF_INET6, Host.c_str(), buf)) {
277 co_return TAddress{Host, Port};
278 }
279
280 auto addresses = co_await resolver.Resolve(Host);
281 if (addresses.empty()) {
282 throw std::runtime_error("Empty address");
283 }
284
285 co_return addresses.front().WithPort(Port);
286 }
287
288 const std::string ToString() const {
289 return Host + ":" + std::to_string(Port);
290 }
291
292 const std::string& GetHost() const {
293 return Host;
294 }
295
296 int GetPort() const {
297 return Port;
298 }
299
300private:
301 std::string Host;
302 int Port;
303};
304
305} // namespace NNet
A class representing an IPv4 or IPv6 address (with port).
Definition address.hpp:38
A host:port pair that resolves to a TAddress.
Definition resolver.hpp:261
TFuture< TAddress > Resolve(TResolver &resolver)
Resolves the host to a TAddress using the given resolver.
Definition resolver.hpp:274
Reads nameserver addresses from a resolv.conf-style file or stream.
Definition resolver.hpp:19
std::vector< TAddress > Nameservers
A vector of nameserver addresses.
Definition resolver.hpp:41
Async DNS resolver over UDP.
Definition resolver.hpp:115
TFuture< std::vector< TAddress > > Resolve(const std::string &hostname, EDNSType type=EDNSType::DEFAULT)
Resolves a hostname to a list of IP addresses.
Definition resolver.cpp:266
TResolver(TPoller &poller, EDNSType defaultType=EDNSType::A)
Constructs a TResolver using the default resolv configuration.
Definition resolver.hpp:227
High-level asynchronous socket for network communication.
Definition socket.hpp:367
Implementation of a promise/future system for coroutines.
Owned coroutine handle that carries a result of type T.
Definition corochain.hpp:185
Definition resolver.hpp:57