diff options
author | Adam <Adam@anope.org> | 2010-09-09 23:43:11 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2010-09-09 23:43:11 -0400 |
commit | 46813ccb8c6ab572b8a9ff0a39afb1d92dc4482b (patch) | |
tree | 562da502a102230ce207bbe921fdc978ee71e20c | |
parent | fdd196e50b4616ac377bd0ee0ae5ce6c57b657ee (diff) |
Added an asynchronous DNS system and m_dnsbl, which checks clients against DNS blacklists.
Rewrote internal handling of IPs, we now properly support users using IPv6.
Fixed a few problems with the UnrealIRCd protocol module.
34 files changed, 1304 insertions, 349 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f3c1e26df..a92086489 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -322,7 +322,6 @@ check_include_file(sys/select.h HAVE_SYS_SELECT_H) check_include_file(sys/eventfd.h HAVE_SYS_EVENTFD_H) # Check for the existance of the following functions -check_function_exists(gethostbyname HAVE_GETHOSTBYNAME) check_function_exists(gettimeofday HAVE_GETTIMEOFDAY) check_function_exists(setgrent HAVE_SETGRENT) check_function_exists(strcasecmp HAVE_STRCASECMP) diff --git a/data/example.conf b/data/example.conf index d7c7c9372..b98595ef7 100644 --- a/data/example.conf +++ b/data/example.conf @@ -820,6 +820,29 @@ mail } /* + * [OPTIONAL] DNS Config + * + * This section is used to configure DNS. + * At this time DNS is only used by a few modules (m_dnsbl) + * and is not required by the core to function. + */ +dns +{ + /* + * The nameserver to use for resolving hostnames, must be an IP or a resolver configuration file. + * The below should work fine on all nix like systems. Windows users will have to find their nameservers + * from ipconfig /all and put the IP here + */ + nameserver = "/etc/resolv.conf" + #nameserver = "127.0.0.1" + + /* + * How long to wait before a DNS query has timed out + */ + timeout = 5 +} + +/* * [REQUIRED] NickServ Config * * This section is used to set up the Nickname Registration Service pseudo-client. @@ -1585,7 +1608,13 @@ module { name = "os_ignore" } module { name = "cs_appendtopic" } module { name = "cs_enforce" } module { name = "ns_maxemail" } -module { name = "hs_request" } + +/* + * [OPTIONAL] Module-Specific Options + * + * The following blocks are used for options pertaining to modules and are not part of the core. + * Unless otherwise stated, most of the options are optional. + */ /* * m_ssl @@ -1595,10 +1624,9 @@ module { name = "hs_request" } module { name = "m_ssl" } /* - * [OPTIONAL] Module-Specific Options + * db_plain * - * The following blocks are used for options pertaining to modules and are not part of the core. - * Unless otherwise stated, most of the options are optional. + * This is the default flatfile database format */ db_plain { @@ -1608,11 +1636,20 @@ db_plain database = "anope.db" } +/* + * cs_set_misc + * + * Allows you to create misc /chanserv set commands, and have the data + * show up in /chanserv info + */ module { name = "cs_set_misc" } cs_set_misc { + /* The name of the command */ name = "OINFO" + /* A short description of the command */ desc = "Associate oper only information to this channel" + /* Set to yes if only opers can set it and see it */ operonly = yes } cs_set_misc @@ -1654,6 +1691,7 @@ m_helpchan helpchannel = "#help" } +module { name = "hs_request" } hs_request { /* @@ -1683,3 +1721,36 @@ ns_maxemail #maxemails = 1 } +module { name = "m_dnsbl" } +m_dnsbl +{ + /* + * If set, Services will check clients against the DNSBLs when services connect to its uplink. + * This is not recommended, and on large networks will open a very large amount of DNS queries. + * While services are not drastically affected by this, your nameserver/DNSBL might care. + */ + check_on_connect = no; + + /* + * If set, Services will check clients when coming back from a netsplit. This can cause a large number + * of DNS queries open at once. While services are not drastically affected by this, your nameserver/DNSBL + * might care. + */ + check_on_netburst = no; +} +blacklist +{ + /* Name of the blacklist */ + name = "rbl.efnetrbl.org"; + /* How long to set the akill for */ + time = 4h; + /* Reason for akill, %h is replaced with the hostname of the user, and %i with the IP */ + reason = "You are listed in the efnet RBL, visit http://rbl.efnetrbl.org/?i=%i for info" +} +blacklist +{ + name = "dnsbl.dronebl.org"; + time = 4h; + reason = "You have a host listed in the DroneBL. For more information, visit http://dronebl.org/lookup.do?ip=%i" +} + diff --git a/docs/Changes.conf b/docs/Changes.conf index c49953e6d..992624d63 100644 --- a/docs/Changes.conf +++ b/docs/Changes.conf @@ -12,6 +12,8 @@ options:socketengine added to choose what socket engine to use module:cs_set_misc and module:ns_set_misc added to replace the old set url/icq/email modules options:hideprivilegedcommands added to hide privileged commands from normal users log block added to customize logging +dns block added to configure dns settings +m_dnsbl added ** MODIFIED CONFIGURATION DIRECTIVES ** opertype:commands changed operserv/sgline to opserv/snline diff --git a/include/config.h b/include/config.h index 11969707f..4ea2aae23 100644 --- a/include/config.h +++ b/include/config.h @@ -572,6 +572,11 @@ class ServerConfig /* Don't quote the To: address */ bool DontQuoteAddresses; + /* Nameserver to use for resolving hostnames */ + Anope::string NameServer; + /* TIme before a DNS query is considered dead */ + time_t DNSTimeout; + /* Prefix of guest nicks when a user gets forced off of a nick */ Anope::string NSGuestNickPrefix; /* Allow users to set kill immed on */ diff --git a/include/dns.h b/include/dns.h new file mode 100644 index 000000000..a8ef82d44 --- /dev/null +++ b/include/dns.h @@ -0,0 +1,181 @@ +#ifndef DNS_H +#define DNS_H + +/** Valid query types + */ +enum QueryType +{ + /* Nothing */ + DNS_QUERY_NONE, + /* A simple A lookup */ + DNS_QUERY_A = 1, + /* A CNAME lookup */ + DNS_QUERY_CNAME = 5, + /* Reverse DNS lookup */ + DNS_QUERY_PTR = 12, + /* IPv6 AAAA lookup */ + DNS_QUERY_AAAA = 28 +}; + +/** Flags that can be AND'd into DNSPacket::flags to receive certain values + */ +enum QueryFlags +{ + DNS_QUERYFLAGS_QR = 0x8000, + DNS_QUERYFLAGS_OPCODE = 0x7800, + DNS_QUERYFLAGS_AA = 0x400, + DBS_QUERYFLAGS_TC = 0x200, + DNS_QUERYFLAGS_RD = 0x100, + DNS_QUERYFLAGS_RA = 0x80, + DNS_QUERYFLAGS_Z = 0x70, + DNS_QUERYFLAGS_RCODE = 0xF +}; + +enum DNSError +{ + DNS_ERROR_UNKNOWN, + DNS_ERROR_TIMEOUT, + DNS_ERROR_NOT_AN_ANSWER, + DNS_ERROR_NONSTANDARD_QUERY, + DNS_ERROR_FORMAT_ERROR, + DNS_ERROR_SERVER_FAILURE, + DNS_ERROR_DOMAIN_NOT_FOUND, + DNS_ERROR_NOT_IMPLEMENTED, + DNS_ERROR_REFUSED, + DNS_ERROR_NO_RECORDS, + DNS_ERROR_INVALID_TYPE +}; + +class DNSRequestTimeout; // Forward declarations +struct DNSRecord; + +/** The request + */ +class DNSRequest +{ + /* Timeout timer for this request */ + DNSRequestTimeout *timeout; + + public: + /* Address we're looking up */ + Anope::string address; + /* QueryType, A, AAAA, PTR etc */ + QueryType QT; + + DNSRequest(const Anope::string &addr, QueryType qt, bool cache = false); + + virtual ~DNSRequest(); + + virtual void OnLookupComplete(const DNSRecord *r) = 0; + + virtual void OnError(DNSError error, const Anope::string &message); +}; + +/** A full packet sent to the nameserver, may contain multiple queries + */ +struct DNSPacket +{ + /* Our 16-bit id for this header */ + unsigned short id; + /* Flags on the query */ + unsigned short flags; + /* Number of queries */ + unsigned short qdcount; + /* Number of resource records in answer */ + unsigned short ancount; + /* Number of NS resource records in authority records section */ + unsigned short nscount; + /* Number of resource records in the additional records section */ + unsigned short arcount; + /* How many of the bytes of the payload are in use */ + unsigned short payload_count; + /* The queries, at most can be 512 bytes */ + unsigned char payload[512]; + + inline DNSPacket(); + + bool AddQuestion(const Anope::string &address, QueryType qt); + + inline void FillPacket(const unsigned char *input, const size_t length); + + inline void FillBuffer(unsigned char *buffer); +}; + +struct DNSRecord +{ + /* Name of the initial lookup */ + Anope::string name; + /* Result of the lookup */ + Anope::string result; + /* Type of query this was */ + QueryType type; + /* Record class, should always be 1 */ + unsigned short record_class; + /* Time to live */ + unsigned long ttl; + /* Record length */ + unsigned short rdlength; + + inline DNSRecord(); + time_t created; +}; + +/** The socket used to talk to the nameserver, uses UDP + */ +class DNSSocket : public ClientSocket +{ + private: + sockaddrs server_addr; + + int SendTo(const unsigned char *buf, size_t len) const; + int RecvFrom(char *buf, size_t size, sockaddrs &addrs) const; + public: + DNSSocket(const Anope::string &nTargetHost, int nPort); + virtual ~DNSSocket(); + + bool ProcessRead(); + + bool ProcessWrite(); +}; + +/** DNS manager, manages the connection and all requests + */ +class DNSManager : public Timer +{ + std::multimap<Anope::string, DNSRecord *> cache; + public: + DNSSocket *sock; + + std::deque<DNSPacket *> packets; + std::map<short, DNSRequest *> requests; + + static const int DNSPort = 53; + + DNSManager(); + + ~DNSManager(); + + void AddCache(DNSRecord *rr); + bool CheckCache(DNSRequest *request); + void Tick(time_t now); +}; + +/** A DNS timeout, one is made for every DNS request to detect timeouts + */ +class DNSRequestTimeout : public Timer +{ + DNSRequest *request; + public: + bool done; + + DNSRequestTimeout(DNSRequest *r, time_t timeout); + + ~DNSRequestTimeout(); + + void Tick(time_t); +}; + +extern DNSManager *DNSEngine; + +#endif // DNS_H + diff --git a/include/extern.h b/include/extern.h index 48336be5f..2294c3f28 100644 --- a/include/extern.h +++ b/include/extern.h @@ -276,7 +276,6 @@ E uint16 netmask_to_cidr(uint32 mask); E bool str_is_wildcard(const Anope::string &str); E bool str_is_pure_wildcard(const Anope::string &str); -E uint32 str_is_ip(const Anope::string &str); E bool str_is_cidr(const Anope::string &str, uint32 &ip, uint32 &mask, Anope::string &host); /**** modes.cpp ****/ @@ -362,7 +361,7 @@ E User *finduser(const Anope::string &nick); E Anope::string TS6SID; -E User *do_nick(const Anope::string &source, const Anope::string &nick, const Anope::string &username, const Anope::string &host, const Anope::string &server, const Anope::string &realname, time_t ts, uint32 ip, const Anope::string &vhost, const Anope::string &uid); +E User *do_nick(const Anope::string &source, const Anope::string &nick, const Anope::string &username, const Anope::string &host, const Anope::string &server, const Anope::string &realname, time_t ts, const Anope::string &ip, const Anope::string &vhost, const Anope::string &uid); E void do_umode(const Anope::string &source, int ac, const char **av); E void do_quit(const Anope::string &source, int ac, const char **av); @@ -382,9 +381,6 @@ E void UserSetInternalModes(User *user, int ac, const char **av); E void b64_encode(const Anope::string &src, Anope::string &target); E void b64_decode(const Anope::string &src, Anope::string &target); -E int decode_ip(const Anope::string &buf); - -E Anope::string host_resolve(const Anope::string &host); #ifdef _WIN32 E Anope::string GetWindowsVersion(); diff --git a/include/services.h b/include/services.h index f44425656..63e2492b2 100644 --- a/include/services.h +++ b/include/services.h @@ -363,6 +363,9 @@ template<typename T, size_t Size = 32> class Flags #include "sockets.h" #include "socketengine.h" +#include "extensible.h" +#include "timers.h" +#include "dns.h" /*************************************************************************/ @@ -427,7 +430,6 @@ class Server; struct EList; struct Session; -#include "extensible.h" #include "threadengine.h" #include "opertype.h" #include "modes.h" @@ -457,7 +459,6 @@ struct IRCDVar int vident; /* Supports vidents */ int svshold; /* Supports svshold */ int tsonmode; /* Timestamp on mode changes */ - int nickip; /* Sends IP on NICK */ int omode; /* On the fly o:lines */ int umode; /* change user modes */ int nickvhost; /* Users vhost sent during NICK */ @@ -1054,8 +1055,6 @@ struct Uplink Uplink(const Anope::string &_host, int _port, const Anope::string &_password, bool _ipv6) : host(_host), port(_port), password(_password), ipv6(_ipv6) { } }; -#include "timers.h" - /** Timer for colliding nicks to force people off of nicknames */ class NickServCollide : public Timer diff --git a/include/socketengine.h b/include/socketengine.h index 7279ba3ad..c45249810 100644 --- a/include/socketengine.h +++ b/include/socketengine.h @@ -43,12 +43,12 @@ class CoreExport SocketEngineBase /** Mark a socket as writeable * @param s The socket */ - virtual void MarkWriteable(Socket *s) { } + virtual void MarkWritable(Socket *s) { } /** Unmark a socket as writeable * @param s The socket */ - virtual void ClearWriteable(Socket *s) { } + virtual void ClearWritable(Socket *s) { } /** Read from sockets and do things */ diff --git a/include/sockets.h b/include/sockets.h index 6eeb3bf3d..772a08a65 100644 --- a/include/sockets.h +++ b/include/sockets.h @@ -22,6 +22,49 @@ # define CloseSocket close #endif +/** A sockaddr union used to combine IPv4 and IPv6 sockaddrs + */ +union CoreExport sockaddrs +{ + sockaddr sa; + sockaddr_in sa4; + sockaddr_in6 sa6; + + /** Get the size of the sockaddr we represent + * @return The size + */ + size_t size() const; + + /** Get the port represented by this addr + * @return The port, or -1 on fail + */ + int port() const; + + /** Get the address represented by this addr + * @return The address + */ + Anope::string addr() const; + + /** Construct the object, sets everything to 0 + */ + sockaddrs(); + + /** Check if this sockaddr has data in it + */ + bool operator()() const; + + /** Compares with sockaddr with another. Compares address type, port, and address + * @return true if they are the same + */ + bool operator==(const sockaddrs &other) const; + /* The same as above but not */ + inline bool operator!=(const sockaddrs &other) const { return !(*this == other); } + + void pton(int type, const Anope::string &address, int pport = 0); + + void ntop(int type, const void *src); +}; + class SocketException : public CoreException { public: @@ -44,12 +87,13 @@ enum SocketType enum SocketFlag { - SF_DEAD + SF_DEAD, + SF_WRITABLE }; -class CoreExport Socket : public Flags<SocketFlag, 1> +class CoreExport Socket : public Flags<SocketFlag, 2> { - private: + protected: /** Really recieve something from the buffer * @param buf The buf to read to * @param sz How much to read @@ -63,7 +107,6 @@ class CoreExport Socket : public Flags<SocketFlag, 1> */ virtual int SendInternal(const Anope::string &buf) const; - protected: /* Socket FD */ int Sock; /* Port we're connected to */ @@ -88,8 +131,9 @@ class CoreExport Socket : public Flags<SocketFlag, 1> /** Default constructor * @param nsock The socket to use, 0 if we need to create our own * @param nIPv6 true if using ipv6 + * @param type The socket type, defaults to SOCK_STREAM */ - Socket(int nsock, bool nIPv6); + Socket(int nsock, bool nIPv6, int type = SOCK_STREAM); /** Default destructor */ @@ -130,7 +174,7 @@ class CoreExport Socket : public Flags<SocketFlag, 1> */ virtual bool ProcessRead(); - /** Called when there is something to be written to this socket + /** Called when the socket is ready to be written to * @return true on success, false to drop this socket */ virtual bool ProcessWrite(); @@ -156,20 +200,37 @@ class CoreExport Socket : public Flags<SocketFlag, 1> class CoreExport Pipe : public Socket { private: + /** The FD of the write pipe (if this isn't evenfd) + * this->Sock is the readfd + */ int WritePipe; + /** Our overloaded RecvInternal call + */ int RecvInternal(char *buf, size_t sz) const; + /** Our overloaded SendInternal call + */ int SendInternal(const Anope::string &buf) const; public: + /** Constructor + */ Pipe(); + /** Called when data is to be read + */ bool ProcessRead(); + /** Function that calls OnNotify + */ bool Read(const Anope::string &); + /** Called when this pipe needs to be woken up + */ void Notify(); + /** Should be overloaded to do something useful + */ virtual void OnNotify(); }; @@ -190,8 +251,9 @@ class CoreExport ClientSocket : public Socket * @param nPort The target port to connect to * @param nBindHost The host to bind to for connecting * @param nIPv6 true to use IPv6 + * @param type The socket type, defaults to SOCK_STREAM */ - ClientSocket(const Anope::string &nTargetHost, int nPort, const Anope::string &nBindHost, bool nIPv6); + ClientSocket(const Anope::string &nTargetHost, int nPort, const Anope::string &nBindHost = "", bool nIPv6 = false, int type = SOCK_STREAM); /** Default destructor */ diff --git a/include/sysconf.h.cmake b/include/sysconf.h.cmake index 0184c5359..2d278e3dd 100644 --- a/include/sysconf.h.cmake +++ b/include/sysconf.h.cmake @@ -8,7 +8,6 @@ #cmakedefine HAVE_STDINT_H 1 #cmakedefine HAVE_STDDEF_H 1 #cmakedefine HAVE_BACKTRACE 1 -#cmakedefine HAVE_GETHOSTBYNAME 1 #cmakedefine HAVE_GETTIMEOFDAY 1 #cmakedefine HAVE_SETGRENT 1 #cmakedefine HAVE_STRCASECMP 1 diff --git a/include/users.h b/include/users.h index 596f5ee39..b880a197a 100644 --- a/include/users.h +++ b/include/users.h @@ -51,10 +51,10 @@ class CoreExport User : public Extensible Anope::string nick; /* User's current nick */ Anope::string host; /* User's real hostname */ - Anope::string hostip; /* User's IP number */ - Anope::string vhost; /* User's virtual hostname */ - Anope::string chost; /* User's cloaked hostname */ - Anope::string realname; /* Realname */ + Anope::string vhost; /* User's virtual hostname */ + Anope::string chost; /* User's cloaked hostname */ + Anope::string realname; /* Realname */ + sockaddrs ip; /* User's IP */ Server *server; /* Server user is connected to */ time_t timestamp; /* Timestamp of the nick */ time_t my_signon; /* When did _we_ see the user? */ diff --git a/modules/core/os_defcon.cpp b/modules/core/os_defcon.cpp index 009d34dda..711046beb 100644 --- a/modules/core/os_defcon.cpp +++ b/modules/core/os_defcon.cpp @@ -274,7 +274,7 @@ class OSDefcon : public Module void OnUserConnect(User *u) { Session *session = findsession(u->host); - Exception *exception = find_hostip_exception(u->host, u->hostip); + Exception *exception = find_hostip_exception(u->host, u->ip.addr()); if (CheckDefCon(DEFCON_REDUCE_SESSION) && !exception) { diff --git a/modules/extra/m_dnsbl.cpp b/modules/extra/m_dnsbl.cpp new file mode 100644 index 000000000..cecff84a8 --- /dev/null +++ b/modules/extra/m_dnsbl.cpp @@ -0,0 +1,123 @@ +/* + * (C) 2003-2010 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + */ + +#include "module.h" + +struct Blacklist +{ + Anope::string name; + time_t bantime; + Anope::string reason; + + Blacklist(const Anope::string &n, time_t b, const Anope::string &r) : name(n), bantime(b), reason(r) { } +}; + +class DNSBLResolver : public DNSRequest +{ + dynamic_reference<User> user; + Blacklist blacklist; + public: + DNSBLResolver(User *u, const Blacklist &b, const Anope::string &host) : DNSRequest(host, DNS_QUERY_A, true), user(u), blacklist(b) { } + + void OnLookupComplete(const DNSRecord *) + { + if (!user || !SGLine) + return; + + Anope::string reason = this->blacklist.reason; + reason = reason.replace_all_ci("%i", user->ip.addr()); + reason = reason.replace_all_ci("%h", user->host); + + XLine *x = SGLine->Add(NULL, NULL, Anope::string("*@") + user->host, time(NULL) + this->blacklist.bantime, reason); + if (x) + { + static Command command_akill("AKILL", 0); + command_akill.service = OperServ; + Log(LOG_COMMAND, OperServ, &command_akill) << "for " << user->GetMask() << " (Listed in " << this->blacklist.name << ")"; + /* If AkillOnAdd is disabled send it anyway, noone wants bots around... */ + if (!Config->AkillOnAdd) + ircdproto->SendAkill(x); + } + } +}; + +class ModuleDNSBL : public Module +{ + std::vector<Blacklist> blacklists; + bool check_on_connect; + bool check_on_netburst; + + public: + ModuleDNSBL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator) + { + OnReload(false); + + Implementation i[] = { I_OnReload, I_OnPreUserConnect }; + ModuleManager::Attach(i, this, 2); + } + + void OnReload(bool) + { + ConfigReader config; + + this->check_on_connect = config.ReadFlag("m_dnsbl", "check_on_connect", "no", 0); + this->check_on_netburst = config.ReadFlag("m_dnsbl", "check_on_netburst", "no", 0); + + this->blacklists.clear(); + for (int i = 0, num = config.Enumerate("blacklist"); i < num; ++i) + { + Anope::string bname = config.ReadValue("blacklist", "name", "", i); + if (bname.empty()) + continue; + Anope::string sbantime = config.ReadValue("blacklist", "time", "4h", i); + time_t bantime = dotime(sbantime); + if (bantime < 0) + bantime = 9000; + Anope::string reason = config.ReadValue("blacklist", "reason", "", i); + + this->blacklists.push_back(Blacklist(bname, bantime, reason)); + } + } + + EventReturn OnPreUserConnect(User *u) + { + if (!this->check_on_connect && !Me->IsSynced()) + return EVENT_CONTINUE; + if (!this->check_on_netburst && !u->server->IsSynced()) + return EVENT_CONTINUE; + /* At this time we only support IPv4 */ + if (u->ip.sa.sa_family != AF_INET) + return EVENT_CONTINUE; + + unsigned long ip = u->ip.sa4.sin_addr.s_addr; + unsigned long reverse_ip = ((ip & 0xFF) << 24) | ((ip & 0xFF00) << 8) | ((ip & 0xFF0000) >> 8) | ((ip & 0xFF000000) >> 24); + + sockaddrs user_ip; + user_ip.sa4.sin_family = AF_INET; + user_ip.sa4.sin_addr.s_addr = reverse_ip; + + for (unsigned i = 0; i < this->blacklists.size(); ++i) + { + const Blacklist &b = this->blacklists[i]; + + try + { + Anope::string dnsbl_host = user_ip.addr() + "." + b.name; + new DNSBLResolver(u, b, dnsbl_host); + } + catch (const SocketException &ex) + { + Log() << "Resolver: " << ex.GetReason(); + } + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(ModuleDNSBL) + diff --git a/modules/protocol/bahamut.cpp b/modules/protocol/bahamut.cpp index ae65bd364..9938d33c7 100644 --- a/modules/protocol/bahamut.cpp +++ b/modules/protocol/bahamut.cpp @@ -34,7 +34,6 @@ IRCDVar myIrcd[] = { 0, /* vidents */ 1, /* svshold */ 1, /* time stamp on mode */ - 1, /* NICKIP */ 0, /* O:LINE */ 1, /* UMODE */ 0, /* VHOST ON NICK */ @@ -451,7 +450,7 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) if (ac != 2) { - user = do_nick(source, av[0], av[4], av[5], av[6], av[9], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, Anope::string(av[8]).is_pos_number_only() ? convertTo<uint32>(av[8]) : 0, "", ""); + user = do_nick(source, av[0], av[4], av[5], av[6], av[9], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, av[8], "", ""); if (user) { UserSetInternalModes(user, 1, &av[3]); @@ -467,7 +466,7 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) } } else - do_nick(source, av[0], "", "", "", "", Anope::string(av[1]).is_pos_number_only() ? convertTo<time_t>(av[1]) : 0, 0, "", ""); + do_nick(source, av[0], "", "", "", "", Anope::string(av[1]).is_pos_number_only() ? convertTo<time_t>(av[1]) : 0, "", "", ""); return MOD_CONT; } diff --git a/modules/protocol/inspircd11.cpp b/modules/protocol/inspircd11.cpp index ebbedd362..042814465 100644 --- a/modules/protocol/inspircd11.cpp +++ b/modules/protocol/inspircd11.cpp @@ -35,7 +35,6 @@ IRCDVar myIrcd[] = { 1, /* vidents */ 1, /* svshold */ 0, /* time stamp on mode */ - 0, /* NICKIP */ 1, /* O:LINE */ 1, /* UMODE */ 1, /* VHOST ON NICK */ @@ -714,11 +713,9 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) { time_t ts = Anope::string(av[0]).is_pos_number_only() ? convertTo<time_t>(av[0]) : 0; - user = do_nick("", av[1], av[4], av[2], source, av[7], ts, 0, av[3], ""); + user = do_nick("", av[1], av[4], av[2], source, av[7], ts, av[6], av[3], ""); if (user) { - user->hostip = av[6]; - UserSetInternalModes(user, 1, &av[5]); user->SetCloakedHost(av[3]); @@ -735,7 +732,7 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) } } else - do_nick(source, av[0], "", "", "", "", 0, 0, "", ""); + do_nick(source, av[0], "", "", "", "", 0, "", "", ""); return MOD_CONT; } diff --git a/modules/protocol/inspircd12.cpp b/modules/protocol/inspircd12.cpp index 8b6c55907..0fec91e25 100644 --- a/modules/protocol/inspircd12.cpp +++ b/modules/protocol/inspircd12.cpp @@ -35,7 +35,6 @@ IRCDVar myIrcd[] = { 1, /* vidents */ 1, /* svshold */ 0, /* time stamp on mode */ - 0, /* NICKIP */ 0, /* O:LINE */ 1, /* UMODE */ 1, /* VHOST ON NICK */ @@ -725,7 +724,7 @@ int anope_event_sethost(const Anope::string &source, int ac, const char **av) int anope_event_nick(const Anope::string &source, int ac, const char **av) { - do_nick(source, av[0], "", "", "", "", 0, 0, "", ""); + do_nick(source, av[0], "", "", "", "", 0, "", "", ""); return MOD_CONT; } @@ -764,7 +763,7 @@ int anope_event_uid(const Anope::string &source, int ac, const char **av) } user = NULL; - user = do_nick("", av[2], av[5], av[3], s->GetName(), av[ac - 1], ts, 0, av[4], av[0]); + user = do_nick("", av[2], av[5], av[3], s->GetName(), av[ac - 1], ts, av[6], av[4], av[0]); if (user) { UserSetInternalModes(user, 1, &av[8]); diff --git a/modules/protocol/inspircd20.cpp b/modules/protocol/inspircd20.cpp index be7b7c760..4445b82b9 100644 --- a/modules/protocol/inspircd20.cpp +++ b/modules/protocol/inspircd20.cpp @@ -35,7 +35,6 @@ IRCDVar myIrcd[] = { 1, /* vidents */ 1, /* svshold */ 0, /* time stamp on mode */ - 0, /* NICKIP */ 0, /* O:LINE */ 1, /* UMODE */ 1, /* VHOST ON NICK */ @@ -722,7 +721,7 @@ int anope_event_sethost(const Anope::string &source, int ac, const char **av) int anope_event_nick(const Anope::string &source, int ac, const char **av) { - do_nick(source, av[0], "", "", "", "", 0, 0, "", ""); + do_nick(source, av[0], "", "", "", "", 0, "", "", ""); return MOD_CONT; } @@ -761,7 +760,7 @@ int anope_event_uid(const Anope::string &source, int ac, const char **av) } user = NULL; - user = do_nick("", av[2], av[5], av[3], s->GetName(), av[ac - 1], ts, 0, av[4], av[0]); + user = do_nick("", av[2], av[5], av[3], s->GetName(), av[ac - 1], ts, av[6], av[4], av[0]); if (user) { UserSetInternalModes(user, 1, &av[8]); diff --git a/modules/protocol/ratbox.cpp b/modules/protocol/ratbox.cpp index db5c4d5bd..d43a80b52 100644 --- a/modules/protocol/ratbox.cpp +++ b/modules/protocol/ratbox.cpp @@ -34,7 +34,6 @@ IRCDVar myIrcd[] = { 0, /* vidents */ 0, /* svshold */ 0, /* time stamp on mode */ - 0, /* NICKIP */ 0, /* UMODE */ 0, /* O:LINE */ 0, /* VHOST ON NICK */ @@ -405,7 +404,7 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) { Server *s = Server::Find(source); /* Source is always the server */ - user = do_nick("", av[0], av[4], av[5], s->GetName(), av[8], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, 0, "*", av[7]); + user = do_nick("", av[0], av[4], av[5], s->GetName(), av[8], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, av[6], "*", av[7]); if (user) { UserSetInternalModes(user, 1, &av[3]); @@ -421,7 +420,7 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) } } else if (ac == 2) - do_nick(source, av[0], "", "", "", "", Anope::string(av[1]).is_pos_number_only() ? convertTo<time_t>(av[1]) : 0, 0, "", ""); + do_nick(source, av[0], "", "", "", "", Anope::string(av[1]).is_pos_number_only() ? convertTo<time_t>(av[1]) : 0, "", "", ""); return MOD_CONT; } diff --git a/modules/protocol/unreal32.cpp b/modules/protocol/unreal32.cpp index 047f51b15..fb12892e2 100644 --- a/modules/protocol/unreal32.cpp +++ b/modules/protocol/unreal32.cpp @@ -34,7 +34,6 @@ IRCDVar myIrcd[] = { 1, /* vidents */ 1, /* svshold */ 1, /* time stamp on mode */ - 1, /* NICKIP */ 1, /* O:LINE */ 1, /* UMODE */ 1, /* VHOST ON NICK */ @@ -133,7 +132,7 @@ class UnrealIRCdProto : public IRCDProto time_t timeleft = x->Expires - time(NULL); if (timeleft > 172800) timeleft = 172800; - send_cmd("", "BD + G %s %s %s %ld %ld :%s", x->GetUser().c_str(), x->GetHost().c_str(), x->By.c_str(), static_cast<long>(time(NULL) + timeleft), static_cast<long>(x->Expires), x->Reason.c_str()); + send_cmd("", "BD + G %s %s %s %ld %ld :%s", x->GetUser().c_str(), x->GetHost().c_str(), x->By.c_str(), static_cast<long>(time(NULL) + timeleft), static_cast<long>(x->Created), x->Reason.c_str()); } void SendSVSKillInternal(const BotInfo *source, const User *user, const Anope::string &buf) @@ -175,7 +174,7 @@ class UnrealIRCdProto : public IRCDProto void SendClientIntroduction(const Anope::string &nick, const Anope::string &user, const Anope::string &host, const Anope::string &real, const Anope::string &modes, const Anope::string &) { EnforceQlinedNick(nick, Config->ServerName); - send_cmd("", "& %s 1 %ld %s %s %s 0 %s %s%s :%s", nick.c_str(), static_cast<long>(time(NULL)), user.c_str(), host.c_str(), Config->ServerName.c_str(), modes.c_str(), host.c_str(), myIrcd->nickip ? " *" : "", real.c_str()); + send_cmd("", "& %s 1 %ld %s %s %s 0 %s %s * :%s", nick.c_str(), static_cast<long>(time(NULL)), user.c_str(), host.c_str(), Config->ServerName.c_str(), modes.c_str(), host.c_str(), real.c_str()); } void SendKickInternal(const BotInfo *source, const Channel *chan, const User *user, const Anope::string &buf) @@ -860,11 +859,17 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) <codemastr> it's sent when a nick collision occurs - so we have to leave it around for now -TSL */ - do_nick(source, av[0], av[3], av[4], av[5], av[6], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, 0, "*", ""); + do_nick(source, av[0], av[3], av[4], av[5], av[6], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, "", "*", ""); } else if (ac == 11) { - user = do_nick(source, av[0], av[3], av[4], av[5], av[10], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, ntohl(decode_ip(av[9])), av[8], ""); + Anope::string decoded_ip; + b64_decode(av[9], decoded_ip); + + sockaddrs ip; + ip.ntop(strlen(av[9]) == 8 ? AF_INET : AF_INET6, decoded_ip.c_str()); + + user = do_nick(source, av[0], av[3], av[4], av[5], av[10], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, ip.addr(), av[8], ""); if (user) { UserSetInternalModes(user, 1, &av[7]); @@ -883,7 +888,7 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) else { /* NON NICKIP */ - user = do_nick(source, av[0], av[3], av[4], av[5], av[9], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, 0, av[8], ""); + user = do_nick(source, av[0], av[3], av[4], av[5], av[9], Anope::string(av[2]).is_pos_number_only() ? convertTo<time_t>(av[2]) : 0, "", av[8], ""); if (user) { UserSetInternalModes(user, 1, &av[7]); @@ -901,7 +906,7 @@ int anope_event_nick(const Anope::string &source, int ac, const char **av) } } else - do_nick(source, av[0], "", "", "", "", Anope::string(av[1]).is_pos_number_only() ? convertTo<time_t>(av[1]) : 0, 0, "", ""); + do_nick(source, av[0], "", "", "", "", Anope::string(av[1]).is_pos_number_only() ? convertTo<time_t>(av[1]) : 0, "", "", ""); return MOD_CONT; } @@ -1136,7 +1141,7 @@ void moduleAddIRCDMsgs() Anope::AddMessage("PRIVMSG", anope_event_privmsg); Anope::AddMessage("!", anope_event_privmsg); Anope::AddMessage("QUIT", anope_event_quit); - Anope::AddMessage("),", anope_event_quit); + Anope::AddMessage(",", anope_event_quit); Anope::AddMessage("SERVER", anope_event_server); Anope::AddMessage("'", anope_event_server); Anope::AddMessage("SQUIT", anope_event_squit); diff --git a/modules/socketengines/m_socketengine_epoll.cpp b/modules/socketengines/m_socketengine_epoll.cpp index 5a2561b25..5edc40502 100644 --- a/modules/socketengines/m_socketengine_epoll.cpp +++ b/modules/socketengines/m_socketengine_epoll.cpp @@ -80,8 +80,11 @@ class SocketEngineEPoll : public SocketEngineBase --SocketCount; } - void MarkWriteable(Socket *s) + void MarkWritable(Socket *s) { + if (s->HasFlag(SF_WRITABLE)) + return; + epoll_event ev; memset(&ev, 0, sizeof(ev)); @@ -91,10 +94,15 @@ class SocketEngineEPoll : public SocketEngineBase if (epoll_ctl(EngineHandle, EPOLL_CTL_MOD, ev.data.fd, &ev) == -1) Log() << "Unable to mark fd " << ev.data.fd << " as writable in socketengine epoll: " << strerror(errno); + else + s->SetFlag(SF_WRITABLE); } - void ClearWriteable(Socket *s) + void ClearWriteble(Socket *s) { + if (!s->HasFlag(SF_WRITABLE)) + return; + epoll_event ev; memset(&ev, 0, sizeof(ev)); @@ -104,6 +112,8 @@ class SocketEngineEPoll : public SocketEngineBase if (epoll_ctl(EngineHandle, EPOLL_CTL_MOD, ev.data.fd, &ev) == -1) Log() << "Unable to mark fd " << ev.data.fd << " as unwritable in socketengine epoll: " << strerror(errno); + else + s->UnsetFlag(SF_WRITABLE); } void Process() @@ -150,7 +160,7 @@ class SocketEngineEPoll : public SocketEngineBase class ModuleSocketEngineEPoll : public Module { - SocketEngineEPoll *engine; + SocketEngineEPoll engine; public: ModuleSocketEngineEPoll(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator) @@ -159,13 +169,11 @@ class ModuleSocketEngineEPoll : public Module this->SetPermanent(true); this->SetType(SOCKETENGINE); - engine = new SocketEngineEPoll(); - SocketEngine = engine; + SocketEngine = &engine; } ~ModuleSocketEngineEPoll() { - delete engine; SocketEngine = NULL; } }; diff --git a/modules/socketengines/m_socketengine_select.cpp b/modules/socketengines/m_socketengine_select.cpp index e708693d2..b966820bd 100644 --- a/modules/socketengines/m_socketengine_select.cpp +++ b/modules/socketengines/m_socketengine_select.cpp @@ -41,14 +41,20 @@ class SocketEngineSelect : public SocketEngineBase Sockets.erase(s->GetSock()); } - void MarkWriteable(Socket *s) + void MarkWritable(Socket *s) { + if (s->HasFlag(SF_WRITABLE)) + return; FD_SET(s->GetSock(), &WriteFDs); + s->SetFlag(SF_WRITABLE); } - void ClearWriteable(Socket *s) + void ClearWritable(Socket *s) { + if (!s->HasFlag(SF_WRITABLE)) + return; FD_CLR(s->GetSock(), &WriteFDs); + s->UnsetFlag(SF_WRITABLE); } void Process() @@ -101,7 +107,7 @@ class SocketEngineSelect : public SocketEngineBase class ModuleSocketEngineSelect : public Module { - SocketEngineSelect *engine; + SocketEngineSelect engine; public: ModuleSocketEngineSelect(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator) @@ -110,13 +116,11 @@ class ModuleSocketEngineSelect : public Module this->SetPermanent(true); this->SetType(SOCKETENGINE); - engine = new SocketEngineSelect(); - SocketEngine = engine; + SocketEngine = &engine; } ~ModuleSocketEngineSelect() { - delete engine; SocketEngine = NULL; } }; diff --git a/src/actions.cpp b/src/actions.cpp index bdc5a041d..92ecca132 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -73,8 +73,7 @@ void kill_user(const Anope::string &source, const Anope::string &user, const Ano */ void common_unban(ChannelInfo *ci, const Anope::string &nick) { - Anope::string host; - uint32 ip = 0; + //uint32 ip = 0; User *u; Entry *ban, *next; @@ -87,27 +86,13 @@ void common_unban(ChannelInfo *ci, const Anope::string &nick) if (!ci->c->bans || !ci->c->bans->count) return; - if (u->hostip.empty()) - { - host = host_resolve(u->host); - /* we store the just resolved hostname so we don't - * need to do this again */ - if (!host.empty()) - u->hostip = host; - } - else - host = u->hostip; - /* Convert the host to an IP.. */ - if (!host.empty()) - ip = str_is_ip(host); - if (ircd->svsmode_unban) ircdproto->SendBanDel(ci->c, nick); else for (ban = ci->c->bans->entries; ban; ban = next) { next = ban->next; - if (entry_match(ban, u->nick, u->GetIdent(), u->host, ip) || entry_match(ban, u->nick, u->GetIdent(), u->GetDisplayedHost(), ip)) + if (entry_match(ban, u->nick, u->GetIdent(), u->host, /*ip XXX */ 0) || entry_match(ban, u->nick, u->GetIdent(), u->GetDisplayedHost(), /*ip XXX */0)) ci->c->RemoveMode(NULL, CMODE_BAN, ban->mask); } } diff --git a/src/base64.cpp b/src/base64.cpp index 4f51eb5e8..cddfb273f 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -161,17 +161,3 @@ void b64_decode(const Anope::string &src, Anope::string &target) target.erase(target.length() - 1); } -int decode_ip(const Anope::string &buf) -{ - int len = buf.length(); - Anope::string targ; - - b64_decode(buf, targ); - const struct in_addr ia = *reinterpret_cast<const struct in_addr *>(targ.c_str()); - if (len == 24) /* IPv6 */ - return 0; - else if (len == 8) /* IPv4 */ - return ia.s_addr; - else /* Error?? */ - return 0; -} diff --git a/src/channels.cpp b/src/channels.cpp index 389577f71..882dc887a 100644 --- a/src/channels.cpp +++ b/src/channels.cpp @@ -1765,33 +1765,17 @@ Entry *elist_match_mask(EList *list, const Anope::string &mask, uint32 ip) Entry *elist_match_user(EList *list, User *u) { Entry *res; - Anope::string host; - uint32 ip = 0; + //uint32 ip = 0; if (!list || !list->entries || !u) return NULL; - if (u->hostip.empty()) - { - host = host_resolve(u->host); - /* we store the just resolved hostname so we don't - * need to do this again */ - if (!host.empty()) - u->hostip = host; - } - else - host = u->hostip; - - /* Convert the host to an IP.. */ - if (!host.empty()) - ip = str_is_ip(host); - /* Match what we ve got against the lists.. */ - res = elist_match(list, u->nick, u->GetIdent(), u->host, ip); + res = elist_match(list, u->nick, u->GetIdent(), u->host, /*ip XXX*/0); if (!res) - res = elist_match(list, u->nick, u->GetIdent(), u->GetDisplayedHost(), ip); + res = elist_match(list, u->nick, u->GetIdent(), u->GetDisplayedHost(), /*ip XXX*/0); if (!res && !u->GetCloakedHost().empty() && !u->GetCloakedHost().equals_cs(u->GetDisplayedHost())) - res = elist_match(list, u->nick, u->GetIdent(), u->GetCloakedHost(), ip); + res = elist_match(list, u->nick, u->GetIdent(), u->GetCloakedHost(), /*ip XXX*/ 0); return res; } diff --git a/src/config.cpp b/src/config.cpp index d43f97e6f..d8ac4f863 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -349,6 +349,36 @@ ServerConfig::ServerConfig() : errstr(""), config_data() } SetDefaultMLock(this); + + if (IsFile(this->NameServer)) + { + std::ifstream f(this->NameServer.c_str()); + Anope::string server; + bool success = false; + + while (f.is_open() && getline(f, server.str())) + { + if (server.find("nameserver ") == 0 && getline(f, server.str())) + { + if (server.substr(11).is_pos_number_only()) + { + this->NameServer = server.substr(11); + Log(LOG_DEBUG) << "Nameserver set to " << this->NameServer; + success = true; + break; + } + } + } + + if (f.is_open()) + f.close(); + + if (!success) + { + Log() << "Unable to find nameserver, defaulting to 127.0.0.1"; + this->NameServer = "127.0.0.1"; + } + } } bool ServerConfig::CheckOnce(const Anope::string &tag) @@ -1053,6 +1083,8 @@ void ServerConfig::Read() {"mail", "restrict", "no", new ValueContainerBool(&this->RestrictMail), DT_BOOLEAN, NoValidation}, {"mail", "delay", "0", new ValueContainerTime(&this->MailDelay), DT_TIME, NoValidation}, {"mail", "dontquoteaddresses", "no", new ValueContainerBool(&this->DontQuoteAddresses), DT_BOOLEAN, NoValidation}, + {"dns", "nameserver", "127.0.0.1", new ValueContainerString(&this->NameServer), DT_STRING, NoValidation}, + {"dns", "timeout", "5", new ValueContainerTime(&this->DNSTimeout), DT_TIME, NoValidation}, {"chanserv", "nick", "ChanServ", new ValueContainerString(&this->s_ChanServ), DT_STRING | DT_NORELOAD, ValidateNotEmpty}, {"chanserv", "description", "Channel Registration Service", new ValueContainerString(&this->desc_ChanServ), DT_STRING | DT_NORELOAD, ValidateNotEmpty}, {"chanserv", "modules", "", new ValueContainerString(&ChanCoreModules), DT_STRING, NoValidation}, diff --git a/src/dns.cpp b/src/dns.cpp new file mode 100644 index 000000000..51cd14938 --- /dev/null +++ b/src/dns.cpp @@ -0,0 +1,550 @@ +#include "services.h" + +DNSManager *DNSEngine; + +static inline unsigned short GetRandomID() +{ + return random(); +} + +DNSRequest::DNSRequest(const Anope::string &addr, QueryType qt, bool cache) : address(addr), QT(qt) +{ + if (!DNSEngine) + DNSEngine = new DNSManager(); + if (DNSEngine->packets.size() == 65535) + throw SocketException("DNS queue full"); + + Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << addr << ", of type " << qt; + + if (cache && DNSEngine->CheckCache(this)) + { + delete this; + return; + } + + DNSPacket *p = new DNSPacket(); + + while (DNSEngine->requests.count((p->id = GetRandomID()))); + + DNSEngine->requests[p->id] = this; + DNSEngine->packets.push_back(p); + + p->flags = DNS_QUERYFLAGS_RD; + if (!p->AddQuestion(addr, qt)) + { + delete this; + return; + } + + SocketEngine->MarkWritable(DNSEngine->sock); + + this->timeout = new DNSRequestTimeout(this, Config->DNSTimeout); +} + +DNSRequest::~DNSRequest() +{ + if (!this->timeout->done) + { + delete this->timeout; + } +} + +void DNSRequest::OnError(DNSError error, const Anope::string &message) +{ +} + +inline DNSPacket::DNSPacket() +{ + this->id = this->flags = this->qdcount = this->ancount = this->nscount = this->arcount = this->payload_count = 0; + memset(&this->payload, 0, sizeof(this->payload)); +} + +bool DNSPacket::AddQuestion(const Anope::string &name, const QueryType qt) +{ + unsigned char temp_buffer[512] = ""; + unsigned short buffer_pos = 0; + + Anope::string working_name = name; + + switch (qt) + { + case DNS_QUERY_PTR: + { + if (working_name.find(':') != Anope::string::npos) + { + in6_addr ip; + if (!inet_pton(AF_INET6, working_name.c_str(), &ip)) + { + Log(LOG_DEBUG_2) << "Resolver: Received an invalid IP for PTR lookup (" << working_name << "): " << strerror(errno); + return false; + } + + static const char *const hex = "0123456789abcdef"; + char reverse_ip[128]; + unsigned reverse_ip_count = 0; + for (int i = 15; i >= 0; --i) + { + reverse_ip[reverse_ip_count++] = hex[ip.s6_addr[i] & 0xF]; + reverse_ip[reverse_ip_count++] = '.'; + reverse_ip[reverse_ip_count++] = hex[ip.s6_addr[i] >> 4]; + reverse_ip[reverse_ip_count++] = '.'; + } + reverse_ip[reverse_ip_count++] = 0; + + working_name = reverse_ip; + working_name += "ip6.arpa"; + Log(LOG_DEBUG_2) << "IP changed to in6.arpa format: " << working_name; + } + else + { + in_addr ip; + if (!inet_pton(AF_INET, working_name.c_str(), &ip)) + { + Log(LOG_DEBUG_2) << "Resolver: Received an invalid IP for PTR lookup (" << working_name << "): " << strerror(errno); + return false; + } + unsigned long reverse_ip = ((ip.s_addr & 0xFF) << 24) | ((ip.s_addr & 0xFF00) << 8) | ((ip.s_addr & 0xFF0000) >> 8) | ((ip.s_addr & 0xFF000000) >> 24); + char ipbuf[INET_ADDRSTRLEN]; + if (!inet_ntop(AF_INET, &reverse_ip, ipbuf, sizeof(ipbuf))) + { + Log(LOG_DEBUG_2) << "Resolver: Reformatted IP " << working_name << " back into an invalid IP: " << strerror(errno); + return false; + } + + working_name = ipbuf; + working_name += ".in-addr.arpa"; + Log(LOG_DEBUG_2) << "IP changed to in-addr.arpa format: " << working_name; + } + } + case DNS_QUERY_CNAME: + case DNS_QUERY_A: + case DNS_QUERY_AAAA: + { + sepstream sep(working_name, '.'); + Anope::string token; + while (sep.GetToken(token)) + { + temp_buffer[buffer_pos++] = token.length(); + memcpy(&temp_buffer[buffer_pos], token.c_str(), token.length()); + buffer_pos += token.length(); + } + break; + } + default: + Log(LOG_DEBUG_2) << "Resolver: Received an unknown query type format " << qt; + return false; + } + + temp_buffer[buffer_pos++] = 0; + + short i = htons(qt); + memcpy(&temp_buffer[buffer_pos], &i, 2); + buffer_pos += 2; + + i = htons(1); + memcpy(&temp_buffer[buffer_pos], &i, 2); + buffer_pos += 2; + + Log(LOG_DEBUG_3) << "Resolver: Packet payload to: " << temp_buffer; + Log(LOG_DEBUG_3) << "Resolver: Bytes used: " << buffer_pos << ", payload count " << this->payload_count; + + if (buffer_pos > (sizeof(this->payload) - this->payload_count)) + return false; + + memmove(this->payload + this->payload_count, temp_buffer, buffer_pos); + this->payload_count += buffer_pos; + + this->qdcount++; + + return true; +} + +inline void DNSPacket::FillPacket(const unsigned char *input, const size_t length) +{ + this->id = (input[0] << 8) | input[1]; + this->flags = (input[2] << 8) | input[3]; + this->qdcount = (input[4] << 8) | input[5]; + this->ancount = (input[6] << 8) | input[7]; + this->nscount = (input[8] << 8) | input[9]; + this->arcount = (input[10] << 8) | input[11]; + memcpy(this->payload, &input[12], length); + this->payload_count = length; +} + +inline void DNSPacket::FillBuffer(unsigned char *buffer) +{ + buffer[0] = this->id >> 8; + buffer[1] = this->id & 0xFF; + buffer[2] = this->flags >> 8; + buffer[3] = this->flags & 0xFF; + buffer[4] = this->qdcount >> 8; + buffer[5] = this->qdcount & 0xFF; + buffer[6] = this->ancount >> 8; + buffer[7] = this->ancount & 0xFF; + buffer[8] = this->nscount >> 8; + buffer[9] = this->nscount & 0xFF; + buffer[10] = this->arcount >> 8; + buffer[11] = this->arcount & 0xFF; + memcpy(&buffer[12], this->payload, this->payload_count); +} + +inline DNSRecord::DNSRecord() +{ + this->type = DNS_QUERY_NONE; + this->record_class = this->ttl = this->rdlength = 0; + this->created = time(NULL); +} + +DNSSocket::DNSSocket(const Anope::string &nTargetHost, int nPort) : ClientSocket(nTargetHost, nPort, "", false, SOCK_DGRAM) +{ + this->server_addr.pton(AF_INET, TargetHost, Port); +} + +DNSSocket::~DNSSocket() +{ + Log() << "Resolver: Lost connection to nameserver"; + DNSEngine->sock = NULL; +} + +int DNSSocket::SendTo(const unsigned char *buf, size_t len) const +{ + return sendto(this->GetSock(), buf, len, 0, &this->server_addr.sa, this->server_addr.size()); +} + +int DNSSocket::RecvFrom(char *buf, size_t len, sockaddrs &addrs) const +{ + socklen_t x = sizeof(addrs); + return recvfrom(this->GetSock(), buf, len, 0, &addrs.sa, &x); +} + +bool DNSSocket::ProcessRead() +{ + Log(LOG_DEBUG_2) << "Resolver: Reading from UDP socket"; + + sockaddrs from_server; + unsigned char packet_buffer[524]; + int length = this->RecvFrom(reinterpret_cast<char *>(&packet_buffer), sizeof(packet_buffer), from_server); + + if (length < 0) + return false; + + if (this->server_addr != from_server) + { + Log(LOG_DEBUG_2) << "Resolver: Received an answer from the wrong nameserver, Bad NAT or DNS forging attempt? '" << this->server_addr.addr() << "' != '" << from_server.addr() << "'"; + return true; + } + + if (length < 12) + { + Log(LOG_DEBUG_2) << "Resolver: Received a corrupted packet"; + return true; + } + + /* Remove header length */ + length -= 12; + + DNSPacket recv_packet; + recv_packet.FillPacket(packet_buffer, length); + + std::map<short, DNSRequest *>::iterator it = DNSEngine->requests.find(recv_packet.id); + if (it == DNSEngine->requests.end()) + { + Log(LOG_DEBUG_2) << "Resolver: Received an answer for something we didn't request"; + return true; + } + DNSRequest *request = it->second; + DNSEngine->requests.erase(it); + + if (!(recv_packet.flags & DNS_QUERYFLAGS_QR)) + { + Log(LOG_DEBUG_2) << "Resolver: Received a non-answer"; + request->OnError(DNS_ERROR_NOT_AN_ANSWER, "Received a non-answer"); + } + else if (recv_packet.flags & DNS_QUERYFLAGS_OPCODE) + { + Log(LOG_DEBUG_2) << "Resolver: Received a nonstandard query"; + request->OnError(DNS_ERROR_NONSTANDARD_QUERY, "Received a nonstandard query"); + } + else if (recv_packet.flags & DNS_QUERYFLAGS_RCODE) + { + switch (recv_packet.flags & DNS_QUERYFLAGS_RCODE) + { + case 1: + Log(LOG_DEBUG_2) << "Resolver: Format error"; + request->OnError(DNS_ERROR_FORMAT_ERROR, "Format error"); + break; + case 2: + Log(LOG_DEBUG_2) << "Resolver: Server failure"; + request->OnError(DNS_ERROR_SERVER_FAILURE, "Server failure"); + break; + case 3: + Log(LOG_DEBUG_2) << "Resolver: Domain name not found"; + request->OnError(DNS_ERROR_DOMAIN_NOT_FOUND, "Domain not found"); + break; + case 4: + Log(LOG_DEBUG_2) << "Resolver: Not Implemented - The name server does not support the requested kind of query."; + request->OnError(DNS_ERROR_NOT_IMPLEMENTED, "Not Implemented - The name server does not support the requested kind of query."); + break; + case 5: + Log(LOG_DEBUG_2) << "Resolver: Server refused to perform the specificed operation"; + request->OnError(DNS_ERROR_REFUSED, "Server refused to perform the specified operation"); + break; + default: + Log(LOG_DEBUG_2) << "Resolver: Unknown error"; + request->OnError(DNS_ERROR_UNKNOWN, "Unknown error"); + } + } + else if (recv_packet.ancount < 1) + { + Log(LOG_DEBUG_2) << "Resolver: No resource records returned"; + request->OnError(DNS_ERROR_NO_RECORDS, "No resource records returned"); + } + else + { + unsigned packet_pos = 0; + + /* Skip over the questions in this reponse */ + for (unsigned qdcount = 0; qdcount < recv_packet.qdcount; ++qdcount) + { + for (unsigned offset = recv_packet.payload[packet_pos]; offset; packet_pos += offset + 1, offset = recv_packet.payload[packet_pos]) + { + if (offset & 0xC0) + { + packet_pos += 2; + offset = 0; + } + } + + packet_pos += 5; + } + + for (unsigned ancount = 0; ancount < recv_packet.ancount; ++ancount) + { + Anope::string name; + unsigned packet_pos_ptr = packet_pos; + for (unsigned offset = recv_packet.payload[packet_pos_ptr]; offset; packet_pos_ptr += offset + 1, offset = recv_packet.payload[packet_pos_ptr]) + { + if (offset & 0xC0) + { + offset = (offset & 0x3F) << 8 | recv_packet.payload[++packet_pos_ptr]; + packet_pos_ptr = offset - 12; + offset = recv_packet.payload[packet_pos_ptr]; + } + for (unsigned i = 1; i <= offset; ++i) + name += recv_packet.payload[packet_pos_ptr + i]; + name += "."; + } + name.erase(name.begin() + name.length() - 1); + + if (packet_pos_ptr < packet_pos) + packet_pos += 2; + else + packet_pos = packet_pos_ptr + 1; + + DNSRecord *rr = new DNSRecord; + rr->name = name; + + Log(LOG_DEBUG_2) << "Resolver: Received answer for " << name; + + rr->type = static_cast<QueryType>(recv_packet.payload[packet_pos] << 8 | (recv_packet.payload[packet_pos + 1] & 0xFF)); + packet_pos += 2; + + rr->record_class = recv_packet.payload[packet_pos] << 8 | (recv_packet.payload[packet_pos + 1] & 0xFF); + packet_pos += 2; + + rr->ttl = (recv_packet.payload[packet_pos] << 24) | (recv_packet.payload[packet_pos + 1] << 16) | (recv_packet.payload[packet_pos + 2] << 8) | recv_packet.payload[packet_pos + 3]; + packet_pos += 4; + + rr->rdlength = (recv_packet.payload[packet_pos] << 8 | recv_packet.payload[packet_pos + 1]); + packet_pos += 2; + + switch (rr->type) + { + case DNS_QUERY_A: + { + unsigned long ip = (recv_packet.payload[packet_pos]) | (recv_packet.payload[packet_pos + 1]) << 8 | (recv_packet.payload[packet_pos + 2] << 16) | (recv_packet.payload[packet_pos + 3] << 24); + packet_pos += 4; + + char ipbuf[INET_ADDRSTRLEN]; + if (!inet_ntop(AF_INET, &ip, ipbuf, sizeof(ipbuf))) + { + Log(LOG_DEBUG_2) << "Resolver: Received an invalid IP for DNS_QUERY_A: " << strerror(errno); + request->OnError(DNS_ERROR_FORMAT_ERROR, "Received an invalid IP from the nameserver: " + stringify(strerror(errno))); + delete rr; + rr = NULL; + } + else + rr->result = ipbuf; + break; + } + case DNS_QUERY_AAAA: + { + unsigned char ip[16]; + for (int i = 0; i < 16; ++i) + ip[i] = recv_packet.payload[packet_pos + i]; + packet_pos += 16; + + char ipbuf[INET6_ADDRSTRLEN]; + if (!inet_ntop(AF_INET6, &ip, ipbuf, sizeof(ipbuf))) + { + Log(LOG_DEBUG_2) << "Resolver: Received an invalid IP for DNS_QUERY_A: " << strerror(errno); + request->OnError(DNS_ERROR_FORMAT_ERROR, "Received an invalid IP from the nameserver: " + stringify(strerror(errno))); + delete rr; + rr = NULL; + } + else + rr->result = ipbuf; + break; + } + case DNS_QUERY_CNAME: + case DNS_QUERY_PTR: + { + name.clear(); + packet_pos_ptr = packet_pos; + for (unsigned offset = recv_packet.payload[packet_pos_ptr]; offset; packet_pos_ptr += offset + 1, offset = recv_packet.payload[packet_pos_ptr]) + { + if (offset & 0xC0) + { + offset = (offset & 0x3F) << 8 | recv_packet.payload[++packet_pos_ptr]; + packet_pos_ptr = offset - 12; + offset = recv_packet.payload[packet_pos_ptr]; + } + for (unsigned i = 1; i <= offset; ++i) + name += recv_packet.payload[packet_pos_ptr + i]; + name += "."; + } + name.erase(name.begin() + name.length() - 1); + + if (packet_pos_ptr < packet_pos) + packet_pos += 2; + else + packet_pos = packet_pos_ptr + 1; + + rr->result = name; + break; + } + default: + delete rr; + request->OnError(DNS_ERROR_INVALID_TYPE, "Invalid query type"); + rr = NULL; + } + + if (rr && request->QT != rr->type) + { + delete rr; + rr = NULL; + } + + if (rr) + { + request->OnLookupComplete(rr); + DNSEngine->AddCache(rr); + } + } + } + + delete request; + return true; +} + +bool DNSSocket::ProcessWrite() +{ + Log(LOG_DEBUG_2) << "Resolver: Writing to UDP socket"; + + bool cont = true; + for (unsigned i = DNSEngine->packets.size(); i > 0; --i) + { + DNSPacket *r = DNSEngine->packets[i - 1]; + + unsigned char buffer[524]; + r->FillBuffer(buffer); + + cont = this->SendTo(buffer, r->payload_count + 12) >= 0; + + if (!cont) + break; + + delete r; + DNSEngine->packets.erase(DNSEngine->packets.begin() + i - 1); + } + SocketEngine->ClearWritable(this); + return cont; +} + +DNSManager::DNSManager() : Timer(3600, time(NULL), true) +{ + this->sock = NULL; + + try + { + this->sock = new DNSSocket(Config->NameServer, DNSManager::DNSPort); + } + catch (const SocketException &ex) + { + throw SocketException("Unable to connect to nameserver: " + ex.GetReason()); + } +} + +DNSManager::~DNSManager() +{ + delete this->sock; +} + +void DNSManager::AddCache(DNSRecord *rr) +{ + this->cache.insert(std::make_pair(rr->name, rr)); +} + +bool DNSManager::CheckCache(DNSRequest *request) +{ + std::multimap<Anope::string, DNSRecord *>::iterator it = this->cache.find(request->address); + if (it != this->cache.end()) + { + std::multimap<Anope::string, DNSRecord *>::iterator it_end = this->cache.upper_bound(request->address); + + time_t now = time(NULL); + for (; it != it_end; ++it) + { + DNSRecord *rec = it->second; + if (rec->created + rec->ttl >= now) + { + request->OnLookupComplete(rec); + } + } + + return true; + } + + return false; +} + +void DNSManager::Tick(time_t now) +{ + for (std::multimap<Anope::string, DNSRecord *>::iterator it = this->cache.begin(), it_end = this->cache.end(); it != it_end; ++it) + { + DNSRecord *req = it->second; + + if (req->created + req->ttl < now) + { + delete req; + this->cache.erase(it); + } + } +} + +DNSRequestTimeout::DNSRequestTimeout(DNSRequest *r, time_t timeout) : Timer(timeout), request(r) +{ + this->done = false; +} + +DNSRequestTimeout::~DNSRequestTimeout() +{ +} + +void DNSRequestTimeout::Tick(time_t) +{ + this->done = true; + this->request->OnError(DNS_ERROR_TIMEOUT, "Request timed out"); + delete this->request; +} + diff --git a/src/init.cpp b/src/init.cpp index aae8e9eb2..c91cc3b31 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -373,6 +373,15 @@ void Init(int ac, char **av) if (ModuleManager::LoadModule(Config->SocketEngine, NULL)) throw FatalException("Unable to load socket engine " + Config->SocketEngine); + try + { + DNSEngine = new DNSManager(); + } + catch (const SocketException &ex) + { + throw FatalException(ex.GetReason()); + } + /* Add Core MSG handles */ moduleAddMsgs(); diff --git a/src/logger.cpp b/src/logger.cpp index a5637d4d8..673f7f9cb 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -290,7 +290,7 @@ bool LogInfo::HasType(LogType type) case LOG_RAWIO: return this->RawIO; case LOG_DEBUG: - return this->Debug; + return debug ? true : this->Debug; // LOG_DEBUG_[234] default: break; diff --git a/src/main.cpp b/src/main.cpp index 04baf1dd3..d325cdd06 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -243,10 +243,6 @@ static void services_shutdown() void sighandler(int signum) { - /* We set the quit message to something default, just to be sure it is - * always set when we need it. It seems some signals slip through to the - * QUIT code without having a valid quitmsg. -GD - */ if (quitmsg.empty()) quitmsg = Anope::string("Services terminating via signal ") + strsignal(signum) + " (" + stringify(signum) + ")"; bool fatal = false; @@ -286,7 +282,6 @@ void sighandler(int signum) expire_all(); save_databases(); - quitmsg = "shutting down on sigterm"; default: fatal = true; break; diff --git a/src/misc.cpp b/src/misc.cpp index 66beff127..05240b77e 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -815,32 +815,6 @@ int myNumToken(const Anope::string &str, char dilim) /*************************************************************************/ -/** - * Resolve a host to an IP - * @param host to convert - * @return ip address - */ -Anope::string host_resolve(const Anope::string &host) -{ - struct hostent *hentp = gethostbyname(host.c_str()); - Anope::string ipreturn; - - if (hentp) - { - uint32 ip; - memcpy(&ip, hentp->h_addr, sizeof(hentp->h_length)); - struct in_addr addr; - addr.s_addr = ip; - char ipbuf[16]; - ntoa(addr, ipbuf, sizeof(ipbuf)); - ipreturn = ipbuf; - Log(LOG_DEBUG) << "resolved " << host << " to " << ipbuf; - } - return ipreturn; -} - -/*************************************************************************/ - /** Build a string list from a source string * @param src The source string * @return a list of strings @@ -1199,43 +1173,6 @@ bool str_is_pure_wildcard(const Anope::string &str) /*************************************************************************/ /** - * Check if the given string is an IP, and return the IP. - * @param str String to check - * @return The IP, if one found. 0 if none. - */ -uint32 str_is_ip(const Anope::string &str) -{ - int octets[4] = { -1, -1, -1, -1 }; - std::vector<Anope::string> octets_str = BuildStringVector(str, '.'); - - if (octets_str.size() != 4) - return false; - - for (unsigned i = 0; i < 4; ++i) - { - Anope::string octet = octets_str[i]; - - if (!octet.is_number_only()) - return 0; - - octets[i] = convertTo<int>(octet); - /* Bail out if the octet is invalid or wrongly terminated */ - if (octets[i] < 0 || octets[i] > 255) - return 0; - } - - /* Fill the IP - the dirty way */ - uint32 ip = octets[3]; - ip += octets[2] * 256; - ip += octets[1] * 65536; - ip += octets[0] * 16777216; - - return ip; -} - -/*************************************************************************/ - -/** * Check if the given string is an IP or CIDR mask, and fill the given * ip/cidr params if so. * @param str String to check diff --git a/src/operserv.cpp b/src/operserv.cpp index bbc38eb1a..1a6f20eb5 100644 --- a/src/operserv.cpp +++ b/src/operserv.cpp @@ -239,7 +239,7 @@ std::pair<XLineManager *, XLine *> XLineManager::CheckAll(User *u) if (x) { ret.first = xlm; - ret.second = x;; + ret.second = x; break; } } @@ -425,7 +425,7 @@ XLine *XLineManager::Check(User *u) if (!x->GetUser().empty() && !Anope::Match(u->GetIdent(), x->GetUser())) continue; - if (x->GetHost().empty() || (!u->hostip.empty() && Anope::Match(u->hostip, x->GetHost())) || Anope::Match(u->host, x->GetHost()) || (!u->chost.empty() && Anope::Match(u->chost, x->GetHost())) || (!u->vhost.empty() && Anope::Match(u->vhost, x->GetHost()))) + if (x->GetHost().empty() || (u->ip() && Anope::Match(u->ip.addr(), x->GetHost())) || Anope::Match(u->host, x->GetHost()) || (!u->chost.empty() && Anope::Match(u->chost, x->GetHost())) || (!u->vhost.empty() && Anope::Match(u->vhost, x->GetHost()))) { OnMatch(u, x); return x; @@ -486,14 +486,14 @@ XLine *SGLineManager::Add(BotInfo *bi, User *u, const Anope::string &mask, time_ notice_lang(bi->nick, u, OPER_AKILL_ALREADY_COVERED, mask.c_str(), canAdd.second->Mask.c_str()); } - return canAdd.second; + return NULL; } Anope::string realreason = reason; if (u && Config->AddAkiller) realreason = "[" + u->nick + "]" + reason; - XLine *x = new XLine(mask, u ? u->nick : "", expires, realreason); + XLine *x = new XLine(mask, u ? u->nick : OperServ->nick, expires, realreason); EventReturn MOD_RESULT; FOREACH_RESULT(I_OnAddAkill, OnAddAkill(u, x)); @@ -549,10 +549,10 @@ XLine *SNLineManager::Add(BotInfo *bi, User *u, const Anope::string &mask, time_ notice_lang(bi->nick, u, OPER_SNLINE_ALREADY_COVERED, mask.c_str(), canAdd.second->Mask.c_str()); } - return canAdd.second; + return NULL; } - XLine *x = new XLine(mask, u ? u->nick : "", expires, reason); + XLine *x = new XLine(mask, u ? u->nick : OperServ->nick, expires, reason); EventReturn MOD_RESULT; FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, X_SNLINE)); @@ -629,10 +629,10 @@ XLine *SQLineManager::Add(BotInfo *bi, User *u, const Anope::string &mask, time_ notice_lang(bi->nick, u, OPER_SQLINE_ALREADY_COVERED, mask.c_str(), canAdd.second->Mask.c_str()); } - return canAdd.second; + return NULL; } - XLine *x = new XLine(mask, u ? u->nick : "", expires, reason); + XLine *x = new XLine(mask, u ? u->nick : OperServ->nick, expires, reason); EventReturn MOD_RESULT; FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, X_SQLINE)); @@ -747,10 +747,10 @@ XLine *SZLineManager::Add(BotInfo *bi, User *u, const Anope::string &mask, time_ notice_lang(bi->nick, u, OPER_SZLINE_ALREADY_COVERED, mask.c_str(), canAdd.second->Mask.c_str()); } - return canAdd.second; + return NULL; } - XLine *x = new XLine(mask, u ? u->nick : "", expires, reason); + XLine *x = new XLine(mask, u ? u->nick : OperServ->nick, expires, reason); EventReturn MOD_RESULT; FOREACH_RESULT(I_OnAddXLine, OnAddXLine(u, x, X_SZLINE)); diff --git a/src/servers.cpp b/src/servers.cpp index 4b843411c..a5721083f 100644 --- a/src/servers.cpp +++ b/src/servers.cpp @@ -236,18 +236,18 @@ void Server::Sync(bool SyncLinks) this->Links[i]->Sync(true); } - if (this == Me->GetLinks().front()) + if (this->GetUplink() && this->GetUplink() == Me) { FOREACH_MOD(I_OnPreUplinkSync, OnPreUplinkSync(this)); ircdproto->SendEOB(); - Me->UnsetFlag(SERVER_SYNCING); + Me->Sync(false); } Log(this, "sync") << "is done syncing"; FOREACH_MOD(I_OnServerSync, OnServerSync(this)); - if (this == Me->GetLinks().front()) + if (this->GetUplink() && this->GetUplink() == Me) { FOREACH_MOD(I_OnUplinkSync, OnUplinkSync(this)); restore_unsynced_topics(); @@ -407,9 +407,6 @@ void CapabParse(int ac, const char **av) if (Capab_Info[j].Token.equals_ci(av[i])) { Capab.SetFlag(Capab_Info[j].Flag); - - if (Capab_Info[j].Token.equals_ci("NICKIP") && !ircd->nickip) - ircd->nickip = 1; break; } } diff --git a/src/sockets.cpp b/src/sockets.cpp index 7dcdde678..f58aa2498 100644 --- a/src/sockets.cpp +++ b/src/sockets.cpp @@ -15,11 +15,147 @@ static void TrimBuf(std::string &buffer) buffer.erase(buffer.length() - 1); } +/** Get the size of the sockaddr we represent + * @return The size + */ +size_t sockaddrs::size() const +{ + switch (sa.sa_family) + { + case AF_INET: + return sizeof(sa4); + case AF_INET6: + return sizeof(sa6); + default: + break; + } + + return 0; +} + +/** Get the port represented by this addr + * @return The port, or -1 on fail + */ +int sockaddrs::port() const +{ + switch (sa.sa_family) + { + case AF_INET: + return ntohs(sa4.sin_port); + case AF_INET6: + return ntohs(sa6.sin6_port); + default: + break; + } + + return -1; +} + +/** Get the address represented by this addr + * @return The address + */ +Anope::string sockaddrs::addr() const +{ + char address[INET6_ADDRSTRLEN + 1] = ""; + + switch (sa.sa_family) + { + case AF_INET: + if (!inet_ntop(AF_INET, &sa4.sin_addr, address, sizeof(address))) + throw SocketException(strerror(errno)); + return address; + case AF_INET6: + if (!inet_ntop(AF_INET6, &sa6.sin6_addr, address, sizeof(address))) + throw SocketException(strerror(errno)); + return address; + default: + break; + } + + return address; +} + +/** Construct the object, sets everything to 0 + */ +sockaddrs::sockaddrs() +{ + memset(this, 0, sizeof(*this)); +} + +/** Check if this sockaddr has data in it + */ +bool sockaddrs::operator()() const +{ + return this->sa.sa_family != 0; +} + +/** Compares with sockaddr with another. Compares address type, port, and address + * @return true if they are the same + */ +bool sockaddrs::operator==(const sockaddrs &other) const +{ + if (sa.sa_family != other.sa.sa_family) + return false; + switch (sa.sa_family) + { + case AF_INET: + return (sa4.sin_port == other.sa4.sin_port) && (sa4.sin_addr.s_addr == other.sa4.sin_addr.s_addr); + case AF_INET6: + return (sa6.sin6_port == other.sa6.sin6_port) && !memcmp(sa6.sin6_addr.s6_addr, other.sa6.sin6_addr.s6_addr, 16); + default: + return !memcmp(this, &other, sizeof(*this)); + } + + return false; +} + +void sockaddrs::pton(int type, const Anope::string &address, int pport) +{ + switch (type) + { + case AF_INET: + if (inet_pton(type, address.c_str(), &sa4.sin_addr) < 1) + throw SocketException(Anope::string("Invalid host: ") + strerror(errno)); + sa4.sin_family = type; + sa4.sin_port = htons(pport); + return; + case AF_INET6: + if (inet_pton(type, address.c_str(), &sa6.sin6_addr) < 1) + throw SocketException(Anope::string("Invalid host: ") + strerror(errno)); + sa6.sin6_family = type; + sa6.sin6_port = htons(pport); + return; + default: + break; + } + + throw CoreException("Invalid socket type"); +} + +void sockaddrs::ntop(int type, const void *src) +{ + switch (type) + { + case AF_INET: + sa4.sin_addr = *reinterpret_cast<const in_addr *>(src); + sa4.sin_family = type; + return; + case AF_INET6: + sa6.sin6_addr = *reinterpret_cast<const in6_addr *>(src); + sa6.sin6_family = type; + return; + default: + break; + } + + throw CoreException("Invalid socket type"); +} + SocketEngineBase::SocketEngineBase() { #ifdef _WIN32 if (WSAStartup(MAKEWORD(2, 0), &wsa)) - Log() << "Failed to initialize WinSock library"; + throw FatalException("Failed to initialize WinSock library"); #endif } @@ -40,13 +176,14 @@ Socket::Socket() /** Constructor * @param nsock The socket * @param nIPv6 IPv6? + * @param type The socket type, defaults to SOCK_STREAM */ -Socket::Socket(int nsock, bool nIPv6) +Socket::Socket(int nsock, bool nIPv6, int type) { Type = SOCKTYPE_CLIENT; IPv6 = nIPv6; if (nsock == 0) - Sock = socket(IPv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); + Sock = socket(IPv6 ? AF_INET6 : AF_INET, type, 0); else Sock = nsock; SocketEngine->AddSocket(this); @@ -56,7 +193,8 @@ Socket::Socket(int nsock, bool nIPv6) */ Socket::~Socket() { - SocketEngine->DelSocket(this); + if (SocketEngine) + SocketEngine->DelSocket(this); CloseSocket(Sock); } @@ -183,7 +321,7 @@ bool Socket::ProcessRead() return true; } -/** Called when there is something to be written to this socket +/** Called when the socket is ready to be written to * @return true on success, false to drop this socket */ bool Socket::ProcessWrite() @@ -197,7 +335,7 @@ bool Socket::ProcessWrite() return false; } WriteBuffer.clear(); - SocketEngine->ClearWriteable(this); + SocketEngine->ClearWritable(this); return true; } @@ -243,7 +381,7 @@ void Socket::Write(const char *message, ...) void Socket::Write(const Anope::string &message) { WriteBuffer.append(message.str() + "\r\n"); - SocketEngine->MarkWriteable(this); + SocketEngine->MarkWritable(this); } /** Constructor @@ -251,103 +389,22 @@ void Socket::Write(const Anope::string &message) * @param nu The user using this socket * @param nsock The socket * @param nIPv6 IPv6 + * @param type The socket type, defaults to SOCK_STREAM */ -ClientSocket::ClientSocket(const Anope::string &nTargetHost, int nPort, const Anope::string &nBindHost, bool nIPv6) : Socket(0, nIPv6), TargetHost(nTargetHost), Port(nPort), BindHost(nBindHost) +ClientSocket::ClientSocket(const Anope::string &nTargetHost, int nPort, const Anope::string &nBindHost, bool nIPv6, int type) : Socket(0, nIPv6, type), TargetHost(nTargetHost), Port(nPort), BindHost(nBindHost) { - addrinfo hints; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = 0; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_family = IPv6 ? AF_INET6 : AF_INET; - if (!BindHost.empty()) { - addrinfo *bindar; - sockaddr_in bindaddr; - sockaddr_in6 bindaddr6; - - if (!getaddrinfo(BindHost.c_str(), NULL, &hints, &bindar)) - { - if (IPv6) - memcpy(&bindaddr6, bindar->ai_addr, bindar->ai_addrlen); - else - memcpy(&bindaddr, bindar->ai_addr, bindar->ai_addrlen); - - freeaddrinfo(bindar); - } - else - { - if (IPv6) - { - bindaddr6.sin6_family = AF_INET6; - - if (inet_pton(AF_INET6, BindHost.c_str(), &bindaddr6.sin6_addr) < 1) - throw SocketException(Anope::string("Invalid bind host: ") + strerror(errno)); - } - else - { - bindaddr.sin_family = AF_INET; - - if (inet_pton(AF_INET, BindHost.c_str(), &bindaddr.sin_addr) < 1) - throw SocketException(Anope::string("Invalid bind host: ") + strerror(errno)); - } - } - - if (IPv6) - { - if (bind(Sock, reinterpret_cast<sockaddr *>(&bindaddr6), sizeof(bindaddr6)) == -1) - throw SocketException(Anope::string("Unable to bind to address: ") + strerror(errno)); - } - else - { - if (bind(Sock, reinterpret_cast<sockaddr *>(&bindaddr), sizeof(bindaddr)) == -1) - throw SocketException(Anope::string("Unable to bind to address: ") + strerror(errno)); - } - } - - addrinfo *conar; - sockaddr_in6 addr6; - sockaddr_in addr; - - if (!getaddrinfo(TargetHost.c_str(), NULL, &hints, &conar)) - { - if (IPv6) - memcpy(&addr6, conar->ai_addr, conar->ai_addrlen); - else - memcpy(&addr, conar->ai_addr, conar->ai_addrlen); - - freeaddrinfo(conar); - } - else - { - if (IPv6) - { - if (inet_pton(AF_INET6, TargetHost.c_str(), &addr6.sin6_addr) < 1) - throw SocketException(Anope::string("Invalid server host: ") + strerror(errno)); - } - else - { - if (inet_pton(AF_INET, TargetHost.c_str(), &addr.sin_addr) < 1) - throw SocketException(Anope::string("Invalid server host: ") + strerror(errno)); - } - } - - if (IPv6) - { - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(nPort); - - if (connect(Sock, reinterpret_cast<sockaddr *>(&addr6), sizeof(addr6)) == -1) - throw SocketException(Anope::string("Error connecting to server: ") + strerror(errno)); + sockaddrs bindaddr; + bindaddr.pton(IPv6 ? AF_INET6 : AF_INET, BindHost, 0); + if (bind(Sock, &bindaddr.sa, bindaddr.size()) == -1) + throw SocketException(Anope::string("Unable to bind to address: ") + strerror(errno)); } - else - { - addr.sin_family = AF_INET; - addr.sin_port = htons(nPort); - if (connect(Sock, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) == -1) - throw SocketException(Anope::string("Error connecting to server: ") + strerror(errno)); - } + sockaddrs conaddr; + conaddr.pton(IPv6 ? AF_INET6 : AF_INET, TargetHost, Port); + if (connect(Sock, &conaddr.sa, conaddr.size()) == -1) + throw SocketException(Anope::string("Error connecting to server: ") + strerror(errno)); this->SetNonBlocking(); } @@ -377,36 +434,11 @@ ListenSocket::ListenSocket(const Anope::string &bindip, int port) : Socket(0, (b BindIP = bindip; Port = port; - sockaddr_in sock_addr; - sockaddr_in6 sock_addr6; - - if (IPv6) - { - sock_addr6.sin6_family = AF_INET6; - sock_addr6.sin6_port = htons(port); - - if (inet_pton(AF_INET6, bindip.c_str(), &sock_addr6.sin6_addr) < 1) - throw SocketException(Anope::string("Invalid bind host: ") + strerror(errno)); - } - else - { - sock_addr.sin_family = AF_INET; - sock_addr.sin_port = htons(port); - - if (inet_pton(AF_INET, bindip.c_str(), &sock_addr.sin_addr) < 1) - throw SocketException(Anope::string("Invalid bind host: ") + strerror(errno)); - } + sockaddrs sockaddr; + sockaddr.pton(IPv6 ? AF_INET6 : AF_INET, BindIP, Port); - if (IPv6) - { - if (bind(Sock, reinterpret_cast<sockaddr *>(&sock_addr6), sizeof(sock_addr6)) == -1) - throw SocketException(Anope::string("Unable to bind to address: ") + strerror(errno)); - } - else - { - if (bind(Sock, reinterpret_cast<sockaddr *>(&sock_addr), sizeof(sock_addr)) == -1) - throw SocketException(Anope::string("Unable to bind to address: ") + strerror(errno)); - } + if (bind(Sock, &sockaddr.sa, sockaddr.size()) == -1) + throw SocketException(Anope::string("Unable to bind to address: ") + strerror(errno)); if (listen(Sock, 5) == -1) throw SocketException(Anope::string("Unable to listen: ") + strerror(errno)); @@ -426,7 +458,7 @@ bool ListenSocket::ProcessRead() { int newsock = accept(Sock, NULL, NULL); -#ifndef _WIN32 +#ifndef INVALID_SOCKET # define INVALID_SOCKET 0 #endif diff --git a/src/users.cpp b/src/users.cpp index c9c5ce20e..36b32071b 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -186,7 +186,7 @@ User::~User() { Log(LOG_DEBUG_2) << "User::~User() called"; - Log(this, "disconnect") << " (" << this->realname << ") " << "disconnected from the network (" << this->server->GetName() << ")"; + Log(this, "disconnect") << "(" << this->realname << ") " << "disconnected from the network (" << this->server->GetName() << ")"; this->Logout(); @@ -642,16 +642,13 @@ User *finduser(const Anope::string &nick) /* Handle a server NICK command. */ -User *do_nick(const Anope::string &source, const Anope::string &nick, const Anope::string &username, const Anope::string &host, const Anope::string &server, const Anope::string &realname, time_t ts, uint32 ip, const Anope::string &vhost, const Anope::string &uid) +User *do_nick(const Anope::string &source, const Anope::string &nick, const Anope::string &username, const Anope::string &host, const Anope::string &server, const Anope::string &realname, time_t ts, const Anope::string &ip, const Anope::string &vhost, const Anope::string &uid) { User *user = NULL; Anope::string vhost2 = vhost; if (source.empty()) { - char ipbuf[16]; - struct in_addr addr; - if (ircd->nickvhost && !vhost2.empty() && vhost2.equals_cs("*")) { vhost2.clear(); @@ -660,12 +657,6 @@ User *do_nick(const Anope::string &source, const Anope::string &nick, const Anop /* This is a new user; create a User structure for it. */ Log(LOG_DEBUG) << "new user: " << nick; - if (ircd->nickip) - { - addr.s_addr = htonl(ip); - ntoa(addr, ipbuf, sizeof(ipbuf)); - } - Server *serv = Server::Find(server); /* Allocate User structure and fill it in. */ @@ -677,14 +668,24 @@ User *do_nick(const Anope::string &source, const Anope::string &nick, const Anop if (!vhost2.empty()) user->SetCloakedHost(vhost2); user->SetVIdent(username); - /* We now store the user's ip in the user_ struct, - * because we will use it in serveral places -- DrStein */ - if (ircd->nickip) - user->hostip = ipbuf; - else - user->hostip = ""; - Log(user, "connect") << (ircd->nickvhost && !vhost2.empty() ? Anope::string("(") + vhost2 + ")" : "") << ") (" << user->realname << ") " << (ircd->nickip ? Anope::string("[") + ipbuf + "] " : "") << "connected to the network (" << serv->GetName() << ")"; + if (!ip.empty()) + { + try + { + if (ip.find(':') != Anope::string::npos) + user->ip.pton(AF_INET6, ip); + else + user->ip.pton(AF_INET, ip); + } + catch (const SocketException &ex) + { + Log() << "Received an invalid IP for user " << user->nick << " (" << ip << ")"; + Log() << ex.GetReason(); + } + } + + Log(user, "connect") << (ircd->nickvhost && !vhost2.empty() ? Anope::string("(") + vhost2 + ")" : "") << ") (" << user->realname << ") " << (user->ip() ? Anope::string("[") + user->ip.addr() + "] " : "") << "connected to the network (" << serv->GetName() << ")"; EventReturn MOD_RESULT; FOREACH_RESULT(I_OnPreUserConnect, OnPreUserConnect(user)); @@ -692,7 +693,7 @@ User *do_nick(const Anope::string &source, const Anope::string &nick, const Anop return finduser(nick); if (Config->LimitSessions && !serv->IsULined()) - add_session(nick, host, ipbuf); + add_session(nick, host, user->ip() ? user->ip.addr() : ""); XLineManager::CheckAll(user); |