Electroneum
Loading...
Searching...
No Matches
dns_utils.cpp
Go to the documentation of this file.
1// Copyrights(c) 2017-2021, The Electroneum Project
2// Copyright (c) 2014-2019, The Monero Project
3//
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without modification, are
7// permitted provided that the following conditions are met:
8//
9// 1. Redistributions of source code must retain the above copyright notice, this list of
10// conditions and the following disclaimer.
11//
12// 2. Redistributions in binary form must reproduce the above copyright notice, this list
13// of conditions and the following disclaimer in the documentation and/or other
14// materials provided with the distribution.
15//
16// 3. Neither the name of the copyright holder nor the names of its contributors may be
17// used to endorse or promote products derived from this software without specific
18// prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "common/dns_utils.h"
31// check local first (in the event of static or in-source compilation of libunbound)
32#include "unbound.h"
33
34#include <stdlib.h>
35#include "include_base_utils.h"
36#include "common/threadpool.h"
37#include "crypto/crypto.h"
38#include <boost/optional.hpp>
39#include <random>
40#include <boost/filesystem/fstream.hpp>
41#include <boost/thread/mutex.hpp>
42#include <boost/thread/thread.hpp>
43#include <boost/algorithm/string/join.hpp>
44using namespace epee;
45
46#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
47#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "net.dns"
48
49static const char *DEFAULT_DNS_PUBLIC_ADDR[] =
50{
51 "194.150.168.168", // CCC (Germany)
52 "80.67.169.40", // FDN (France)
53 "89.233.43.71", // http://censurfridns.dk (Denmark)
54 "109.69.8.51", // punCAT (Spain)
55 "193.58.251.251" // SkyDNS (Russia)
56};
57
58static boost::mutex instance_lock;
59
60namespace
61{
62
63/*
64 * The following two functions were taken from unbound-anchor.c, from
65 * the unbound library packaged with this source. The license and source
66 * can be found in $PROJECT_ROOT/external/unbound
67 */
68
69/* Cert builtin commented out until it's used, as the compiler complains
70
71// return the built in root update certificate
72static const char*
73get_builtin_cert(void)
74{
75 return
76// The ICANN CA fetched at 24 Sep 2010. Valid to 2028
77"-----BEGIN CERTIFICATE-----\n"
78"MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n"
79"TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n"
80"BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n"
81"DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n"
82"IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n"
83"MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n"
84"cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n"
85"G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n"
86"ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n"
87"paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n"
88"MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n"
89"iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
90"Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n"
91"DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n"
92"6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n"
93"2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n"
94"15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n"
95"0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n"
96"j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n"
97"-----END CERTIFICATE-----\n"
98 ;
99}
100*/
101
103static const char* const*
104get_builtin_ds(void)
105{
106 static const char * const ds[] =
107 {
108 ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n",
109 ". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n",
110 NULL
111 };
112 return ds;
113}
114
115/************************************************************
116 ************************************************************
117 ***********************************************************/
118
119} // anonymous namespace
120
121namespace tools
122{
123
124static const char *get_record_name(int record_type)
125{
126 switch (record_type)
127 {
128 case DNS_TYPE_A: return "A";
129 case DNS_TYPE_TXT: return "TXT";
130 case DNS_TYPE_AAAA: return "AAAA";
131 default: return "unknown";
132 }
133}
134
135// fuck it, I'm tired of dealing with getnameinfo()/inet_ntop/etc
136boost::optional<std::string> ipv4_to_string(const char* src, size_t len)
137{
138 if (len < 4)
139 {
140 MERROR("Invalid IPv4 address: " << std::string(src, len));
141 return boost::none;
142 }
143
144 std::stringstream ss;
145 unsigned int bytes[4];
146 for (int i = 0; i < 4; i++)
147 {
148 unsigned char a = src[i];
149 bytes[i] = a;
150 }
151 ss << bytes[0] << "."
152 << bytes[1] << "."
153 << bytes[2] << "."
154 << bytes[3];
155 return ss.str();
156}
157
158// this obviously will need to change, but is here to reflect the above
159// stop-gap measure and to make the tests pass at least...
160boost::optional<std::string> ipv6_to_string(const char* src, size_t len)
161{
162 if (len < 8)
163 {
164 MERROR("Invalid IPv4 address: " << std::string(src, len));
165 return boost::none;
166 }
167
168 std::stringstream ss;
169 unsigned int bytes[8];
170 for (int i = 0; i < 8; i++)
171 {
172 unsigned char a = src[i];
173 bytes[i] = a;
174 }
175 ss << bytes[0] << ":"
176 << bytes[1] << ":"
177 << bytes[2] << ":"
178 << bytes[3] << ":"
179 << bytes[4] << ":"
180 << bytes[5] << ":"
181 << bytes[6] << ":"
182 << bytes[7];
183 return ss.str();
184}
185
186boost::optional<std::string> txt_to_string(const char* src, size_t len)
187{
188 if (len == 0)
189 return boost::none;
190 return std::string(src+1, len-1);
191}
192
193// custom smart pointer.
194// TODO: see if std::auto_ptr and the like support custom destructors
195template<typename type, void (*freefunc)(type*)>
197{
198public:
200 ptr(nullptr)
201 {
202 }
203 scoped_ptr(type *p):
204 ptr(p)
205 {
206 }
208 {
209 freefunc(ptr);
210 }
211 operator type *() { return ptr; }
212 type **operator &() { return &ptr; }
213 type *operator->() { return ptr; }
214 operator const type*() const { return &ptr; }
215
216private:
217 type* ptr;
218};
219
221
226
227// work around for bug https://www.nlnetlabs.nl/bugs-script/show_bug.cgi?id=515 needed for it to compile on e.g. Debian 7
229public:
230 string_copy(const char *s): str(strdup(s)) {}
231 ~string_copy() { free(str); }
232 operator char*() { return str; }
233
234public:
235 char *str;
236};
237
238static void add_anchors(ub_ctx *ctx)
239{
240 const char * const *ds = ::get_builtin_ds();
241 while (*ds)
242 {
243 MINFO("adding trust anchor: " << *ds);
244 ub_ctx_add_ta(ctx, string_copy(*ds++));
245 }
246}
247
248DNSResolver::DNSResolver() : m_data(new DNSResolverData())
249{
250 int use_dns_public = 0;
251 std::vector<std::string> dns_public_addr;
252 const char *DNS_PUBLIC = getenv("DNS_PUBLIC");
253 if (DNS_PUBLIC)
254 {
255 dns_public_addr = tools::dns_utils::parse_dns_public(DNS_PUBLIC);
256 if (!dns_public_addr.empty())
257 {
258 MGINFO("Using public DNS server(s): " << boost::join(dns_public_addr, ", ") << " (TCP)");
259 use_dns_public = 1;
260 }
261 else
262 {
263 MERROR("Failed to parse DNS_PUBLIC");
264 }
265 }
266
267 // init libunbound context
268 m_data->m_ub_context = ub_ctx_create();
269
270 if (use_dns_public)
271 {
272 for (const auto &ip: dns_public_addr)
273 ub_ctx_set_fwd(m_data->m_ub_context, string_copy(ip.c_str()));
274 ub_ctx_set_option(m_data->m_ub_context, string_copy("do-udp:"), string_copy("no"));
275 ub_ctx_set_option(m_data->m_ub_context, string_copy("do-tcp:"), string_copy("yes"));
276 }
277 else {
278 // look for "/etc/resolv.conf" and "/etc/hosts" or platform equivalent
279 ub_ctx_resolvconf(m_data->m_ub_context, NULL);
280 ub_ctx_hosts(m_data->m_ub_context, NULL);
281 }
282
283 add_anchors(m_data->m_ub_context);
284
285 if (!DNS_PUBLIC)
286 {
287 // if no DNS_PUBLIC specified, we try a lookup to what we know
288 // should be a valid DNSSEC record, and switch to known good
289 // DNSSEC resolvers if verification fails
290 bool available, valid;
291 static const char *probe_hostname = "updates.electroneumpulse.org";
292 auto records = get_txt_record(probe_hostname, available, valid);
293 if (!valid)
294 {
295 MINFO("Failed to verify DNSSEC record from " << probe_hostname << ", falling back to TCP with well known DNSSEC resolvers");
296 ub_ctx_delete(m_data->m_ub_context);
297 m_data->m_ub_context = ub_ctx_create();
298 add_anchors(m_data->m_ub_context);
299 for (const auto &ip: DEFAULT_DNS_PUBLIC_ADDR)
300 ub_ctx_set_fwd(m_data->m_ub_context, string_copy(ip));
301 ub_ctx_set_option(m_data->m_ub_context, string_copy("do-udp:"), string_copy("no"));
302 ub_ctx_set_option(m_data->m_ub_context, string_copy("do-tcp:"), string_copy("yes"));
303 }
304 }
305}
306
308{
309 if (m_data)
310 {
311 if (m_data->m_ub_context != NULL)
312 {
313 ub_ctx_delete(m_data->m_ub_context);
314 }
315 delete m_data;
316 }
317}
318
319std::vector<std::string> DNSResolver::get_record(const std::string& url, int record_type, boost::optional<std::string> (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid)
320{
321 std::vector<std::string> addresses;
322 dnssec_available = false;
323 dnssec_valid = false;
324
325 if (!check_address_syntax(url.c_str()))
326 {
327 return addresses;
328 }
329
330 // destructor takes care of cleanup
331 ub_result_ptr result;
332
333 // call DNS resolver, blocking. if return value not zero, something went wrong
334 if (!ub_resolve(m_data->m_ub_context, string_copy(url.c_str()), record_type, DNS_CLASS_IN, &result))
335 {
336 dnssec_available = (result->secure || result->bogus);
337 dnssec_valid = result->secure && !result->bogus;
338 if (result->havedata)
339 {
340 for (size_t i=0; result->data[i] != NULL; i++)
341 {
342 boost::optional<std::string> res = (*reader)(result->data[i], result->len[i]);
343 if (res)
344 {
345 MINFO("Found \"" << *res << "\" in " << get_record_name(record_type) << " record for " << url);
346 addresses.push_back(*res);
347 }
348 }
349 }
350 }
351
352 return addresses;
353}
354
355std::vector<std::string> DNSResolver::get_ipv4(const std::string& url, bool& dnssec_available, bool& dnssec_valid)
356{
357 return get_record(url, DNS_TYPE_A, ipv4_to_string, dnssec_available, dnssec_valid);
358}
359
360std::vector<std::string> DNSResolver::get_ipv6(const std::string& url, bool& dnssec_available, bool& dnssec_valid)
361{
362 return get_record(url, DNS_TYPE_AAAA, ipv6_to_string, dnssec_available, dnssec_valid);
363}
364
365std::vector<std::string> DNSResolver::get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid)
366{
367 return get_record(url, DNS_TYPE_TXT, txt_to_string, dnssec_available, dnssec_valid);
368}
369
370std::string DNSResolver::get_dns_format_from_oa_address(const std::string& oa_addr)
371{
372 std::string addr(oa_addr);
373 auto first_at = addr.find("@");
374 if (first_at == std::string::npos)
375 return addr;
376
377 // convert name@domain.tld to name.domain.tld
378 addr.replace(first_at, 1, ".");
379
380 return addr;
381}
382
384{
385 boost::lock_guard<boost::mutex> lock(instance_lock);
386
387 static DNSResolver staticInstance;
388 return staticInstance;
389}
390
392{
393 return DNSResolver();
394}
395
396bool DNSResolver::check_address_syntax(const char *addr) const
397{
398 // if string doesn't contain a dot, we won't consider it a url for now.
399 if (strchr(addr,'.') == NULL)
400 {
401 return false;
402 }
403 return true;
404}
405
406namespace dns_utils
407{
408
409//-----------------------------------------------------------------------
410// TODO: parse the string in a less stupid way, probably with regex
411std::string address_from_txt_record(const std::string& s)
412{
413 // make sure the txt record has "oa1:etn" and find it
414 auto pos = s.find("oa1:etn");
415 if (pos == std::string::npos)
416 return {};
417 // search from there to find "recipient_address="
418 pos = s.find("recipient_address=", pos);
419 if (pos == std::string::npos)
420 return {};
421 pos += 18; // move past "recipient_address="
422 // find the next semicolon
423 auto pos2 = s.find(";", pos);
424 if (pos2 != std::string::npos)
425 {
426 // length of address == 98, we can at least validate that much here
427 if (pos2 - pos == 98)
428 {
429 return s.substr(pos, 98);
430 }
431 else if (pos2 - pos == 109) // length of address == 109 --> integrated address
432 {
433 return s.substr(pos, 109);
434 }
435 }
436 return {};
437}
438
452std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec_valid)
453{
454 std::vector<std::string> addresses;
455 // get txt records
456 bool dnssec_available, dnssec_isvalid;
457 std::string oa_addr = DNSResolver::instance().get_dns_format_from_oa_address(url);
458 auto records = DNSResolver::instance().get_txt_record(oa_addr, dnssec_available, dnssec_isvalid);
459
460 // TODO: update this to allow for conveying that dnssec was not available
461 if (dnssec_available && dnssec_isvalid)
462 {
463 dnssec_valid = true;
464 }
465 else dnssec_valid = false;
466
467 // for each txt record, try to find a electroneum address in it.
468 for (auto& rec : records)
469 {
470 std::string addr = address_from_txt_record(rec);
471 if (addr.size())
472 {
473 addresses.push_back(addr);
474 }
475 }
476 return addresses;
477}
478
479std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm)
480{
481 // attempt to get address from dns query
482 auto addresses = addresses_from_url(url, dnssec_valid);
483 if (addresses.empty())
484 {
485 LOG_ERROR("wrong address: " << url);
486 return {};
487 }
488 return dns_confirm(url, addresses, dnssec_valid);
489}
490
491namespace
492{
493 bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b)
494 {
495 if (a.size() != b.size()) return false;
496
497 for (const auto& record_in_a : a)
498 {
499 bool ok = false;
500 for (const auto& record_in_b : b)
501 {
502 if (record_in_a == record_in_b)
503 {
504 ok = true;
505 break;
506 }
507 }
508 if (!ok) return false;
509 }
510
511 return true;
512 }
513}
514
515bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std::vector<std::string> &dns_urls, std::string type)
516{
517 // Prevent infinite recursion when distributing
518 if (dns_urls.empty()) return false;
519
520 std::vector<std::vector<std::string> > records;
521 records.resize(dns_urls.size());
522
523 size_t first_index = crypto::rand_idx(dns_urls.size());
524
525 // send all requests in parallel
526 std::deque<bool> avail(dns_urls.size(), false), valid(dns_urls.size(), false);
529 for (size_t n = 0; n < dns_urls.size(); ++n)
530 {
531 tpool.submit(&waiter,[n, dns_urls, &records, &avail, &valid](){
532 records[n] = tools::DNSResolver::instance().get_txt_record(dns_urls[n], avail[n], valid[n]);
533 });
534 }
535 waiter.wait(&tpool);
536
537 size_t cur_index = first_index;
538 do
539 {
540 const std::string &url = dns_urls[cur_index];
541 if (!avail[cur_index])
542 {
543 records[cur_index].clear();
544 LOG_PRINT_L2("DNSSEC not available for " << type << " at URL: " << url << ", skipping.");
545 }
546 if (!valid[cur_index])
547 {
548 records[cur_index].clear();
549 LOG_PRINT_L2("DNSSEC validation failed for " << type << " at URL: " << url << ", skipping.");
550 }
551
552 cur_index++;
553 if (cur_index == dns_urls.size())
554 {
555 cur_index = 0;
556 }
557 } while (cur_index != first_index);
558
559 size_t num_valid_records = 0;
560
561 for( const auto& record_set : records)
562 {
563 if (record_set.size() != 0)
564 {
565 num_valid_records++;
566 }
567 }
568
569 if (num_valid_records < 2)
570 {
571 LOG_PRINT_L1("WARNING: no two valid ElectroneumPulse DNS " << type << " records were received, only " << num_valid_records);
572 return false;
573 }
574
575 int good_records_index = -1;
576 for (size_t i = 0; i < records.size() - 1; ++i)
577 {
578 if (records[i].size() == 0) continue;
579
580 for (size_t j = i + 1; j < records.size(); ++j)
581 {
582 if (dns_records_match(records[i], records[j]))
583 {
584 good_records_index = i;
585 break;
586 }
587 }
588 if (good_records_index >= 0) break;
589 }
590
591 if (good_records_index < 0)
592 {
593 LOG_PRINT_L1("WARNING: no two ElectroneumPulse DNS " << type << " records matched");
594 return false;
595 }
596
597 good_records = records[good_records_index];
598 return true;
599}
600
601std::vector<std::string> parse_dns_public(const char *s)
602{
603 unsigned ip0, ip1, ip2, ip3;
604 char c;
605 std::vector<std::string> dns_public_addr;
606 if (!strcmp(s, "tcp"))
607 {
608 for (size_t i = 0; i < sizeof(DEFAULT_DNS_PUBLIC_ADDR) / sizeof(DEFAULT_DNS_PUBLIC_ADDR[0]); ++i)
609 dns_public_addr.push_back(DEFAULT_DNS_PUBLIC_ADDR[i]);
610 LOG_PRINT_L0("Using default public DNS server(s): " << boost::join(dns_public_addr, ", ") << " (TCP)");
611 }
612 else if (sscanf(s, "tcp://%u.%u.%u.%u%c", &ip0, &ip1, &ip2, &ip3, &c) == 4)
613 {
614 if (ip0 > 255 || ip1 > 255 || ip2 > 255 || ip3 > 255)
615 {
616 MERROR("Invalid IP: " << s << ", using default");
617 }
618 else
619 {
620 dns_public_addr.push_back(std::string(s + strlen("tcp://")));
621 }
622 }
623 else
624 {
625 MERROR("Invalid DNS_PUBLIC contents, ignored");
626 }
627 return dns_public_addr;
628}
629
630} // namespace tools::dns_utils
631
632} // namespace tools
static DNSResolver & instance()
Gets the singleton instance of DNSResolver.
std::vector< std::string > get_txt_record(const std::string &url, bool &dnssec_available, bool &dnssec_valid)
gets all TXT records from a DNS query for the supplied URL; if no TXT record present returns an empty...
std::string get_dns_format_from_oa_address(const std::string &oa_addr)
Gets a DNS address from OpenAlias format.
~DNSResolver()
takes care of freeing C pointers and such
std::vector< std::string > get_ipv4(const std::string &url, bool &dnssec_available, bool &dnssec_valid)
gets ipv4 addresses from DNS query of a URL
std::vector< std::string > get_ipv6(const std::string &url, bool &dnssec_available, bool &dnssec_valid)
gets ipv6 addresses from DNS query
static DNSResolver create()
Gets a new instance of DNSResolver.
type ** operator&()
string_copy(const char *s)
void wait(threadpool *tpool)
A global thread pool.
Definition threadpool.h:44
void submit(waiter *waiter, std::function< void()> f, bool leaf=false)
static threadpool & getInstance()
Definition threadpool.h:46
const char * res
#define MERROR(x)
Definition misc_log_ex.h:73
#define LOG_PRINT_L1(x)
#define LOG_ERROR(x)
Definition misc_log_ex.h:98
#define MGINFO(x)
Definition misc_log_ex.h:80
#define MINFO(x)
Definition misc_log_ex.h:75
#define LOG_PRINT_L2(x)
#define LOG_PRINT_L0(x)
Definition misc_log_ex.h:99
std::enable_if< std::is_unsigned< T >::value, T >::type rand_idx(T sz)
Definition crypto.h:244
bool load_txt_records_from_dns(std::vector< std::string > &good_records, const std::vector< std::string > &dns_urls, std::string type)
std::vector< std::string > addresses_from_url(const std::string &url, bool &dnssec_valid)
gets a electroneum address from the TXT record of a DNS entry
std::string get_account_address_as_str_from_url(const std::string &url, bool &dnssec_valid, std::function< std::string(const std::string &, const std::vector< std::string > &, bool)> dns_confirm)
std::string address_from_txt_record(const std::string &s)
std::vector< std::string > parse_dns_public(const char *s)
Various Tools.
Definition tools.cpp:31
boost::optional< std::string > ipv6_to_string(const char *src, size_t len)
boost::optional< std::string > txt_to_string(const char *src, size_t len)
class scoped_ptr< ub_result, ub_resolve_free > ub_result_ptr
boost::optional< std::string > ipv4_to_string(const char *src, size_t len)
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1124
void ub_ctx_delete(struct ub_ctx *ctx)
int ub_ctx_set_option(struct ub_ctx *ctx, const char *opt, const char *val)
void ub_resolve_free(struct ub_result *result)
int ub_ctx_add_ta(struct ub_ctx *ctx, const char *ta)
int ub_ctx_set_fwd(struct ub_ctx *ctx, const char *addr)
int ub_ctx_hosts(struct ub_ctx *ctx, const char *fname)
int ub_ctx_resolvconf(struct ub_ctx *ctx, const char *fname)
struct ub_ctx * ub_ctx_create(void)
int ub_resolve(struct ub_ctx *ctx, const char *name, int rrtype, int rrclass, struct ub_result **result)