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 /src | |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/actions.cpp | 19 | ||||
-rw-r--r-- | src/base64.cpp | 14 | ||||
-rw-r--r-- | src/channels.cpp | 24 | ||||
-rw-r--r-- | src/config.cpp | 32 | ||||
-rw-r--r-- | src/dns.cpp | 550 | ||||
-rw-r--r-- | src/init.cpp | 9 | ||||
-rw-r--r-- | src/logger.cpp | 2 | ||||
-rw-r--r-- | src/main.cpp | 5 | ||||
-rw-r--r-- | src/misc.cpp | 63 | ||||
-rw-r--r-- | src/operserv.cpp | 20 | ||||
-rw-r--r-- | src/servers.cpp | 9 | ||||
-rw-r--r-- | src/sockets.cpp | 288 | ||||
-rw-r--r-- | src/users.cpp | 39 |
13 files changed, 791 insertions, 283 deletions
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); |