mirror of
https://github.com/anope/anope.git
synced 2026-06-28 23:46:39 +02:00
Move DNS handling to a module
This commit is contained in:
@@ -1034,52 +1034,6 @@ mail
|
||||
%t"
|
||||
}
|
||||
|
||||
/*
|
||||
* [OPTIONAL] DNS Config
|
||||
*
|
||||
* This section is used to configure DNS.
|
||||
* At this time DNS is only used by a few modules (m_dnsbl and os_dns)
|
||||
* 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 unix 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 in seconds before a DNS query has timed out
|
||||
*/
|
||||
timeout = 5
|
||||
|
||||
/* Only edit below if you are expecting to use os_dns */
|
||||
|
||||
/*
|
||||
* The IP and port services use to listen for DNS queries.
|
||||
* Note that ports less than 1024 are privileged on UNIX/Linux systems, and
|
||||
* require Anope to be started as root. If you do this, it is recommended you
|
||||
* set options:user and options:group so Anope can change users after binding
|
||||
* to this port.
|
||||
*/
|
||||
ip = "0.0.0.0"
|
||||
port = 53
|
||||
|
||||
/*
|
||||
* SOA record information.
|
||||
*/
|
||||
admin = "admin@example.com"
|
||||
/* This should be the names of the public facing nameserver serving the records */
|
||||
nameservers = "ns1.example.com ns2.example.com"
|
||||
/* The time slave servers are allowed to cache. This should be reasonably low
|
||||
* if you want your records to be updated without much delay.
|
||||
*/
|
||||
refresh = 3600
|
||||
}
|
||||
|
||||
/*
|
||||
* [REQUIRED] Database configuration.
|
||||
*
|
||||
|
||||
@@ -16,6 +16,52 @@
|
||||
*/
|
||||
module { name = "help" }
|
||||
|
||||
/*
|
||||
* m_dns
|
||||
*
|
||||
* Adds support for the DNS protocol. By itself this module does nothing useful,
|
||||
* but other modules such as m_dnsbl and os_dns require this.
|
||||
*/
|
||||
#module { name = "m_dns" }
|
||||
dns
|
||||
{
|
||||
/*
|
||||
* The nameserver to use for resolving hostnames, must be an IP or a resolver configuration file.
|
||||
* The below should work fine on all unix 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 in seconds before a DNS query has timed out
|
||||
*/
|
||||
timeout = 5
|
||||
|
||||
/* Only edit below if you are expecting to use os_dns or otherwise answer DNS queries. */
|
||||
|
||||
/*
|
||||
* The IP and port services use to listen for DNS queries.
|
||||
* Note that ports less than 1024 are privileged on UNIX/Linux systems, and
|
||||
* require Anope to be started as root. If you do this, it is recommended you
|
||||
* set options:user and options:group so Anope can change users after binding
|
||||
* to this port.
|
||||
*/
|
||||
ip = "0.0.0.0"
|
||||
port = 53
|
||||
|
||||
/*
|
||||
* SOA record information.
|
||||
*/
|
||||
admin = "admin@example.com"
|
||||
/* This should be the names of the public facing nameserver serving the records */
|
||||
nameservers = "ns1.example.com ns2.example.com"
|
||||
/* The time slave servers are allowed to cache. This should be reasonably low
|
||||
* if you want your records to be updated without much delay.
|
||||
*/
|
||||
refresh = 3600
|
||||
}
|
||||
|
||||
/*
|
||||
* m_dnsbl
|
||||
*
|
||||
@@ -23,7 +69,7 @@ module { name = "help" }
|
||||
* is found on the blacklist they will be immediately banned. This is a crucial module
|
||||
* to prevent bot attacks.
|
||||
*/
|
||||
module { name = "m_dnsbl" }
|
||||
#module { name = "m_dnsbl" }
|
||||
m_dnsbl
|
||||
{
|
||||
/*
|
||||
|
||||
@@ -359,6 +359,8 @@ defcon
|
||||
*
|
||||
* Provides the command operserv/dns.
|
||||
*
|
||||
* This module requires that m_dns is loaded.
|
||||
*
|
||||
* This module allows controlling a DNS zone. This is useful for
|
||||
* controlling what servers users are placed on for load balancing,
|
||||
* and to automatically remove split servers.
|
||||
@@ -371,6 +373,7 @@ defcon
|
||||
*
|
||||
* To do this using BIND, configure similar to:
|
||||
*
|
||||
* options { max-refresh-time 60; };
|
||||
* zone "irc.example.com" IN {
|
||||
* type slave;
|
||||
* masters { 127.0.0.1 port 5353; };
|
||||
|
||||
@@ -506,6 +506,13 @@ namespace Anope
|
||||
* @param Raw message from the uplink
|
||||
*/
|
||||
extern void Process(const Anope::string &);
|
||||
|
||||
/** Does a blocking dns query and returns the first IP.
|
||||
* @param host host to look up
|
||||
* @param type inet addr type
|
||||
* @return the IP if it was found, else the host
|
||||
*/
|
||||
extern Anope::string Resolve(const Anope::string &host, int type);
|
||||
}
|
||||
|
||||
/** sepstream allows for splitting token seperated lists.
|
||||
|
||||
@@ -478,20 +478,6 @@ class CoreExport ServerConfig
|
||||
Anope::string MailEmailchangeSubject, MailEmailchangeMessage;
|
||||
Anope::string MailMemoSubject, MailMemoMessage;
|
||||
|
||||
/* Nameserver to use for resolving hostnames */
|
||||
Anope::string NameServer;
|
||||
/* Time before a DNS query is considered dead */
|
||||
time_t DNSTimeout;
|
||||
/* The IP/port DNS queries come in on */
|
||||
Anope::string DNSIP;
|
||||
int DNSPort;
|
||||
/* DNS SOA admin */
|
||||
Anope::string DNSSOAAdmin;
|
||||
/* DNS SOA primary NS */
|
||||
Anope::string DNSSOANS;
|
||||
/* SOA Refresh time */
|
||||
unsigned DNSSOARefresh;
|
||||
|
||||
/* Prefix of guest nicks when a user gets forced off of a nick */
|
||||
Anope::string NSGuestNickPrefix;
|
||||
/* Allow users to set kill immed on */
|
||||
|
||||
@@ -48,7 +48,6 @@ class User;
|
||||
class XLine;
|
||||
class XLineManager;
|
||||
struct BadWord;
|
||||
namespace DNS { struct Query; }
|
||||
struct Exception;
|
||||
struct MemoInfo;
|
||||
struct ModeLock;
|
||||
|
||||
-278
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2003-2012 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*
|
||||
* Based on the original code of Epona by Lara.
|
||||
* Based on the original code of Services by Andy Church.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DNS_H
|
||||
#define DNS_H
|
||||
|
||||
#include "sockets.h"
|
||||
#include "timers.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace DNS
|
||||
{
|
||||
|
||||
/** Valid query types
|
||||
*/
|
||||
enum QueryType
|
||||
{
|
||||
/* Nothing */
|
||||
QUERY_NONE,
|
||||
/* A simple A lookup */
|
||||
QUERY_A = 1,
|
||||
/* An authoritative name server */
|
||||
QUERY_NS = 2,
|
||||
/* A CNAME lookup */
|
||||
QUERY_CNAME = 5,
|
||||
/* Start of a zone of authority */
|
||||
QUERY_SOA = 6,
|
||||
/* Reverse DNS lookup */
|
||||
QUERY_PTR = 12,
|
||||
/* IPv6 AAAA lookup */
|
||||
QUERY_AAAA = 28,
|
||||
/* Zone transfer */
|
||||
QUERY_AXFR = 252
|
||||
};
|
||||
|
||||
/** 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_TIMEOUT,
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
struct CoreExport Question
|
||||
{
|
||||
Anope::string name;
|
||||
QueryType type;
|
||||
unsigned short qclass;
|
||||
|
||||
Question();
|
||||
Question(const Anope::string &, QueryType, unsigned short = 1);
|
||||
};
|
||||
|
||||
struct CoreExport ResourceRecord : public Question
|
||||
{
|
||||
unsigned int ttl;
|
||||
Anope::string rdata;
|
||||
time_t created;
|
||||
|
||||
ResourceRecord(const Anope::string &, QueryType, unsigned short = 1);
|
||||
ResourceRecord(const Question &);
|
||||
};
|
||||
|
||||
struct CoreExport Query
|
||||
{
|
||||
std::vector<Question> questions;
|
||||
std::vector<ResourceRecord> answers, authorities, additional;
|
||||
Error error;
|
||||
|
||||
Query();
|
||||
Query(const Question &q);
|
||||
};
|
||||
|
||||
/** A DNS query.
|
||||
*/
|
||||
class CoreExport Request : public Timer, public Question
|
||||
{
|
||||
/* Use result cache if available */
|
||||
bool use_cache;
|
||||
|
||||
public:
|
||||
/* Request id */
|
||||
unsigned short id;
|
||||
/* Creator of this request */
|
||||
Module *creator;
|
||||
|
||||
Request(const Anope::string &addr, QueryType qt, bool cache = false, Module *c = NULL);
|
||||
virtual ~Request();
|
||||
|
||||
void Process();
|
||||
|
||||
/** Called when this request succeeds
|
||||
* @param r The query sent back from the nameserver
|
||||
*/
|
||||
virtual void OnLookupComplete(const Query *r) = 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 *r);
|
||||
|
||||
/** Used to time out the query, Calls OnError and lets the TimerManager
|
||||
* delete this request.
|
||||
*/
|
||||
void Tick(time_t) anope_override;
|
||||
};
|
||||
|
||||
/** A full packet sent or recieved to/from the nameserver
|
||||
*/
|
||||
class Packet : public Query
|
||||
{
|
||||
static const int POINTER = 0xC0;
|
||||
static const int LABEL = 0x3F;
|
||||
|
||||
void PackName(unsigned char *output, unsigned short output_size, unsigned short &pos, const Anope::string &name);
|
||||
Anope::string UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos);
|
||||
|
||||
Question UnpackQuestion(const unsigned char *input, unsigned short input_size, unsigned short &pos);
|
||||
ResourceRecord UnpackResourceRecord(const unsigned char *input, unsigned short input_size, unsigned short &poss);
|
||||
public:
|
||||
static const int HEADER_LENGTH = 12;
|
||||
|
||||
/* Source or destination of the packet */
|
||||
sockaddrs addr;
|
||||
/* ID for this packet */
|
||||
unsigned short id;
|
||||
/* Flags on the packet */
|
||||
unsigned short flags;
|
||||
|
||||
Packet(sockaddrs *a);
|
||||
void Fill(const unsigned char *input, const unsigned short len);
|
||||
unsigned short Pack(unsigned char *output, unsigned short output_size);
|
||||
};
|
||||
|
||||
/** DNS manager
|
||||
*/
|
||||
class CoreExport Manager : public Timer
|
||||
{
|
||||
class ReplySocket : public virtual Socket
|
||||
{
|
||||
public:
|
||||
virtual ~ReplySocket() { }
|
||||
virtual void Reply(Packet *p) = 0;
|
||||
};
|
||||
|
||||
/* Listens for TCP requests */
|
||||
class TCPSocket : public ListenSocket
|
||||
{
|
||||
/* A TCP client */
|
||||
class Client : public ClientSocket, public Timer, public ReplySocket
|
||||
{
|
||||
TCPSocket *tcpsock;
|
||||
Packet *packet;
|
||||
unsigned char packet_buffer[524];
|
||||
int length;
|
||||
|
||||
public:
|
||||
Client(TCPSocket *ls, int fd, const sockaddrs &addr);
|
||||
~Client();
|
||||
|
||||
/* Times out after a few seconds */
|
||||
void Tick(time_t) anope_override { }
|
||||
void Reply(Packet *p) anope_override;
|
||||
bool ProcessRead() anope_override;
|
||||
bool ProcessWrite() anope_override;
|
||||
};
|
||||
|
||||
public:
|
||||
TCPSocket(const Anope::string &ip, int port);
|
||||
|
||||
ClientSocket *OnAccept(int fd, const sockaddrs &addr) anope_override;
|
||||
};
|
||||
|
||||
/* Listens for UDP requests */
|
||||
class UDPSocket : public ReplySocket
|
||||
{
|
||||
std::deque<Packet *> packets;
|
||||
public:
|
||||
|
||||
UDPSocket(const Anope::string &ip, int port);
|
||||
~UDPSocket();
|
||||
|
||||
void Reply(Packet *p) anope_override;
|
||||
|
||||
std::deque<Packet *>& GetPackets() { return packets; }
|
||||
|
||||
bool ProcessRead() anope_override;
|
||||
|
||||
bool ProcessWrite() anope_override;
|
||||
};
|
||||
|
||||
typedef std::multimap<Anope::string, ResourceRecord, ci::less> cache_map;
|
||||
cache_map cache;
|
||||
|
||||
bool listen;
|
||||
uint32_t serial;
|
||||
|
||||
public:
|
||||
TCPSocket *tcpsock;
|
||||
UDPSocket *udpsock;
|
||||
|
||||
sockaddrs addrs;
|
||||
std::map<unsigned short, Request *> requests;
|
||||
|
||||
Manager(const Anope::string &nameserver, const Anope::string &ip, int port);
|
||||
~Manager();
|
||||
|
||||
bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from);
|
||||
|
||||
/** Add a record to the dns cache
|
||||
* @param r The record
|
||||
*/
|
||||
void AddCache(Query &r);
|
||||
|
||||
/** 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(Request *request);
|
||||
|
||||
/** Tick this timer, used to clear the DNS cache.
|
||||
*/
|
||||
void Tick(time_t now) anope_override;
|
||||
|
||||
/** Cleanup all pending DNS queries for a module
|
||||
* @param mod The module
|
||||
*/
|
||||
void Cleanup(Module *mod);
|
||||
|
||||
void UpdateSerial();
|
||||
uint32_t GetSerial() const;
|
||||
|
||||
/** Does a BLOCKING DNS query and returns the first IP.
|
||||
* Only use this if you know what you are doing. Unless you specifically
|
||||
* need a blocking query use the DNSRequest system
|
||||
*/
|
||||
static Query BlockingQuery(const Anope::string &mask, QueryType qt);
|
||||
};
|
||||
|
||||
extern CoreExport Manager *Engine;
|
||||
|
||||
} // namespace DNS
|
||||
|
||||
#endif // DNS_H
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "channels.h"
|
||||
#include "commands.h"
|
||||
#include "config.h"
|
||||
#include "dns.h"
|
||||
#include "extensible.h"
|
||||
#include "hashcomp.h"
|
||||
#include "language.h"
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "../extra/dns.h"
|
||||
|
||||
static ServiceReference<DNS::Manager> dnsmanager("DNS::Manager", "dns/manager");
|
||||
|
||||
class DNSServer;
|
||||
static std::vector<DNSServer *> dns_servers;
|
||||
@@ -44,8 +47,8 @@ class DNSServer : public Serializable
|
||||
void Pool(bool p)
|
||||
{
|
||||
pooled = p;
|
||||
if (DNS::Engine)
|
||||
DNS::Engine->UpdateSerial();
|
||||
if (dnsmanager)
|
||||
dnsmanager->UpdateSerial();
|
||||
}
|
||||
|
||||
|
||||
@@ -220,8 +223,8 @@ class CommandOSDNS : public Command
|
||||
source.Reply(_("Added IP %s to %s."), params[2].c_str(), s->GetName().c_str());
|
||||
Log(LOG_ADMIN, source, this) << "to add IP " << params[2] << " to " << s->GetName();
|
||||
|
||||
if (s->Pooled())
|
||||
DNS::Engine->UpdateSerial();
|
||||
if (s->Pooled() && dnsmanager)
|
||||
dnsmanager->UpdateSerial();
|
||||
}
|
||||
|
||||
void DelIP(CommandSource &source, const std::vector<Anope::string> ¶ms)
|
||||
@@ -247,8 +250,8 @@ class CommandOSDNS : public Command
|
||||
s->Pool(false);
|
||||
}
|
||||
|
||||
if (s->Pooled())
|
||||
DNS::Engine->UpdateSerial();
|
||||
if (s->Pooled() && dnsmanager)
|
||||
dnsmanager->UpdateSerial();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2003-2012 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*
|
||||
* Based on the original code of Epona by Lara.
|
||||
* Based on the original code of Services by Andy Church.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DNS_H
|
||||
#define DNS_H
|
||||
|
||||
namespace DNS
|
||||
{
|
||||
/** Valid query types
|
||||
*/
|
||||
enum QueryType
|
||||
{
|
||||
/* Nothing */
|
||||
QUERY_NONE,
|
||||
/* A simple A lookup */
|
||||
QUERY_A = 1,
|
||||
/* An authoritative name server */
|
||||
QUERY_NS = 2,
|
||||
/* A CNAME lookup */
|
||||
QUERY_CNAME = 5,
|
||||
/* Start of a zone of authority */
|
||||
QUERY_SOA = 6,
|
||||
/* Reverse DNS lookup */
|
||||
QUERY_PTR = 12,
|
||||
/* IPv6 AAAA lookup */
|
||||
QUERY_AAAA = 28,
|
||||
/* Zone transfer */
|
||||
QUERY_AXFR = 252
|
||||
};
|
||||
|
||||
/** 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_TIMEOUT,
|
||||
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
|
||||
};
|
||||
|
||||
struct Question
|
||||
{
|
||||
Anope::string name;
|
||||
QueryType type;
|
||||
unsigned short qclass;
|
||||
|
||||
Question() : type(QUERY_NONE), qclass(0) { }
|
||||
Question(const Anope::string &n, QueryType t, unsigned short c = 1) : name(n), type(t), qclass(c) { }
|
||||
};
|
||||
|
||||
struct ResourceRecord : public Question
|
||||
{
|
||||
unsigned int ttl;
|
||||
Anope::string rdata;
|
||||
time_t created;
|
||||
|
||||
ResourceRecord(const Anope::string &n, QueryType t, unsigned short c = 1) : Question(n, t, c), ttl(0), created(Anope::CurTime) { }
|
||||
ResourceRecord(const Question &q) : Question(q), ttl(0), created(Anope::CurTime) { }
|
||||
};
|
||||
|
||||
struct Query
|
||||
{
|
||||
std::vector<Question> questions;
|
||||
std::vector<ResourceRecord> answers, authorities, additional;
|
||||
Error error;
|
||||
|
||||
Query() : error(ERROR_NONE) { }
|
||||
Query(const Question &q) : error(ERROR_NONE) { questions.push_back(q); }
|
||||
};
|
||||
|
||||
class Packet : public Query
|
||||
{
|
||||
public:
|
||||
static const int POINTER = 0xC0;
|
||||
static const int LABEL = 0x3F;
|
||||
static const int HEADER_LENGTH = 12;
|
||||
|
||||
virtual ~Packet() { }
|
||||
};
|
||||
|
||||
class ReplySocket;
|
||||
class Request;
|
||||
|
||||
/** DNS manager
|
||||
*/
|
||||
class Manager : public Service
|
||||
{
|
||||
public:
|
||||
Manager(Module *creator) : Service(creator, "DNS::Manager", "dns/manager") { }
|
||||
virtual ~Manager() { }
|
||||
|
||||
virtual void Process(Request *req) = 0;
|
||||
virtual void RemoveRequest(Request *req) = 0;
|
||||
|
||||
virtual bool HandlePacket(ReplySocket *s, const unsigned char *const data, int len, sockaddrs *from) = 0;
|
||||
|
||||
virtual void UpdateSerial() = 0;
|
||||
virtual uint32_t GetSerial() const = 0;
|
||||
};
|
||||
|
||||
/** A DNS query.
|
||||
*/
|
||||
class Request : public Timer, public Question
|
||||
{
|
||||
Manager *manager;
|
||||
public:
|
||||
/* Use result cache if available */
|
||||
bool use_cache;
|
||||
/* Request id */
|
||||
unsigned short id;
|
||||
/* Creator of this request */
|
||||
Module *creator;
|
||||
|
||||
Request(Manager *mgr, Module *c, const Anope::string &addr, QueryType qt, bool cache = false) : Timer(0), Question(addr, qt), manager(mgr),
|
||||
use_cache(cache), id(0), creator(c) { }
|
||||
|
||||
virtual ~Request()
|
||||
{
|
||||
manager->RemoveRequest(this);
|
||||
}
|
||||
|
||||
/** Called when this request succeeds
|
||||
* @param r The query sent back from the nameserver
|
||||
*/
|
||||
virtual void OnLookupComplete(const Query *r) = 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 *r) { }
|
||||
|
||||
/** Used to time out the query, xalls OnError and lets the TimerManager
|
||||
* delete this request.
|
||||
*/
|
||||
void Tick(time_t) anope_override
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name;
|
||||
Query rr(*this);
|
||||
rr.error = ERROR_TIMEOUT;
|
||||
this->OnError(&rr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace DNS
|
||||
|
||||
#endif // DNS_H
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,12 @@
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "dns.h"
|
||||
|
||||
using namespace DNS;
|
||||
|
||||
static ServiceReference<XLineManager> akills("XLineManager", "xlinemanager/sgline");
|
||||
static ServiceReference<Manager> dnsmanager("DNS::Manager", "dns/manager");
|
||||
|
||||
struct Blacklist
|
||||
{
|
||||
@@ -19,21 +23,21 @@ struct Blacklist
|
||||
Blacklist(const Anope::string &n, time_t b, const Anope::string &r, const std::map<int, Anope::string> &re) : name(n), bantime(b), reason(r), replies(re) { }
|
||||
};
|
||||
|
||||
class DNSBLResolver : public DNS::Request
|
||||
class DNSBLResolver : public Request
|
||||
{
|
||||
Reference<User> user;
|
||||
Blacklist blacklist;
|
||||
bool add_to_akill;
|
||||
|
||||
public:
|
||||
DNSBLResolver(Module *c, User *u, const Blacklist &b, const Anope::string &host, bool add_akill) : DNS::Request(host, DNS::QUERY_A, true, c), user(u), blacklist(b), add_to_akill(add_akill) { }
|
||||
DNSBLResolver(Module *c, User *u, const Blacklist &b, const Anope::string &host, bool add_akill) : Request(dnsmanager, c, host, QUERY_A, true), user(u), blacklist(b), add_to_akill(add_akill) { }
|
||||
|
||||
void OnLookupComplete(const DNS::Query *record) anope_override
|
||||
void OnLookupComplete(const Query *record) anope_override
|
||||
{
|
||||
if (!user || user->HasExt("m_dnsbl_akilled"))
|
||||
return;
|
||||
|
||||
const DNS::ResourceRecord &ans_record = record->answers[0];
|
||||
const ResourceRecord &ans_record = record->answers[0];
|
||||
// Replies should be in 127.0.0.0/24
|
||||
if (ans_record.rdata.find("127.0.0.") != 0)
|
||||
return;
|
||||
@@ -127,7 +131,7 @@ class ModuleDNSBL : public Module
|
||||
|
||||
void OnUserConnect(Reference<User> &user, bool &exempt) anope_override
|
||||
{
|
||||
if (exempt || !user || (!this->check_on_connect && !Me->IsSynced()))
|
||||
if (exempt || !user || (!this->check_on_connect && !Me->IsSynced()) || !dnsmanager)
|
||||
return;
|
||||
|
||||
if (!this->check_on_netburst && !user->server->IsSynced())
|
||||
@@ -158,7 +162,7 @@ class ModuleDNSBL : public Module
|
||||
{
|
||||
Anope::string dnsbl_host = user_ip.addr() + "." + b.name;
|
||||
DNSBLResolver *res = new DNSBLResolver(this, user, b, dnsbl_host, this->add_to_akill);
|
||||
res->Process();
|
||||
dnsmanager->Process(res);
|
||||
}
|
||||
catch (const SocketException &ex)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "opertype.h"
|
||||
#include "channels.h"
|
||||
#include "hashcomp.h"
|
||||
#include "dns.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <errno.h>
|
||||
@@ -175,42 +174,6 @@ ServerConfig::ServerConfig()
|
||||
|
||||
ModeManager::UpdateDefaultMLock(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)
|
||||
{
|
||||
size_t ip = server.find_first_of("123456789");
|
||||
if (ip != Anope::string::npos)
|
||||
{
|
||||
if (server.substr(ip).is_pos_number_only())
|
||||
{
|
||||
this->NameServer = server.substr(ip);
|
||||
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";
|
||||
}
|
||||
}
|
||||
delete DNS::Engine;
|
||||
DNS::Engine = new DNS::Manager(this->NameServer, this->DNSIP, this->DNSPort);
|
||||
|
||||
if (this->CaseMap == "ascii")
|
||||
Anope::casemap = std::locale(std::locale(), new Anope::ascii_ctype<char>());
|
||||
else if (this->CaseMap == "rfc1459")
|
||||
@@ -1262,13 +1225,6 @@ ConfigItems::ConfigItems(ServerConfig *conf)
|
||||
{"mail", "emailchange_message", "", new ValueContainerString(&conf->MailEmailchangeMessage), DT_STRING | DT_ALLOW_NEWLINE, ValidateMail},
|
||||
{"mail", "memo_subject", "", new ValueContainerString(&conf->MailMemoSubject), DT_STRING, ValidateMail},
|
||||
{"mail", "memo_message", "", new ValueContainerString(&conf->MailMemoMessage), DT_STRING | DT_ALLOW_NEWLINE, ValidateMail},
|
||||
{"dns", "nameserver", "127.0.0.1", new ValueContainerString(&conf->NameServer), DT_STRING, NoValidation},
|
||||
{"dns", "timeout", "5", new ValueContainerTime(&conf->DNSTimeout), DT_TIME, NoValidation},
|
||||
{"dns", "ip", "0.0.0.0", new ValueContainerString(&conf->DNSIP), DT_STRING, NoValidation},
|
||||
{"dns", "port", "53", new ValueContainerInt(&conf->DNSPort), DT_INTEGER, NoValidation},
|
||||
{"dns", "admin", "admin@example.com", new ValueContainerString(&conf->DNSSOAAdmin), DT_STRING, NoValidation},
|
||||
{"dns", "nameservers", "ns1.example.com", new ValueContainerString(&conf->DNSSOANS), DT_STRING, NoValidation},
|
||||
{"dns", "refresh", "3600", new ValueContainerUInt(&conf->DNSSOARefresh), DT_UINTEGER, NoValidation},
|
||||
{"chanserv", "name", "", new ValueContainerString(&conf->ChanServ), DT_STRING, NoValidation},
|
||||
{"chanserv", "defaults", "keeptopic secure securefounder signkick", new ValueContainerString(&CSDefaults), DT_STRING, ValidateChanServ},
|
||||
{"chanserv", "maxregistered", "0", new ValueContainerUInt(&conf->CSMaxReg), DT_UINTEGER, ValidateChanServ},
|
||||
|
||||
-963
@@ -1,963 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2003-2012 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*
|
||||
* Based on the original code of Epona by Lara.
|
||||
* Based on the original code of Services by Andy Church.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "services.h"
|
||||
#include "anope.h"
|
||||
#include "dns.h"
|
||||
#include "sockets.h"
|
||||
#include "socketengine.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
using namespace DNS;
|
||||
|
||||
Manager *DNS::Engine = NULL;
|
||||
|
||||
Question::Question()
|
||||
{
|
||||
this->type = QUERY_NONE;
|
||||
this->qclass = 0;
|
||||
}
|
||||
|
||||
Question::Question(const Anope::string &n, QueryType t, unsigned short q) : name(n), type(t), qclass(q)
|
||||
{
|
||||
}
|
||||
|
||||
ResourceRecord::ResourceRecord(const Anope::string &n, QueryType t, unsigned short q) : Question(n, t, q)
|
||||
{
|
||||
this->ttl = 0;
|
||||
this->created = Anope::CurTime;
|
||||
}
|
||||
|
||||
ResourceRecord::ResourceRecord(const Question &q) : Question(q)
|
||||
{
|
||||
this->ttl = 0;
|
||||
this->created = Anope::CurTime;
|
||||
}
|
||||
|
||||
Query::Query()
|
||||
{
|
||||
this->error = ERROR_NONE;
|
||||
}
|
||||
|
||||
Query::Query(const Question &q)
|
||||
{
|
||||
this->questions.push_back(q);
|
||||
this->error = ERROR_NONE;
|
||||
}
|
||||
|
||||
Request::Request(const Anope::string &addr, QueryType qt, bool cache, Module *c) : Timer(Config->DNSTimeout), Question(addr, qt), use_cache(cache), id(0), creator(c)
|
||||
{
|
||||
if (!DNS::Engine || !DNS::Engine->udpsock)
|
||||
throw SocketException("No DNS::Engine");
|
||||
if (DNS::Engine->udpsock->GetPackets().size() == 65535)
|
||||
throw SocketException("DNS queue full");
|
||||
|
||||
do
|
||||
{
|
||||
static unsigned short cur_id = rand();
|
||||
this->id = cur_id++;
|
||||
}
|
||||
while (DNS::Engine->requests.count(this->id));
|
||||
|
||||
DNS::Engine->requests[this->id] = this;
|
||||
}
|
||||
|
||||
Request::~Request()
|
||||
{
|
||||
DNS::Engine->requests.erase(this->id);
|
||||
}
|
||||
|
||||
void Request::Process()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Processing request to lookup " << this->name << ", of type " << this->type;
|
||||
|
||||
if (!DNS::Engine || !DNS::Engine->udpsock)
|
||||
throw SocketException("DNS::Engine has not been initialized");
|
||||
|
||||
if (this->use_cache && DNS::Engine->CheckCache(this))
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Using cached result";
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
Packet *p = new Packet(&DNS::Engine->addrs);
|
||||
p->flags = QUERYFLAGS_RD;
|
||||
|
||||
p->id = this->id;
|
||||
p->questions.push_back(*this);
|
||||
DNS::Engine->udpsock->Reply(p);
|
||||
}
|
||||
|
||||
void Request::OnError(const Query *r)
|
||||
{
|
||||
}
|
||||
|
||||
void Request::Tick(time_t)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name;
|
||||
Query rr(*this);
|
||||
rr.error = ERROR_TIMEOUT;
|
||||
this->OnError(&rr);
|
||||
}
|
||||
|
||||
void Packet::PackName(unsigned char *output, unsigned short output_size, unsigned short &pos, const Anope::string &name)
|
||||
{
|
||||
if (name.length() + 2 > output_size)
|
||||
throw SocketException("Unable to pack name");
|
||||
|
||||
Log(LOG_DEBUG_2) << "Resolver: PackName packing " << name;
|
||||
|
||||
sepstream sep(name, '.');
|
||||
Anope::string token;
|
||||
|
||||
while (sep.GetToken(token))
|
||||
{
|
||||
output[pos++] = token.length();
|
||||
memcpy(&output[pos], token.c_str(), token.length());
|
||||
pos += token.length();
|
||||
}
|
||||
|
||||
output[pos++] = 0;
|
||||
}
|
||||
|
||||
Anope::string Packet::UnpackName(const unsigned char *input, unsigned short input_size, unsigned short &pos)
|
||||
{
|
||||
Anope::string name;
|
||||
unsigned short pos_ptr = pos, lowest_ptr = input_size;
|
||||
bool compressed = false;
|
||||
|
||||
if (pos_ptr >= input_size)
|
||||
throw SocketException("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 SocketException("Unable to unpack name - bogus compression header");
|
||||
if (pos_ptr + 1 >= input_size)
|
||||
throw SocketException("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 SocketException("Unable to unpack name - bogus compression pointer");
|
||||
lowest_ptr = pos_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos_ptr + offset + 1 >= input_size)
|
||||
throw SocketException("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;
|
||||
|
||||
Log(LOG_DEBUG_2) << "Resolver: UnpackName successfully unpacked " << name;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
Question Packet::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 SocketException("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 Packet::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 SocketException("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 SocketException("Unable to unpack resource record");
|
||||
|
||||
in_addr a;
|
||||
a.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24);
|
||||
pos += 4;
|
||||
|
||||
sockaddrs addrs;
|
||||
addrs.ntop(AF_INET, &a);
|
||||
|
||||
record.rdata = addrs.addr();
|
||||
break;
|
||||
}
|
||||
case QUERY_AAAA:
|
||||
{
|
||||
if (pos + 16 > input_size)
|
||||
throw SocketException("Unable to unpack resource record");
|
||||
|
||||
in6_addr a;
|
||||
for (int j = 0; j < 16; ++j)
|
||||
a.s6_addr[j] = input[pos + j];
|
||||
pos += 16;
|
||||
|
||||
sockaddrs addrs;
|
||||
addrs.ntop(AF_INET6, &a);
|
||||
|
||||
record.rdata = addrs.addr();
|
||||
break;
|
||||
}
|
||||
case QUERY_CNAME:
|
||||
case QUERY_PTR:
|
||||
{
|
||||
record.rdata = this->UnpackName(input, input_size, pos);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Log(LOG_DEBUG_2) << "Resolver: " << record.name << " -> " << record.rdata;
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
Packet::Packet(sockaddrs *a) : Query(), id(0), flags(0)
|
||||
{
|
||||
if (a)
|
||||
addr = *a;
|
||||
}
|
||||
|
||||
void Packet::Fill(const unsigned char *input, const unsigned short len)
|
||||
{
|
||||
if (len < Packet::HEADER_LENGTH)
|
||||
throw SocketException("Unable to fill packet");
|
||||
|
||||
unsigned short packet_pos = 0;
|
||||
|
||||
this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
|
||||
packet_pos += 2;
|
||||
|
||||
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;
|
||||
|
||||
Log(LOG_DEBUG_2) << "Resolver: qdcount: " << qdcount << " ancount: " << ancount << " nscount: " << nscount << " arcount: " << 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));
|
||||
|
||||
for (unsigned i = 0; i < nscount; ++i)
|
||||
this->authorities.push_back(this->UnpackResourceRecord(input, len, packet_pos));
|
||||
|
||||
for (unsigned i = 0; i < arcount; ++i)
|
||||
this->additional.push_back(this->UnpackResourceRecord(input, len, packet_pos));
|
||||
}
|
||||
|
||||
unsigned short Packet::Pack(unsigned char *output, unsigned short output_size)
|
||||
{
|
||||
if (output_size < Packet::HEADER_LENGTH)
|
||||
throw SocketException("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++] = this->authorities.size() >> 8;
|
||||
output[pos++] = this->authorities.size() & 0xFF;
|
||||
output[pos++] = this->additional.size() >> 8;
|
||||
output[pos++] = this->additional.size() & 0xFF;
|
||||
|
||||
for (unsigned i = 0; i < this->questions.size(); ++i)
|
||||
{
|
||||
Question &q = this->questions[i];
|
||||
|
||||
if (q.type == QUERY_PTR)
|
||||
{
|
||||
sockaddrs ip(q.name);
|
||||
|
||||
if (q.name.find(':') != Anope::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.sa6.sin6_addr.s6_addr[j] & 0xF];
|
||||
reverse_ip[reverse_ip_count++] = '.';
|
||||
reverse_ip[reverse_ip_count++] = hex[ip.sa6.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.sa4.sin_addr.s_addr;
|
||||
in_addr reverse;
|
||||
reverse.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
|
||||
|
||||
ip.ntop(AF_INET, &reverse);
|
||||
|
||||
q.name = ip.addr() + ".in-addr.arpa";
|
||||
}
|
||||
}
|
||||
|
||||
this->PackName(output, output_size, pos, q.name);
|
||||
|
||||
if (pos + 4 >= output_size)
|
||||
throw SocketException("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;
|
||||
}
|
||||
|
||||
std::vector<ResourceRecord> types[] = { this->answers, this->authorities, this->additional };
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (unsigned j = 0; j < types[i].size(); ++j)
|
||||
{
|
||||
ResourceRecord &rr = types[i][j];
|
||||
|
||||
this->PackName(output, output_size, pos, rr.name);
|
||||
|
||||
if (pos + 8 >= output_size)
|
||||
throw SocketException("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 SocketException("Unable to pack packet");
|
||||
|
||||
sockaddrs a(rr.rdata);
|
||||
|
||||
s = htons(4);
|
||||
memcpy(&output[pos], &s, 2);
|
||||
pos += 2;
|
||||
|
||||
memcpy(&output[pos], &a.sa4.sin_addr, 4);
|
||||
pos += 4;
|
||||
break;
|
||||
}
|
||||
case QUERY_AAAA:
|
||||
{
|
||||
if (pos + 18 > output_size)
|
||||
throw SocketException("Unable to pack packet");
|
||||
|
||||
sockaddrs a(rr.rdata);
|
||||
|
||||
s = htons(16);
|
||||
memcpy(&output[pos], &s, 2);
|
||||
pos += 2;
|
||||
|
||||
memcpy(&output[pos], &a.sa6.sin6_addr, 16);
|
||||
pos += 16;
|
||||
break;
|
||||
}
|
||||
case QUERY_NS:
|
||||
case QUERY_CNAME:
|
||||
case QUERY_PTR:
|
||||
{
|
||||
if (pos + 2 >= output_size)
|
||||
throw SocketException("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;
|
||||
}
|
||||
case QUERY_SOA:
|
||||
{
|
||||
if (pos + 2 >= output_size)
|
||||
throw SocketException("Unable to pack packet");
|
||||
|
||||
unsigned short packet_pos_save = pos;
|
||||
pos += 2;
|
||||
|
||||
std::vector<Anope::string> nameservers;
|
||||
spacesepstream(Config->DNSSOANS).GetTokens(nameservers);
|
||||
this->PackName(output, output_size, pos, !nameservers.empty() ? nameservers[0] : "");
|
||||
this->PackName(output, output_size, pos, Config->DNSSOAAdmin.replace_all_cs('@', '.'));
|
||||
|
||||
if (pos + 20 >= output_size)
|
||||
throw SocketException("Unable to pack SOA");
|
||||
|
||||
l = htonl(DNS::Engine->GetSerial());
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
l = htonl(Config->DNSSOARefresh); // Refresh
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
l = htonl(Config->DNSSOARefresh); // Retry
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
l = htonl(604800); // Expire
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
l = htonl(0); // Minimum
|
||||
memcpy(&output[pos], &l, 4);
|
||||
pos += 4;
|
||||
|
||||
s = htons(pos - packet_pos_save - 2);
|
||||
memcpy(&output[packet_pos_save], &s, 2);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
Manager::TCPSocket::Client::Client(TCPSocket *l, int fd, const sockaddrs &addr) : Socket(fd, l->IsIPv6()), ClientSocket(l, addr), Timer(5), tcpsock(l), packet(NULL), length(0)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: New client from " << addr.addr();
|
||||
}
|
||||
|
||||
Manager::TCPSocket::Client::~Client()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Exiting client from " << clientaddr.addr();
|
||||
delete packet;
|
||||
}
|
||||
|
||||
void Manager::TCPSocket::Client::Reply(Packet *p)
|
||||
{
|
||||
delete packet;
|
||||
packet = p;
|
||||
SocketEngine::Change(this, true, SF_WRITABLE);
|
||||
}
|
||||
|
||||
bool Manager::TCPSocket::Client::ProcessRead()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Reading from DNS TCP socket";
|
||||
|
||||
int i = recv(this->GetFD(), reinterpret_cast<char *>(packet_buffer) + length, sizeof(packet_buffer) - length, 0);
|
||||
if (i <= 0)
|
||||
return false;
|
||||
|
||||
length += i;
|
||||
|
||||
unsigned short want_len = packet_buffer[0] << 8 | packet_buffer[1];
|
||||
if (length >= want_len + 2)
|
||||
{
|
||||
int len = length - 2;
|
||||
length -= want_len + 2;
|
||||
return DNS::Engine->HandlePacket(this, packet_buffer + 2, len, NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Manager::TCPSocket::Client::ProcessWrite()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Writing to DNS TCP socket";
|
||||
|
||||
if (packet != NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
unsigned char buffer[65535];
|
||||
unsigned short len = packet->Pack(buffer + 2, sizeof(buffer) - 2);
|
||||
|
||||
short s = htons(len);
|
||||
memcpy(buffer, &s, 2);
|
||||
len += 2;
|
||||
|
||||
send(this->GetFD(), reinterpret_cast<char *>(buffer), len, 0);
|
||||
}
|
||||
catch (const SocketException &) { }
|
||||
|
||||
delete packet;
|
||||
packet = NULL;
|
||||
}
|
||||
|
||||
SocketEngine::Change(this, false, SF_WRITABLE);
|
||||
return true; /* Do not return false here, bind is unhappy we close the connection so soon after sending */
|
||||
}
|
||||
|
||||
Manager::TCPSocket::TCPSocket(const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos), ListenSocket(ip, port, ip.find(':') != Anope::string::npos)
|
||||
{
|
||||
}
|
||||
|
||||
ClientSocket *Manager::TCPSocket::OnAccept(int fd, const sockaddrs &addr)
|
||||
{
|
||||
return new Client(this, fd, addr);
|
||||
}
|
||||
|
||||
Manager::UDPSocket::UDPSocket(const Anope::string &ip, int port) : Socket(-1, ip.find(':') != Anope::string::npos, SOCK_DGRAM)
|
||||
{
|
||||
}
|
||||
|
||||
Manager::UDPSocket::~UDPSocket()
|
||||
{
|
||||
for (unsigned i = 0; i < packets.size(); ++i)
|
||||
delete packets[i];
|
||||
}
|
||||
|
||||
void Manager::UDPSocket::Reply(Packet *p)
|
||||
{
|
||||
packets.push_back(p);
|
||||
SocketEngine::Change(this, true, SF_WRITABLE);
|
||||
}
|
||||
|
||||
bool Manager::UDPSocket::ProcessRead()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Reading from DNS UDP socket";
|
||||
|
||||
unsigned char packet_buffer[524];
|
||||
sockaddrs from_server;
|
||||
socklen_t x = sizeof(from_server);
|
||||
int length = recvfrom(this->GetFD(), reinterpret_cast<char *>(&packet_buffer), sizeof(packet_buffer), 0, &from_server.sa, &x);
|
||||
return DNS::Engine->HandlePacket(this, packet_buffer, length, &from_server);
|
||||
}
|
||||
|
||||
bool Manager::UDPSocket::ProcessWrite()
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Writing to DNS UDP socket";
|
||||
|
||||
Packet *r = !packets.empty() ? packets.front() : NULL;
|
||||
if (r != NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
unsigned char buffer[524];
|
||||
unsigned short len = r->Pack(buffer, sizeof(buffer));
|
||||
|
||||
sendto(this->GetFD(), reinterpret_cast<char *>(buffer), len, 0, &r->addr.sa, r->addr.size());
|
||||
}
|
||||
catch (const SocketException &) { }
|
||||
|
||||
delete r;
|
||||
packets.pop_front();
|
||||
}
|
||||
|
||||
if (packets.empty())
|
||||
SocketEngine::Change(this, false, SF_WRITABLE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Manager::Manager(const Anope::string &nameserver, const Anope::string &ip, int port) : Timer(300, Anope::CurTime, true), listen(false), serial(0), tcpsock(NULL), udpsock(NULL)
|
||||
{
|
||||
this->addrs.pton(nameserver.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, nameserver, port);
|
||||
|
||||
try
|
||||
{
|
||||
udpsock = new UDPSocket(ip, port);
|
||||
}
|
||||
catch (const SocketException &ex)
|
||||
{
|
||||
Log() << "Unable to create socket for Manager: " << ex.GetReason();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
udpsock->Bind(ip, port);
|
||||
tcpsock = new TCPSocket(ip, port);
|
||||
listen = true;
|
||||
}
|
||||
catch (const SocketException &ex)
|
||||
{
|
||||
/* This error can be from normal operation as most people don't use services to handle DNS queries, so put it in debug log */
|
||||
Log(LOG_DEBUG) << "Unable to bind Manager to port " << port << ": " << ex.GetReason();
|
||||
}
|
||||
|
||||
this->UpdateSerial();
|
||||
}
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
delete udpsock;
|
||||
delete tcpsock;
|
||||
|
||||
for (std::map<unsigned short, Request *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end; ++it)
|
||||
{
|
||||
Request *request = it->second;
|
||||
|
||||
Query rr(*request);
|
||||
rr.error = ERROR_UNKNOWN;
|
||||
request->OnError(&rr);
|
||||
|
||||
delete request;
|
||||
}
|
||||
this->requests.clear();
|
||||
|
||||
this->cache.clear();
|
||||
|
||||
DNS::Engine = NULL;
|
||||
}
|
||||
|
||||
bool Manager::HandlePacket(ReplySocket *s, const unsigned char *const packet_buffer, int length, sockaddrs *from)
|
||||
{
|
||||
if (length < Packet::HEADER_LENGTH)
|
||||
return true;
|
||||
|
||||
Packet recv_packet(from);
|
||||
|
||||
try
|
||||
{
|
||||
recv_packet.Fill(packet_buffer, length);
|
||||
}
|
||||
catch (const SocketException &ex)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << ex.GetReason();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(recv_packet.flags & QUERYFLAGS_QR))
|
||||
{
|
||||
if (!listen)
|
||||
return true;
|
||||
else if (recv_packet.questions.empty())
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received a question with no questions?";
|
||||
return true;
|
||||
}
|
||||
|
||||
Packet *packet = new Packet(recv_packet);
|
||||
packet->flags |= QUERYFLAGS_QR; /* This is a reponse */
|
||||
packet->flags |= QUERYFLAGS_AA; /* And we are authoritative */
|
||||
|
||||
packet->answers.clear();
|
||||
packet->authorities.clear();
|
||||
packet->additional.clear();
|
||||
|
||||
for (unsigned i = 0; i < recv_packet.questions.size(); ++i)
|
||||
{
|
||||
const Question& q = recv_packet.questions[i];
|
||||
|
||||
if (q.type == QUERY_AXFR || q.type == QUERY_SOA)
|
||||
{
|
||||
ResourceRecord rr(q.name, QUERY_SOA);
|
||||
packet->answers.push_back(rr);
|
||||
|
||||
if (q.type == QUERY_AXFR)
|
||||
{
|
||||
Anope::string token;
|
||||
spacesepstream sep(Config->DNSSOANS);
|
||||
while (sep.GetToken(token))
|
||||
{
|
||||
ResourceRecord rr2(q.name, QUERY_NS);
|
||||
rr2.rdata = token;
|
||||
packet->answers.push_back(rr2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FOREACH_MOD(I_OnDnsRequest, OnDnsRequest(recv_packet, packet));
|
||||
|
||||
for (unsigned i = 0; i < recv_packet.questions.size(); ++i)
|
||||
{
|
||||
const Question& q = recv_packet.questions[i];
|
||||
|
||||
if (q.type == QUERY_AXFR)
|
||||
{
|
||||
ResourceRecord rr(q.name, QUERY_SOA);
|
||||
packet->answers.push_back(rr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s->Reply(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (from == NULL)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received an answer over TCP. This is not supported.";
|
||||
return true;
|
||||
}
|
||||
else if (this->addrs != *from)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received an answer from the wrong nameserver, Bad NAT or DNS forging attempt? '" << this->addrs.addr() << "' != '" << from->addr() << "'";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<unsigned short, Request *>::iterator it = DNS::Engine->requests.find(recv_packet.id);
|
||||
if (it == DNS::Engine->requests.end())
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received an answer for something we didn't request";
|
||||
return true;
|
||||
}
|
||||
Request *request = it->second;
|
||||
|
||||
if (recv_packet.flags & QUERYFLAGS_OPCODE)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Received a nonstandard query";
|
||||
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:
|
||||
Log(LOG_DEBUG_2) << "Resolver: format error";
|
||||
error = ERROR_FORMAT_ERROR;
|
||||
break;
|
||||
case 2:
|
||||
Log(LOG_DEBUG_2) << "Resolver: server error";
|
||||
error = ERROR_SERVER_FAILURE;
|
||||
break;
|
||||
case 3:
|
||||
Log(LOG_DEBUG_2) << "Resolver: domain not found";
|
||||
error = ERROR_DOMAIN_NOT_FOUND;
|
||||
break;
|
||||
case 4:
|
||||
Log(LOG_DEBUG_2) << "Resolver: not implemented";
|
||||
error = ERROR_NOT_IMPLEMENTED;
|
||||
break;
|
||||
case 5:
|
||||
Log(LOG_DEBUG_2) << "Resolver: refused";
|
||||
error = ERROR_REFUSED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
recv_packet.error = error;
|
||||
request->OnError(&recv_packet);
|
||||
}
|
||||
else if (recv_packet.answers.empty())
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: No resource records returned";
|
||||
recv_packet.error = ERROR_NO_RECORDS;
|
||||
request->OnError(&recv_packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Lookup complete for " << request->name;
|
||||
request->OnLookupComplete(&recv_packet);
|
||||
DNS::Engine->AddCache(recv_packet);
|
||||
}
|
||||
|
||||
delete request;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Manager::AddCache(Query &r)
|
||||
{
|
||||
for (unsigned i = 0; i < r.answers.size(); ++i)
|
||||
{
|
||||
ResourceRecord &rr = r.answers[i];
|
||||
Log(LOG_DEBUG_3) << "Resolver cache: added cache for " << rr.name << " -> " << rr.rdata << ", ttl: " << rr.ttl;
|
||||
this->cache.insert(std::make_pair(rr.name, rr));
|
||||
}
|
||||
}
|
||||
|
||||
bool Manager::CheckCache(Request *request)
|
||||
{
|
||||
cache_map::iterator it = this->cache.find(request->name);
|
||||
if (it != this->cache.end())
|
||||
{
|
||||
Query record(*request);
|
||||
|
||||
for (cache_map::iterator it_end = this->cache.upper_bound(request->name); it != it_end; ++it)
|
||||
{
|
||||
ResourceRecord &rec = it->second;
|
||||
if (rec.created + static_cast<time_t>(rec.ttl) >= Anope::CurTime)
|
||||
record.answers.push_back(rec);
|
||||
}
|
||||
|
||||
if (!record.answers.empty())
|
||||
{
|
||||
Log(LOG_DEBUG_3) << "Resolver: Using cached result for " << request->name;
|
||||
request->OnLookupComplete(&record);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Manager::Tick(time_t now)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Resolver: Purging DNS cache";
|
||||
|
||||
for (cache_map::iterator it = this->cache.begin(), it_next; it != this->cache.end(); it = it_next)
|
||||
{
|
||||
ResourceRecord &req = it->second;
|
||||
it_next = it;
|
||||
++it_next;
|
||||
|
||||
if (req.created + static_cast<time_t>(req.ttl) < now)
|
||||
this->cache.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::Cleanup(Module *mod)
|
||||
{
|
||||
for (std::map<unsigned short, Request *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end;)
|
||||
{
|
||||
unsigned short id = it->first;
|
||||
Request *req = it->second;
|
||||
++it;
|
||||
|
||||
if (req->creator && req->creator == mod)
|
||||
{
|
||||
Query rr(*req);
|
||||
rr.error = ERROR_UNLOADED;
|
||||
req->OnError(&rr);
|
||||
|
||||
delete req;
|
||||
this->requests.erase(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::UpdateSerial()
|
||||
{
|
||||
serial = Anope::CurTime;
|
||||
}
|
||||
|
||||
uint32_t Manager::GetSerial() const
|
||||
{
|
||||
return serial;
|
||||
}
|
||||
|
||||
Query Manager::BlockingQuery(const Anope::string &mask, QueryType qt)
|
||||
{
|
||||
Question question(mask, qt);
|
||||
Query result(question);
|
||||
|
||||
int type = AF_UNSPEC;
|
||||
if (qt == QUERY_A)
|
||||
type = AF_INET;
|
||||
else if (qt == QUERY_AAAA)
|
||||
type = AF_INET6;
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = type;
|
||||
|
||||
Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: Looking up " << mask;
|
||||
|
||||
addrinfo *addrresult;
|
||||
if (getaddrinfo(mask.c_str(), NULL, &hints, &addrresult) == 0)
|
||||
{
|
||||
for (addrinfo *cur = addrresult; cur; cur = cur->ai_next)
|
||||
{
|
||||
sockaddrs addr;
|
||||
memcpy(&addr, addrresult->ai_addr, addrresult->ai_addrlen);
|
||||
try
|
||||
{
|
||||
ResourceRecord rr(mask, qt);
|
||||
rr.rdata = addr.addr();
|
||||
result.answers.push_back(rr);
|
||||
|
||||
Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: " << mask << " -> " << rr.rdata;
|
||||
}
|
||||
catch (const SocketException &) { }
|
||||
}
|
||||
|
||||
freeaddrinfo(addrresult);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,10 +18,13 @@
|
||||
#include "bots.h"
|
||||
#include "language.h"
|
||||
#include "regexpr.h"
|
||||
#include "sockets.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(true), desc(descending)
|
||||
{
|
||||
@@ -652,3 +655,31 @@ Anope::string Anope::NormalizeBuffer(const Anope::string &buf)
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
Anope::string Anope::Resolve(const Anope::string &host, int type)
|
||||
{
|
||||
Anope::string result = host;
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = type;
|
||||
|
||||
Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: Looking up " << host;
|
||||
|
||||
addrinfo *addrresult = NULL;
|
||||
if (getaddrinfo(host.c_str(), NULL, &hints, &addrresult) == 0)
|
||||
{
|
||||
sockaddrs addr;
|
||||
memcpy(&addr, addrresult->ai_addr, addrresult->ai_addrlen);
|
||||
try
|
||||
{
|
||||
result = addr.addr();
|
||||
Log(LOG_DEBUG_2) << "Resolver: " << host << " -> " << result;
|
||||
}
|
||||
catch (const SocketException &) { }
|
||||
|
||||
freeaddrinfo(addrresult);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -9,8 +9,8 @@
|
||||
|
||||
#include "services.h"
|
||||
#include "modules.h"
|
||||
#include "dns.h"
|
||||
#include "language.h"
|
||||
#include "account.h"
|
||||
|
||||
#ifdef GETTEXT_FOUND
|
||||
# include <libintl.h>
|
||||
@@ -46,8 +46,6 @@ Module::Module(const Anope::string &modname, const Anope::string &, ModType modt
|
||||
|
||||
Module::~Module()
|
||||
{
|
||||
if (DNS::Engine)
|
||||
DNS::Engine->Cleanup(this);
|
||||
/* Detach all event hooks for this module */
|
||||
ModuleManager::DetachAll(this);
|
||||
/* Clear any active callbacks this module has */
|
||||
|
||||
+3
-5
@@ -15,7 +15,6 @@
|
||||
#include "config.h"
|
||||
#include "protocol.h"
|
||||
#include "servers.h"
|
||||
#include "dns.h"
|
||||
|
||||
UplinkSocket *UplinkSock = NULL;
|
||||
|
||||
@@ -48,10 +47,9 @@ void Uplink::Connect()
|
||||
if (!Config->LocalHost.empty())
|
||||
UplinkSock->Bind(Config->LocalHost);
|
||||
FOREACH_MOD(I_OnPreServerConnect, OnPreServerConnect());
|
||||
DNS::Query rep = DNS::Manager::BlockingQuery(u->host, u->ipv6 ? DNS::QUERY_AAAA : DNS::QUERY_A);
|
||||
Anope::string reply_ip = !rep.answers.empty() ? rep.answers.front().rdata : u->host;
|
||||
Log(LOG_TERMINAL) << "Attempting to connect to uplink #" << (Anope::CurrentUplink + 1) << " " << u->host << " (" << reply_ip << "), port " << u->port;
|
||||
UplinkSock->Connect(reply_ip, u->port);
|
||||
Anope::string ip = Anope::Resolve(u->host, u->ipv6 ? AF_INET6 : AF_INET);
|
||||
Log(LOG_TERMINAL) << "Attempting to connect to uplink #" << (Anope::CurrentUplink + 1) << " " << u->host << " (" << ip << "), port " << u->port;
|
||||
UplinkSock->Connect(ip, u->port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user