dont use cppcodec
[ghsmtp.git] / dns_tool.cpp
bloba3ec0515a3a216f839ed07753f8c336e216a3026
1 #include "DNS-fcrdns.hpp"
2 #include "DNS-iostream.hpp"
3 #include "DNS.hpp"
4 #include "IP.hpp"
5 #include "IP4.hpp"
6 #include "IP6.hpp"
7 #include "TLD.hpp"
8 #include "osutil.hpp"
10 #include <algorithm>
11 #include <iomanip>
12 #include <iostream>
14 #include <experimental/iterator>
16 #include <fmt/format.h>
18 void check_dnsrbl(DNS::Resolver& res, char const* a)
20 char const* rbls[]{
21 "b.barracudacentral.org",
22 "dnsbl-1.uceprotect.net",
23 "psbl.surriel.com",
24 "zen.spamhaus.org",
27 auto const reversed{IP4::reverse(a)};
29 for (auto rbl : rbls) {
30 auto as = DNS::get_strings(res, DNS::RR_type::A, reversed + rbl);
31 if (!as.empty()) {
32 if (as.front() == "127.255.255.252") {
33 LOG(INFO) << "Typing error in DNSBL Name";
34 continue;
36 if (as.front() == "127.255.255.254") {
37 LOG(INFO) << "Anonymous query through public resolver";
38 continue;
40 if (as.front() == "127.255.255.255") {
41 LOG(INFO) << "Excessive Number of Queries";
42 continue;
44 std::cout << a << " advice from " << rbl << '\n';
45 for (auto aa : as) {
46 std::cout << " returned: " << aa << '\n';
49 else {
50 std::cout << "not on " << rbl << '\n';
55 void check_uribls(DNS::Resolver& res, char const* dom)
57 char const* uribls[]{
58 "multi.uribl.com",
59 "dbl.spamhaus.org",
60 "multi.surbl.org",
63 for (auto uribl : uribls) {
64 auto const lookup = fmt::format("{}.{}", dom, uribl);
65 auto as = DNS::get_strings(res, DNS::RR_type::A, lookup);
66 if (!as.empty()) {
67 if (as.front() == "127.255.255.252") {
68 LOG(INFO) << "Typing error in DNSBL Name";
69 continue;
71 if (as.front() == "127.255.255.254") {
72 LOG(INFO) << "Anonymous query through public resolver";
73 continue;
75 if (as.front() == "127.255.255.255") {
76 LOG(INFO) << "Excessive Number of Queries";
77 continue;
79 std::cout << dom << " advice from " << uribl << '\n';
80 for (auto aa : as) {
81 std::cout << " returned: " << aa << '\n';
87 void do_addr(DNS::Resolver& res, char const* a)
89 auto const names = DNS::fcrdns(res, a);
90 std::vector<Domain> doms;
91 for (auto const& name : names) {
92 doms.emplace_back(name);
94 if (!doms.empty()) {
95 std::cout << a << " [";
96 std::copy(begin(doms), end(doms),
97 std::experimental::make_ostream_joiner(std::cout, ", "));
98 std::cout << "]\n";
100 else {
101 if (IP4::is_address(a)) {
102 auto const reversed{IP4::reverse(a)};
103 auto const ptrs =
104 res.get_strings(DNS::RR_type::PTR, reversed + "in-addr.arpa");
105 for (auto const& ptr : ptrs) {
106 std::cout << a << " has a PTR to " << ptr << '\n';
109 if (IP6::is_address(a)) {
110 auto const reversed{IP6::reverse(a)};
111 auto const ptrs =
112 res.get_strings(DNS::RR_type::PTR, reversed + "ip6.arpa");
113 for (auto const& ptr : ptrs) {
114 std::cout << a << " has a PTR to " << ptr << '\n';
117 std::cout << a << '\n';
119 if (IP4::is_address(a)) {
120 check_dnsrbl(res, a);
124 DNS::RR_collection
125 get_tlsa_rrs(DNS::Resolver& res, Domain const& domain, uint16_t port)
127 auto const tlsa = fmt::format("_{}._tcp.{}", port, domain.ascii());
129 DNS::Query q(res, DNS::RR_type::TLSA, tlsa);
131 if (q.nx_domain()) {
132 // LOG(INFO) << "TLSA data not found for " << domain << ':' << port;
135 if (q.bogus_or_indeterminate()) {
136 LOG(WARNING) << "TLSA data is bogus or indeterminate";
139 auto tlsa_rrs = q.get_records();
140 if (!tlsa_rrs.empty()) {
141 LOG(INFO) << "### TLSA data found for " << domain << ':' << port << " ###";
144 return tlsa_rrs;
147 void do_domain(DNS::Resolver& res, char const* dom_cp)
149 auto const dom{Domain{dom_cp}};
151 auto cnames = res.get_strings(DNS::RR_type::CNAME, dom.ascii().c_str());
152 if (!cnames.empty()) {
153 // RFC 2181 section 10.1. CNAME resource records
154 CHECK_EQ(cnames.size(), 1);
155 std::cout << dom << " is an alias for " << cnames.front() << '\n';
158 auto as = res.get_strings(DNS::RR_type::A, dom.ascii().c_str());
159 for (auto const& a : as) {
160 do_addr(res, a.c_str());
163 auto aaaas = res.get_strings(DNS::RR_type::AAAA, dom.ascii().c_str());
164 for (auto const& aaaa : aaaas) {
165 do_addr(res, aaaa.c_str());
168 uint16_t port = 25;
169 auto tlsa_rrs{get_tlsa_rrs(res, dom, port)};
170 if (!tlsa_rrs.empty()) {
171 for (auto const& tlsa_rr : tlsa_rrs) {
172 if (std::holds_alternative<DNS::RR_TLSA>(tlsa_rr)) {
173 auto const rp = std::get<DNS::RR_TLSA>(tlsa_rr);
174 std::cout << rp << "\n";
176 else {
177 std::cout << "not a RR_TLSA\n";
182 auto q{DNS::Query{res, DNS::RR_type::MX, dom.ascii()}};
183 if (!q.has_record()) {
184 std::cout << "no MX records\n";
187 TLD tld_db;
188 auto reg_dom{tld_db.get_registered_domain(dom.ascii())};
189 if (reg_dom && dom != reg_dom) {
190 std::cout << "registered domain is " << reg_dom << '\n';
193 auto txts = res.get_strings(DNS::RR_type::TXT, dom.ascii().c_str());
194 for (auto const& txt : txts) {
195 std::cout << "TXT " << txt << '\n';
198 if (q.has_record()) {
199 check_uribls(res, dom.ascii().c_str());
202 if (q.has_record() && q.authentic_data()) {
203 std::cout << "MX records authentic for domain " << dom << '\n';
206 auto mxs{q.get_records()};
208 mxs.erase(std::remove_if(begin(mxs), end(mxs),
209 [](auto const& rr) {
210 return !std::holds_alternative<DNS::RR_MX>(rr);
212 end(mxs));
214 if (!mxs.empty()) {
215 std::cout << "mail for " << dom << " is handled by\n";
218 std::sort(begin(mxs), end(mxs), [](auto const& a, auto const& b) {
219 auto mxa = std::get<DNS::RR_MX>(a);
220 auto mxb = std::get<DNS::RR_MX>(b);
221 if (mxa.preference() == mxb.preference())
222 return mxa.exchange() < mxb.exchange();
223 return mxa.preference() < mxb.preference();
226 for (auto const& mx : mxs) {
227 if (std::holds_alternative<DNS::RR_MX>(mx)) {
228 auto x = std::get<DNS::RR_MX>(mx);
229 std::cout << std::setfill(' ') << std::setw(3) << x.preference() << ' '
230 << x.exchange() << '\n';
235 int main(int argc, char* argv[])
237 fs::path config_path = osutil::get_config_dir();
238 DNS::Resolver res(config_path);
240 for (int i = 1; i < argc; ++i) {
241 auto const arg = argv[i];
242 if (IP::is_address(arg)) {
243 do_addr(res, arg);
245 else {
246 do_domain(res, arg);