allow
[ghsmtp.git] / dns_tool.cpp
blob2d3225064390529cf1530a298ce21692ad7ea439
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 std::cout << a << " blocked on advice from " << rbl << '\n';
37 void check_uribls(DNS::Resolver& res, char const* dom)
39 char const* uribls[]{
40 "multi.uribl.com",
41 "dbl.spamhaus.org",
42 "multi.surbl.org",
45 for (auto uribl : uribls) {
46 auto const lookup = fmt::format("{}.{}", dom, uribl);
47 auto as = DNS::get_strings(res, DNS::RR_type::A, lookup);
48 if (!as.empty()) {
49 if (as.front() == "127.0.0.1")
50 continue;
51 std::cout << dom << " blocked on advice from " << uribl << '\n';
56 void do_addr(DNS::Resolver& res, char const* a)
58 auto const names = DNS::fcrdns(res, a);
59 std::vector<Domain> doms;
60 for (auto const& name : names) {
61 doms.emplace_back(name);
63 if (!doms.empty()) {
64 std::cout << a << " [";
65 std::copy(begin(doms), end(doms),
66 std::experimental::make_ostream_joiner(std::cout, ", "));
67 std::cout << "]\n";
69 else {
70 if (IP4::is_address(a)) {
71 auto const reversed{IP4::reverse(a)};
72 auto const ptrs
73 = res.get_strings(DNS::RR_type::PTR, reversed + "in-addr.arpa");
74 for (auto const& ptr : ptrs) {
75 std::cout << a << " has a PTR to " << ptr << '\n';
78 if (IP6::is_address(a)) {
79 auto const reversed{IP6::reverse(a)};
80 auto const ptrs
81 = res.get_strings(DNS::RR_type::PTR, reversed + "ip6.arpa");
82 for (auto const& ptr : ptrs) {
83 std::cout << a << " has a PTR to " << ptr << '\n';
86 if (IP4::is_address(a)) {
87 check_dnsrbl(res, a);
89 std::cout << a << '\n';
93 DNS::RR_collection
94 get_tlsa_rrs(DNS::Resolver& res, Domain const& domain, uint16_t port)
96 auto const tlsa = fmt::format("_{}._tcp.{}", port, domain.ascii());
98 DNS::Query q(res, DNS::RR_type::TLSA, tlsa);
100 if (q.nx_domain()) {
101 LOG(INFO) << "TLSA data not found for " << domain << ':' << port;
104 if (q.bogus_or_indeterminate()) {
105 LOG(WARNING) << "TLSA data is bogus or indeterminate";
108 auto tlsa_rrs = q.get_records();
109 if (!tlsa_rrs.empty()) {
110 LOG(INFO) << "### TLSA data found for " << domain << ':' << port << " ###";
113 return tlsa_rrs;
116 void do_domain(DNS::Resolver& res, char const* dom_cp)
118 auto const dom{Domain{dom_cp}};
120 auto cnames = res.get_strings(DNS::RR_type::CNAME, dom.ascii().c_str());
121 if (!cnames.empty()) {
122 // RFC 2181 section 10.1. CNAME resource records
123 CHECK_EQ(cnames.size(), 1);
124 std::cout << dom << " is an alias for " << cnames.front() << '\n';
127 auto as = res.get_strings(DNS::RR_type::A, dom.ascii().c_str());
128 for (auto const& a : as) {
129 do_addr(res, a.c_str());
132 auto aaaas = res.get_strings(DNS::RR_type::AAAA, dom.ascii().c_str());
133 for (auto const& aaaa : aaaas) {
134 do_addr(res, aaaa.c_str());
137 uint16_t port = 25;
138 auto tlsa_rrs{get_tlsa_rrs(res, dom, port)};
139 if (!tlsa_rrs.empty()) {
140 for (auto const& tlsa_rr : tlsa_rrs) {
141 auto const rp = std::get<DNS::RR_TLSA>(tlsa_rr);
142 std::cout << rp << "\n";
146 auto q{DNS::Query{res, DNS::RR_type::MX, dom.ascii()}};
147 if (!q.has_record()) {
148 std::cout << "no MX records\n";
151 TLD tld_db;
152 auto reg_dom{tld_db.get_registered_domain(dom.ascii())};
153 if (reg_dom && dom != reg_dom) {
154 std::cout << "registered domain is " << reg_dom << '\n';
157 auto txts = res.get_strings(DNS::RR_type::TXT, dom.ascii().c_str());
158 for (auto const& txt : txts) {
159 std::cout << "TXT " << txt << '\n';
162 if (q.has_record()) {
163 check_uribls(res, dom.ascii().c_str());
166 if (q.has_record() && q.authentic_data()) {
167 std::cout << "MX records authentic for domain " << dom << '\n';
170 auto mxs{q.get_records()};
172 mxs.erase(std::remove_if(begin(mxs), end(mxs),
173 [](auto const& rr) {
174 return !std::holds_alternative<DNS::RR_MX>(rr);
176 end(mxs));
178 if (!mxs.empty()) {
179 std::cout << "mail for " << dom << " is handled by\n";
182 std::sort(begin(mxs), end(mxs), [](auto const& a, auto const& b) {
183 auto mxa = std::get<DNS::RR_MX>(a);
184 auto mxb = std::get<DNS::RR_MX>(b);
185 if (mxa.preference() == mxb.preference())
186 return mxa.exchange() < mxb.exchange();
187 return mxa.preference() < mxb.preference();
190 for (auto const& mx : mxs) {
191 if (std::holds_alternative<DNS::RR_MX>(mx)) {
192 auto x = std::get<DNS::RR_MX>(mx);
193 std::cout << std::setfill(' ') << std::setw(3) << x.preference() << ' '
194 << x.exchange() << '\n';
199 int main(int argc, char* argv[])
201 fs::path config_path = osutil::get_config_dir();
202 DNS::Resolver res(config_path);
204 for (int i = 1; i < argc; ++i) {
205 auto const arg = argv[i];
206 if (IP::is_address(arg)) {
207 do_addr(res, arg);
209 else {
210 do_domain(res, arg);