another test case
[ghsmtp.git] / dns_tool.cpp
blob0c0810dbec53312662ab4898fcc291fb87cd1de9
1 #include "DNS-fcrdns.hpp"
2 #include "DNS.hpp"
3 #include "IP.hpp"
4 #include "IP4.hpp"
5 #include "IP6.hpp"
6 #include "TLD.hpp"
7 #include "osutil.hpp"
9 #include <algorithm>
10 #include <iomanip>
11 #include <iostream>
13 #include <experimental/iterator>
15 #include <fmt/format.h>
17 void check_dnsrbl(DNS::Resolver& res, char const* a)
19 char const* rbls[]{
20 "b.barracudacentral.org",
21 "psbl.surriel.com",
22 "zen.spamhaus.org",
25 auto const reversed{IP4::reverse(a)};
27 for (auto rbl : rbls) {
28 auto as = DNS::get_strings(res, DNS::RR_type::A, reversed + rbl);
29 if (!as.empty()) {
30 std::cout << a << " blocked on advice from " << rbl << '\n';
35 void check_uribls(DNS::Resolver& res, char const* dom)
37 char const* uribls[]{
38 "multi.uribl.com",
39 "dbl.spamhaus.org",
40 "multi.surbl.org",
43 for (auto uribl : uribls) {
44 auto const lookup = fmt::format("{}.{}", dom, uribl);
45 auto as = DNS::get_strings(res, DNS::RR_type::A, lookup);
46 if (!as.empty()) {
47 if (as.front() == "127.0.0.1")
48 continue;
49 std::cout << dom << " blocked on advice from " << uribl << '\n';
54 void do_addr(DNS::Resolver& res, char const* a)
56 auto const names = DNS::fcrdns(res, a);
57 std::vector<Domain> doms;
58 for (auto const& name : names) {
59 doms.emplace_back(name);
61 if (!doms.empty()) {
62 std::cout << a << " [";
63 std::copy(begin(doms), end(doms),
64 std::experimental::make_ostream_joiner(std::cout, ", "));
65 std::cout << "]\n";
67 else {
68 if (IP4::is_address(a)) {
69 auto const reversed{IP4::reverse(a)};
70 auto const ptrs
71 = res.get_strings(DNS::RR_type::PTR, reversed + "in-addr.arpa");
72 for (auto const& ptr : ptrs) {
73 std::cout << a << " has a PTR to " << ptr << '\n';
76 if (IP6::is_address(a)) {
77 auto const reversed{IP6::reverse(a)};
78 auto const ptrs
79 = res.get_strings(DNS::RR_type::PTR, reversed + "ip6.arpa");
80 for (auto const& ptr : ptrs) {
81 std::cout << a << " has a PTR to " << ptr << '\n';
84 if (IP4::is_address(a)) {
85 check_dnsrbl(res, a);
87 std::cout << a << '\n';
91 void do_domain(DNS::Resolver& res, char const* dom_cp)
93 auto const dom{Domain{dom_cp}};
95 auto cnames = res.get_strings(DNS::RR_type::CNAME, dom.ascii().c_str());
96 if (!cnames.empty()) {
97 // RFC 2181 section 10.1. CNAME resource records
98 CHECK_EQ(cnames.size(), 1);
99 std::cout << dom << " is an alias for " << cnames.front() << '\n';
102 auto as = res.get_strings(DNS::RR_type::A, dom.ascii().c_str());
103 for (auto const& a : as) {
104 do_addr(res, a.c_str());
107 auto aaaas = res.get_strings(DNS::RR_type::AAAA, dom.ascii().c_str());
108 for (auto const& aaaa : aaaas) {
109 do_addr(res, aaaa.c_str());
112 auto q{DNS::Query{res, DNS::RR_type::MX, dom.ascii()}};
113 if (!q.has_record()) {
114 std::cout << "no MX records\n";
117 TLD tld_db;
118 auto reg_dom{tld_db.get_registered_domain(dom.ascii())};
119 if (dom != reg_dom) {
120 std::cout << "registered domain is " << reg_dom << '\n';
123 auto txts = res.get_strings(DNS::RR_type::TXT, dom.ascii().c_str());
124 for (auto const& txt : txts) {
125 std::cout << "TXT " << txt << '\n';
128 if (q.has_record()) {
129 check_uribls(res, dom.ascii().c_str());
132 if (q.has_record() && q.authentic_data()) {
133 std::cout << "MX records authentic for domain " << dom << '\n';
136 auto mxs{q.get_records()};
138 mxs.erase(std::remove_if(begin(mxs), end(mxs),
139 [](auto const& rr) {
140 return !std::holds_alternative<DNS::RR_MX>(rr);
142 end(mxs));
144 if (!mxs.empty()) {
145 std::cout << "mail for " << dom << " is handled by\n";
148 std::sort(begin(mxs), end(mxs), [](auto const& a, auto const& b) {
149 auto mxa = std::get<DNS::RR_MX>(a);
150 auto mxb = std::get<DNS::RR_MX>(b);
151 if (mxa.preference() == mxb.preference())
152 return mxa.exchange() < mxb.exchange();
153 return mxa.preference() < mxb.preference();
156 for (auto const& mx : mxs) {
157 if (std::holds_alternative<DNS::RR_MX>(mx)) {
158 auto x = std::get<DNS::RR_MX>(mx);
159 std::cout << std::setfill(' ') << std::setw(3) << x.preference() << ' '
160 << x.exchange() << '\n';
165 int main(int argc, char* argv[])
167 fs::path config_path = osutil::get_config_dir();
168 DNS::Resolver res(config_path);
170 for (int i = 1; i < argc; ++i) {
171 auto const arg = argv[i];
172 if (IP::is_address(arg)) {
173 do_addr(res, arg);
175 else {
176 do_domain(res, arg);