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
21public:
27 TResolvConf(const std::string& fn = "/etc/resolv.conf");
36 TResolvConf(std::istream& input);
42 std::vector<TAddress> Nameservers;
43
44private:
45 void Load(std::istream& input);
46};
47
52enum class EDNSType {
53 DEFAULT = 0,
54 A = 1,
55 AAAA = 28,
56};
57
59 TTypelessSocket() = default;
60
61 template<typename TSocket>
62 TTypelessSocket(TSocket&& sock) {
63 auto socket = std::make_shared<TSocket>(std::move(sock));
64 Connector = [socket](const TAddress& addr, TTime deadline) -> TFuture<void> {
65 co_await socket->Connect(addr, deadline);
66 };
67 Reader = [socket](void* buf, size_t size) -> TFuture<ssize_t> {
68 co_return co_await socket->ReadSome(buf, size);
69 };
70 Writer = [socket](const void* buf, size_t size) -> TFuture<ssize_t> {
71 co_return co_await socket->WriteSome(buf, size);
72 };
73 }
74
75 TFuture<void> Connect(const TAddress& addr, TTime deadline = TTime::max()) {
76 co_await Connector(addr, deadline);
77 }
78
79 TFuture<ssize_t> ReadSome(void* buf, size_t size) {
80 co_return co_await Reader(buf, size);
81 }
82
83 TFuture<ssize_t> WriteSome(const void* buf, size_t size) {
84 co_return co_await Writer(buf, size);
85 }
86
87private:
88 std::function<TFuture<void>(const TAddress& addr, TTime deadline)> Connector;
89 std::function<TFuture<ssize_t>(void* buf, size_t size)> Reader;
90 std::function<TFuture<ssize_t>(const void* buf, size_t size)> Writer;
91};
92
119public:
129 template<typename TPoller>
130 TResolver(TPoller& poller, EDNSType defaultType = EDNSType::A);
141 template<typename TPoller>
142 TResolver(const TResolvConf& conf, TPoller& poller, EDNSType defaultType = EDNSType::A);
152 template<typename TPoller>
153 TResolver(TAddress dnsAddr, TPoller& poller, EDNSType defaultType = EDNSType::A);
154 ~TResolver();
155
169 TFuture<std::vector<TAddress>> Resolve(const std::string& hostname, EDNSType type = EDNSType::DEFAULT);
170
171private:
172 TFuture<void> SenderTask();
173 TFuture<void> ReceiverTask();
174 TFuture<void> TimeoutsTask(std::function<TFuture<void>(TTime until)> timeout);
175
176 void ResumeSender();
177
178 TFuture<void> Sender;
179 TFuture<void> Receiver;
180 TFuture<void> Timeouts;
181 std::coroutine_handle<> SenderSuspended;
182
183 TAddress DnsAddr;
184 TTypelessSocket Socket;
185 EDNSType DefaultType;
186
187 struct TResolveRequest {
188 std::string Name;
189 EDNSType Type;
190
191 bool operator==(const TResolveRequest& other) const {
192 return Name == other.Name && Type == other.Type;
193 }
194
195 size_t hash() const {
196 std::size_t result = 0;
197 result ^= std::hash<std::string>()(Name) + 0x9e3779b9 + (result << 6) + (result >> 2);
198 result ^= std::hash<EDNSType>()(Type) + 0x9e3779b9 + (result << 6) + (result >> 2);
199 return result;
200 }
201 };
202
203 struct TResolveRequestHash
204 {
205 std::size_t operator()(const TResolveRequest& c) const
206 {
207 return c.hash();
208 }
209 };
210
211 std::queue<TResolveRequest> AddResolveQueue;
212 std::queue<std::pair<TTime, TResolveRequest>> TimeoutsQueue;
213
214 struct TResolveResult {
215 std::vector<TAddress> Addresses = {};
216 std::exception_ptr Exception = nullptr;
217 int Retries = 0;
218 };
219
220 void ResumeWaiters(TResolveResult&& result, const TResolveRequest& req);
221
222 std::unordered_map<TResolveRequest, TResolveResult, TResolveRequestHash> Results;
223 std::unordered_map<TResolveRequest, std::vector<std::coroutine_handle<>>, TResolveRequestHash> WaitingAddrs;
224 std::unordered_map<uint64_t, TResolveRequest> Inflight;
225
226 uint16_t Xid = 1;
227};
228
229template<typename TPoller>
230TResolver::TResolver(TPoller& poller, EDNSType defaultType)
231 : TResolver(TResolvConf(), poller, defaultType)
232{ }
233
234template<typename TPoller>
235TResolver::TResolver(const TResolvConf& conf, TPoller& poller, EDNSType defaultType)
236 : TResolver(conf.Nameservers[0], poller, defaultType)
237{ }
238
239template<typename TPoller>
240TResolver::TResolver(TAddress dnsAddr, TPoller& poller, EDNSType defaultType)
241 : DnsAddr(std::move(dnsAddr))
242 , Socket(typename TPoller::TSocket(poller, DnsAddr.Domain(), SOCK_DGRAM))
243 , DefaultType(defaultType)
244{
245 // Start tasks after fields initialization
246 Sender = SenderTask();
247 Receiver = ReceiverTask();
248 Timeouts = TimeoutsTask([&poller](TTime until) -> TFuture<void> {
249 co_await poller.Sleep(until);
250 });
251}
252
254public:
255 THostPort(const std::string& hostPort);
256 THostPort(const std::string& host, int port);
257
258 TFuture<TAddress> Resolve(TResolver& resolver) {
259 char buf[16];
260 if (inet_pton(AF_INET, Host.c_str(), buf) == 1 || inet_pton(AF_INET6, Host.c_str(), buf)) {
261 co_return TAddress{Host, Port};
262 }
263
264 auto addresses = co_await resolver.Resolve(Host);
265 if (addresses.empty()) {
266 throw std::runtime_error("Empty address");
267 }
268
269 co_return addresses.front().WithPort(Port);
270 }
271
272 const std::string ToString() const {
273 return Host + ":" + std::to_string(Port);
274 }
275
276 const std::string& GetHost() const {
277 return Host;
278 }
279
280 int GetPort() const {
281 return Port;
282 }
283
284private:
285 std::string Host;
286 int Port;
287};
288
289} // namespace NNet
A class representing an IPv4 or IPv6 address (with port).
Definition address.hpp:38
Definition resolver.hpp:253
Reads and stores DNS configuration from a file or an input stream.
Definition resolver.hpp:20
std::vector< TAddress > Nameservers
A vector of nameserver addresses.
Definition resolver.hpp:42
Resolves hostnames into IP addresses using a custom poller.
Definition resolver.hpp:118
TFuture< std::vector< TAddress > > Resolve(const std::string &hostname, EDNSType type=EDNSType::DEFAULT)
Resolves a hostname to a list of addresses.
Definition resolver.cpp:266
TResolver(TPoller &poller, EDNSType defaultType=EDNSType::A)
Constructs a TResolver using the default resolv configuration.
Definition resolver.hpp:230
High-level asynchronous socket for network communication.
Definition socket.hpp:364
Implementation of a promise/future system for coroutines.
Future type for coroutines returning a value of type T.
Definition corochain.hpp:182
Definition resolver.hpp:58