#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include #include #include #include #include } template struct quickscope_wrapper { F func; ~quickscope_wrapper() { func(); } }; template quickscope_wrapper(F) -> quickscope_wrapper; static const char * inet_ntop_generic(const struct sockaddr_storage * sa, char * dst, socklen_t size) { switch(sa->ss_family) { case AF_INET: return inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), dst, size); case AF_INET6: return inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), dst, size); default: return nullptr; } } static sockaddr_in ACL[] = {{ .sin_family = AF_INET, // 192.168.1.1 knot }, { .sin_family = AF_INET, .sin_addr = {htobe32(INADDR_LOOPBACK)}, }}; int main(int, const char * const * argv) { inet_pton(AF_INET, "192.168.1.1", &ACL[0].sin_addr); setlinebuf(stderr); auto zones_hinfo_network = knot_dname_from_str_alloc("zones.hinfo.network."); assert(zones_hinfo_network); sockaddr_in listening_in = { .sin_family = AF_INET, .sin_port = htobe16(0x4849 + 1), }; inet_pton(AF_INET, argv[1], &listening_in.sin_addr); int listening = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); { const int enable = 1; assert(setsockopt(listening, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) != -1); } assert(!bind(listening, reinterpret_cast(&listening_in), sizeof(listening_in))); assert(!listen(listening, 3)); knot_pkt * response_pkt = knot_pkt_new(nullptr, KNOT_WIRE_MAX_PKTSIZE, nullptr); assert(response_pkt); std::uint16_t last = -1; for(;;) { struct sockaddr_storage peer = {}; socklen_t peerlen = sizeof(peer); auto peer_fd = accept(listening, reinterpret_cast(&peer), &peerlen); if(peer_fd == -1) continue; quickscope_wrapper peer_fd_deleter{[&] { close(peer_fd); }}; if(peer.ss_family != AF_INET) continue; if(std::none_of(std::begin(ACL), std::end(ACL), [&, peer_in = reinterpret_cast(&peer)](auto && acl) { return acl.sin_addr.s_addr == peer_in->sin_addr.s_addr; })) continue; std::uint16_t msglen; if(recv(peer_fd, &msglen, sizeof(msglen), 0) != sizeof(msglen)) continue; msglen = be16toh(msglen); char buf[KNOT_WIRE_MAX_PKTSIZE + 1]; if(recv(peer_fd, buf, sizeof(buf), 0) != msglen) continue; auto pkt = knot_pkt_new(buf, msglen, nullptr); if(!pkt) continue; quickscope_wrapper pkt_deleter{[&] { knot_pkt_free(pkt); }}; if(auto p = knot_pkt_parse(pkt, 0); p != KNOT_EOK && p != KNOT_ETRAIL) continue; auto reply = [&](short rcode) { knot_pkt_clear(response_pkt); if(knot_pkt_init_response(response_pkt, pkt) != KNOT_EOK) { std::fputs("\t_rerr\n", stderr); return; } { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); std::fprintf(stderr, "\t%s\t%llu.%09d\n", knot_rcode_names[rcode].name, (unsigned long long)now.tv_sec, (int)now.tv_nsec); } knot_wire_set_rcode(response_pkt->wire, rcode); std::uint16_t size = htobe16(response_pkt->size); if(send(peer_fd, &size, sizeof(size), 0) == sizeof(size)) (void)send(peer_fd, response_pkt->wire, response_pkt->size, 0); }; #define REPLY(...) \ __extension__({ \ reply(__VA_ARGS__); \ continue; \ }) { char peername[std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; struct timespec now; clock_gettime(CLOCK_REALTIME, &now); std::fprintf(stderr, "%llu.%09d\t%s", (unsigned long long)now.tv_sec, (int)now.tv_nsec, inet_ntop_generic(&peer, peername, sizeof(peername)) ?: ""); } if(knot_wire_get_opcode(pkt->wire) != KNOT_OPCODE_NOTIFY) REPLY(KNOT_RCODE_NOTAUTH); if(!knot_dname_is_case_equal(knot_pkt_qname(pkt), zones_hinfo_network)) REPLY(KNOT_RCODE_NOTAUTH); if(knot_wire_get_id(pkt->wire) == last) { std::fputs("\tunchanged", stderr); REPLY(KNOT_RCODE_NOERROR); } last = knot_wire_get_id(pkt->wire); switch(vfork()) { case -1: std::fprintf(stderr, "\t%m\n"); continue; // force timeout and retry case 0: execl("/bin/sh", "/bin/sh", "-ec", R"RAW( systemctl restart cron-hinfonetwork-www-data-0.service while systemctl is-active --quiet cron-hinfonetwork-www-data-0.service; do sleep 0.5 done exec setpriv --reuid=nabijaczleweli --regid="$(id -gn nabijaczleweli)" --init-groups ~nabijaczleweli/hinfo.network/.cachefly.sh )RAW", (char *)nullptr); _exit(-1); } int ret; while(wait(&ret) == -1 && errno == EINTR) // no other errors possible ; std::fprintf(stderr, "\t%d", WIFSIGNALED(ret) ? 128 + WTERMSIG(ret) : WEXITSTATUS(ret)); REPLY(KNOT_RCODE_NOERROR); } }