├── 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 | [![pipeline status](https://gitlab.com/ReimuNotMoe/cpp-dns/badges/master/pipeline.svg)](https://gitlab.com/ReimuNotMoe/cpp-dns/commits/master) 3 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/5ce2d6fb2bce40ea81a96e2554dbb5c8)](https://www.codacy.com/gh/YukiWorkshop/cpp-dns) 4 | [![coverage report](https://gitlab.com/ReimuNotMoe/cpp-dns/badges/master/coverage.svg)](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 | --------------------------------------------------------------------------------