diff options
28 files changed, 1522 insertions, 2162 deletions
diff --git a/include/configreader.h b/include/configreader.h index a2dfd2a34..bdba0efc3 100644 --- a/include/configreader.h +++ b/include/configreader.h @@ -320,10 +320,6 @@ class CoreExport ServerConfig */ std::string FixedPart; - /** The DNS server to use for DNS queries - */ - std::string DNSServer; - /** Pretend disabled commands don't exist. */ bool DisabledDontExist; @@ -448,10 +444,6 @@ class CoreExport ServerConfig */ std::map<irc::string, bool> ulines; - /** If set to true, no user DNS lookups are to be performed - */ - bool NoUserDns; - /** If set to true, provide syntax hints for unknown commands */ bool SyntaxHints; diff --git a/include/dns.h b/include/dns.h deleted file mode 100644 index 120a8f428..000000000 --- a/include/dns.h +++ /dev/null @@ -1,439 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2005-2008 Craig Edwards <craigedwards@brainbox.cc> - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> - * - * This file is part of InspIRCd. InspIRCd is free software: you can - * redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* -dns.h - dns library very very loosely based on -firedns, Copyright (C) 2002 Ian Gulliver - -This program is free software; you can redistribute it and/or modify -it under the terms of version 2 of the GNU General Public License as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - - -#pragma once - -#include "socket.h" -#include "hashcomp.h" - -/** - * Result status, used internally - */ -class CoreExport DNSResult -{ - public: - /** Result ID - */ - int id; - /** Result body, a hostname or IP address - */ - std::string result; - /** Time-to-live value of the result - */ - unsigned long ttl; - /** The original request, a hostname or IP address - */ - std::string original; - - /** Build a DNS result. - * @param i The request ID - * @param res The request result, a hostname or IP - * @param timetolive The request time-to-live - * @param orig The original request, a hostname or IP - */ - DNSResult(int i, const std::string &res, unsigned long timetolive, const std::string &orig) : id(i), result(res), ttl(timetolive), original(orig) { } -}; - -/** - * Information on a completed lookup, used internally - */ -typedef std::pair<unsigned char*, std::string> DNSInfo; - -/** Cached item stored in the query cache. - */ -class CoreExport CachedQuery -{ - public: - /** The cached result data, an IP or hostname - */ - std::string data; - /** The time when the item is due to expire - */ - time_t expires; - - /** Build a cached query - * @param res The result data, an IP or hostname - * @param ttl The time-to-live value of the query result - */ - CachedQuery(const std::string &res, unsigned int ttl); - - /** Returns the number of seconds remaining before this - * cache item has expired and should be removed. - */ - int CalcTTLRemaining(); -}; - -/** DNS cache information. Holds IPs mapped to hostnames, and hostnames mapped to IPs. - */ -typedef TR1NS::unordered_map<irc::string, CachedQuery, irc::hash> dnscache; - -/** - * Error types that class Resolver can emit to its error method. - */ -enum ResolverError -{ - RESOLVER_NOERROR = 0, - RESOLVER_NSDOWN = 1, - RESOLVER_NXDOMAIN = 2, - RESOLVER_BADIP = 3, - RESOLVER_TIMEOUT = 4, - RESOLVER_FORCEUNLOAD = 5 -}; - -/** - * Query and resource record types - */ -enum QueryType -{ - /** Uninitialized Query */ - DNS_QUERY_NONE = 0, - /** 'A' record: an ipv4 address */ - DNS_QUERY_A = 1, - /** 'CNAME' record: An alias */ - DNS_QUERY_CNAME = 5, - /** 'PTR' record: a hostname */ - DNS_QUERY_PTR = 12, - /** 'AAAA' record: an ipv6 address */ - DNS_QUERY_AAAA = 28, - - /** Force 'PTR' to use IPV4 scemantics */ - DNS_QUERY_PTR4 = 0xFFFD, - /** Force 'PTR' to use IPV6 scemantics */ - DNS_QUERY_PTR6 = 0xFFFE -}; - -/** - * Used internally to force PTR lookups to use a certain protocol scemantics, - * e.g. x.x.x.x.in-addr.arpa for v4, and *.ip6.arpa for v6. - */ -enum ForceProtocol -{ - /** Forced to use ipv4 */ - PROTOCOL_IPV4 = 0, - /** Forced to use ipv6 */ - PROTOCOL_IPV6 = 1 -}; - -/** - * The Resolver class is a high-level abstraction for resolving DNS entries. - * It can do forward and reverse IPv4 lookups, and where IPv6 is supported, will - * also be able to do those, transparent of protocols. Module developers must - * extend this class via inheritence, and then insert a pointer to their derived - * class into the core using Server::AddResolver(). Once you have done this, - * the class will be able to receive callbacks. There are two callbacks which - * can occur by calling virtual methods, one is a success situation, and the other - * an error situation. - */ -class CoreExport Resolver -{ - protected: - /** - * Pointer to creator module (if any, or NULL) - */ - ModuleRef Creator; - /** - * The input data, either a host or an IP address - */ - std::string input; - /** - * True if a forward lookup is being performed, false if otherwise - */ - QueryType querytype; - /** - * The DNS erver being used for lookups. If this is an empty string, - * the value of ServerConfig::DNSServer is used instead. - */ - std::string server; - /** - * The ID allocated to your lookup. This is a pseudo-random number - * between 0 and 65535, a value of -1 indicating a failure. - * The core uses this to route results to the correct objects. - */ - int myid; - - /** - * Cached result, if there is one - */ - CachedQuery *CQ; - - /** - * Time left before cache expiry - */ - int time_left; - - public: - /** - * Initiate DNS lookup. Your class should not attempt to delete or free these - * objects, as the core will do this for you. They must always be created upon - * the heap using new, as you cannot be sure at what time they will be deleted. - * Allocating them on the stack or attempting to delete them yourself could cause - * the object to go 'out of scope' and cause a segfault in the core if the result - * arrives at a later time. - * @param source The IP or hostname to resolve - * @param qt The query type to perform. Resolution of 'A', 'AAAA', 'PTR' and 'CNAME' records - * is supported. Use one of the QueryType enum values to initiate this type of - * lookup. Resolution of 'AAAA' ipv6 records is always supported, regardless of - * wether InspIRCd is built with ipv6 support. - * To look up reverse records, specify one of DNS_QUERY_PTR4 or DNS_QUERY_PTR6 depending - * on the type of address you are looking up. - * @param cached The constructor will set this boolean to true or false depending - * on whether the DNS lookup you are attempting is cached (and not expired) or not. - * If the value is cached, upon return this will be set to true, otherwise it will - * be set to false. You should pass this value to InspIRCd::AddResolver(), which - * will then influence the behaviour of the method and determine whether a cached - * or non-cached result is obtained. The value in this variable is always correct - * for the given request when the constructor exits. - * @param creator See the note below. - * @throw ModuleException This class may throw an instance of ModuleException, in the - * event a lookup could not be allocated, or a similar hard error occurs such as - * the network being down. This will also be thrown if an invalid IP address is - * passed when resolving a 'PTR' record. - * - * NOTE: If you are instantiating your DNS lookup from a module, you should set the - * value of creator to point at your Module class. This way if your module is unloaded - * whilst lookups are in progress, they can be safely removed and your module will not - * crash the server. - */ - Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator); - - /** - * The default destructor does nothing. - */ - virtual ~Resolver(); - - /** - * When your lookup completes, this method will be called. - * @param result The resulting DNS lookup, either an IP address or a hostname. - * @param ttl The time-to-live value of the result, in the instance of a cached - * result, this is the number of seconds remaining before refresh/expiry. - * @param cached True if the result is a cached result, false if it was requested - * from the DNS server. - */ - virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) = 0; - - /** - * If an error occurs (such as NXDOMAIN, no domain name found) then this method - * will be called. - * @param e A ResolverError enum containing the error type which has occured. - * @param errormessage The error text of the error that occured. - */ - virtual void OnError(ResolverError e, const std::string &errormessage); - - /** - * Returns the id value of this class. This is primarily used by the core - * to determine where in various tables to place a pointer to your class, but it - * is safe to call and use this method. - * As specified in RFC1035, each dns request has a 16 bit ID value, ranging - * from 0 to 65535. If there is an issue and the core cannot send your request, - * this method will return -1. - */ - int GetId(); - - /** - * Returns the creator module, or NULL - */ - Module* GetCreator(); - - /** - * If the result is a cached result, this triggers the objects - * OnLookupComplete. This is done because it is not safe to call - * the abstract virtual method from the constructor. - */ - void TriggerCachedResult(); -}; - -/** DNS is a singleton class used by the core to dispatch dns - * requests to the dns server, and route incoming dns replies - * back to Resolver objects, based upon the request ID. You - * should never use this class yourself. - */ -class CoreExport DNS : public EventHandler -{ - private: - - /** - * The maximum value of a dns request id, - * 16 bits wide, 0xFFFF. - */ - static const int MAX_REQUEST_ID = 0xFFFF; - - /** - * Currently cached items - */ - dnscache* cache; - - /** A timer which ticks every hour to remove expired - * items from the DNS cache. - */ - class CacheTimer* PruneTimer; - - /** - * Build a dns packet payload - */ - int MakePayload(const char* name, const QueryType rr, const unsigned short rr_class, unsigned char* payload); - - public: - - irc::sockets::sockaddrs myserver; - - /** - * Currently active Resolver classes - */ - Resolver* Classes[MAX_REQUEST_ID]; - - /** - * Requests that are currently 'in flight' - */ - DNSRequest* requests[MAX_REQUEST_ID]; - - /** - * The port number DNS requests are made on, - * and replies have as a source-port number. - */ - static const int QUERY_PORT = 53; - - /** - * Fill an rr (resource record) with data from input - */ - static void FillResourceRecord(ResourceRecord* rr, const unsigned char* input); - - /** - * Fill a header with data from input limited by a length - */ - static void FillHeader(DNSHeader *header, const unsigned char *input, const int length); - - /** - * Empty out a header into a data stream ready for transmission "on the wire" - */ - static void EmptyHeader(unsigned char *output, const DNSHeader *header, const int length); - - /** - * Start the lookup of an ipv4 from a hostname - */ - int GetIP(const char* name); - - /** - * Start lookup of a hostname from an ip, but - * force a specific protocol to be used for the lookup - * for example to perform an ipv6 reverse lookup. - */ - int GetNameForce(const char *ip, ForceProtocol fp); - - /** - * Start lookup of an ipv6 from a hostname - */ - int GetIP6(const char *name); - - /** - * Start lookup of a CNAME from another hostname - */ - int GetCName(const char* alias); - - /** - * Fetch the result string (an ip or host) - * and/or an error message to go with it. - */ - DNSResult GetResult(); - - /** - * Handle a SocketEngine read event - * Inherited from EventHandler - */ - void HandleEvent(EventType et, int errornum = 0); - - /** - * Add a Resolver* to the list of active classes - */ - bool AddResolverClass(Resolver* r); - - /** - * Add a query to the list to be sent - */ - DNSRequest* AddQuery(DNSHeader *header, int &id, const char* original); - - /** - * The constructor initialises the dns socket, - * and clears the request lists. - */ - DNS(); - - /** - * Re-initialize the DNS subsystem. - */ - void Rehash(); - - /** - * Destructor - */ - ~DNS(); - - /** - * Turn an in6_addr into a .ip6.arpa domain - */ - static void MakeIP6Int(char* query, const in6_addr *ip); - - /** - * Clean out all dns resolvers owned by a particular - * module, to make unloading a module safe if there - * are dns requests currently in progress. - */ - void CleanResolvers(Module* module); - - /** Return the cached value of an IP or hostname - * @param source An IP or hostname to find in the cache. - * @return A pointer to a CachedQuery if the item exists, - * otherwise NULL. - */ - CachedQuery* GetCache(const std::string &source); - - /** Delete a cached item from the DNS cache. - * @param source An IP or hostname to remove - */ - void DelCache(const std::string &source); - - /** Clear all items from the DNS cache immediately. - */ - int ClearCache(); - - /** Prune the DNS cache, e.g. remove all expired - * items and rehash the cache buckets, but leave - * items in the hash which are still valid. - */ - int PruneCache(); -}; diff --git a/include/inspircd.h b/include/inspircd.h index 25ef288aa..bc0b9cd1b 100644 --- a/include/inspircd.h +++ b/include/inspircd.h @@ -432,10 +432,6 @@ class CoreExport InspIRCd */ SnomaskManager* SNO; - /** DNS class, provides resolver facilities to the core and modules - */ - DNS* Res; - /** Timer manager class, triggers Timer timer events */ TimerManager* Timers; @@ -612,24 +608,6 @@ class CoreExport InspIRCd */ caller1<bool, const std::string&> IsIdent; - /** Add a dns Resolver class to this server's active set - * @param r The resolver to add - * @param cached If this value is true, then the cache will - * be searched for the DNS result, immediately. If the value is - * false, then a request will be sent to the nameserver, and the - * result will not be immediately available. You should usually - * use the boolean value which you passed to the Resolver - * constructor, which Resolver will set appropriately depending - * on if cached results are available and haven't expired. It is - * however safe to force this value to false, forcing a remote DNS - * lookup, but not an update of the cache. - * @return True if the operation completed successfully. Note that - * if this method returns true, you should not attempt to access - * the resolver class you pass it after this call, as depending upon - * the request given, the object may be deleted! - */ - bool AddResolver(Resolver* r, bool cached); - /** Add a command to this server's command parser * @param f A Command command handler object to add * @throw ModuleException Will throw ModuleExcption if the command already exists diff --git a/include/modules.h b/include/modules.h index 359195c39..0b91e6048 100644 --- a/include/modules.h +++ b/include/modules.h @@ -34,7 +34,6 @@ #include <sstream> #include "timer.h" #include "mode.h" -#include "dns.h" /** Used to define a set of behavior bits for a module */ diff --git a/include/modules/dns.h b/include/modules/dns.h new file mode 100644 index 000000000..65a1762b3 --- /dev/null +++ b/include/modules/dns.h @@ -0,0 +1,193 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013 Adam <Adam@anope.org> + * Copyright (C) 2003-2013 Anope Team <team@anope.org> + * + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +namespace DNS +{ + /** Valid query types + */ + enum QueryType + { + /* Nothing */ + QUERY_NONE, + /* A simple A lookup */ + QUERY_A = 1, + /* A CNAME lookup */ + QUERY_CNAME = 5, + /* Reverse DNS lookup */ + QUERY_PTR = 12, + /* IPv6 AAAA lookup */ + QUERY_AAAA = 28 + }; + + /** Flags that can be AND'd into DNSPacket::flags to receive certain values + */ + enum + { + QUERYFLAGS_QR = 0x8000, + QUERYFLAGS_OPCODE = 0x7800, + QUERYFLAGS_AA = 0x400, + QUERYFLAGS_TC = 0x200, + QUERYFLAGS_RD = 0x100, + QUERYFLAGS_RA = 0x80, + QUERYFLAGS_Z = 0x70, + QUERYFLAGS_RCODE = 0xF + }; + + enum Error + { + ERROR_NONE, + ERROR_UNKNOWN, + ERROR_UNLOADED, + ERROR_TIMEDOUT, + ERROR_NOT_AN_ANSWER, + ERROR_NONSTANDARD_QUERY, + ERROR_FORMAT_ERROR, + ERROR_SERVER_FAILURE, + ERROR_DOMAIN_NOT_FOUND, + ERROR_NOT_IMPLEMENTED, + ERROR_REFUSED, + ERROR_NO_RECORDS, + ERROR_INVALIDTYPE + }; + + const int PORT = 53; + + /** + * The maximum value of a dns request id, + * 16 bits wide, 0xFFFF. + */ + const int MAX_REQUEST_ID = 0xFFFF; + + class Exception : public ModuleException + { + public: + Exception(const std::string& message) : ModuleException(message) { } + }; + + struct Question + { + std::string name; + QueryType type; + unsigned short qclass; + + Question() : type(QUERY_NONE), qclass(0) { } + Question(const std::string& n, QueryType t, unsigned short c = 1) : name(n), type(t), qclass(c) { } + inline bool operator==(const Question& other) const { return name == other.name && type == other.type && qclass == other.qclass; } + + struct hash + { + size_t operator()(const Question& question) const + { + return irc::insensitive()(question.name); + } + }; + }; + + struct ResourceRecord : Question + { + unsigned int ttl; + std::string rdata; + time_t created; + + ResourceRecord(const std::string& n, QueryType t, unsigned short c = 1) : Question(n, t, c), ttl(0), created(ServerInstance->Time()) { } + ResourceRecord(const Question& question) : Question(question), ttl(0), created(ServerInstance->Time()) { } + }; + + struct Query + { + std::vector<Question> questions; + std::vector<ResourceRecord> answers; + Error error; + bool cached; + + Query() : error(ERROR_NONE), cached(false) { } + Query(const Question& question) : error(ERROR_NONE), cached(false) { questions.push_back(question); } + }; + + class ReplySocket; + class Request; + + /** DNS manager + */ + class Manager : public DataProvider + { + public: + Manager(Module* mod) : DataProvider(mod, "DNS") { } + + virtual void Process(Request* req) = 0; + virtual void RemoveRequest(Request* req) = 0; + virtual std::string GetErrorStr(Error) = 0; + }; + + /** A DNS query. + */ + class Request : public Timer, public Question + { + protected: + Manager* const manager; + public: + /* Use result cache if available */ + bool use_cache; + /* Request id */ + unsigned short id; + /* Creator of this request */ + Module* const creator; + + Request(Manager* mgr, Module* mod, const std::string& addr, QueryType qt, bool usecache = true) + : Timer((ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5), ServerInstance->Time()) + , Question(addr, qt) + , manager(mgr) + , use_cache(usecache) + , id(0) + , creator(mod) + { + ServerInstance->Timers->AddTimer(this); + } + + virtual ~Request() + { + manager->RemoveRequest(this); + } + + /** Called when this request succeeds + * @param r The query sent back from the nameserver + */ + virtual void OnLookupComplete(const Query* req) = 0; + + /** Called when this request fails or times out. + * @param r The query sent back from the nameserver, check the error code. + */ + virtual void OnError(const Query* req) { } + + /** Used to time out the query, calls OnError and asks the TimerManager + * to delete this request + */ + bool Tick(time_t now) + { + Query rr(*this); + rr.error = ERROR_TIMEDOUT; + this->OnError(&rr); + return false; + } + }; + +} // namespace DNS + diff --git a/include/socket.h b/include/socket.h index f6934b771..3abbeef32 100644 --- a/include/socket.h +++ b/include/socket.h @@ -109,9 +109,6 @@ namespace irc */ CoreExport bool MatchCIDR(const std::string &address, const std::string &cidr_mask, bool match_with_username); - /** Return the size of the structure for syscall passing */ - inline int sa_size(const irc::sockets::sockaddrs& sa) { return sa.sa_size(); } - /** Convert an address-port pair into a binary sockaddr * @param addr The IP address, IPv4 or IPv6 * @param port The port, 0 for unspecified diff --git a/include/typedefs.h b/include/typedefs.h index b19426b6a..404175ddb 100644 --- a/include/typedefs.h +++ b/include/typedefs.h @@ -28,8 +28,6 @@ class Channel; class Command; class ConfigReader; class ConfigTag; -class DNSHeader; -class DNSRequest; class Extensible; class FakeUser; class InspIRCd; @@ -44,13 +42,11 @@ class ServerConfig; class ServerLimits; class Thread; class User; -class UserResolver; class XLine; class XLineManager; class XLineFactory; struct ConnectClass; struct ModResult; -struct ResourceRecord; #include "hashcomp.h" #include "base.h" diff --git a/include/users.h b/include/users.h index def7a146d..9b7d28d31 100644 --- a/include/users.h +++ b/include/users.h @@ -26,7 +26,6 @@ #include "socket.h" #include "inspsocket.h" -#include "dns.h" #include "mode.h" #include "membership.h" @@ -761,12 +760,6 @@ class CoreExport LocalUser : public User, public InviteBase */ unsigned int quitting_sendq:1; - /** True when DNS lookups are completed. - * The UserResolver classes res_forward and res_reverse will - * set this value once they complete. - */ - unsigned int dns_done:1; - /** has the user responded to their previous ping? */ unsigned int lastping:1; @@ -791,16 +784,6 @@ class CoreExport LocalUser : public User, public InviteBase static already_sent_t already_sent_id; already_sent_t already_sent; - /** Stored reverse lookup from res_forward. Should not be used after resolution. - */ - std::string stored_host; - - /** Starts a DNS lookup of the user's IP. - * This will cause two UserResolver classes to be instantiated. - * When complete, these objects set User::dns_done to true. - */ - void StartDNSLookup(); - /** Check if the user matches a G or K line, and disconnect them if they do. * @param doZline True if ZLines should be checked (if IP has changed since initial connect) * Returns true if the user matched a ban, false else. @@ -915,35 +898,3 @@ inline FakeUser* IS_SERVER(User* u) return u->usertype == USERTYPE_SERVER ? static_cast<FakeUser*>(u) : NULL; } -/** Derived from Resolver, and performs user forward/reverse lookups. - */ -class CoreExport UserResolver : public Resolver -{ - private: - /** UUID we are looking up */ - std::string uuid; - /** True if the lookup is forward, false if is a reverse lookup - */ - bool fwd; - public: - /** Create a resolver. - * @param user The user to begin lookup on - * @param to_resolve The IP or host to resolve - * @param qt The query type - * @param cache Modified by the constructor if the result was cached - */ - UserResolver(LocalUser* user, std::string to_resolve, QueryType qt, bool &cache); - - /** Called on successful lookup - * @param result Result string - * @param ttl Time to live for result - * @param cached True if the result was found in the cache - */ - void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached); - - /** Called on failed lookup - * @param e Error code - * @param errormessage Error message string - */ - void OnError(ResolverError e, const std::string &errormessage); -}; diff --git a/src/commands/cmd_clearcache.cpp b/src/commands/cmd_clearcache.cpp deleted file mode 100644 index 5914f9a8f..000000000 --- a/src/commands/cmd_clearcache.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net> - * - * This file is part of InspIRCd. InspIRCd is free software: you can - * redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "inspircd.h" - -/** Handle /CLEARCACHE. These command handlers can be reloaded by the core, - * and handle basic RFC1459 commands. Commands within modules work - * the same way, however, they can be fully unloaded, where these - * may not. - */ -class CommandClearcache : public Command -{ - public: - /** Constructor for clearcache. - */ - CommandClearcache ( Module* parent) : Command(parent,"CLEARCACHE",0) { flags_needed = 'o'; } - /** Handle command. - * @param parameters The parameters to the comamnd - * @param pcnt The number of parameters passed to teh command - * @param user The user issuing the command - * @return A value from CmdResult to indicate command success or failure. - */ - CmdResult Handle(const std::vector<std::string>& parameters, User *user); -}; - -/** Handle /CLEARCACHE - */ -CmdResult CommandClearcache::Handle (const std::vector<std::string>& parameters, User *user) -{ - int n = ServerInstance->Res->ClearCache(); - user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick.c_str(), n); - return CMD_SUCCESS; -} - -COMMAND_INIT(CommandClearcache) diff --git a/src/commands/cmd_dns.cpp b/src/commands/cmd_dns.cpp new file mode 100644 index 000000000..29467a88b --- /dev/null +++ b/src/commands/cmd_dns.cpp @@ -0,0 +1,845 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013 Adam <Adam@anope.org> + * Copyright (C) 2003-2013 Anope Team <team@anope.org> + * + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "inspircd.h" +#include "modules/dns.h" +#include <iostream> +#include <fstream> + +#ifdef _WIN32 +#include <Iphlpapi.h> +#pragma comment(lib, "Iphlpapi.lib") +#endif + +using namespace DNS; + +/** A full packet sent or recieved to/from the nameserver + */ +class Packet : public Query +{ + void PackName(unsigned char* output, unsigned short output_size, unsigned short& pos, const std::string& name) + { + if (pos + name.length() + 2 > output_size) + throw Exception("Unable to pack name"); + + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Packing name " + name); + + irc::sepstream sep(name, '.'); + std::string token; + + while (sep.GetToken(token)) + { + output[pos++] = token.length(); + memcpy(&output[pos], token.data(), token.length()); + pos += token.length(); + } + + output[pos++] = 0; + } + + std::string UnpackName(const unsigned char* input, unsigned short input_size, unsigned short& pos) + { + std::string name; + unsigned short pos_ptr = pos, lowest_ptr = input_size; + bool compressed = false; + + if (pos_ptr >= input_size) + throw Exception("Unable to unpack name - no input"); + + while (input[pos_ptr] > 0) + { + unsigned short offset = input[pos_ptr]; + + if (offset & POINTER) + { + if ((offset & POINTER) != POINTER) + throw Exception("Unable to unpack name - bogus compression header"); + if (pos_ptr + 1 >= input_size) + throw Exception("Unable to unpack name - bogus compression header"); + + /* Place pos at the second byte of the first (farthest) compression pointer */ + if (compressed == false) + { + ++pos; + compressed = true; + } + + pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1]; + + /* Pointers can only go back */ + if (pos_ptr >= lowest_ptr) + throw Exception("Unable to unpack name - bogus compression pointer"); + lowest_ptr = pos_ptr; + } + else + { + if (pos_ptr + offset + 1 >= input_size) + throw Exception("Unable to unpack name - offset too large"); + if (!name.empty()) + name += "."; + for (unsigned i = 1; i <= offset; ++i) + name += input[pos_ptr + i]; + + pos_ptr += offset + 1; + if (compressed == false) + /* Move up pos */ + pos = pos_ptr; + } + } + + /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */ + ++pos; + + if (name.empty()) + throw Exception("Unable to unpack name - no name"); + + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Unpack name " + name); + + return name; + } + + Question UnpackQuestion(const unsigned char* input, unsigned short input_size, unsigned short& pos) + { + Question question; + + question.name = this->UnpackName(input, input_size, pos); + + if (pos + 4 > input_size) + throw Exception("Unable to unpack question"); + + question.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]); + pos += 2; + + question.qclass = input[pos] << 8 | input[pos + 1]; + pos += 2; + + return question; + } + + ResourceRecord UnpackResourceRecord(const unsigned char* input, unsigned short input_size, unsigned short& pos) + { + ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos)); + + if (pos + 6 > input_size) + throw Exception("Unable to unpack resource record"); + + record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3]; + pos += 4; + + //record.rdlength = input[pos] << 8 | input[pos + 1]; + pos += 2; + + switch (record.type) + { + case QUERY_A: + { + if (pos + 4 > input_size) + throw Exception("Unable to unpack resource record"); + + irc::sockets::sockaddrs addrs; + memset(&addrs, 0, sizeof(addrs)); + + addrs.in4.sin_family = AF_INET; + addrs.in4.sin_addr.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24); + pos += 4; + + record.rdata = addrs.addr(); + break; + } + case QUERY_AAAA: + { + if (pos + 16 > input_size) + throw Exception("Unable to unpack resource record"); + + irc::sockets::sockaddrs addrs; + memset(&addrs, 0, sizeof(addrs)); + + addrs.in6.sin6_family = AF_INET6; + for (int j = 0; j < 16; ++j) + addrs.in6.sin6_addr.s6_addr[j] = input[pos + j]; + pos += 16; + + record.rdata = addrs.addr(); + + break; + } + case QUERY_CNAME: + case QUERY_PTR: + { + record.rdata = this->UnpackName(input, input_size, pos); + break; + } + default: + break; + } + + if (!record.name.empty() && !record.rdata.empty()) + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: " + record.name + " -> " + record.rdata); + + return record; + } + + public: + static const int POINTER = 0xC0; + static const int LABEL = 0x3F; + static const int HEADER_LENGTH = 12; + + /* ID for this packet */ + unsigned short id; + /* Flags on the packet */ + unsigned short flags; + + Packet() : id(0), flags(0) + { + } + + void Fill(const unsigned char* input, const unsigned short len) + { + if (len < HEADER_LENGTH) + throw Exception("Unable to fill packet"); + + unsigned short packet_pos = 0; + + this->id = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + if (this->id >= MAX_REQUEST_ID) + throw Exception("Query ID too large?"); + + this->flags = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1]; + packet_pos += 2; + + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: qdcount: " + ConvToStr(qdcount) + " ancount: " + ConvToStr(ancount) + " nscount: " + ConvToStr(nscount) + " arcount: " + ConvToStr(arcount)); + + for (unsigned i = 0; i < qdcount; ++i) + this->questions.push_back(this->UnpackQuestion(input, len, packet_pos)); + + for (unsigned i = 0; i < ancount; ++i) + this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos)); + } + + unsigned short Pack(unsigned char* output, unsigned short output_size) + { + if (output_size < HEADER_LENGTH) + throw Exception("Unable to pack packet"); + + unsigned short pos = 0; + + output[pos++] = this->id >> 8; + output[pos++] = this->id & 0xFF; + output[pos++] = this->flags >> 8; + output[pos++] = this->flags & 0xFF; + output[pos++] = this->questions.size() >> 8; + output[pos++] = this->questions.size() & 0xFF; + output[pos++] = this->answers.size() >> 8; + output[pos++] = this->answers.size() & 0xFF; + output[pos++] = 0; + output[pos++] = 0; + output[pos++] = 0; + output[pos++] = 0; + + for (unsigned i = 0; i < this->questions.size(); ++i) + { + Question& q = this->questions[i]; + + if (q.type == QUERY_PTR) + { + irc::sockets::sockaddrs ip; + irc::sockets::aptosa(q.name, 0, ip); + + if (q.name.find(':') != std::string::npos) + { + static const char* const hex = "0123456789abcdef"; + char reverse_ip[128]; + unsigned reverse_ip_count = 0; + for (int j = 15; j >= 0; --j) + { + reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] & 0xF]; + reverse_ip[reverse_ip_count++] = '.'; + reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] >> 4]; + reverse_ip[reverse_ip_count++] = '.'; + } + reverse_ip[reverse_ip_count++] = 0; + + q.name = reverse_ip; + q.name += "ip6.arpa"; + } + else + { + unsigned long forward = ip.in4.sin_addr.s_addr; + ip.in4.sin_addr.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24; + + q.name = ip.addr() + ".in-addr.arpa"; + } + } + + this->PackName(output, output_size, pos, q.name); + + if (pos + 4 >= output_size) + throw Exception("Unable to pack packet"); + + short s = htons(q.type); + memcpy(&output[pos], &s, 2); + pos += 2; + + s = htons(q.qclass); + memcpy(&output[pos], &s, 2); + pos += 2; + } + + for (unsigned int i = 0; i < answers.size(); i++) + { + ResourceRecord& rr = answers[i]; + + this->PackName(output, output_size, pos, rr.name); + + if (pos + 8 >= output_size) + throw Exception("Unable to pack packet"); + + short s = htons(rr.type); + memcpy(&output[pos], &s, 2); + pos += 2; + + s = htons(rr.qclass); + memcpy(&output[pos], &s, 2); + pos += 2; + + long l = htonl(rr.ttl); + memcpy(&output[pos], &l, 4); + pos += 4; + + switch (rr.type) + { + case QUERY_A: + { + if (pos + 6 > output_size) + throw Exception("Unable to pack packet"); + + irc::sockets::sockaddrs a; + irc::sockets::aptosa(rr.rdata, 0, a); + + s = htons(4); + memcpy(&output[pos], &s, 2); + pos += 2; + + memcpy(&output[pos], &a.in4.sin_addr, 4); + pos += 4; + break; + } + case QUERY_AAAA: + { + if (pos + 18 > output_size) + throw Exception("Unable to pack packet"); + + irc::sockets::sockaddrs a; + irc::sockets::aptosa(rr.rdata, 0, a); + + s = htons(16); + memcpy(&output[pos], &s, 2); + pos += 2; + + memcpy(&output[pos], &a.in6.sin6_addr, 16); + pos += 16; + break; + } + case QUERY_CNAME: + case QUERY_PTR: + { + if (pos + 2 >= output_size) + throw Exception("Unable to pack packet"); + + unsigned short packet_pos_save = pos; + pos += 2; + + this->PackName(output, output_size, pos, rr.rdata); + + s = htons(pos - packet_pos_save - 2); + memcpy(&output[packet_pos_save], &s, 2); + break; + } + default: + break; + } + } + + return pos; + } +}; + +class MyManager : public Manager, public Timer, public EventHandler +{ + typedef TR1NS::unordered_map<Question, Query, Question::hash> cache_map; + cache_map cache; + + irc::sockets::sockaddrs myserver; + + static bool IsExpired(const Query& record, time_t now = ServerInstance->Time()) + { + const ResourceRecord& req = record.answers[0]; + return (req.created + static_cast<time_t>(req.ttl) < now); + } + + /** Check the DNS cache to see if request can be handled by a cached result + * @return true if a cached result was found. + */ + bool CheckCache(DNS::Request* req, const DNS::Question& question) + { + ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: cache: Checking cache for " + question.name); + + cache_map::iterator it = this->cache.find(question); + if (it == this->cache.end()) + return false; + + Query& record = it->second; + if (IsExpired(record)) + { + this->cache.erase(it); + return false; + } + + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: Using cached result for " + question.name); + record.cached = true; + req->OnLookupComplete(&record); + return true; + } + + /** Add a record to the dns cache + * @param r The record + */ + void AddCache(Query& r) + { + const ResourceRecord& rr = r.answers[0]; + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: added cache for " + rr.name + " -> " + rr.rdata + " ttl: " + ConvToStr(rr.ttl)); + this->cache[r.questions[0]] = r; + } + + public: + DNS::Request* requests[MAX_REQUEST_ID]; + + MyManager(Module* c) : Manager(c), Timer(3600, ServerInstance->Time(), true) + { + for (int i = 0; i < MAX_REQUEST_ID; ++i) + requests[i] = NULL; + ServerInstance->Timers->AddTimer(this); + } + + ~MyManager() + { + for (int i = 0; i < MAX_REQUEST_ID; ++i) + { + DNS::Request* request = requests[i]; + if (!request) + continue; + + Query rr(*request); + rr.error = ERROR_UNKNOWN; + request->OnError(&rr); + + delete request; + } + } + + void Process(DNS::Request* req) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Processing request to lookup " + req->name + " of type " + ConvToStr(req->type) + " to " + this->myserver.addr()); + + /* Create an id */ + unsigned int tries = 0; + do + { + req->id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID); + + if (++tries == DNS::MAX_REQUEST_ID*5) + { + // If we couldn't find an empty slot this many times, do a sequential scan as a last + // resort. If an empty slot is found that way, go on, otherwise throw an exception + req->id = 0; + for (int i = 1; i < DNS::MAX_REQUEST_ID; i++) + { + if (!this->requests[i]) + { + req->id = i; + break; + } + } + + if (req->id == 0) + throw Exception("DNS: All ids are in use"); + + break; + } + } + while (!req->id || this->requests[req->id]); + + this->requests[req->id] = req; + + Packet p; + p.flags = QUERYFLAGS_RD; + p.id = req->id; + p.questions.push_back(*req); + + unsigned char buffer[524]; + unsigned short len = p.Pack(buffer, sizeof(buffer)); + + /* Note that calling Pack() above can actually change the contents of p.questions[0].name, if the query is a PTR, + * to contain the value that would be in the DNS cache, which is why this is here. + */ + if (req->use_cache && this->CheckCache(req, p.questions[0])) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Using cached result"); + delete req; + return; + } + + if (ServerInstance->SE->SendTo(this, buffer, len, 0, &this->myserver.sa, this->myserver.sa_size()) != len) + throw Exception("DNS: Unable to send query"); + } + + void RemoveRequest(DNS::Request* req) + { + this->requests[req->id] = NULL; + } + + std::string GetErrorStr(Error e) + { + switch (e) + { + case ERROR_UNLOADED: + return "Module is unloading"; + case ERROR_TIMEDOUT: + return "Request timed out"; + case ERROR_NOT_AN_ANSWER: + case ERROR_NONSTANDARD_QUERY: + case ERROR_FORMAT_ERROR: + return "Malformed answer"; + case ERROR_SERVER_FAILURE: + case ERROR_NOT_IMPLEMENTED: + case ERROR_REFUSED: + case ERROR_INVALIDTYPE: + return "Nameserver failure"; + case ERROR_DOMAIN_NOT_FOUND: + case ERROR_NO_RECORDS: + return "Domain not found"; + case ERROR_NONE: + case ERROR_UNKNOWN: + default: + return "Unknown error"; + } + } + + void HandleEvent(EventType et, int) + { + if (et == EVENT_ERROR) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: UDP socket got an error event"); + return; + } + + unsigned char buffer[524]; + irc::sockets::sockaddrs from; + socklen_t x = sizeof(from); + + int length = ServerInstance->SE->RecvFrom(this, buffer, sizeof(buffer), 0, &from.sa, &x); + + if (length < Packet::HEADER_LENGTH) + return; + + Packet recv_packet; + + try + { + recv_packet.Fill(buffer, length); + } + catch (Exception& ex) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, std::string(ex.GetReason())); + return; + } + + if (myserver != from) + { + std::string server1 = from.str(); + std::string server2 = myserver.str(); + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'", + server1.c_str(), server2.c_str()); + return; + } + + DNS::Request* request = this->requests[recv_packet.id]; + if (request == NULL) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Received an answer for something we didn't request"); + return; + } + + if (recv_packet.flags & QUERYFLAGS_OPCODE) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Received a nonstandard query"); + ServerInstance->stats->statsDnsBad++; + recv_packet.error = ERROR_NONSTANDARD_QUERY; + request->OnError(&recv_packet); + } + else if (recv_packet.flags & QUERYFLAGS_RCODE) + { + Error error = ERROR_UNKNOWN; + + switch (recv_packet.flags & QUERYFLAGS_RCODE) + { + case 1: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: format error"); + error = ERROR_FORMAT_ERROR; + break; + case 2: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: server error"); + error = ERROR_SERVER_FAILURE; + break; + case 3: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: domain not found"); + error = ERROR_DOMAIN_NOT_FOUND; + break; + case 4: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: not implemented"); + error = ERROR_NOT_IMPLEMENTED; + break; + case 5: + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: refused"); + error = ERROR_REFUSED; + break; + default: + break; + } + + ServerInstance->stats->statsDnsBad++; + recv_packet.error = error; + request->OnError(&recv_packet); + } + else if (recv_packet.questions.empty() || recv_packet.answers.empty()) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: No resource records returned"); + ServerInstance->stats->statsDnsBad++; + recv_packet.error = ERROR_NO_RECORDS; + request->OnError(&recv_packet); + } + else + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Lookup complete for " + request->name); + ServerInstance->stats->statsDnsGood++; + request->OnLookupComplete(&recv_packet); + this->AddCache(recv_packet); + } + + ServerInstance->stats->statsDns++; + + /* Request's destructor removes it from the request map */ + delete request; + } + + bool Tick(time_t now) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: purging DNS cache"); + + for (cache_map::iterator it = this->cache.begin(); it != this->cache.end(); ) + { + const Query& query = it->second; + if (IsExpired(query, now)) + this->cache.erase(it++); + else + ++it; + } + return true; + } + + void Rehash(const std::string& dnsserver) + { + if (this->GetFd() > -1) + { + ServerInstance->SE->DelFd(this); + ServerInstance->SE->Shutdown(this, 2); + ServerInstance->SE->Close(this); + this->SetFd(-1); + + /* Remove expired entries from the cache */ + this->Tick(ServerInstance->Time()); + } + + irc::sockets::aptosa(dnsserver, DNS::PORT, myserver); + + /* Initialize mastersocket */ + int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0); + this->SetFd(s); + + /* Have we got a socket? */ + if (this->GetFd() != -1) + { + ServerInstance->SE->SetReuse(s); + ServerInstance->SE->NonBlocking(s); + + irc::sockets::sockaddrs bindto; + memset(&bindto, 0, sizeof(bindto)); + bindto.sa.sa_family = myserver.sa.sa_family; + + if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0) + { + /* Failed to bind */ + ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Error binding dns socket - hostnames will NOT resolve"); + ServerInstance->SE->Close(this); + this->SetFd(-1); + } + else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE)) + { + ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Internal error starting DNS - hostnames will NOT resolve."); + ServerInstance->SE->Close(this); + this->SetFd(-1); + } + } + else + { + ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Error creating DNS socket - hostnames will NOT resolve"); + } + } +}; + +class ModuleDNS : public Module +{ + MyManager manager; + std::string DNSServer; + + void FindDNSServer() + { +#ifdef _WIN32 + // attempt to look up their nameserver from the system + ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"WARNING: <dns:server> not defined, attempting to find a working server in the system settings..."); + + PFIXED_INFO pFixedInfo; + DWORD dwBufferSize = sizeof(FIXED_INFO); + pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO)); + + if (pFixedInfo) + { + if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW) + { + HeapFree(GetProcessHeap(), 0, pFixedInfo); + pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize); + } + + if (pFixedInfo) + { + if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR) + DNSServer = pFixedInfo->DnsServerList.IpAddress.String; + + HeapFree(GetProcessHeap(), 0, pFixedInfo); + } + + if (!DNSServer.empty()) + { + ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str()); + return; + } + } + + ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"No viable nameserver found! Defaulting to nameserver '127.0.0.1'!"); +#else + // attempt to look up their nameserver from /etc/resolv.conf + ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf..."); + + std::ifstream resolv("/etc/resolv.conf"); + + while (resolv >> DNSServer) + { + if (DNSServer == "nameserver") + { + resolv >> DNSServer; + if (DNSServer.find_first_not_of("0123456789.") == std::string::npos) + { + ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str()); + return; + } + } + } + + ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!"); +#endif + DNSServer = "127.0.0.1"; + } + + public: + ModuleDNS() : manager(this) + { + } + + void init() + { + Implementation i[] = { I_OnRehash, I_OnUnloadModule }; + ServerInstance->Modules->Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ServerInstance->Modules->AddService(this->manager); + + this->OnRehash(NULL); + } + + void OnRehash(User* user) + { + std::string oldserver = DNSServer; + DNSServer = ServerInstance->Config->ConfValue("dns")->getString("server"); + if (DNSServer.empty()) + FindDNSServer(); + + if (oldserver != DNSServer) + this->manager.Rehash(DNSServer); + } + + void OnUnloadModule(Module* mod) + { + for (int i = 0; i < MAX_REQUEST_ID; ++i) + { + DNS::Request* req = this->manager.requests[i]; + if (!req) + continue; + + if (req->creator == mod) + { + Query rr(*req); + rr.error = ERROR_UNLOADED; + req->OnError(&rr); + + delete req; + } + } + } + + Version GetVersion() + { + return Version("DNS support", VF_CORE|VF_VENDOR); + } +}; + +MODULE_INIT(ModuleDNS) + diff --git a/src/commands/cmd_hostname_lookup.cpp b/src/commands/cmd_hostname_lookup.cpp new file mode 100644 index 000000000..2b9b30d73 --- /dev/null +++ b/src/commands/cmd_hostname_lookup.cpp @@ -0,0 +1,249 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013 Adam <Adam@anope.org> + * + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "inspircd.h" +#include "modules/dns.h" + +namespace +{ + LocalIntExt* dl; + LocalStringExt* ph; +} + +/** Derived from Resolver, and performs user forward/reverse lookups. + */ +class CoreExport UserResolver : public DNS::Request +{ + /** UUID we are looking up */ + const std::string uuid; + + /** True if the lookup is forward, false if is a reverse lookup + */ + const bool fwd; + + public: + /** Create a resolver. + * @param mgr DNS Manager + * @param me this module + * @param user The user to begin lookup on + * @param to_resolve The IP or host to resolve + * @param qt The query type + */ + UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt) + : DNS::Request(mgr, me, to_resolve, qt) + , uuid(user->uuid) + , fwd(qt == DNS::QUERY_A || qt == DNS::QUERY_AAAA) + { + } + + /** Called on successful lookup + * if a previous result has already come back. + * @param r The finished query + */ + void OnLookupComplete(const DNS::Query* r) + { + LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid); + if (!bound_user) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str()); + return; + } + + const DNS::ResourceRecord& ans_record = r->answers[0]; + + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), ans_record.name.c_str(), ans_record.rdata.c_str()); + + if (!fwd) + { + // first half of resolution is done. We now need to verify that the host matches. + ph->set(bound_user, ans_record.rdata); + + UserResolver* res_forward; + if (bound_user->client_sa.sa.sa_family == AF_INET6) + { + /* IPV6 forward lookup */ + res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record.rdata, DNS::QUERY_AAAA); + } + else + { + /* IPV4 lookup */ + res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record.rdata, DNS::QUERY_A); + } + try + { + this->manager->Process(res_forward); + } + catch (DNS::Exception& e) + { + delete res_forward; + ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG,"Error in resolver: %s",e.GetReason()); + + bound_user->WriteServ("NOTICE Auth :*** There was an internal error resolving your host, using your IP address (%s) instead.", bound_user->GetIPString().c_str()); + dl->set(bound_user, 0); + } + } + else + { + /* Both lookups completed */ + + irc::sockets::sockaddrs* user_ip = &bound_user->client_sa; + bool rev_match = false; + if (user_ip->sa.sa_family == AF_INET6) + { + struct in6_addr res_bin; + if (inet_pton(AF_INET6, ans_record.rdata.c_str(), &res_bin)) + { + rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin)); + } + } + else + { + struct in_addr res_bin; + if (inet_pton(AF_INET, ans_record.rdata.c_str(), &res_bin)) + { + rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin)); + } + } + + dl->set(bound_user, 0); + + if (rev_match) + { + std::string* hostname = ph->get(bound_user); + + if (hostname == NULL) + { + ServerInstance->Logs->Log("RESOLVER", LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup"); + bound_user->WriteServ("NOTICE Auth :*** There was an internal error resolving your host, using your IP address (%s) instead.", bound_user->GetIPString().c_str()); + return; + } + else if (hostname->length() < 65) + { + /* Hostnames starting with : are not a good thing (tm) */ + if ((*hostname)[0] == ':') + hostname->insert(0, "0"); + + bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname->c_str(), (r->cached ? " -- cached" : "")); + bound_user->host.assign(*hostname, 0, 64); + bound_user->dhost = bound_user->host; + + /* Invalidate cache */ + bound_user->InvalidateCache(); + } + else + { + bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", bound_user->GetIPString().c_str()); + } + + ph->unset(bound_user); + } + else + { + bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", bound_user->GetIPString().c_str()); + } + } + } + + /** Called on failed lookup + * @param query The errored query + */ + void OnError(const DNS::Query* query) + { + LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid); + if (bound_user) + { + bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", this->manager->GetErrorStr(query->error).c_str(), bound_user->GetIPString().c_str()); + dl->set(bound_user, 0); + ServerInstance->stats->statsDnsBad++; + } + } +}; + +class ModuleHostnameLookup : public Module +{ + bool nouserdns; + LocalIntExt dnsLookup; + LocalStringExt ptrHosts; + dynamic_reference<DNS::Manager> DNS; + + public: + ModuleHostnameLookup() + : dnsLookup("dnsLookup", this) + , ptrHosts("ptrHosts", this) + , DNS(this, "DNS") + { + dl = &dnsLookup; + ph = &ptrHosts; + } + + void init() + { + OnRehash(NULL); + ServerInstance->Modules->AddService(this->dnsLookup); + ServerInstance->Modules->AddService(this->ptrHosts); + + Implementation i[] = { I_OnUserInit, I_OnCheckReady, I_OnRehash }; + ServerInstance->Modules->Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + void OnRehash(User* user) + { + nouserdns = ServerInstance->Config->ConfValue("performance")->getBool("nouserdns"); + } + + void OnUserInit(LocalUser *user) + { + if (!DNS || nouserdns) + { + user->WriteServ("NOTICE %s :*** Skipping host resolution (disabled by server administrator)", user->nick.c_str()); + return; + } + + user->WriteServ("NOTICE Auth :*** Looking up your hostname..."); + + UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR); + try + { + /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely + * before Process() completes, which is why dnsLookup.set() is here, before Process() + */ + this->dnsLookup.set(user, 1); + this->DNS->Process(res_reverse); + } + catch (DNS::Exception& e) + { + this->dnsLookup.set(user, 0); + delete res_reverse; + ServerInstance->Logs->Log("USERS", LOG_DEBUG,"Error in resolver: %s", e.GetReason()); + ServerInstance->stats->statsDnsBad++; + } + } + + ModResult OnCheckReady(LocalUser* user) + { + return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU; + } + + Version GetVersion() + { + return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR); + } +}; + +MODULE_INIT(ModuleHostnameLookup) diff --git a/src/configreader.cpp b/src/configreader.cpp index 70e8cea78..32e8966fa 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -29,14 +29,10 @@ #include "exitcodes.h" #include "configparser.h" #include <iostream> -#ifdef _WIN32 -#include <Iphlpapi.h> -#pragma comment(lib, "Iphlpapi.lib") -#endif ServerConfig::ServerConfig() { - RawLog = NoUserDns = HideBans = HideSplits = UndernetMsgPrefix = false; + RawLog = HideBans = HideSplits = UndernetMsgPrefix = false; WildcardIPv6 = CycleHosts = InvBypassModes = true; dns_timeout = 5; MaxTargets = 20; @@ -60,14 +56,6 @@ static void range(T& value, V min, V max, V def, const char* msg) value = def; } - -static void ValidIP(const std::string& ip, const std::string& key) -{ - irc::sockets::sockaddrs dummy; - if (!irc::sockets::aptosa(ip, 0, dummy)) - throw CoreException("The value of "+key+" is not an IP address"); -} - static void ValidHost(const std::string& p, const std::string& msg) { int num_dots = 0; @@ -109,64 +97,6 @@ bool ServerConfig::ApplyDisabledCommands(const std::string& data) return true; } -static void FindDNS(std::string& server) -{ - if (!server.empty()) - return; -#ifdef _WIN32 - // attempt to look up their nameserver from the system - ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"WARNING: <dns:server> not defined, attempting to find a working server in the system settings..."); - - PFIXED_INFO pFixedInfo; - DWORD dwBufferSize = sizeof(FIXED_INFO); - pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO)); - - if(pFixedInfo) - { - if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW) { - HeapFree(GetProcessHeap(), 0, pFixedInfo); - pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize); - } - - if(pFixedInfo) { - if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR) - server = pFixedInfo->DnsServerList.IpAddress.String; - - HeapFree(GetProcessHeap(), 0, pFixedInfo); - } - - if(!server.empty()) - { - ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"<dns:server> set to '%s' as first active resolver in the system settings.", server.c_str()); - return; - } - } - - ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"No viable nameserver found! Defaulting to nameserver '127.0.0.1'!"); -#else - // attempt to look up their nameserver from /etc/resolv.conf - ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf..."); - - std::ifstream resolv("/etc/resolv.conf"); - - while (resolv >> server) - { - if (server == "nameserver") - { - resolv >> server; - if (server.find_first_not_of("0123456789.") == std::string::npos) - { - ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",server.c_str()); - return; - } - } - } - - ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!"); -#endif - server = "127.0.0.1"; -} - static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make) { ConfigTagList tags = conf->ConfTags(tag); @@ -474,7 +404,6 @@ void ServerConfig::Fill() HideKillsServer = security->getString("hidekills"); RestrictBannedUsers = security->getBool("restrictbannedusers", true); GenericOper = security->getBool("genericoper"); - NoUserDns = ConfValue("performance")->getBool("nouserdns"); SyntaxHints = options->getBool("syntaxhints"); CycleHosts = options->getBool("cyclehosts"); CycleHostsFromUser = options->getBool("cyclehostsfromuser"); @@ -504,8 +433,6 @@ void ServerConfig::Fill() range(MaxTargets, 1, 31, 20, "<security:maxtargets>"); range(NetBufferSize, 1024, 65534, 10240, "<performance:netbuffersize>"); - ValidIP(DNSServer, "<dns:server>"); - std::string defbind = options->getString("defaultbind"); if (assign(defbind) == "ipv4") { @@ -599,11 +526,6 @@ void ServerConfig::Read() valid = false; errstr << err.GetReason(); } - if (valid) - { - DNSServer = ConfValue("dns")->getString("server"); - FindDNS(DNSServer); - } } void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) @@ -891,7 +813,6 @@ void ConfigReaderThread::Finish() */ ServerInstance->XLines->CheckELines(); ServerInstance->XLines->ApplyLines(); - ServerInstance->Res->Rehash(); ModeReference ban(NULL, "ban"); static_cast<ListModeBase*>(*ban)->DoRehash(); Config->ApplyDisabledCommands(Config->DisabledCommands); diff --git a/src/dns.cpp b/src/dns.cpp deleted file mode 100644 index b9c605e78..000000000 --- a/src/dns.cpp +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2012 William Pitcock <nenolod@dereferenced.org> - * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2006, 2009 Robin Burchell <robin+git@viroteck.net> - * Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org> - * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org> - * Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc> - * - * This file is part of InspIRCd. InspIRCd is free software: you can - * redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -/* $Core */ - -/* -dns.cpp - Nonblocking DNS functions. -Very very loosely based on the firedns library, -Copyright (C) 2002 Ian Gulliver. This file is no -longer anything like firedns, there are many major -differences between this code and the original. -Please do not assume that firedns works like this, -looks like this, walks like this or tastes like this. -*/ - -#ifndef _WIN32 -#include <sys/types.h> -#include <sys/socket.h> -#include <errno.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#else -#include "inspircd_win32wrapper.h" -#endif - -#include "inspircd.h" -#include "socketengine.h" -#include "configreader.h" -#include "socket.h" - -#define DN_COMP_BITMASK 0xC000 /* highest 6 bits in a DN label header */ - -/** Masks to mask off the responses we get from the DNSRequest methods - */ -enum QueryInfo -{ - ERROR_MASK = 0x10000 /* Result is an error */ -}; - -/** Flags which can be ORed into a request or reply for different meanings - */ -enum QueryFlags -{ - FLAGS_MASK_RD = 0x01, /* Recursive */ - FLAGS_MASK_TC = 0x02, - FLAGS_MASK_AA = 0x04, /* Authoritative */ - FLAGS_MASK_OPCODE = 0x78, - FLAGS_MASK_QR = 0x80, - FLAGS_MASK_RCODE = 0x0F, /* Request */ - FLAGS_MASK_Z = 0x70, - FLAGS_MASK_RA = 0x80 -}; - - -/** Represents a dns resource record (rr) - */ -struct ResourceRecord -{ - QueryType type; /* Record type */ - unsigned int rr_class; /* Record class */ - unsigned long ttl; /* Time to live */ - unsigned int rdlength; /* Record length */ -}; - -/** Represents a dns request/reply header, and its payload as opaque data. - */ -class DNSHeader -{ - public: - unsigned char id[2]; /* Request id */ - unsigned int flags1; /* Flags */ - unsigned int flags2; /* Flags */ - unsigned int qdcount; - unsigned int ancount; /* Answer count */ - unsigned int nscount; /* Nameserver count */ - unsigned int arcount; - unsigned char payload[512]; /* Packet payload */ -}; - -class DNSRequest -{ - public: - unsigned char id[2]; /* Request id */ - unsigned char* res; /* Result processing buffer */ - unsigned int rr_class; /* Request class */ - QueryType type; /* Request type */ - DNS* dnsobj; /* DNS caller (where we get our FD from) */ - unsigned long ttl; /* Time to live */ - std::string orig; /* Original requested name/ip */ - - DNSRequest(DNS* dns, int id, const std::string &original); - ~DNSRequest(); - DNSInfo ResultIsReady(DNSHeader &h, unsigned length); - int SendRequests(const DNSHeader *header, const int length, QueryType qt); -}; - -class CacheTimer : public Timer -{ - private: - DNS* dns; - public: - CacheTimer(DNS* thisdns) - : Timer(3600, ServerInstance->Time(), true), dns(thisdns) { } - - virtual bool Tick(time_t) - { - dns->PruneCache(); - return true; - } -}; - -class RequestTimeout : public Timer -{ - DNSRequest* watch; - int watchid; - public: - RequestTimeout(unsigned long n, DNSRequest* watching, int id) : Timer(n, ServerInstance->Time()), watch(watching), watchid(id) - { - } - ~RequestTimeout() - { - if (ServerInstance->Res) - Tick(0); - } - - bool Tick(time_t) - { - if (ServerInstance->Res->requests[watchid] == watch) - { - /* Still exists, whack it */ - if (ServerInstance->Res->Classes[watchid]) - { - ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out"); - delete ServerInstance->Res->Classes[watchid]; - ServerInstance->Res->Classes[watchid] = NULL; - } - ServerInstance->Res->requests[watchid] = NULL; - delete watch; - } - return false; - } -}; - -CachedQuery::CachedQuery(const std::string &res, unsigned int ttl) : data(res) -{ - expires = ServerInstance->Time() + ttl; -} - -int CachedQuery::CalcTTLRemaining() -{ - int n = expires - ServerInstance->Time(); - return (n < 0 ? 0 : n); -} - -/* Allocate the processing buffer */ -DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns) -{ - /* hardening against overflow here: make our work buffer twice the theoretical - * maximum size so that hostile input doesn't screw us over. - */ - res = new unsigned char[sizeof(DNSHeader) * 2]; - *res = 0; - orig = original; - RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid); - ServerInstance->Timers->AddTimer(RT); /* The timer manager frees this */ -} - -/* Deallocate the processing buffer */ -DNSRequest::~DNSRequest() -{ - delete[] res; -} - -/** Fill a ResourceRecord class based on raw data input */ -inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input) -{ - rr->type = (QueryType)((input[0] << 8) + input[1]); - rr->rr_class = (input[2] << 8) + input[3]; - rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7]; - rr->rdlength = (input[8] << 8) + input[9]; -} - -/** Fill a DNSHeader class based on raw data input of a given length */ -inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length) -{ - header->id[0] = input[0]; - header->id[1] = input[1]; - header->flags1 = input[2]; - header->flags2 = input[3]; - header->qdcount = (input[4] << 8) + input[5]; - header->ancount = (input[6] << 8) + input[7]; - header->nscount = (input[8] << 8) + input[9]; - header->arcount = (input[10] << 8) + input[11]; - memcpy(header->payload,&input[12],length); -} - -/** Empty a DNSHeader class out into raw data, ready for transmission */ -inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length) -{ - output[0] = header->id[0]; - output[1] = header->id[1]; - output[2] = header->flags1; - output[3] = header->flags2; - output[4] = header->qdcount >> 8; - output[5] = header->qdcount & 0xFF; - output[6] = header->ancount >> 8; - output[7] = header->ancount & 0xFF; - output[8] = header->nscount >> 8; - output[9] = header->nscount & 0xFF; - output[10] = header->arcount >> 8; - output[11] = header->arcount & 0xFF; - memcpy(&output[12],header->payload,length); -} - -/** Send requests we have previously built down the UDP socket */ -int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt) -{ - ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG,"DNSRequest::SendRequests"); - - unsigned char payload[sizeof(DNSHeader)]; - - this->rr_class = 1; - this->type = qt; - - DNS::EmptyHeader(payload,header,length); - - if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, &(dnsobj->myserver.sa), sa_size(dnsobj->myserver)) != length+12) - return -1; - - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"Sent OK"); - return 0; -} - -/** Add a query with a predefined header, and allocate an ID for it. */ -DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original) -{ - /* Is the DNS connection down? */ - if (this->GetFd() == -1) - return NULL; - - /* Create an id */ - unsigned int tries = 0; - do { - id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID); - if (++tries == DNS::MAX_REQUEST_ID*5) - { - // If we couldn't find an empty slot this many times, do a sequential scan as a last - // resort. If an empty slot is found that way, go on, otherwise throw an exception - id = -1; - for (int i = 0; i < DNS::MAX_REQUEST_ID; i++) - { - if (!requests[i]) - { - id = i; - break; - } - } - - if (id == -1) - throw ModuleException("DNS: All ids are in use"); - - break; - } - } while (requests[id]); - - DNSRequest* req = new DNSRequest(this, id, original); - - header->id[0] = req->id[0] = id >> 8; - header->id[1] = req->id[1] = id & 0xFF; - header->flags1 = FLAGS_MASK_RD; - header->flags2 = 0; - header->qdcount = 1; - header->ancount = 0; - header->nscount = 0; - header->arcount = 0; - - /* At this point we already know the id doesnt exist, - * so there needs to be no second check for the ::end() - */ - requests[id] = req; - - /* According to the C++ spec, new never returns NULL. */ - return req; -} - -int DNS::ClearCache() -{ - /* This ensures the buckets are reset to sane levels */ - int rv = this->cache->size(); - delete this->cache; - this->cache = new dnscache(); - return rv; -} - -int DNS::PruneCache() -{ - int n = 0; - dnscache* newcache = new dnscache(); - for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++) - /* Dont include expired items (theres no point) */ - if (i->second.CalcTTLRemaining()) - newcache->insert(*i); - else - n++; - - delete this->cache; - this->cache = newcache; - return n; -} - -void DNS::Rehash() -{ - if (this->GetFd() > -1) - { - ServerInstance->SE->DelFd(this); - ServerInstance->SE->Shutdown(this, 2); - ServerInstance->SE->Close(this); - this->SetFd(-1); - - /* Rehash the cache */ - this->PruneCache(); - } - else - { - /* Create initial dns cache */ - this->cache = new dnscache(); - } - - irc::sockets::aptosa(ServerInstance->Config->DNSServer, DNS::QUERY_PORT, myserver); - - /* Initialize mastersocket */ - int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0); - this->SetFd(s); - - /* Have we got a socket and is it nonblocking? */ - if (this->GetFd() != -1) - { - ServerInstance->SE->SetReuse(s); - ServerInstance->SE->NonBlocking(s); - irc::sockets::sockaddrs bindto; - memset(&bindto, 0, sizeof(bindto)); - bindto.sa.sa_family = myserver.sa.sa_family; - if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0) - { - /* Failed to bind */ - ServerInstance->Logs->Log("RESOLVER",LOG_SPARSE,"Error binding dns socket - hostnames will NOT resolve"); - ServerInstance->SE->Shutdown(this, 2); - ServerInstance->SE->Close(this); - this->SetFd(-1); - } - else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE)) - { - ServerInstance->Logs->Log("RESOLVER",LOG_SPARSE,"Internal error starting DNS - hostnames will NOT resolve."); - ServerInstance->SE->Shutdown(this, 2); - ServerInstance->SE->Close(this); - this->SetFd(-1); - } - } - else - { - ServerInstance->Logs->Log("RESOLVER",LOG_SPARSE,"Error creating DNS socket - hostnames will NOT resolve"); - } -} - -/** Initialise the DNS UDP socket so that we can send requests */ -DNS::DNS() -{ - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"DNS::DNS"); - /* Clear the Resolver class table */ - memset(Classes,0,sizeof(Classes)); - - /* Clear the requests class table */ - memset(requests,0,sizeof(requests)); - - /* DNS::Rehash() sets this to a valid ptr - */ - this->cache = NULL; - - /* Again, DNS::Rehash() sets this to a - * valid value - */ - this->SetFd(-1); - - /* Actually read the settings - */ - this->Rehash(); - - this->PruneTimer = new CacheTimer(this); - - ServerInstance->Timers->AddTimer(this->PruneTimer); -} - -/** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */ -int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload) -{ - short payloadpos = 0; - const char* tempchr, *tempchr2 = name; - unsigned short length; - - /* split name up into labels, create query */ - while ((tempchr = strchr(tempchr2,'.')) != NULL) - { - length = tempchr - tempchr2; - if (payloadpos + length + 1 > 507) - return -1; - payload[payloadpos++] = length; - memcpy(&payload[payloadpos],tempchr2,length); - payloadpos += length; - tempchr2 = &tempchr[1]; - } - length = strlen(tempchr2); - if (length) - { - if (payloadpos + length + 2 > 507) - return -1; - payload[payloadpos++] = length; - memcpy(&payload[payloadpos],tempchr2,length); - payloadpos += length; - payload[payloadpos++] = 0; - } - if (payloadpos > 508) - return -1; - length = htons(rr); - memcpy(&payload[payloadpos],&length,2); - length = htons(rr_class); - memcpy(&payload[payloadpos + 2],&length,2); - return payloadpos + 4; -} - -/** Start lookup of an hostname to an IP address */ -int DNS::GetIP(const char *name) -{ - DNSHeader h; - int id; - int length; - - if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1) - return -1; - - DNSRequest* req = this->AddQuery(&h, id, name); - - if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1)) - return -1; - - return id; -} - -/** Start lookup of an hostname to an IPv6 address */ -int DNS::GetIP6(const char *name) -{ - DNSHeader h; - int id; - int length; - - if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1) - return -1; - - DNSRequest* req = this->AddQuery(&h, id, name); - - if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1)) - return -1; - - return id; -} - -/** Start lookup of a cname to another name */ -int DNS::GetCName(const char *alias) -{ - DNSHeader h; - int id; - int length; - - if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1) - return -1; - - DNSRequest* req = this->AddQuery(&h, id, alias); - - if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1)) - return -1; - - return id; -} - -/** Start lookup of an IP address to a hostname */ -int DNS::GetNameForce(const char *ip, ForceProtocol fp) -{ - char query[128]; - DNSHeader h; - int id; - int length; - - if (fp == PROTOCOL_IPV6) - { - in6_addr i; - if (inet_pton(AF_INET6, ip, &i) > 0) - { - DNS::MakeIP6Int(query, &i); - } - else - { - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"DNS::GetNameForce IPv6 bad format for '%s'", ip); - /* Invalid IP address */ - return -1; - } - } - else - { - in_addr i; - if (inet_aton(ip, &i)) - { - unsigned char* c = (unsigned char*)&i.s_addr; - sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]); - } - else - { - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"DNS::GetNameForce IPv4 bad format for '%s'", ip); - /* Invalid IP address */ - return -1; - } - } - - length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload); - if (length == -1) - { - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"DNS::GetNameForce can't query '%s' using '%s' because it's too long", ip, query); - return -1; - } - - DNSRequest* req = this->AddQuery(&h, id, ip); - - if (!req) - { - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"DNS::GetNameForce can't add query (resolver down?)"); - return -1; - } - - if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1) - { - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"DNS::GetNameForce can't send (firewall?)"); - return -1; - } - - return id; -} - -/** Build an ipv6 reverse domain from an in6_addr - */ -void DNS::MakeIP6Int(char* query, const in6_addr *ip) -{ - const char* hex = "0123456789abcdef"; - for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */ - { - if (index % 2) - /* low nibble */ - *query++ = hex[ip->s6_addr[index / 2] & 0x0F]; - else - /* high nibble */ - *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4]; - *query++ = '.'; /* Seperator */ - } - strcpy(query,"ip6.arpa"); /* Suffix the string */ -} - -/** Return the next id which is ready, and the result attached to it */ -DNSResult DNS::GetResult() -{ - /* Fetch dns query response and decide where it belongs */ - DNSHeader header; - DNSRequest *req; - unsigned char buffer[sizeof(DNSHeader)]; - irc::sockets::sockaddrs from; - memset(&from, 0, sizeof(from)); - socklen_t x = sizeof(from); - - int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x); - - /* Did we get the whole header? */ - if (length < 12) - { - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"GetResult didn't get a full packet (len=%d)", length); - /* Nope - something screwed up. */ - return DNSResult(-1,"",0,""); - } - - /* Check wether the reply came from a different DNS - * server to the one we sent it to, or the source-port - * is not 53. - * A user could in theory still spoof dns packets anyway - * but this is less trivial than just sending garbage - * to the server, which is possible without this check. - * - * -- Thanks jilles for pointing this one out. - */ - if (from != myserver) - { - std::string server1 = from.str(); - std::string server2 = myserver.str(); - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'", - server1.c_str(), server2.c_str()); - return DNSResult(-1,"",0,""); - } - - /* Put the read header info into a header class */ - DNS::FillHeader(&header,buffer,length - 12); - - /* Get the id of this request. - * Its a 16 bit value stored in two char's, - * so we use logic shifts to create the value. - */ - unsigned long this_id = header.id[1] + (header.id[0] << 8); - - /* Do we have a pending request matching this id? */ - if (!requests[this_id]) - { - /* Somehow we got a DNS response for a request we never made... */ - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id); - return DNSResult(-1,"",0,""); - } - else - { - /* Remove the query from the list of pending queries */ - req = requests[this_id]; - requests[this_id] = NULL; - } - - /* Inform the DNSRequest class that it has a result to be read. - * When its finished it will return a DNSInfo which is a pair of - * unsigned char* resource record data, and an error message. - */ - DNSInfo data = req->ResultIsReady(header, length); - std::string resultstr; - - /* Check if we got a result, if we didnt, its an error */ - if (data.first == NULL) - { - /* An error. - * Mask the ID with the value of ERROR_MASK, so that - * the dns_deal_with_classes() function knows that its - * an error response and needs to be treated uniquely. - * Put the error message in the second field. - */ - std::string ro = req->orig; - delete req; - return DNSResult(this_id | ERROR_MASK, data.second, 0, ro); - } - else - { - unsigned long ttl = req->ttl; - char formatted[128]; - - /* Forward lookups come back as binary data. We must format them into ascii */ - switch (req->type) - { - case DNS_QUERY_A: - snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]); - resultstr = formatted; - break; - - case DNS_QUERY_AAAA: - { - inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)); - char* c = strstr(formatted,":0:"); - if (c != NULL) - { - memmove(c+1,c+2,strlen(c+2) + 1); - c += 2; - while (memcmp(c,"0:",2) == 0) - memmove(c,c+2,strlen(c+2) + 1); - if (memcmp(c,"0",2) == 0) - *c = 0; - if (memcmp(formatted,"0::",3) == 0) - memmove(formatted,formatted + 1, strlen(formatted + 1) + 1); - } - resultstr = formatted; - - /* Special case. Sending ::1 around between servers - * and to clients is dangerous, because the : on the - * start makes the client or server interpret the IP - * as the last parameter on the line with a value ":1". - */ - if (*formatted == ':') - resultstr.insert(0, "0"); - } - break; - - case DNS_QUERY_CNAME: - /* Identical handling to PTR */ - - case DNS_QUERY_PTR: - /* Reverse lookups just come back as char* */ - resultstr = std::string((const char*)data.first); - break; - - default: - break; - } - - /* Build the reply with the id and hostname/ip in it */ - std::string ro = req->orig; - delete req; - return DNSResult(this_id,resultstr,ttl,ro); - } -} - -/** A result is ready, process it */ -DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, unsigned length) -{ - unsigned i = 0, o; - int q = 0; - int curanswer; - ResourceRecord rr; - unsigned short ptr; - - /* This is just to keep _FORTIFY_SOURCE happy */ - rr.type = DNS_QUERY_NONE; - rr.rdlength = 0; - rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */ - rr.rr_class = 0; /* Same for VC++ */ - - if (!(header.flags1 & FLAGS_MASK_QR)) - return std::make_pair((unsigned char*)NULL,"Not a query result"); - - if (header.flags1 & FLAGS_MASK_OPCODE) - return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet"); - - if (header.flags2 & FLAGS_MASK_RCODE) - return std::make_pair((unsigned char*)NULL,"Domain name not found"); - - if (header.ancount < 1) - return std::make_pair((unsigned char*)NULL,"No resource records returned"); - - /* Subtract the length of the header from the length of the packet */ - length -= 12; - - while ((unsigned int)q < header.qdcount && i < length) - { - if (header.payload[i] > 63) - { - i += 6; - q++; - } - else - { - if (header.payload[i] == 0) - { - q++; - i += 5; - } - else i += header.payload[i] + 1; - } - } - curanswer = 0; - while ((unsigned)curanswer < header.ancount) - { - q = 0; - while (q == 0 && i < length) - { - if (header.payload[i] > 63) - { - i += 2; - q = 1; - } - else - { - if (header.payload[i] == 0) - { - i++; - q = 1; - } - else i += header.payload[i] + 1; /* skip length and label */ - } - } - if (static_cast<int>(length - i) < 10) - return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply"); - - /* XXX: We actually initialise 'rr' here including its ttl field */ - DNS::FillResourceRecord(&rr,&header.payload[i]); - - i += 10; - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"Resolver: rr.type is %d and this.type is %d rr.class %d this.class %d", rr.type, this->type, rr.rr_class, this->rr_class); - if (rr.type != this->type) - { - curanswer++; - i += rr.rdlength; - continue; - } - if (rr.rr_class != this->rr_class) - { - curanswer++; - i += rr.rdlength; - continue; - } - break; - } - if ((unsigned int)curanswer == header.ancount) - return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)"); - - if (i + rr.rdlength > (unsigned int)length) - return std::make_pair((unsigned char*)NULL,"Resource record larger than stated"); - - if (rr.rdlength > 1023) - return std::make_pair((unsigned char*)NULL,"Resource record too large"); - - this->ttl = rr.ttl; - - switch (rr.type) - { - /* - * CNAME and PTR are compressed. We need to decompress them. - */ - case DNS_QUERY_CNAME: - case DNS_QUERY_PTR: - { - unsigned short lowest_pos = length; - o = 0; - q = 0; - while (q == 0 && i < length && o + 256 < 1023) - { - /* DN label found (byte over 63) */ - if (header.payload[i] > 63) - { - memcpy(&ptr,&header.payload[i],2); - - i = ntohs(ptr); - - /* check that highest two bits are set. if not, we've been had */ - if ((i & DN_COMP_BITMASK) != DN_COMP_BITMASK) - return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus"); - - /* mask away the two highest bits. */ - i &= ~DN_COMP_BITMASK; - - /* and decrease length by 12 bytes. */ - i -= 12; - - if (i >= lowest_pos) - return std::make_pair((unsigned char *) NULL, "Invalid decompression pointer"); - lowest_pos = i; - } - else - { - if (header.payload[i] == 0) - { - q = 1; - } - else - { - res[o] = 0; - if (o != 0) - res[o++] = '.'; - - if (o + header.payload[i] > sizeof(DNSHeader)) - return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?"); - - memcpy(&res[o], &header.payload[i + 1], header.payload[i]); - o += header.payload[i]; - i += header.payload[i] + 1; - } - } - } - res[o] = 0; - } - break; - case DNS_QUERY_AAAA: - if (rr.rdlength != sizeof(struct in6_addr)) - return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet?"); - - memcpy(res,&header.payload[i],rr.rdlength); - res[rr.rdlength] = 0; - break; - case DNS_QUERY_A: - if (rr.rdlength != sizeof(struct in_addr)) - return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet?"); - - memcpy(res,&header.payload[i],rr.rdlength); - res[rr.rdlength] = 0; - break; - default: - return std::make_pair((unsigned char *) NULL, "don't know how to handle undefined type (" + ConvToStr(rr.type) + ") -- rejecting"); - break; - } - return std::make_pair(res,"No error"); -} - -/** Close the master socket */ -DNS::~DNS() -{ - ServerInstance->SE->Shutdown(this, 2); - ServerInstance->SE->Close(this); - ServerInstance->Timers->DelTimer(this->PruneTimer); - if (cache) - delete cache; -} - -CachedQuery* DNS::GetCache(const std::string &source) -{ - dnscache::iterator x = cache->find(source.c_str()); - if (x != cache->end()) - return &(x->second); - else - return NULL; -} - -void DNS::DelCache(const std::string &source) -{ - cache->erase(source.c_str()); -} - -void Resolver::TriggerCachedResult() -{ - if (CQ) - OnLookupComplete(CQ->data, time_left, true); -} - -/** High level abstraction of dns used by application at large */ -Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt) -{ - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"Resolver::Resolver"); - cached = false; - - CQ = ServerInstance->Res->GetCache(source); - if (CQ) - { - time_left = CQ->CalcTTLRemaining(); - if (!time_left) - { - ServerInstance->Res->DelCache(source); - } - else - { - cached = true; - return; - } - } - - switch (querytype) - { - case DNS_QUERY_A: - this->myid = ServerInstance->Res->GetIP(source.c_str()); - break; - - case DNS_QUERY_PTR4: - querytype = DNS_QUERY_PTR; - this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4); - break; - - case DNS_QUERY_PTR6: - querytype = DNS_QUERY_PTR; - this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6); - break; - - case DNS_QUERY_AAAA: - this->myid = ServerInstance->Res->GetIP6(source.c_str()); - break; - - case DNS_QUERY_CNAME: - this->myid = ServerInstance->Res->GetCName(source.c_str()); - break; - - default: - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"DNS request with unknown query type %d", querytype); - this->myid = -1; - break; - } - if (this->myid == -1) - { - throw ModuleException("Resolver: Couldn't get an id to make a request"); - } - else - { - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"DNS request id %d", this->myid); - } -} - -/** Called when an error occurs */ -void Resolver::OnError(ResolverError, const std::string&) -{ - /* Nothing in here */ -} - -/** Destroy a resolver */ -Resolver::~Resolver() -{ - /* Nothing here (yet) either */ -} - -/** Get the request id associated with this class */ -int Resolver::GetId() -{ - return this->myid; -} - -Module* Resolver::GetCreator() -{ - return this->Creator; -} - -/** Process a socket read event */ -void DNS::HandleEvent(EventType, int) -{ - /* Fetch the id and result of the next available packet */ - DNSResult res(0,"",0,""); - res.id = 0; - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"Handle DNS event"); - - res = this->GetResult(); - - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"Result id %d", res.id); - - /* Is there a usable request id? */ - if (res.id != -1) - { - /* Its an error reply */ - if (res.id & ERROR_MASK) - { - /* Mask off the error bit */ - res.id -= ERROR_MASK; - /* Marshall the error to the correct class */ - if (Classes[res.id]) - { - if (ServerInstance && ServerInstance->stats) - ServerInstance->stats->statsDnsBad++; - Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result); - delete Classes[res.id]; - Classes[res.id] = NULL; - } - return; - } - else - { - /* It is a non-error result, marshall the result to the correct class */ - if (Classes[res.id]) - { - if (ServerInstance && ServerInstance->stats) - ServerInstance->stats->statsDnsGood++; - - if (!this->GetCache(res.original.c_str())) - this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl))); - - Classes[res.id]->OnLookupComplete(res.result, res.ttl, false); - delete Classes[res.id]; - Classes[res.id] = NULL; - } - } - - if (ServerInstance && ServerInstance->stats) - ServerInstance->stats->statsDns++; - } -} - -/** Add a derived Resolver to the working set */ -bool DNS::AddResolverClass(Resolver* r) -{ - ServerInstance->Logs->Log("RESOLVER",LOG_DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r); - /* Check the pointers validity and the id's validity */ - if ((r) && (r->GetId() > -1)) - { - /* Check the slot isnt already occupied - - * This should NEVER happen unless we have - * a severely broken DNS server somewhere - */ - if (!Classes[r->GetId()]) - { - /* Set up the pointer to the class */ - Classes[r->GetId()] = r; - return true; - } - } - - /* Pointer or id not valid, or duplicate id. - * Free the item and return - */ - delete r; - return false; -} - -void DNS::CleanResolvers(Module* module) -{ - for (int i = 0; i < MAX_REQUEST_ID; i++) - { - if (Classes[i]) - { - if (Classes[i]->GetCreator() == module) - { - Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading"); - delete Classes[i]; - Classes[i] = NULL; - } - } - } -} diff --git a/src/inspircd.cpp b/src/inspircd.cpp index aa9bfb65c..7b3d36e1c 100644 --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@ -132,8 +132,6 @@ void InspIRCd::Cleanup() /* Must be deleted before modes as it decrements modelines */ if (FakeClient) FakeClient->cull(); - if (Res) - Res->cull(); DeleteZero(this->FakeClient); DeleteZero(this->Users); DeleteZero(this->Modes); @@ -144,7 +142,6 @@ void InspIRCd::Cleanup() DeleteZero(this->BanCache); DeleteZero(this->SNO); DeleteZero(this->Config); - DeleteZero(this->Res); DeleteZero(this->chanlist); DeleteZero(this->PI); DeleteZero(this->Threads); @@ -309,7 +306,6 @@ InspIRCd::InspIRCd(int argc, char** argv) : this->Parser = 0; this->XLines = 0; this->Modes = 0; - this->Res = 0; this->ConfigThread = NULL; this->FakeClient = NULL; @@ -512,8 +508,6 @@ InspIRCd::InspIRCd(int argc, char** argv) : Logs->OpenFileLogs(); ModeParser::InitBuiltinModes(); - this->Res = new DNS(); - // If we don't have a SID, generate one based on the server name and the server description if (Config->sid.empty()) Config->sid = UIDGenerator::GenerateSID(Config->ServerName, Config->ServerDesc); diff --git a/src/inspsocket.cpp b/src/inspsocket.cpp index 5f2da2341..b1bfaa9fd 100644 --- a/src/inspsocket.cpp +++ b/src/inspsocket.cpp @@ -98,7 +98,7 @@ BufferedSocketError BufferedSocket::BeginConnect(const irc::sockets::sockaddrs& ServerInstance->SE->NonBlocking(fd); - if (ServerInstance->SE->Connect(this, &dest.sa, sa_size(dest)) == -1) + if (ServerInstance->SE->Connect(this, &dest.sa, dest.sa_size()) == -1) { if (errno != EINPROGRESS) return I_ERR_CONNECT; diff --git a/src/modmanager_dynamic.cpp b/src/modmanager_dynamic.cpp index adff8630d..9935fb61a 100644 --- a/src/modmanager_dynamic.cpp +++ b/src/modmanager_dynamic.cpp @@ -22,7 +22,6 @@ #include "socket.h" #include "socketengine.h" #include "command_parse.h" -#include "dns.h" #include "exitcodes.h" #include <iostream> diff --git a/src/modules.cpp b/src/modules.cpp index e14e33811..c9118d1a7 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -29,7 +29,6 @@ #include "socket.h" #include "socketengine.h" #include "command_parse.h" -#include "dns.h" #include "exitcodes.h" #ifndef _WIN32 @@ -387,9 +386,6 @@ void ModuleManager::DoSafeUnload(Module* mod) dynamic_reference_base::reset_all(); - /* Tidy up any dangling resolvers */ - ServerInstance->Res->CleanResolvers(mod); - DetachAll(mod); Modules.erase(modfind); @@ -568,18 +564,6 @@ void InspIRCd::SendGlobalMode(const std::vector<std::string>& parameters, User * this->PI->SendMode(parameters[0], Modes->GetLastParseParams(), Modes->GetLastParseTranslate()); } -bool InspIRCd::AddResolver(Resolver* r, bool cached) -{ - if (!cached) - return this->Res->AddResolverClass(r); - else - { - r->TriggerCachedResult(); - delete r; - return true; - } -} - Module* ModuleManager::Find(const std::string &name) { std::map<std::string, Module*>::iterator modfind = Modules.find(name); diff --git a/src/modules/m_cgiirc.cpp b/src/modules/m_cgiirc.cpp index 7259f9459..9d87a01b3 100644 --- a/src/modules/m_cgiirc.cpp +++ b/src/modules/m_cgiirc.cpp @@ -25,6 +25,7 @@ #include "inspircd.h" #include "xline.h" +#include "modules/dns.h" /* $ModDesc: Change user's hosts connecting from known CGI:IRC hosts */ @@ -116,21 +117,21 @@ class CommandWebirc : public Command /** Resolver for CGI:IRC hostnames encoded in ident/GECOS */ -class CGIResolver : public Resolver +class CGIResolver : public DNS::Request { std::string typ; std::string theiruid; LocalIntExt& waiting; bool notify; public: - CGIResolver(Module* me, bool NotifyOpers, const std::string &source, LocalUser* u, - const std::string &type, bool &cached, LocalIntExt& ext) - : Resolver(source, DNS_QUERY_PTR4, cached, me), typ(type), theiruid(u->uuid), + CGIResolver(DNS::Manager *mgr, Module* me, bool NotifyOpers, const std::string &source, LocalUser* u, + const std::string &ttype, LocalIntExt& ext) + : DNS::Request(mgr, me, source, DNS::QUERY_PTR), typ(ttype), theiruid(u->uuid), waiting(ext), notify(NotifyOpers) { } - virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) + void OnLookupComplete(const DNS::Query *r) { /* Check the user still exists */ User* them = ServerInstance->FindUUID(theiruid); @@ -140,19 +141,20 @@ class CGIResolver : public Resolver if (!lu) return; + const DNS::ResourceRecord &ans_record = r->answers[0]; + if (ans_record.rdata.empty() || ans_record.rdata.length() > 64) + return; + if (notify) - ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick.c_str(), them->host.c_str(), result.c_str(), typ.c_str()); + ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s detected as using CGI:IRC (%s), changing real host to %s from %s", them->nick.c_str(), them->host.c_str(), ans_record.rdata.c_str(), typ.c_str()); - if (result.length() > 64) - return; - them->host = result; - them->dhost = result; + them->host = them->dhost = ans_record.rdata; them->InvalidateCache(); lu->CheckLines(true); } } - virtual void OnError(ResolverError e, const std::string &errormessage) + void OnError(const DNS::Query *r) { if (!notify) return; @@ -179,6 +181,8 @@ class ModuleCgiIRC : public Module { CommandWebirc cmd; LocalIntExt waiting; + dynamic_reference<DNS::Manager> DNS; + bool nouserdns; static void RecheckClass(LocalUser* user) { @@ -204,25 +208,31 @@ class ModuleCgiIRC : public Module user->InvalidateCache(); RecheckClass(user); // Don't create the resolver if the core couldn't put the user in a connect class or when dns is disabled - if (user->quitting || ServerInstance->Config->NoUserDns) + if (user->quitting || !DNS || nouserdns) return; + CGIResolver* r = new CGIResolver(*this->DNS, this, cmd.notify, newip, user, (was_pass ? "PASS" : "IDENT"), waiting); try { - bool cached; - CGIResolver* r = new CGIResolver(this, cmd.notify, newip, user, (was_pass ? "PASS" : "IDENT"), cached, waiting); - ServerInstance->AddResolver(r, cached); waiting.set(user, waiting.get(user) + 1); + this->DNS->Process(r); } - catch (...) + catch (DNS::Exception &ex) { + int count = waiting.get(user); + if (count) + waiting.set(user, count - 1); + delete r; if (cmd.notify) - ServerInstance->SNO->WriteToSnoMask('a', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname!", user->nick.c_str(), user->host.c_str()); + ServerInstance->SNO->WriteToSnoMask('a', "Connecting user %s detected as using CGI:IRC (%s), but I could not resolve their hostname; %s", user->nick.c_str(), user->host.c_str(), ex.GetReason()); } } public: - ModuleCgiIRC() : cmd(this), waiting("cgiirc-delay", this) + ModuleCgiIRC() + : cmd(this) + , waiting("cgiirc-delay", this) + , DNS(this, "DNS") { } @@ -238,6 +248,7 @@ public: void OnRehash(User* user) { + nouserdns = ServerInstance->Config->ConfValue("performance")->getBool("nouserdns"); cmd.Hosts.clear(); // Do we send an oper notice when a CGI:IRC has their host changed? diff --git a/src/modules/m_dnsbl.cpp b/src/modules/m_dnsbl.cpp index 8429eb21b..9e0b89fc3 100644 --- a/src/modules/m_dnsbl.cpp +++ b/src/modules/m_dnsbl.cpp @@ -23,6 +23,7 @@ #include "inspircd.h" #include "xline.h" +#include "modules/dns.h" /* $ModDesc: Provides handling of DNS blacklists */ @@ -45,7 +46,7 @@ class DNSBLConfEntry /** Resolver for CGI:IRC hostnames encoded in ident/GECOS */ -class DNSBLResolver : public Resolver +class DNSBLResolver : public DNS::Request { std::string theiruid; LocalStringExt& nameExt; @@ -54,157 +55,158 @@ class DNSBLResolver : public Resolver public: - DNSBLResolver(Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, DNSBLConfEntry *conf, bool &cached) - : Resolver(hostname, DNS_QUERY_A, cached, me), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf) + DNSBLResolver(DNS::Manager *mgr, Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, DNSBLConfEntry *conf) + : DNS::Request(mgr, me, hostname, DNS::QUERY_A, true), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf) { } /* Note: This may be called multiple times for multiple A record results */ - virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) + void OnLookupComplete(const DNS::Query *r) { /* Check the user still exists */ LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid); - if (them) + if (!them) + return; + + const DNS::ResourceRecord &ans_record = r->answers[0]; + + int i = countExt.get(them); + if (i) + countExt.set(them, i - 1); + + // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d + + unsigned int bitmask = 0, record = 0; + bool match = false; + in_addr resultip; + + inet_aton(ans_record.rdata.c_str(), &resultip); + + switch (ConfEntry->type) + { + case DNSBLConfEntry::A_BITMASK: + bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */ + bitmask &= ConfEntry->bitmask; + match = (bitmask != 0); + break; + case DNSBLConfEntry::A_RECORD: + record = resultip.s_addr >> 24; /* Last octet */ + match = (ConfEntry->records[record] == 1); + break; + } + + if (match) { - int i = countExt.get(them); - if (i) - countExt.set(them, i - 1); - // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d - if(result.length()) + std::string reason = ConfEntry->reason; + std::string::size_type x = reason.find("%ip%"); + while (x != std::string::npos) { - unsigned int bitmask = 0, record = 0; - bool match = false; - in_addr resultip; + reason.erase(x, 4); + reason.insert(x, them->GetIPString()); + x = reason.find("%ip%"); + } - inet_aton(result.c_str(), &resultip); + ConfEntry->stats_hits++; - switch (ConfEntry->type) + switch (ConfEntry->banaction) + { + case DNSBLConfEntry::I_KILL: { - case DNSBLConfEntry::A_BITMASK: - bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */ - bitmask &= ConfEntry->bitmask; - match = (bitmask != 0); - break; - case DNSBLConfEntry::A_RECORD: - record = resultip.s_addr >> 24; /* Last octet */ - match = (ConfEntry->records[record] == 1); + ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")"); break; } - - if (match) + case DNSBLConfEntry::I_MARK: { - std::string reason = ConfEntry->reason; - std::string::size_type x = reason.find("%ip%"); - while (x != std::string::npos) + if (!ConfEntry->ident.empty()) { - reason.erase(x, 4); - reason.insert(x, them->GetIPString()); - x = reason.find("%ip%"); + them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason); + them->ChangeIdent(ConfEntry->ident.c_str()); } - ConfEntry->stats_hits++; - - switch (ConfEntry->banaction) + if (!ConfEntry->host.empty()) { - case DNSBLConfEntry::I_KILL: - { - ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")"); - break; - } - case DNSBLConfEntry::I_MARK: - { - if (!ConfEntry->ident.empty()) - { - them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason); - them->ChangeIdent(ConfEntry->ident.c_str()); - } - - if (!ConfEntry->host.empty()) - { - them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason); - them->ChangeDisplayedHost(ConfEntry->host.c_str()); - } - - nameExt.set(them, ConfEntry->name); - break; - } - case DNSBLConfEntry::I_KLINE: - { - KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(), - "*", them->GetIPString()); - if (ServerInstance->XLines->AddLine(kl,NULL)) - { - std::string timestr = ServerInstance->TimeString(kl->expiry); - ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s", - them->GetIPString().c_str(), timestr.c_str(), reason.c_str()); - ServerInstance->XLines->ApplyLines(); - } - else - delete kl; - break; - } - case DNSBLConfEntry::I_GLINE: - { - GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(), - "*", them->GetIPString()); - if (ServerInstance->XLines->AddLine(gl,NULL)) - { - std::string timestr = ServerInstance->TimeString(gl->expiry); - ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s", - them->GetIPString().c_str(), timestr.c_str(), reason.c_str()); - ServerInstance->XLines->ApplyLines(); - } - else - delete gl; - break; - } - case DNSBLConfEntry::I_ZLINE: - { - ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(), - them->GetIPString()); - if (ServerInstance->XLines->AddLine(zl,NULL)) - { - std::string timestr = ServerInstance->TimeString(zl->expiry); - ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s", - them->GetIPString().c_str(), timestr.c_str(), reason.c_str()); - ServerInstance->XLines->ApplyLines(); - } - else - delete zl; - break; - } - case DNSBLConfEntry::I_UNKNOWN: - { - break; - } - break; + them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason); + them->ChangeDisplayedHost(ConfEntry->host.c_str()); } - ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record); + nameExt.set(them, ConfEntry->name); + break; } - else - ConfEntry->stats_misses++; + case DNSBLConfEntry::I_KLINE: + { + KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(), + "*", them->GetIPString()); + if (ServerInstance->XLines->AddLine(kl,NULL)) + { + std::string timestr = ServerInstance->TimeString(kl->expiry); + ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s", + them->GetIPString().c_str(), timestr.c_str(), reason.c_str()); + ServerInstance->XLines->ApplyLines(); + } + else + delete kl; + break; + } + case DNSBLConfEntry::I_GLINE: + { + GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(), + "*", them->GetIPString()); + if (ServerInstance->XLines->AddLine(gl,NULL)) + { + std::string timestr = ServerInstance->TimeString(gl->expiry); + ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s", + them->GetIPString().c_str(), timestr.c_str(), reason.c_str()); + ServerInstance->XLines->ApplyLines(); + } + else + delete gl; + break; + } + case DNSBLConfEntry::I_ZLINE: + { + ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(), + them->GetIPString()); + if (ServerInstance->XLines->AddLine(zl,NULL)) + { + std::string timestr = ServerInstance->TimeString(zl->expiry); + ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s", + them->GetIPString().c_str(), timestr.c_str(), reason.c_str()); + ServerInstance->XLines->ApplyLines(); + } + else + delete zl; + break; + } + case DNSBLConfEntry::I_UNKNOWN: + default: + break; } - else - ConfEntry->stats_misses++; + + ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record); } + else + ConfEntry->stats_misses++; } - virtual void OnError(ResolverError e, const std::string &errormessage) + void OnError(const DNS::Query *q) { LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid); - if (them) - { - int i = countExt.get(them); - if (i) - countExt.set(them, i - 1); - } + if (!them) + return; + + int i = countExt.get(them); + if (i) + countExt.set(them, i - 1); + + if (q->error == DNS::ERROR_NO_RECORDS || q->error == DNS::ERROR_DOMAIN_NOT_FOUND) + ConfEntry->stats_misses++; } }; class ModuleDNSBL : public Module { std::vector<DNSBLConfEntry *> DNSBLConfEntries; + dynamic_reference<DNS::Manager> DNS; LocalStringExt nameExt; LocalIntExt countExt; @@ -227,7 +229,7 @@ class ModuleDNSBL : public Module return DNSBLConfEntry::I_UNKNOWN; } public: - ModuleDNSBL() : nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { } + ModuleDNSBL() : DNS(this, "DNS"), nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { } void init() { @@ -347,7 +349,7 @@ class ModuleDNSBL : public Module void OnSetUserIP(LocalUser* user) { - if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET)) + if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET) || !DNS) return; if (user->MyClass) @@ -373,19 +375,25 @@ class ModuleDNSBL : public Module countExt.set(user, DNSBLConfEntries.size()); // For each DNSBL, we will run through this lookup - unsigned int i = 0; - while (i < DNSBLConfEntries.size()) + for (unsigned i = 0; i < DNSBLConfEntries.size(); ++i) { // Fill hostname with a dnsbl style host (d.c.b.a.domain.tld) std::string hostname = reversedip + "." + DNSBLConfEntries[i]->domain; /* now we'd need to fire off lookups for `hostname'. */ - bool cached; - DNSBLResolver *r = new DNSBLResolver(this, nameExt, countExt, hostname, user, DNSBLConfEntries[i], cached); - ServerInstance->AddResolver(r, cached); + DNSBLResolver *r = new DNSBLResolver(*this->DNS, this, nameExt, countExt, hostname, user, DNSBLConfEntries[i]); + try + { + this->DNS->Process(r); + } + catch (DNS::Exception &ex) + { + delete r; + ServerInstance->Logs->Log("m_dnsbl", LOG_DEBUG, std::string(ex.GetReason())); + } + if (user->quitting) break; - i++; } } diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp index 6cca6462c..018fcece7 100644 --- a/src/modules/m_spanningtree/main.cpp +++ b/src/modules/m_spanningtree/main.cpp @@ -37,7 +37,7 @@ #include "protocolinterface.h" ModuleSpanningTree::ModuleSpanningTree() - : commands(NULL), Utils(NULL) + : commands(NULL), DNS(this, "DNS"), Utils(NULL) { } @@ -269,8 +269,7 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y) return; } - QueryType start_type = DNS_QUERY_A; - start_type = DNS_QUERY_AAAA; + DNS::QueryType start_type = DNS::QUERY_AAAA; if (strchr(x->IPAddr.c_str(),':')) { in6_addr n; @@ -300,16 +299,20 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y) ServerInstance->GlobalCulls.AddItem(newsocket); } } + else if (!DNS) + { + ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and m_dns.so is not loaded, unable to resolve.", x->Name.c_str()); + } else { + ServernameResolver* snr = new ServernameResolver(Utils, *DNS, x->IPAddr, x, start_type, y); try { - bool cached = false; - ServernameResolver* snr = new ServernameResolver(Utils, x->IPAddr, x, cached, start_type, y); - ServerInstance->AddResolver(snr, cached); + DNS->Process(snr); } - catch (ModuleException& e) + catch (DNS::Exception& e) { + delete snr; ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason()); ConnectServer(y, false); } diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h index 7401274c3..3ff369ea6 100644 --- a/src/modules/m_spanningtree/main.h +++ b/src/modules/m_spanningtree/main.h @@ -25,6 +25,7 @@ #include "inspircd.h" +#include "modules/dns.h" /** If you make a change which breaks the protocol, increment this. * If you completely change the protocol, completely change the number. * @@ -54,6 +55,8 @@ class ModuleSpanningTree : public Module void LocalMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list, const char* message_type); public: + dynamic_reference<DNS::Manager> DNS; + SpanningTreeUtilities* Utils; /** Set to true if inside a spanningtree call, to prevent sending diff --git a/src/modules/m_spanningtree/resolvers.cpp b/src/modules/m_spanningtree/resolvers.cpp index ff5544064..b2d14069c 100644 --- a/src/modules/m_spanningtree/resolvers.cpp +++ b/src/modules/m_spanningtree/resolvers.cpp @@ -36,13 +36,15 @@ * callback to OnLookupComplete or OnError when completed. Once it has completed we * will have an IP address which we can then use to continue our connection. */ -ServernameResolver::ServernameResolver(SpanningTreeUtilities* Util, const std::string &hostname, Link* x, bool &cached, QueryType qt, Autoconnect* myac) - : Resolver(hostname, qt, cached, Util->Creator), Utils(Util), query(qt), host(hostname), MyLink(x), myautoconnect(myac) +ServernameResolver::ServernameResolver(SpanningTreeUtilities* Util, DNS::Manager *mgr, const std::string &hostname, Link* x, DNS::QueryType qt, Autoconnect* myac) + : DNS::Request(mgr, Util->Creator, hostname, qt), Utils(Util), query(qt), host(hostname), MyLink(x), myautoconnect(myac) { } -void ServernameResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) +void ServernameResolver::OnLookupComplete(const DNS::Query *r) { + const DNS::ResourceRecord &ans_record = r->answers[0]; + /* Initiate the connection, now that we have an IP to use. * Passing a hostname directly to BufferedSocket causes it to * just bail and set its FD to -1. @@ -50,7 +52,7 @@ void ServernameResolver::OnLookupComplete(const std::string &result, unsigned in TreeServer* CheckDupe = Utils->FindServer(MyLink->Name.c_str()); if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */ { - TreeSocket* newsocket = new TreeSocket(Utils, MyLink, myautoconnect, result); + TreeSocket* newsocket = new TreeSocket(Utils, MyLink, myautoconnect, ans_record.rdata); if (newsocket->GetFd() > -1) { /* We're all OK */ @@ -65,49 +67,64 @@ void ServernameResolver::OnLookupComplete(const std::string &result, unsigned in } } -void ServernameResolver::OnError(ResolverError e, const std::string &errormessage) +void ServernameResolver::OnError(const DNS::Query *r) { /* Ooops! */ - if (query == DNS_QUERY_AAAA) + if (query == DNS::QUERY_AAAA) { - bool cached = false; - ServernameResolver* snr = new ServernameResolver(Utils, host, MyLink, cached, DNS_QUERY_A, myautoconnect); - ServerInstance->AddResolver(snr, cached); - return; + ServernameResolver* snr = new ServernameResolver(Utils, this->manager, host, MyLink, DNS::QUERY_A, myautoconnect); + try + { + this->manager->Process(snr); + return; + } + catch (DNS::Exception &) + { + delete snr; + } } - ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s", MyLink->Name.c_str(), errormessage.c_str() ); + + ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s", MyLink->Name.c_str(), this->manager->GetErrorStr(r->error).c_str()); Utils->Creator->ConnectServer(myautoconnect, false); } -SecurityIPResolver::SecurityIPResolver(Module* me, SpanningTreeUtilities* U, const std::string &hostname, Link* x, bool &cached, QueryType qt) - : Resolver(hostname, qt, cached, me), MyLink(x), Utils(U), mine(me), host(hostname), query(qt) +SecurityIPResolver::SecurityIPResolver(Module* me, SpanningTreeUtilities* U, DNS::Manager *mgr, const std::string &hostname, Link* x, DNS::QueryType qt) + : DNS::Request(mgr, me, hostname, qt), MyLink(x), Utils(U), mine(me), host(hostname), query(qt) { } -void SecurityIPResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) +void SecurityIPResolver::OnLookupComplete(const DNS::Query *r) { + const DNS::ResourceRecord &ans_record = r->answers[0]; + for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i != Utils->LinkBlocks.end(); ++i) { Link* L = *i; if (L->IPAddr == host) { - Utils->ValidIPs.push_back(result); + Utils->ValidIPs.push_back(ans_record.rdata); break; } } } -void SecurityIPResolver::OnError(ResolverError e, const std::string &errormessage) +void SecurityIPResolver::OnError(const DNS::Query *r) { - if (query == DNS_QUERY_AAAA) + if (query == DNS::QUERY_AAAA) { - bool cached = false; - SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, host, MyLink, cached, DNS_QUERY_A); - ServerInstance->AddResolver(res, cached); - return; + SecurityIPResolver* res = new SecurityIPResolver(mine, Utils, this->manager, host, MyLink, DNS::QUERY_A); + try + { + this->manager->Process(res); + return; + } + catch (DNS::Exception &) + { + delete res; + } } ServerInstance->Logs->Log("m_spanningtree",LOG_DEFAULT,"Could not resolve IP associated with Link '%s': %s", - MyLink->Name.c_str(),errormessage.c_str()); + MyLink->Name.c_str(), this->manager->GetErrorStr(r->error).c_str()); } CacheRefreshTimer::CacheRefreshTimer(SpanningTreeUtilities* Util) diff --git a/src/modules/m_spanningtree/resolvers.h b/src/modules/m_spanningtree/resolvers.h index bcdf38625..83aaf8db0 100644 --- a/src/modules/m_spanningtree/resolvers.h +++ b/src/modules/m_spanningtree/resolvers.h @@ -21,24 +21,25 @@ #pragma once #include "inspircd.h" +#include "modules/dns.h" #include "utils.h" #include "link.h" /** Handle resolving of server IPs for the cache */ -class SecurityIPResolver : public Resolver +class SecurityIPResolver : public DNS::Request { private: reference<Link> MyLink; SpanningTreeUtilities* Utils; Module* mine; std::string host; - QueryType query; + DNS::QueryType query; public: - SecurityIPResolver(Module* me, SpanningTreeUtilities* U, const std::string &hostname, Link* x, bool &cached, QueryType qt); - void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached); - void OnError(ResolverError e, const std::string &errormessage); + SecurityIPResolver(Module* me, SpanningTreeUtilities* U, DNS::Manager *mgr, const std::string &hostname, Link* x, DNS::QueryType qt); + void OnLookupComplete(const DNS::Query *r); + void OnError(const DNS::Query *q); }; /** This class is used to resolve server hostnames during /connect and autoconnect. @@ -47,16 +48,16 @@ class SecurityIPResolver : public Resolver * callback to OnLookupComplete or OnError when completed. Once it has completed we * will have an IP address which we can then use to continue our connection. */ -class ServernameResolver : public Resolver +class ServernameResolver : public DNS::Request { private: SpanningTreeUtilities* Utils; - QueryType query; + DNS::QueryType query; std::string host; reference<Link> MyLink; reference<Autoconnect> myautoconnect; public: - ServernameResolver(SpanningTreeUtilities* Util, const std::string &hostname, Link* x, bool &cached, QueryType qt, Autoconnect* myac); - void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached); - void OnError(ResolverError e, const std::string &errormessage); + ServernameResolver(SpanningTreeUtilities* Util, DNS::Manager *mgr, const std::string &hostname, Link* x, DNS::QueryType qt, Autoconnect* myac); + void OnLookupComplete(const DNS::Query *r); + void OnError(const DNS::Query *q); }; diff --git a/src/modules/m_spanningtree/utils.cpp b/src/modules/m_spanningtree/utils.cpp index 18e9d72b8..428ef7224 100644 --- a/src/modules/m_spanningtree/utils.cpp +++ b/src/modules/m_spanningtree/utils.cpp @@ -287,16 +287,16 @@ void SpanningTreeUtilities::RefreshIPCache() bool ipvalid = irc::sockets::aptosa(L->IPAddr, L->Port, dummy); if ((L->IPAddr == "*") || (ipvalid)) ValidIPs.push_back(L->IPAddr); - else + else if (this->Creator->DNS) { + SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, *this->Creator->DNS, L->IPAddr, L, DNS::QUERY_AAAA); try { - bool cached = false; - SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, DNS_QUERY_AAAA); - ServerInstance->AddResolver(sr, cached); + this->Creator->DNS->Process(sr); } - catch (...) + catch (DNS::Exception &) { + delete sr; } } } diff --git a/src/user_resolver.cpp b/src/user_resolver.cpp deleted file mode 100644 index 9c530506d..000000000 --- a/src/user_resolver.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net> - * - * This file is part of InspIRCd. InspIRCd is free software: you can - * redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "inspircd.h" -UserResolver::UserResolver(LocalUser* user, std::string to_resolve, QueryType qt, bool &cache) : - Resolver(to_resolve, qt, cache, NULL), uuid(user->uuid) -{ - this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA); -} - -void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) -{ - UserResolver *res_forward; // for forward-resolution - LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid); - if (!bound_user) - { - ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str()); - return; - } - - ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), input.c_str(), result.c_str()); - - if (!fwd) - { - // first half of resolution is done. We now need to verify that the host matches. - bound_user->stored_host = result; - try - { - /* Check we didnt time out */ - if (bound_user->registered != REG_ALL) - { - bool lcached = false; - if (bound_user->client_sa.sa.sa_family == AF_INET6) - { - /* IPV6 forward lookup */ - res_forward = new UserResolver(bound_user, result, DNS_QUERY_AAAA, lcached); - } - else - { - /* IPV4 lookup */ - res_forward = new UserResolver(bound_user, result, DNS_QUERY_A, lcached); - } - ServerInstance->AddResolver(res_forward, lcached); - } - } - catch (CoreException& e) - { - ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG,"Error in resolver: %s",e.GetReason()); - } - } - else - { - /* Both lookups completed */ - - irc::sockets::sockaddrs* user_ip = &bound_user->client_sa; - bool rev_match = false; - if (user_ip->sa.sa_family == AF_INET6) - { - struct in6_addr res_bin; - if (inet_pton(AF_INET6, result.c_str(), &res_bin)) - { - rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin)); - } - } - else - { - struct in_addr res_bin; - if (inet_pton(AF_INET, result.c_str(), &res_bin)) - { - rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin)); - } - } - - if (rev_match) - { - std::string hostname = bound_user->stored_host; - if (hostname.length() < 65) - { - /* Check we didnt time out */ - if ((bound_user->registered != REG_ALL) && (!bound_user->dns_done)) - { - /* Hostnames starting with : are not a good thing (tm) */ - if (hostname[0] == ':') - hostname.insert(0, "0"); - - bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : "")); - bound_user->dns_done = true; - bound_user->dhost.assign(hostname, 0, 64); - bound_user->host.assign(hostname, 0, 64); - /* Invalidate cache */ - bound_user->InvalidateCache(); - } - } - else - { - if (!bound_user->dns_done) - { - bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", bound_user->GetIPString().c_str()); - bound_user->dns_done = true; - } - } - } - else - { - if (!bound_user->dns_done) - { - bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", bound_user->GetIPString().c_str()); - bound_user->dns_done = true; - } - } - - // Save some memory by freeing this up; it's never used again in the user's lifetime. - bound_user->stored_host.resize(0); - } -} - -void UserResolver::OnError(ResolverError e, const std::string &errormessage) -{ - LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid); - if (bound_user) - { - bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", errormessage.c_str(), bound_user->GetIPString().c_str()); - bound_user->dns_done = true; - bound_user->stored_host.resize(0); - ServerInstance->stats->statsDnsBad++; - } -} diff --git a/src/usermanager.cpp b/src/usermanager.cpp index abd450b11..f1a3c0183 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -139,10 +139,6 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs this->QuitUser(New, "Internal error handling connection"); } - /* NOTE: even if dns lookups are *off*, we still need to display this. - * BOPM and other stuff requires it. - */ - New->WriteServ("NOTICE Auth :*** Looking up your hostname..."); if (ServerInstance->Config->RawLog) New->WriteServ("NOTICE Auth :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded."); @@ -151,16 +147,6 @@ void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs return; FOREACH_MOD(I_OnUserInit,OnUserInit(New)); - - if (ServerInstance->Config->NoUserDns) - { - New->WriteServ("NOTICE %s :*** Skipping host resolution (disabled by server administrator)", New->nick.c_str()); - New->dns_done = true; - } - else - { - New->StartDNSLookup(); - } } void UserManager::QuitUser(User *user, const std::string &quitreason, const char* operreason) diff --git a/src/userprocess.cpp b/src/userprocess.cpp index 382f4ffa0..c78f0bb0b 100644 --- a/src/userprocess.cpp +++ b/src/userprocess.cpp @@ -80,7 +80,7 @@ void InspIRCd::DoBackgroundUserStuff() } break; case REG_NICKUSER: - if (AllModulesReportReady(curr) && curr->dns_done) + if (AllModulesReportReady(curr)) { /* User has sent NICK/USER, modules are okay, DNS finished. */ curr->FullConnect(); diff --git a/src/users.cpp b/src/users.cpp index 899ccecc6..d6363171c 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -108,26 +108,6 @@ std::string User::ProcessNoticeMasks(const char *sm) return output; } -void LocalUser::StartDNSLookup() -{ - try - { - bool cached = false; - UserResolver *res_reverse; - - QueryType resolvtype = this->client_sa.sa.sa_family == AF_INET6 ? DNS_QUERY_PTR6 : DNS_QUERY_PTR4; - res_reverse = new UserResolver(this, this->GetIPString(), resolvtype, cached); - - ServerInstance->AddResolver(res_reverse, cached); - } - catch (CoreException& e) - { - ServerInstance->Logs->Log("USERS", LOG_DEBUG,"Error in resolver: %s",e.GetReason()); - dns_done = true; - ServerInstance->stats->statsDnsBad++; - } -} - bool User::IsNoticeMaskSet(unsigned char sm) { if (!isalpha(sm)) @@ -220,7 +200,7 @@ LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::so bytes_in(0), bytes_out(0), cmds_in(0), cmds_out(0), nping(0), CommandFloodPenalty(0), already_sent(0) { - exempt = quitting_sendq = dns_done = false; + exempt = quitting_sendq = false; idle_lastmsg = 0; ident = "unknown"; lastping = 0; |