#include "flags.h" #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include #include } using namespace std::literals; static void htmlescape(const std::string_view & data) { for(auto c : data) switch(c) { case '<': std::fputs("<", stdout); break; case '&': std::fputs("&", stdout); break; default: std::fputc(c, stdout); break; } } static void singleescape(const std::string_view & data) { for(auto c : data) switch(c) { case '\'': std::fputs("%27", stdout); break; default: std::fputc(c, stdout); break; } } enum class tp : bool { a, mx }; enum class line_state_t : std::uint8_t { none, ptr, soa, hinfo }; int main(int, char ** names) { struct __res_state state; assert(!res_ninit(&state)); std::map cache; unsigned char buf[NS_MAXMSG]; for(auto itr = names + 1; *itr; ++itr) if(res_nquery(&state, *itr, ns_c_in, ns_t_a, buf, sizeof(buf)) != -1 || res_nquery(&state, *itr, ns_c_in, ns_t_aaaa, buf, sizeof(buf)) != -1) cache[*itr] = tp::a; else if(auto sz = res_nquery(&state, *itr, ns_c_in, ns_t_mx, buf, sizeof(buf)); sz != -1) { bool dont = false; if(auto pkt = knot_pkt_new(buf, sz, nullptr)) { if(auto p = knot_pkt_parse(pkt, 0); (p == KNOT_EOK || p == KNOT_ETRAIL) && pkt->sections[KNOT_ANSWER].count == 1) { auto mx = knot_pkt_rr(&pkt->sections[KNOT_ANSWER], 0); if(mx->rrs.count) { if(!*knot_rdata_name(mx->rrs.rdata, mx->type) && !std::memcmp(mx->rrs.rdata->data, "\0\0", 2)) // == "." && == 0 dont = true; // fwrite(mx->rrs.rdata->data, 1, mx->rrs.rdata->len, stderr); } } knot_pkt_free(pkt); } if(!dont) cache[*itr] = tp::mx; } std::set ids; char * line_raw{}; std::size_t linecap{}; auto line_state = line_state_t::none; for(ssize_t len; (len = getline(&line_raw, &linecap, stdin)) != -1;) { std::string_view line{line_raw, static_cast(len)}, append; if(line.back() == '\n') line.remove_suffix(1); while(line.back() == ' ') line.remove_suffix(1); if(line.empty()) { if(line_state == line_state_t::ptr) std::fputs("", stdout); goto dump; } if(line.starts_with(";;"sv)) { if(line.ends_with(" IN PTR"sv)) { std::fputs("
", stdout); append = ""sv; line_state = line_state_t::ptr; } else if(line.ends_with(" IN SOA"sv)) { append = ""sv; line_state = line_state_t::soa; } else if(line.ends_with(" IN HINFO"sv)) { append = ""sv; line_state = line_state_t::hinfo; } else line_state = line_state_t::none; goto dump; } if(line_state == line_state_t::ptr && line.starts_with(' ')) { line.remove_prefix(line.find_first_not_of(' ')); if(line.starts_with(";"sv)) { std::fwrite(line.data(), 1, line.size(), stdout); continue; } } // ;;zones.hinfo.network. IN SOA // zones.hinfo.network. 3600 IN SOA ns.nabijaczleweli.xyz. наб.hinfo.network. 2023-09-18T00:00:09+02:00 if(line_state == line_state_t::soa && line.contains(" IN SOA "sv)) { auto mail = line; mail.remove_prefix(mail.find_first_of(' ')); mail.remove_prefix(mail.find_first_not_of(' ')); mail.remove_prefix(mail.find_first_of(' ')); mail.remove_prefix(mail.find_first_not_of(' ')); mail.remove_prefix(mail.find_first_of(' ')); mail.remove_prefix(mail.find_first_not_of(' ')); mail.remove_prefix(mail.find_first_of(' ')); mail.remove_prefix(mail.find_first_not_of(' ')); mail.remove_prefix(mail.find_first_of(' ')); mail.remove_prefix(mail.find_first_not_of(' ')); mail.remove_suffix(mail.size() - mail.find_first_of(' ')); htmlescape({line.data(), mail.data()}); std::fputs(" '~'; })) { char * out{}; if(auto rc = idn2_to_ascii_8z(std::string{basename}.c_str(), &out, IDN2_NONTRANSITIONAL | IDN2_NFC_INPUT); rc != IDN2_OK) std::fputs(idn2_strerror(rc), stderr); if(out) { std::fputs("' title='Or ", stdout); singleescape(out); std::fputs("@", stdout); } } std::fputs("'>", stdout); htmlescape(mail); std::fputs("", stdout); line = {mail.data() + mail.size(), line.data() + line.size()}; } // ;;ibid. IN HINFO // hinfo.group. 60267 IN HINFO "X32" "DEBIAN/BOOKWORM" if(line_state == line_state_t::hinfo && line.contains(" IN HINFO "sv)) { if(line.back() == '\n') line.remove_suffix(1); while(line.back() == ' ') line.remove_suffix(1); auto flags = *reinterpret_cast(&line.back()); if(flags & f_CANARY) { line.remove_suffix(1); std::fputs("", stdout); } auto sp = line.find(' '); auto name = line; name.remove_suffix(line.size() - sp); if(auto type = cache.find(name); type != std::end(cache)) { line.remove_prefix(sp); switch(type->second) { case tp::a: std::fputs("second == tp::mx) std::fputs("?subject=Cool host!", stdout); std::fputs("'>", stdout); htmlescape(name); std::fputs("", stdout); } htmlescape(line); line = {}; if(flags & f_CANARY) { auto note = [&](auto shrt, auto lng) { std::fputs(" ", stdout); std::fputs(shrt, stdout); std::fputs("", stdout); }; if(flags & f_fcount) note("FC", "Field Count"); if(flags & f_cpulen) note("CL", "CPU Length"); if(flags & f_syslen) note("SL", "System Length"); if(flags & f_cpunvl) note("CN", "CPU Invalid"); if(flags & f_sysnvl) note("SN", "System Invalid"); std::fputs("", stdout); } for(std::size_t dot; (dot = name.find('.')) != std::string_view::npos;) { if(ids.emplace(name).second) { std::fputs("", stdout); } name.remove_prefix(dot + 1); } } dump: htmlescape(line); std::fwrite(append.data(), 1, append.size(), stdout); std::fputc('\n', stdout); } }