├── CMakeLists.txt
├── .gitlab-ci.yml
├── cpp-dns.hpp
├── README.md
├── DNSResolver.hpp
├── Test.cpp
├── LICENSE
└── DNSResolver.cpp
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.2)
2 | project(cpp-dns)
3 |
4 | set(CMAKE_CXX_STANDARD 17)
5 |
6 | add_library(cpp-dns DNSResolver.cpp DNSResolver.hpp)
7 | add_executable(cpp_dns_test Test.cpp)
8 |
9 | target_link_libraries(cpp-dns boost_system pthread udns)
10 | target_link_libraries(cpp_dns_test cpp-dns)
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: ubuntu:19.10
2 |
3 | stages:
4 | - build
5 | - test
6 |
7 | before_script:
8 | - apt-get update && apt-get -y install git build-essential cmake libboost-all-dev libudns-dev lcov
9 |
10 | build:
11 | stage: build
12 | artifacts:
13 | untracked: true
14 | script:
15 | - mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS='-O0 -g -pg -fprofile-arcs -ftest-coverage' -DCMAKE_EXE_LINKER_FLAGS=-pg -DCMAKE_SHARED_LINKER_FLAGS=-pg .. && make
16 |
17 | test:
18 | stage: test
19 | dependencies:
20 | - build
21 | artifacts:
22 | untracked: true
23 | script:
24 | - cd build
25 | - sh -c './cpp_dns_test'
26 | - lcov -d . -c --output-file app-raw.info
27 | - lcov --remove app-raw.info '/usr/include/*' -o app.info
28 | - lcov -l app.info
29 |
--------------------------------------------------------------------------------
/cpp-dns.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of CPPDNSResolver.
3 | Copyright (C) 2020 ReimuNotMoe
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as
7 | published by the Free Software Foundation, either version 3 of the
8 | License, or (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #pragma once
20 |
21 | #include "DNSResolver.hpp"
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cpp-dns
2 | [](https://gitlab.com/ReimuNotMoe/cpp-dns/commits/master)
3 | [](https://www.codacy.com/gh/YukiWorkshop/cpp-dns)
4 | [](https://gitlab.com/ReimuNotMoe/cpp-dns/commits/master)
5 |
6 | C++ async DNS resolver using UDNS and Boost.
7 |
8 | ## Requirements
9 | Reasonably new versions of:
10 | - C++17 compatible compiler
11 | - CMake
12 | - Boost
13 | - [UDNS](https://www.corpit.ru/mjt/udns.html)
14 |
15 | Quick command for Ubuntu users:
16 | ```shell script
17 | apt install build-essential cmake libboost-all-dev libudns-dev
18 | ```
19 |
20 | ## Install
21 | Use of Git submodule and CMake subdirectory is recommended.
22 |
23 | ```shell script
24 | mkdir cpp_modules && cd cpp_modules
25 | git submodule add https://github.com/YukiWorkshop/cpp-dns
26 | ```
27 |
28 | ```cmake
29 | add_subdirectory(cpp_modules/cpp-dns)
30 | include_directories(cpp_modules/cpp-dns)
31 | target_link_libraries(your_project cpp-dns)
32 | ```
33 |
34 | ## Usage
35 | ```cpp
36 | #include
37 |
38 | using namespace YukiWorkshop;
39 | ```
40 |
41 | Create a new resolver instance with default nameservers.
42 |
43 | ```cpp
44 | DNSResolver d(io_svc);
45 | ```
46 |
47 | Or with custom nameservers.
48 |
49 | ```cpp
50 | DNSResolver d(io_svc, {"1.1.1.1", "8.8.8.8", "8.8.4.4"});
51 | ```
52 |
53 | Get A records of `google.com`.
54 |
55 | ```cpp
56 | d.resolve_a4("google.com", [](int err, auto& addrs, auto& qname, auto& cname, uint ttl){
57 | if (!err) {
58 | std::cout << "Query: " << qname << "\n";
59 | std::cout << "CNAME: " << cname << "\n";
60 | std::cout << "TTL: " << ttl << "\n";
61 |
62 | for (auto &it : addrs) {
63 | std::cout << "A Record: " << it.to_string() << "\n";
64 | }
65 | }
66 | });
67 | ```
68 |
69 | Get `matrix` SRV records of `riot.im`.
70 |
71 | ```cpp
72 | d.resolve_srv("riot.im", "matrix", "tcp", [&](int err, auto& hosts, auto& qname, auto& cname, uint ttl){
73 | if (!err) {
74 | std::cout << "Query: " << qname << "\n";
75 | std::cout << "CNAME: " << cname << "\n";
76 | std::cout << "TTL: " << ttl << "\n";
77 |
78 | for (auto &it : hosts) {
79 | std::cout << "SRV Record: " << it.priority << " " << it.weight << " " << it.port << " " << it.name << "\n";
80 | }
81 | }
82 | });
83 | ```
84 |
85 | See what's going wrong.
86 |
87 | ```cpp
88 | d.resolve_a4("kefpdngjvoeqjgp3rnskldv", [&](int err, auto& addrs, auto& qname, auto& cname, uint ttl){
89 | assert(err);
90 | std::cout << "Oops. " << DNSResolver::error_string(err) << "\n";
91 | });
92 | ```
93 |
94 | See [Test.cpp](https://github.com/YukiWorkshop/cpp-dns/blob/master/Test.cpp) for more examples.
95 |
96 | Callback definitions are in [DNSResolver.hpp](https://github.com/YukiWorkshop/cpp-dns/blob/master/DNSResolver.hpp).
97 |
98 | Important notice: You need to prevent `io_service` from stopping. Otherwise you'll need to recreate `DNSResolver` since the UDP socket will get removed from `io_service` when it stops.
99 |
100 | ## License
101 | LGPL
102 |
--------------------------------------------------------------------------------
/DNSResolver.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of CPPDNSResolver.
3 | Copyright (C) 2020 ReimuNotMoe
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as
7 | published by the Free Software Foundation, either version 3 of the
8 | License, or (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #pragma once
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | #include
32 |
33 | #include
34 | #include
35 | #include
36 |
37 | #include
38 |
39 | #include
40 | #include
41 |
42 | #define THROW_ERRNO throw std::system_error(errno, std::system_category(), strerror(errno))
43 |
44 | namespace YukiWorkshop {
45 | class DNSResolver {
46 | public:
47 | struct MXRecord {
48 | int priority;
49 | std::string_view name;
50 |
51 | MXRecord(int __prio, char *__name) : priority(__prio), name((const char *)__name) {
52 |
53 | }
54 | };
55 |
56 | struct SRVRecord {
57 | int priority;
58 | int weight;
59 | int port;
60 | std::string_view name;
61 |
62 | SRVRecord(int __prio, int __weight, int __port, char *__name) : priority(__prio), weight(__weight), port(__port), name((const char *)__name) {
63 |
64 | }
65 | };
66 |
67 | // Error, Results, Query Name, CNAME, TTL
68 | typedef std::function &, const std::string_view &, const std::string_view &, uint)> A4Callback;
69 | typedef std::function &, const std::string_view &, const std::string_view &, uint)> A6Callback;
70 | typedef std::function &, const std::string_view &, const std::string_view &, uint)> PtrCallback;
71 | typedef std::function &, const std::string_view &, const std::string_view &, uint)> TXTCallback;
72 | typedef std::function &, const std::string_view &, const std::string_view &, uint)> MXCallback;
73 | typedef std::function &, const std::string_view &, const std::string_view &, uint)> SRVCallback;
74 |
75 | private:
76 | dns_ctx *ctx_udns = nullptr;
77 | int fd_udns = -1;
78 |
79 | boost::asio::io_service &asio_iosvc;
80 | std::unique_ptr asio_socket;
81 |
82 | uint32_t requests_pending = 0;
83 |
84 | void __init();
85 | void __fini();
86 | void __open();
87 |
88 | void io_wait_read();
89 | void iocb_read_avail();
90 |
91 | void set_servers(const std::initializer_list &__nameservers);
92 | void post_resolve();
93 |
94 | static void dnscb_a4(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data);
95 | static void dnscb_a6(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data);
96 | static void dnscb_txt(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data);
97 | static void dnscb_mx(struct dns_ctx *ctx, struct dns_rr_mx *result, void *data);
98 | static void dnscb_srv(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data);
99 | static void dnscb_ptr(struct dns_ctx *ctx, struct dns_rr_ptr *result, void *data);
100 |
101 | public:
102 | explicit DNSResolver(boost::asio::io_service &__io_svc);
103 |
104 | DNSResolver(boost::asio::io_service &__io_svc, const std::initializer_list &__nameservers);
105 |
106 | ~DNSResolver();
107 |
108 | void resolve_a4(const std::string &__hostname, const A4Callback &__callback); // A
109 | void resolve_a6(const std::string &__hostname, const A6Callback &__callback); // AAAA
110 | void resolve_a4ptr(const boost::asio::ip::address_v4 &__addr, const PtrCallback &__callback);
111 | void resolve_a6ptr(const boost::asio::ip::address_v6 &__addr, const PtrCallback &__callback);
112 | void resolve_txt(const std::string &__hostname, const TXTCallback &__callback);
113 | void resolve_mx(const std::string &__hostname, const MXCallback &__callback);
114 | void resolve_srv(const std::string &__hostname, const std::string &__srv, const std::string &__proto, const SRVCallback &__callback);
115 |
116 | static const std::string& error_string(int __err);
117 |
118 | };
119 | }
120 |
--------------------------------------------------------------------------------
/Test.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of CPPDNSResolver.
3 | Copyright (C) 2020 ReimuNotMoe
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as
7 | published by the Free Software Foundation, either version 3 of the
8 | License, or (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #include "DNSResolver.hpp"
20 |
21 | #include
22 | #include
23 |
24 | using DNSResolver = YukiWorkshop::DNSResolver;
25 |
26 | int main(int argc, char **argv) {
27 | boost::asio::io_service iosvc;
28 | std::mutex print_lock;
29 | DNSResolver d(iosvc, {"1.1.1.1", "8.8.8.8", "8.8.4.4"});
30 |
31 | d.resolve_a6("google.com", [&](int err, auto& addrs, auto& qname, auto& cname, uint ttl){
32 | if (!err) {
33 | std::unique_lock l(print_lock);
34 |
35 | std::cout << "==================" << "\n";
36 | std::cout << "Query: " << qname << "\n";
37 | std::cout << "CNAME: " << cname << "\n";
38 | std::cout << "TTL: " << ttl << "\n";
39 |
40 | for (auto &it : addrs) {
41 | std::cout << "AAAA Record: " << it.to_string() << "\n";
42 | }
43 | std::cout << "==================" << "\n";
44 | }
45 | });
46 |
47 | d.resolve_a4("baidu.com", [&](int err, auto& addrs, auto& qname, auto& cname, uint ttl){
48 | if (!err) {
49 | std::unique_lock l(print_lock);
50 |
51 | std::cout << "==================" << "\n";
52 | std::cout << "Query: " << qname << "\n";
53 | std::cout << "CNAME: " << cname << "\n";
54 | std::cout << "TTL: " << ttl << "\n";
55 |
56 | for (auto &it : addrs) {
57 | std::cout << "A Record: " << it.to_string() << "\n";
58 | }
59 | std::cout << "==================" << "\n";
60 | }
61 | });
62 |
63 | d.resolve_a4ptr(boost::asio::ip::address_v4::from_string("1.1.1.1"),
64 | [&](int err, auto& hosts, auto& qname, auto& cname, uint ttl){
65 | if (!err) {
66 | std::unique_lock l(print_lock);
67 |
68 | std::cout << "==================" << "\n";
69 | std::cout << "Query: " << qname << "\n";
70 | std::cout << "CNAME: " << cname << "\n";
71 | std::cout << "TTL: " << ttl << "\n";
72 |
73 | for (auto &it : hosts) {
74 | std::cout << "PTR Record: " << it << "\n";
75 | }
76 |
77 | std::cout << "==================" << "\n";
78 | }
79 | });
80 |
81 | d.resolve_a6ptr(boost::asio::ip::address_v6::from_string("2404:6800:4004:81f::200e"),
82 | [&](int err, auto& hosts, auto& qname, auto& cname, uint ttl){
83 | if (!err) {
84 | std::unique_lock l(print_lock);
85 |
86 | std::cout << "==================" << "\n";
87 | std::cout << "Query: " << qname << "\n";
88 | std::cout << "CNAME: " << cname << "\n";
89 | std::cout << "TTL: " << ttl << "\n";
90 |
91 | for (auto &it : hosts) {
92 | std::cout << "PTR Record: " << it << "\n";
93 | }
94 |
95 | std::cout << "==================" << "\n";
96 | }
97 | });
98 |
99 | d.resolve_mx("google.com", [&](int err, auto& hosts, auto& qname, auto& cname, uint ttl){
100 | if (!err) {
101 | std::unique_lock l(print_lock);
102 |
103 | std::cout << "==================" << "\n";
104 | std::cout << "Query: " << qname << "\n";
105 | std::cout << "CNAME: " << cname << "\n";
106 | std::cout << "TTL: " << ttl << "\n";
107 |
108 | for (auto &it : hosts) {
109 | std::cout << "MX Record: " << it.priority << " " << it.name << "\n";
110 | }
111 |
112 | std::cout << "==================" << "\n";
113 | }
114 | });
115 |
116 | d.resolve_srv("riot.im", "matrix", "tcp", [&](int err, auto& hosts, auto& qname, auto& cname, uint ttl){
117 | if (!err) {
118 | std::unique_lock l(print_lock);
119 |
120 | std::cout << "==================" << "\n";
121 | std::cout << "Query: " << qname << "\n";
122 | std::cout << "CNAME: " << cname << "\n";
123 | std::cout << "TTL: " << ttl << "\n";
124 |
125 | for (auto &it : hosts) {
126 | std::cout << "SRV Record: " << it.priority << " " << it.weight << " " << it.port << " " << it.name << "\n";
127 | }
128 |
129 | std::cout << "==================" << "\n";
130 | }
131 | });
132 |
133 | d.resolve_txt("google.com", [&](int err, auto& hosts, auto& qname, auto& cname, uint ttl){
134 | if (!err) {
135 | std::unique_lock l(print_lock);
136 |
137 | std::cout << "==================" << "\n";
138 | std::cout << "Query: " << qname << "\n";
139 | std::cout << "CNAME: " << cname << "\n";
140 | std::cout << "TTL: " << ttl << "\n";
141 |
142 | for (auto &it : hosts) {
143 | std::cout << "TXT Record: " << it << "\n";
144 | }
145 |
146 | std::cout << "==================" << "\n";
147 | }
148 | });
149 |
150 |
151 | d.resolve_a4("kefpdngjvoeqjgp3rnskldv", [&](int err, auto& addrs, auto& qname, auto& cname, uint ttl){
152 | std::unique_lock l(print_lock);
153 | std::cout << "==================" << "\n";
154 | std::cout << "Oops. " << DNSResolver::error_string(err) << "\n";
155 | std::cout << "==================" << "\n";
156 |
157 | });
158 |
159 | iosvc.run();
160 |
161 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/DNSResolver.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of CPPDNSResolver.
3 | Copyright (C) 2020 ReimuNotMoe
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as
7 | published by the Free Software Foundation, either version 3 of the
8 | License, or (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | #include "DNSResolver.hpp"
20 |
21 | using namespace YukiWorkshop;
22 |
23 | void DNSResolver::__init() {
24 | dns_init(nullptr, 0);
25 |
26 | ctx_udns = dns_new(nullptr);
27 | if (!ctx_udns)
28 | throw std::system_error(ENOMEM, std::system_category(), strerror(ENOMEM));
29 |
30 | if (dns_init(ctx_udns, 0) < 0)
31 | THROW_ERRNO;
32 | }
33 |
34 | void DNSResolver::__fini() {
35 | if (ctx_udns) {
36 | fprintf(stderr,"__fini\n");
37 | dns_free(ctx_udns);
38 | ctx_udns = nullptr;
39 | }
40 | }
41 |
42 | void DNSResolver::__open() {
43 | if ((fd_udns = dns_open(ctx_udns)) < 0) {
44 | THROW_ERRNO;
45 | }
46 |
47 | struct sockaddr sa;
48 | socklen_t len = sizeof(sa);
49 | if (getsockname(fd_udns, &sa, &len))
50 | throw std::system_error(std::error_code(errno, std::system_category()), "getsockname");
51 |
52 | asio_socket = std::make_unique(asio_iosvc,
53 | sa.sa_family == AF_INET ?
54 | boost::asio::ip::udp::v4()
55 | : boost::asio::ip::udp::v6(),
56 | dns_sock(ctx_udns));
57 | }
58 |
59 | void DNSResolver::io_wait_read() {
60 | fprintf(stderr,"io_wait_read\n");
61 | asio_socket->async_receive(boost::asio::null_buffers(),
62 | boost::bind(&DNSResolver::iocb_read_avail, this));
63 | }
64 |
65 | void DNSResolver::iocb_read_avail() {
66 | fprintf(stderr,"iocb_read_avail\n");
67 | dns_ioevent(ctx_udns, time(nullptr));
68 |
69 | if (requests_pending)
70 | io_wait_read();
71 | }
72 |
73 | void DNSResolver::set_servers(const std::initializer_list &__nameservers) {
74 | dns_add_serv(ctx_udns, nullptr);
75 |
76 | for (auto &it : __nameservers) {
77 | if (dns_add_serv(ctx_udns, it.c_str()) < 0) {
78 | THROW_ERRNO;
79 | }
80 | }
81 | }
82 |
83 | void DNSResolver::post_resolve() {
84 | requests_pending++;
85 | dns_timeouts(ctx_udns, -1, time(nullptr));
86 | io_wait_read();
87 | }
88 |
89 | void DNSResolver::dnscb_a4(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data) {
90 | auto *pd = (std::pair *) data;
91 | pd->first->requests_pending--;
92 |
93 | std::vector addrs;
94 |
95 | if (result) {
96 | for (uint32_t i = 0; i < result->dnsa4_nrr; i++) {
97 | std::array buf;
98 | memcpy(buf.data(), &result->dnsa4_addr[i].s_addr, 4);
99 | addrs.emplace_back(buf);
100 | }
101 |
102 | std::string_view cname(result->dnsa4_cname);
103 | std::string_view qname(result->dnsa4_qname);
104 |
105 | pd->second(DNS_E_NOERROR, addrs, qname, cname, result->dnsa4_ttl);
106 | free(result);
107 | } else {
108 | pd->second(dns_status(pd->first->ctx_udns), addrs, {}, {}, 0);
109 | }
110 |
111 | delete pd;
112 | }
113 |
114 | void DNSResolver::dnscb_a6(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data) {
115 | auto *pd = (std::pair *) data;
116 | pd->first->requests_pending--;
117 |
118 | std::vector addrs;
119 |
120 | if (result) {
121 | for (uint32_t i = 0; i < result->dnsa6_nrr; i++) {
122 | std::array buf;
123 | memcpy(buf.data(), &result->dnsa6_addr[i], 16);
124 | addrs.emplace_back(buf);
125 | }
126 |
127 | std::string_view cname(result->dnsa6_cname);
128 | std::string_view qname(result->dnsa6_qname);
129 |
130 | pd->second(DNS_E_NOERROR, addrs, qname, cname, result->dnsa6_ttl);
131 | free(result);
132 | } else {
133 | pd->second(dns_status(pd->first->ctx_udns), addrs, {}, {}, 0);
134 | }
135 |
136 | delete pd;
137 | }
138 |
139 | void DNSResolver::dnscb_txt(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data) {
140 | auto *pd = (std::pair *) data;
141 | pd->first->requests_pending--;
142 |
143 | if (result) {
144 | std::vector addrs;
145 |
146 | for (uint32_t i = 0; i < result->dnstxt_nrr; i++) {
147 | addrs.emplace_back((const char *) result->dnstxt_txt[i].txt, result->dnstxt_txt[i].len);
148 | }
149 |
150 | std::string_view cname(result->dnstxt_cname);
151 | std::string_view qname(result->dnstxt_qname);
152 |
153 | pd->second(DNS_E_NOERROR, addrs, qname, cname, result->dnstxt_ttl);
154 | free(result);
155 | } else {
156 | pd->second(dns_status(pd->first->ctx_udns), {}, {}, {}, 0);
157 | }
158 |
159 | delete pd;
160 | }
161 |
162 | void DNSResolver::dnscb_mx(struct dns_ctx *ctx, struct dns_rr_mx *result, void *data) {
163 | auto *pd = (std::pair *) data;
164 | pd->first->requests_pending--;
165 |
166 | if (result) {
167 | std::vector addrs;
168 |
169 | for (uint32_t i = 0; i < result->dnsmx_nrr; i++) {
170 | addrs.emplace_back(result->dnsmx_mx[i].priority, result->dnsmx_mx[i].name);
171 | }
172 |
173 | std::string_view cname(result->dnsmx_cname);
174 | std::string_view qname(result->dnsmx_qname);
175 |
176 | pd->second(DNS_E_NOERROR, addrs, qname, cname, result->dnsmx_ttl);
177 | free(result);
178 | } else {
179 | pd->second(dns_status(pd->first->ctx_udns), {}, {}, {}, 0);
180 | }
181 |
182 | delete pd;
183 | }
184 |
185 | void DNSResolver::dnscb_srv(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data) {
186 | auto *pd = (std::pair *) data;
187 | pd->first->requests_pending--;
188 |
189 | if (result) {
190 | std::vector addrs;
191 |
192 | for (uint32_t i = 0; i < result->dnssrv_nrr; i++) {
193 | auto &r = result->dnssrv_srv[i];
194 | addrs.emplace_back(r.priority, r.weight, r.port, r.name);
195 | }
196 |
197 | std::string_view cname(result->dnssrv_cname);
198 | std::string_view qname(result->dnssrv_qname);
199 |
200 | pd->second(DNS_E_NOERROR, addrs, qname, cname, result->dnssrv_ttl);
201 | free(result);
202 | } else {
203 | pd->second(dns_status(pd->first->ctx_udns), {}, {}, {}, 0);
204 | }
205 |
206 | delete pd;
207 | }
208 |
209 | void DNSResolver::dnscb_ptr(struct dns_ctx *ctx, struct dns_rr_ptr *result, void *data) {
210 | auto *pd = (std::pair *) data;
211 | pd->first->requests_pending--;
212 |
213 | if (result) {
214 | std::vector addrs;
215 |
216 | for (uint32_t i = 0; i < result->dnsptr_nrr; i++) {
217 | addrs.emplace_back(result->dnsptr_ptr[i]);
218 | }
219 |
220 | std::string_view cname(result->dnsptr_cname);
221 | std::string_view qname(result->dnsptr_qname);
222 |
223 | pd->second(DNS_E_NOERROR, addrs, qname, cname, result->dnsptr_ttl);
224 | free(result);
225 | } else {
226 | pd->second(dns_status(pd->first->ctx_udns), {}, {}, {}, 0);
227 | }
228 |
229 | delete pd;
230 | }
231 |
232 | DNSResolver::DNSResolver(boost::asio::io_service &__io_svc) : asio_iosvc(__io_svc) {
233 | __init();
234 | __open();
235 | }
236 |
237 | DNSResolver::DNSResolver(boost::asio::io_service &__io_svc, const std::initializer_list &__nameservers)
238 | : asio_iosvc(__io_svc) {
239 | __init();
240 | set_servers(__nameservers);
241 | __open();
242 | }
243 |
244 | DNSResolver::~DNSResolver() {
245 | __fini();
246 | }
247 |
248 | void DNSResolver::resolve_a4(const std::string &__hostname, const DNSResolver::A4Callback &__callback) {
249 | auto *pd = new std::pair(this, __callback);
250 |
251 | fprintf(stderr,"resolve_a4: %s\n", __hostname.c_str());
252 |
253 | dns_submit_a4(ctx_udns, __hostname.c_str(), 0, &DNSResolver::dnscb_a4, pd);
254 | post_resolve();
255 | }
256 |
257 | void DNSResolver::resolve_a6(const std::string &__hostname, const DNSResolver::A6Callback &__callback) {
258 | auto *pd = new std::pair(this, __callback);
259 |
260 | fprintf(stderr,"resolve_a6: %s\n", __hostname.c_str());
261 |
262 | dns_submit_a6(ctx_udns, __hostname.c_str(), 0, &DNSResolver::dnscb_a6, pd);
263 | post_resolve();
264 | }
265 |
266 | void DNSResolver::resolve_a4ptr(const boost::asio::ip::address_v4 &__addr, const DNSResolver::PtrCallback &__callback) {
267 | auto *pd = new std::pair(this, __callback);
268 |
269 | fprintf(stderr,"resolve_a4ptr: %s\n", __addr.to_string().c_str());
270 |
271 | dns_submit_a4ptr(ctx_udns, (const struct in_addr *)__addr.to_bytes().data(), &DNSResolver::dnscb_ptr, pd);
272 | post_resolve();
273 | }
274 |
275 | void DNSResolver::resolve_a6ptr(const boost::asio::ip::address_v6 &__addr, const DNSResolver::PtrCallback &__callback) {
276 | auto *pd = new std::pair(this, __callback);
277 |
278 | fprintf(stderr,"resolve_a6ptr: %s\n", __addr.to_string().c_str());
279 |
280 | dns_submit_a6ptr(ctx_udns, (const struct in6_addr *)__addr.to_bytes().data(), &DNSResolver::dnscb_ptr, pd);
281 | post_resolve();
282 | }
283 |
284 | void DNSResolver::resolve_txt(const std::string &__hostname, const DNSResolver::TXTCallback &__callback) {
285 | auto *pd = new std::pair(this, __callback);
286 |
287 | fprintf(stderr,"resolve_txt: %s\n", __hostname.c_str());
288 |
289 | dns_submit_txt(ctx_udns, __hostname.c_str(), 0, 0, &DNSResolver::dnscb_txt, pd);
290 | post_resolve();
291 | }
292 |
293 | void DNSResolver::resolve_mx(const std::string &__hostname, const DNSResolver::MXCallback &__callback) {
294 | auto *pd = new std::pair(this, __callback);
295 |
296 | fprintf(stderr,"resolve_mx: %s\n", __hostname.c_str());
297 |
298 | dns_submit_mx(ctx_udns, __hostname.c_str(), 0, &DNSResolver::dnscb_mx, pd);
299 | post_resolve();
300 | }
301 |
302 | void DNSResolver::resolve_srv(const std::string &__hostname, const std::string &__srv, const std::string &__proto,
303 | const DNSResolver::SRVCallback &__callback) {
304 | auto *pd = new std::pair(this, __callback);
305 |
306 | fprintf(stderr, "resolve_srv: %s\n", __hostname.c_str());
307 |
308 | dns_submit_srv(ctx_udns, __hostname.c_str(), __srv.c_str(), __proto.c_str(), 0, &DNSResolver::dnscb_srv, pd);
309 | post_resolve();
310 | }
311 |
312 | static const std::string errstr_none = "No error";
313 | static const std::string errstr_tempfail = "Server timeout or down";
314 | static const std::string errstr_protocol = "Malformed reply";
315 | static const std::string errstr_nodata = "Domain exists but no data of requested type";
316 | static const std::string errstr_nomem = "Out of memory";
317 | static const std::string errstr_badquery = "Malformed query";
318 | static const std::string errstr_nxdomain = "Domain does not exist";
319 | static const std::string errstr_invalid = "Invalid error";
320 |
321 | const std::string &DNSResolver::error_string(int __err) {
322 | switch (__err) {
323 | case DNS_E_NOERROR:
324 | return errstr_none;
325 | case DNS_E_TEMPFAIL:
326 | return errstr_tempfail;
327 | case DNS_E_PROTOCOL:
328 | return errstr_protocol;
329 | case DNS_E_NXDOMAIN:
330 | return errstr_nxdomain;
331 | case DNS_E_NODATA:
332 | return errstr_nodata;
333 | case DNS_E_NOMEM:
334 | return errstr_nomem;
335 | case DNS_E_BADQUERY:
336 | return errstr_badquery;
337 | default:
338 | return errstr_invalid;
339 | }
340 | }
341 |
342 |
--------------------------------------------------------------------------------