#pragma once #include "message.hpp" #include "platform.hpp" #include #include #include #include #include namespace llarp::dns { /// a job handling 1 dns query class QueryJob_Base { protected: /// the original dns query Message m_Query; public: explicit QueryJob_Base(Message query) : m_Query{std::move(query)} {} virtual ~QueryJob_Base() = default; Message& Underlying() { return m_Query; } const Message& Underlying() const { return m_Query; } /// cancel this operation and inform anyone who cares void Cancel() const; /// send a raw buffer back to the querier virtual void SendReply(llarp::OwnedBuffer replyBuf) const = 0; }; class PacketSource_Base { public: virtual ~PacketSource_Base() = default; /// return true if traffic with source and dest addresses would cause a /// loop in resolution and thus should not be sent to query handlers virtual bool WouldLoop(const SockAddr& to, const SockAddr& from) const = 0; /// send packet with src and dst address containing buf on this packet source virtual void SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const = 0; /// stop reading packets and end operation virtual void Stop() = 0; /// returns the sockaddr we are bound on if applicable virtual std::optional BoundOn() const = 0; }; /// a packet source which will override the sendto function of an wrapped packet source to /// construct a raw ip packet as a reply class PacketSource_Wrapper : public PacketSource_Base { std::weak_ptr m_Wrapped; std::function m_WritePacket; public: explicit PacketSource_Wrapper( std::weak_ptr wrapped, std::function write_packet) : m_Wrapped{wrapped}, m_WritePacket{write_packet} {} bool WouldLoop(const SockAddr& to, const SockAddr& from) const override { if (auto ptr = m_Wrapped.lock()) return ptr->WouldLoop(to, from); return true; } void SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override { m_WritePacket(net::IPPacket::make_udp(from, to, std::move(buf))); } /// stop reading packets and end operation void Stop() override { if (auto ptr = m_Wrapped.lock()) ptr->Stop(); } /// returns the sockaddr we are bound on if applicable std::optional BoundOn() const override { if (auto ptr = m_Wrapped.lock()) return ptr->BoundOn(); return std::nullopt; } }; /// non complex implementation of QueryJob_Base for use in things that /// only ever called on the mainloop thread class QueryJob : public QueryJob_Base, std::enable_shared_from_this { std::shared_ptr src; const SockAddr resolver; const SockAddr asker; public: explicit QueryJob( std::shared_ptr source, const Message& query, const SockAddr& to_, const SockAddr& from_) : QueryJob_Base{query}, src{source}, resolver{to_}, asker{from_} {} void SendReply(llarp::OwnedBuffer replyBuf) const override { src->SendTo(asker, resolver, std::move(replyBuf)); } }; /// handler of dns query hooking /// intercepts dns for internal processing class Resolver_Base { protected: /// return the sorting order for this resolver /// lower means it will be tried first virtual int Rank() const = 0; public: virtual ~Resolver_Base() = default; /// less than via rank bool operator<(const Resolver_Base& other) const { return Rank() < other.Rank(); } /// greater than via rank bool operator>(const Resolver_Base& other) const { return Rank() > other.Rank(); } /// get local socket address that queries are sent from virtual std::optional GetLocalAddr() const { return std::nullopt; } /// get printable name virtual std::string_view ResolverName() const = 0; /// reset the resolver state, optionally replace upstream info with new info. The default base /// implementation does nothing. virtual void ResetResolver( [[maybe_unused]] std::optional> replace_upstream = std::nullopt) {} /// cancel all pending requests and cease further operation. Default operation is a no-op. virtual void Down() {} /// attempt to handle a dns message /// returns true if we consumed this query and it should not be processed again virtual bool MaybeHookDNS( std::shared_ptr source, const Message& query, const SockAddr& to, const SockAddr& from) = 0; /// Returns true if a packet with to and from addresses is something that would cause a /// resolution loop and thus should not be used on this resolver virtual bool WouldLoop(const SockAddr& to, const SockAddr& from) const { (void)to; (void)from; return false; } }; // Base class for DNS proxy class Server : public std::enable_shared_from_this { protected: /// add a packet source to this server, does share ownership void AddPacketSource(std::shared_ptr resolver); /// add a resolver to this packet handler, does share ownership void AddResolver(std::shared_ptr resolver); /// create the platform dependant dns stuff virtual std::shared_ptr CreatePlatform() const; public: virtual ~Server() = default; explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif_index); /// returns all sockaddr we have from all of our PacketSources std::vector BoundPacketSourceAddrs() const; /// returns the first sockaddr we have on our packet sources if we have one std::optional FirstBoundPacketSourceAddr() const; /// add a resolver to this packet handler, does not share ownership void AddResolver(std::weak_ptr resolver); /// add a packet source to this server, does not share ownership void AddPacketSource(std::weak_ptr resolver); /// create a packet source bound on bindaddr but does not add it virtual std::shared_ptr MakePacketSourceOn(const SockAddr& bindaddr, const llarp::DnsConfig& conf); /// sets up all internal binds and such and begins operation virtual void Start(); /// stops all operation virtual void Stop(); /// reset the internal state virtual void Reset(); /// create the default resolver for out config virtual std::shared_ptr MakeDefaultResolver(); std::vector> GetAllResolvers() const; /// feed a packet buffer from a packet source. /// returns true if we decided to process the packet and consumed it /// returns false if we dont want to process the packet bool MaybeHandlePacket( std::shared_ptr pktsource, const SockAddr& resolver, const SockAddr& from, llarp::OwnedBuffer buf); /// set which dns mode we are in. /// true for intercepting all queries. false for just .loki and .snode void SetDNSMode(bool all_queries); protected: EventLoop_ptr m_Loop; llarp::DnsConfig m_Config; std::shared_ptr m_Platform; private: const unsigned int m_NetIfIndex; std::set, ComparePtr>> m_OwnedResolvers; std::set, CompareWeakPtr> m_Resolvers; std::vector> m_PacketSources; std::vector> m_OwnedPacketSources; }; } // namespace llarp::dns