]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge insp20
authorAttila Molnar <attilamolnar@hush.com>
Mon, 22 Feb 2016 11:52:18 +0000 (12:52 +0100)
committerAttila Molnar <attilamolnar@hush.com>
Mon, 22 Feb 2016 11:52:18 +0000 (12:52 +0100)
21 files changed:
1  2 
docs/conf/helpop-full.conf.example
docs/conf/helpop.conf.example
docs/conf/modules.conf.example
make/template/main.mk
make/template/org.inspircd.plist
make/test/eventfd.cpp
make/test/kqueue.cpp
make/utilities.pm
src/coremods/core_dns.cpp
src/coremods/core_oper/cmd_kill.cpp
src/inspircd.cpp
src/modules/extra/m_ssl_gnutls.cpp
src/modules/m_callerid.cpp
src/modules/m_check.cpp
src/modules/m_dccallow.cpp
src/modules/m_hideoper.cpp
src/modules/m_ircv3.cpp
src/modules/m_namedmodes.cpp
src/modules/m_nationalchars.cpp
win/CMakeLists.txt
win/inspircd_win32wrapper.h

Simple merge
Simple merge
index 69ffb537d688941d2fbfab0e66c35553d12e3cd8,97d69da90477b77dac16bae68e3d2419d1bc0c5a..ef2765c2836d27464d4ace0030ef9d6d22fe3580
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Channelban: Implements extended ban j:, which stops anyone already
  # in a channel matching a ban like +b j:#channel*mask from joining.
 -#<module name="m_channelban.so">
 -
 -#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
 -# Chanprotect module: Gives +q and +a channel modes.
 -#<module name="m_chanprotect.so">
 -
 -<chanprotect
 -      # noservices: With this set to yes, when a user joins an empty channel,
 -      # the server will set +q on them. If set to no, it will only set +o
 -      # on them until they register the channel.
 -      noservices="no"
 -
 -      # qprefix: Prefix (symbol) to use for +q users.
 -      qprefix="~"
 -
 -      # aprefix: Prefix (symbol) to use for +a users.
 -      aprefix="&amp;"
 -
 -      # deprotectself: If this value is set (true, yes or 1), it will allow
 -      # +a and +q users to remove the +a and +q from themselves, otherwise,
 -      # the status will have to be removed by services.
 -      deprotectself="yes"
 -
 -      # deprotectothers: If this value is set to yes, true, or 1, then any
 -      # user with +q or +a may remove the +q or +a from other users.
 -      deprotectothers="yes">
 -
+ # Note that by default wildcard characters * and ? are allowed in
+ # channel names. To disallow them, load m_channames and add characters
+ # 42 and 63 to denyrange (see above).
 +#<module name="channelban">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
  # Check module: Adds the /CHECK command.
  # National characters module:
  # 1) Allows using national characters in nicknames.
  # 2) Allows using custom (national) casemapping over the network.
 -#<module name="m_nationalchars.so">
 +#<module name="nationalchars">
  #
- # file - filename of existing file in "locales" directory
- # casemapping - custom value for 005 numeric (if you want it to be
- #               different from the filename). Set this to the name of
- #               the locale if you are specifying an absolute path.
+ # file - Location of the file which contains casemapping rules. If this
+ #        is a relative path then it is relative to "<PWD>/../locales"
+ #        on UNIX and "<PWD>/locales" on Windows.
+ # casemapping - The name of the casemapping sent to clients in the 005
+ #               numeric. If this is not set then it defaults to the name
+ #               of the casemapping file unless the file name contains a
+ #               space in which case you will have to specify it manually.
  #<nationalchars file="bynets/russian-w1251-charlink" casemapping="ru_RU.cp1251-charlink">
  
  #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
index 3e7ee184401c60e2f19f5af167454634b0d93178,fa2375ac139ba3ed34b3997e8b74cefbb4abc132..cc201a12654c726207af7680e5d974de9df45604
@@@ -43,8 -41,8 +43,9 @@@ CORELDFLAGS = -rdynamic -L. $(LDFLAGS
  PICLDFLAGS = -fPIC -shared -rdynamic $(LDFLAGS)
  BASE = "$(DESTDIR)@BASE_DIR@"
  CONPATH = "$(DESTDIR)@CONFIG_DIR@"
 +MANPATH = "$(DESTDIR)@MANUAL_DIR@"
  MODPATH = "$(DESTDIR)@MODULE_DIR@"
+ LOGPATH = "$(DESTDIR)@LOG_DIR@"
  DATPATH = "$(DESTDIR)@DATA_DIR@"
  BINPATH = "$(DESTDIR)@BINARY_DIR@"
  INSTALL = install
@@@ -322,4 -314,6 +323,6 @@@ help
        @echo ' deinstall Removes the files created by "make install"'
        @echo
  
 -.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall squeakyclean configureclean help
+ .NOTPARALLEL:
 +.PHONY: all target debug debug-header mod-header mod-footer std-header finishmessage install clean deinstall configureclean help
index 07a3446b51537d0a422a48c1b374746a7e844c79,4dac209f666e3ef8aa612835dd55fa3a30af1876..ae4e90916f3891ca0c07c616384130400ee2c753
        </array>
        <key>ServiceIPC</key>
        <false/>
+       <key>StandardOutPath</key>
+       <string>@LOG_DIR@/launchd-stdout.log</string>
+       <key>StandardErrorPath</key>
+       <string>@LOG_DIR@/launchd-stderr.log</string>
        <key>UserName</key>
 -      <string>ircdaemon</string>
 +      <string>@USER@</string>
 +      <key>GroupName</key>
 +      <string>@GROUP@</string>
  </dict>
  </plist>
index 980d04485bb2774d896474c27a4f31ae50e4d424,0000000000000000000000000000000000000000..9b38b793b3e32072fef964f1accd249468af104f
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,31 @@@
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
++ *   Copyright (C) 2012 William Pitcock <nenolod@dereferenced.org>
++ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include <sys/eventfd.h>
 +
 +int main() {
 +      eventfd_t efd_data;
 +      int fd;
 +
 +      fd = eventfd(0, EFD_NONBLOCK);
 +      eventfd_read(fd, &efd_data);
 +
 +      return (fd < 0);
 +}
index a8b9882cfed1cbc78eec2ec6a320239836608453,0000000000000000000000000000000000000000..708677adf9ff142d5641395ec570ec1a93d88a1c
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,26 @@@
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
++ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include <sys/types.h>
 +#include <sys/event.h>
 +
 +int main() {
 +      int fd = kqueue();
 +      return (fd < 0);
 +}
index 565ac5fff385677187f5675b338ab531efe15915,baba584ad240754030525f0d60fcaae7ec975302..f2d645f3312cdd37bce35f601108fbb52474dddc
@@@ -30,14 -28,20 +30,15 @@@ use strict
  use warnings FATAL => qw(all);
  
  use Exporter 'import';
 -use POSIX;
 +use Fcntl;
 +use File::Path;
+ use File::Temp;
  use Getopt::Long;
 -use Fcntl;
 -our @EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
 -
 -# Parse the output of a *_config program,
 -# such as pcre_config, take out the -L
 -# directive and return an rpath for it.
 +use POSIX;
  
 -# \e[1;32msrc/Makefile\e[0m
 +our @EXPORT = qw(make_rpath pkgconfig_get_include_dirs pkgconfig_get_lib_dirs pkgconfig_check_version translate_functions promptstring);
  
  my %already_added = ();
 -my $if_skip_lines = 0;
  
  sub promptstring($$$$$)
  {
index da468af5fae273a4ace9e1c48f266d45aa5552a7,0000000000000000000000000000000000000000..1e8bb753389272725e5aa5bd80c4c0dd615beca9
mode 100644,000000..100644
--- /dev/null
@@@ -1,806 -1,0 +1,814 @@@
-       MyManager(Module* c) : Manager(c), Timer(3600, true)
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2013 Adam <Adam@anope.org>
 + *   Copyright (C) 2003-2013 Anope Team <team@anope.org>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +#include "inspircd.h"
 +#include "modules/dns.h"
 +#include <iostream>
 +#include <fstream>
 +
 +#ifdef _WIN32
 +#include <Iphlpapi.h>
 +#pragma comment(lib, "Iphlpapi.lib")
 +#endif
 +
 +namespace DNS
 +{
 +      /** Maximum value of a dns request id, 16 bits wide, 0xFFFF.
 +       */
 +      const unsigned int MAX_REQUEST_ID = 0xFFFF;
 +}
 +
 +using namespace DNS;
 +
 +/** A full packet sent or recieved to/from the nameserver
 + */
 +class Packet : public Query
 +{
 +      static bool IsValidName(const std::string& name)
 +      {
 +              return (name.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") == std::string::npos);
 +      }
 +
 +      void PackName(unsigned char* output, unsigned short output_size, unsigned short& pos, const std::string& name)
 +      {
 +              if (pos + name.length() + 2 > output_size)
 +                      throw Exception("Unable to pack name");
 +
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Packing name " + name);
 +
 +              irc::sepstream sep(name, '.');
 +              std::string token;
 +
 +              while (sep.GetToken(token))
 +              {
 +                      output[pos++] = token.length();
 +                      memcpy(&output[pos], token.data(), token.length());
 +                      pos += token.length();
 +              }
 +
 +              output[pos++] = 0;
 +      }
 +
 +      std::string UnpackName(const unsigned char* input, unsigned short input_size, unsigned short& pos)
 +      {
 +              std::string name;
 +              unsigned short pos_ptr = pos, lowest_ptr = input_size;
 +              bool compressed = false;
 +
 +              if (pos_ptr >= input_size)
 +                      throw Exception("Unable to unpack name - no input");
 +
 +              while (input[pos_ptr] > 0)
 +              {
 +                      unsigned short offset = input[pos_ptr];
 +
 +                      if (offset & POINTER)
 +                      {
 +                              if ((offset & POINTER) != POINTER)
 +                                      throw Exception("Unable to unpack name - bogus compression header");
 +                              if (pos_ptr + 1 >= input_size)
 +                                      throw Exception("Unable to unpack name - bogus compression header");
 +
 +                              /* Place pos at the second byte of the first (farthest) compression pointer */
 +                              if (compressed == false)
 +                              {
 +                                      ++pos;
 +                                      compressed = true;
 +                              }
 +
 +                              pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1];
 +
 +                              /* Pointers can only go back */
 +                              if (pos_ptr >= lowest_ptr)
 +                                      throw Exception("Unable to unpack name - bogus compression pointer");
 +                              lowest_ptr = pos_ptr;
 +                      }
 +                      else
 +                      {
 +                              if (pos_ptr + offset + 1 >= input_size)
 +                                      throw Exception("Unable to unpack name - offset too large");
 +                              if (!name.empty())
 +                                      name += ".";
 +                              for (unsigned i = 1; i <= offset; ++i)
 +                                      name += input[pos_ptr + i];
 +
 +                              pos_ptr += offset + 1;
 +                              if (compressed == false)
 +                                      /* Move up pos */
 +                                      pos = pos_ptr;
 +                      }
 +              }
 +
 +              /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
 +              ++pos;
 +
 +              if (name.empty())
 +                      throw Exception("Unable to unpack name - no name");
 +
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unpack name " + name);
 +
 +              return name;
 +      }
 +
 +      Question UnpackQuestion(const unsigned char* input, unsigned short input_size, unsigned short& pos)
 +      {
 +              Question q;
 +
 +              q.name = this->UnpackName(input, input_size, pos);
 +
 +              if (pos + 4 > input_size)
 +                      throw Exception("Unable to unpack question");
 +
 +              q.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
 +              pos += 2;
 +
 +              // Skip over query class code
 +              pos += 2;
 +
 +              return q;
 +      }
 +
 +      ResourceRecord UnpackResourceRecord(const unsigned char* input, unsigned short input_size, unsigned short& pos)
 +      {
 +              ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos));
 +
 +              if (pos + 6 > input_size)
 +                      throw Exception("Unable to unpack resource record");
 +
 +              record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
 +              pos += 4;
 +
 +              //record.rdlength = input[pos] << 8 | input[pos + 1];
 +              pos += 2;
 +
 +              switch (record.type)
 +              {
 +                      case QUERY_A:
 +                      {
 +                              if (pos + 4 > input_size)
 +                                      throw Exception("Unable to unpack resource record");
 +
 +                              irc::sockets::sockaddrs addrs;
 +                              memset(&addrs, 0, sizeof(addrs));
 +
 +                              addrs.in4.sin_family = AF_INET;
 +                              addrs.in4.sin_addr.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16)  | (input[pos + 3] << 24);
 +                              pos += 4;
 +
 +                              record.rdata = addrs.addr();
 +                              break;
 +                      }
 +                      case QUERY_AAAA:
 +                      {
 +                              if (pos + 16 > input_size)
 +                                      throw Exception("Unable to unpack resource record");
 +
 +                              irc::sockets::sockaddrs addrs;
 +                              memset(&addrs, 0, sizeof(addrs));
 +
 +                              addrs.in6.sin6_family = AF_INET6;
 +                              for (int j = 0; j < 16; ++j)
 +                                      addrs.in6.sin6_addr.s6_addr[j] = input[pos + j];
 +                              pos += 16;
 +
 +                              record.rdata = addrs.addr();
 +
 +                              break;
 +                      }
 +                      case QUERY_CNAME:
 +                      case QUERY_PTR:
 +                      {
 +                              record.rdata = this->UnpackName(input, input_size, pos);
 +                              if (!IsValidName(record.rdata))
 +                                      throw Exception("Invalid name"); // XXX: Causes the request to time out
 +
 +                              break;
 +                      }
 +                      default:
 +                              break;
 +              }
 +
 +              if (!record.name.empty() && !record.rdata.empty())
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, record.name + " -> " + record.rdata);
 +
 +              return record;
 +      }
 +
 + public:
 +      static const int POINTER = 0xC0;
 +      static const int LABEL = 0x3F;
 +      static const int HEADER_LENGTH = 12;
 +
 +      /* ID for this packet */
 +      RequestId id;
 +      /* Flags on the packet */
 +      unsigned short flags;
 +
 +      Packet() : id(0), flags(0)
 +      {
 +      }
 +
 +      void Fill(const unsigned char* input, const unsigned short len)
 +      {
 +              if (len < HEADER_LENGTH)
 +                      throw Exception("Unable to fill packet");
 +
 +              unsigned short packet_pos = 0;
 +
 +              this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
 +              packet_pos += 2;
 +
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "qdcount: " + ConvToStr(qdcount) + " ancount: " + ConvToStr(ancount) + " nscount: " + ConvToStr(nscount) + " arcount: " + ConvToStr(arcount));
 +
 +              if (qdcount != 1)
 +                      throw Exception("Question count != 1 in incoming packet");
 +
 +              this->question = this->UnpackQuestion(input, len, packet_pos);
 +
 +              for (unsigned i = 0; i < ancount; ++i)
 +                      this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
 +      }
 +
 +      unsigned short Pack(unsigned char* output, unsigned short output_size)
 +      {
 +              if (output_size < HEADER_LENGTH)
 +                      throw Exception("Unable to pack packet");
 +
 +              unsigned short pos = 0;
 +
 +              output[pos++] = this->id >> 8;
 +              output[pos++] = this->id & 0xFF;
 +              output[pos++] = this->flags >> 8;
 +              output[pos++] = this->flags & 0xFF;
 +              output[pos++] = 0; // Question count, high byte
 +              output[pos++] = 1; // Question count, low byte
 +              output[pos++] = 0; // Answer count, high byte
 +              output[pos++] = 0; // Answer count, low byte
 +              output[pos++] = 0;
 +              output[pos++] = 0;
 +              output[pos++] = 0;
 +              output[pos++] = 0;
 +
 +              {
 +                      Question& q = this->question;
 +
 +                      if (q.type == QUERY_PTR)
 +                      {
 +                              irc::sockets::sockaddrs ip;
 +                              irc::sockets::aptosa(q.name, 0, ip);
 +
 +                              if (q.name.find(':') != std::string::npos)
 +                              {
 +                                      static const char* const hex = "0123456789abcdef";
 +                                      char reverse_ip[128];
 +                                      unsigned reverse_ip_count = 0;
 +                                      for (int j = 15; j >= 0; --j)
 +                                      {
 +                                              reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] & 0xF];
 +                                              reverse_ip[reverse_ip_count++] = '.';
 +                                              reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] >> 4];
 +                                              reverse_ip[reverse_ip_count++] = '.';
 +                                      }
 +                                      reverse_ip[reverse_ip_count++] = 0;
 +
 +                                      q.name = reverse_ip;
 +                                      q.name += "ip6.arpa";
 +                              }
 +                              else
 +                              {
 +                                      unsigned long forward = ip.in4.sin_addr.s_addr;
 +                                      ip.in4.sin_addr.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
 +
 +                                      q.name = ip.addr() + ".in-addr.arpa";
 +                              }
 +                      }
 +
 +                      this->PackName(output, output_size, pos, q.name);
 +
 +                      if (pos + 4 >= output_size)
 +                              throw Exception("Unable to pack packet");
 +
 +                      short s = htons(q.type);
 +                      memcpy(&output[pos], &s, 2);
 +                      pos += 2;
 +
 +                      // Query class, always IN
 +                      output[pos++] = 0;
 +                      output[pos++] = 1;
 +              }
 +
 +              return pos;
 +      }
 +};
 +
 +class MyManager : public Manager, public Timer, public EventHandler
 +{
 +      typedef TR1NS::unordered_map<Question, Query, Question::hash> cache_map;
 +      cache_map cache;
 +
 +      irc::sockets::sockaddrs myserver;
 +
++      /** Maximum number of entries in cache
++       */
++      static const unsigned int MAX_CACHE_SIZE = 1000;
++
 +      static bool IsExpired(const Query& record, time_t now = ServerInstance->Time())
 +      {
 +              const ResourceRecord& req = record.answers[0];
 +              return (req.created + static_cast<time_t>(req.ttl) < now);
 +      }
 +
 +      /** Check the DNS cache to see if request can be handled by a cached result
 +       * @return true if a cached result was found.
 +       */
 +      bool CheckCache(DNS::Request* req, const DNS::Question& question)
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "cache: Checking cache for " + question.name);
 +
 +              cache_map::iterator it = this->cache.find(question);
 +              if (it == this->cache.end())
 +                      return false;
 +
 +              Query& record = it->second;
 +              if (IsExpired(record))
 +              {
 +                      this->cache.erase(it);
 +                      return false;
 +              }
 +
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: Using cached result for " + question.name);
 +              record.cached = true;
 +              req->OnLookupComplete(&record);
 +              return true;
 +      }
 +
 +      /** Add a record to the dns cache
 +       * @param r The record
 +       */
 +      void AddCache(Query& r)
 +      {
++              if (cache.size() >= MAX_CACHE_SIZE)
++                      cache.clear();
++
 +              // Determine the lowest TTL value and use that as the TTL of the cache entry
 +              unsigned int cachettl = UINT_MAX;
 +              for (std::vector<ResourceRecord>::const_iterator i = r.answers.begin(); i != r.answers.end(); ++i)
 +              {
 +                      const ResourceRecord& rr = *i;
 +                      if (rr.ttl < cachettl)
 +                              cachettl = rr.ttl;
 +              }
 +
++              cachettl = std::min(cachettl, (unsigned int)5*60);
 +              ResourceRecord& rr = r.answers.front();
 +              // Set TTL to what we've determined to be the lowest
 +              rr.ttl = cachettl;
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: added cache for " + rr.name + " -> " + rr.rdata + " ttl: " + ConvToStr(rr.ttl));
 +              this->cache[r.question] = r;
 +      }
 +
 + public:
 +      DNS::Request* requests[MAX_REQUEST_ID+1];
 +
++      MyManager(Module* c) : Manager(c), Timer(5*60, true)
 +      {
 +              for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
 +                      requests[i] = NULL;
 +              ServerInstance->Timers.AddTimer(this);
 +      }
 +
 +      ~MyManager()
 +      {
 +              for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
 +              {
 +                      DNS::Request* request = requests[i];
 +                      if (!request)
 +                              continue;
 +
 +                      Query rr(*request);
 +                      rr.error = ERROR_UNKNOWN;
 +                      request->OnError(&rr);
 +
 +                      delete request;
 +              }
 +      }
 +
 +      void Process(DNS::Request* req)
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Processing request to lookup " + req->name + " of type " + ConvToStr(req->type) + " to " + this->myserver.addr());
 +
 +              /* Create an id */
 +              unsigned int tries = 0;
 +              int id;
 +              do
 +              {
 +                      id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID+1);
 +
 +                      if (++tries == DNS::MAX_REQUEST_ID*5)
 +                      {
 +                              // If we couldn't find an empty slot this many times, do a sequential scan as a last
 +                              // resort. If an empty slot is found that way, go on, otherwise throw an exception
 +                              id = -1;
 +                              for (unsigned int i = 0; i <= DNS::MAX_REQUEST_ID; i++)
 +                              {
 +                                      if (!this->requests[i])
 +                                      {
 +                                              id = i;
 +                                              break;
 +                                      }
 +                              }
 +
 +                              if (id == -1)
 +                                      throw Exception("DNS: All ids are in use");
 +
 +                              break;
 +                      }
 +              }
 +              while (this->requests[id]);
 +
 +              req->id = id;
 +              this->requests[req->id] = req;
 +
 +              Packet p;
 +              p.flags = QUERYFLAGS_RD;
 +              p.id = req->id;
 +              p.question = *req;
 +
 +              unsigned char buffer[524];
 +              unsigned short len = p.Pack(buffer, sizeof(buffer));
 +
 +              /* Note that calling Pack() above can actually change the contents of p.question.name, if the query is a PTR,
 +               * to contain the value that would be in the DNS cache, which is why this is here.
 +               */
 +              if (req->use_cache && this->CheckCache(req, p.question))
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Using cached result");
 +                      delete req;
 +                      return;
 +              }
 +
 +              // Update name in the original request so question checking works for PTR queries
 +              req->name = p.question.name;
 +
 +              if (SocketEngine::SendTo(this, buffer, len, 0, &this->myserver.sa, this->myserver.sa_size()) != len)
 +                      throw Exception("DNS: Unable to send query");
 +
 +              // Add timer for timeout
 +              ServerInstance->Timers.AddTimer(req);
 +      }
 +
 +      void RemoveRequest(DNS::Request* req)
 +      {
 +              if (requests[req->id] == req)
 +                      requests[req->id] = NULL;
 +      }
 +
 +      std::string GetErrorStr(Error e)
 +      {
 +              switch (e)
 +              {
 +                      case ERROR_UNLOADED:
 +                              return "Module is unloading";
 +                      case ERROR_TIMEDOUT:
 +                              return "Request timed out";
 +                      case ERROR_NOT_AN_ANSWER:
 +                      case ERROR_NONSTANDARD_QUERY:
 +                      case ERROR_FORMAT_ERROR:
 +                      case ERROR_MALFORMED:
 +                              return "Malformed answer";
 +                      case ERROR_SERVER_FAILURE:
 +                      case ERROR_NOT_IMPLEMENTED:
 +                      case ERROR_REFUSED:
 +                      case ERROR_INVALIDTYPE:
 +                              return "Nameserver failure";
 +                      case ERROR_DOMAIN_NOT_FOUND:
 +                      case ERROR_NO_RECORDS:
 +                              return "Domain not found";
 +                      case ERROR_NONE:
 +                      case ERROR_UNKNOWN:
 +                      default:
 +                              return "Unknown error";
 +              }
 +      }
 +
 +      void OnEventHandlerError(int errcode) CXX11_OVERRIDE
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "UDP socket got an error event");
 +      }
 +
 +      void OnEventHandlerRead() CXX11_OVERRIDE
 +      {
 +              unsigned char buffer[524];
 +              irc::sockets::sockaddrs from;
 +              socklen_t x = sizeof(from);
 +
 +              int length = SocketEngine::RecvFrom(this, buffer, sizeof(buffer), 0, &from.sa, &x);
 +
 +              if (length < Packet::HEADER_LENGTH)
 +                      return;
 +
 +              if (myserver != from)
 +              {
 +                      std::string server1 = from.str();
 +                      std::string server2 = myserver.str();
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
 +                              server1.c_str(), server2.c_str());
 +                      return;
 +              }
 +
 +              Packet recv_packet;
 +              bool valid = false;
 +
 +              try
 +              {
 +                      recv_packet.Fill(buffer, length);
 +                      valid = true;
 +              }
 +              catch (Exception& ex)
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, ex.GetReason());
 +              }
 +
 +              // recv_packet.id must be filled in here
 +              DNS::Request* request = this->requests[recv_packet.id];
 +              if (request == NULL)
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer for something we didn't request");
 +                      return;
 +              }
 +
 +              if (static_cast<Question&>(*request) != recv_packet.question)
 +              {
 +                      // This can happen under high latency, drop it silently, do not fail the request
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received an answer that isn't for a question we asked");
 +                      return;
 +              }
 +
 +              if (!valid)
 +              {
 +                      ServerInstance->stats.DnsBad++;
 +                      recv_packet.error = ERROR_MALFORMED;
 +                      request->OnError(&recv_packet);
 +              }
 +              else if (recv_packet.flags & QUERYFLAGS_OPCODE)
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received a nonstandard query");
 +                      ServerInstance->stats.DnsBad++;
 +                      recv_packet.error = ERROR_NONSTANDARD_QUERY;
 +                      request->OnError(&recv_packet);
 +              }
 +              else if (!(recv_packet.flags & QUERYFLAGS_QR) || (recv_packet.flags & QUERYFLAGS_RCODE))
 +              {
 +                      Error error = ERROR_UNKNOWN;
 +
 +                      switch (recv_packet.flags & QUERYFLAGS_RCODE)
 +                      {
 +                              case 1:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "format error");
 +                                      error = ERROR_FORMAT_ERROR;
 +                                      break;
 +                              case 2:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "server error");
 +                                      error = ERROR_SERVER_FAILURE;
 +                                      break;
 +                              case 3:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "domain not found");
 +                                      error = ERROR_DOMAIN_NOT_FOUND;
 +                                      break;
 +                              case 4:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "not implemented");
 +                                      error = ERROR_NOT_IMPLEMENTED;
 +                                      break;
 +                              case 5:
 +                                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "refused");
 +                                      error = ERROR_REFUSED;
 +                                      break;
 +                              default:
 +                                      break;
 +                      }
 +
 +                      ServerInstance->stats.DnsBad++;
 +                      recv_packet.error = error;
 +                      request->OnError(&recv_packet);
 +              }
 +              else if (recv_packet.answers.empty())
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "No resource records returned");
 +                      ServerInstance->stats.DnsBad++;
 +                      recv_packet.error = ERROR_NO_RECORDS;
 +                      request->OnError(&recv_packet);
 +              }
 +              else
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Lookup complete for " + request->name);
 +                      ServerInstance->stats.DnsGood++;
 +                      request->OnLookupComplete(&recv_packet);
 +                      this->AddCache(recv_packet);
 +              }
 +
 +              ServerInstance->stats.Dns++;
 +
 +              /* Request's destructor removes it from the request map */
 +              delete request;
 +      }
 +
 +      bool Tick(time_t now)
 +      {
 +              ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "cache: purging DNS cache");
 +
 +              for (cache_map::iterator it = this->cache.begin(); it != this->cache.end(); )
 +              {
 +                      const Query& query = it->second;
 +                      if (IsExpired(query, now))
 +                              this->cache.erase(it++);
 +                      else
 +                              ++it;
 +              }
 +              return true;
 +      }
 +
 +      void Rehash(const std::string& dnsserver)
 +      {
 +              if (this->GetFd() > -1)
 +              {
 +                      SocketEngine::Shutdown(this, 2);
 +                      SocketEngine::Close(this);
 +
 +                      /* Remove expired entries from the cache */
 +                      this->Tick(ServerInstance->Time());
 +              }
 +
 +              irc::sockets::aptosa(dnsserver, DNS::PORT, myserver);
 +
 +              /* Initialize mastersocket */
 +              int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
 +              this->SetFd(s);
 +
 +              /* Have we got a socket? */
 +              if (this->GetFd() != -1)
 +              {
 +                      SocketEngine::SetReuse(s);
 +                      SocketEngine::NonBlocking(s);
 +
 +                      irc::sockets::sockaddrs bindto;
 +                      memset(&bindto, 0, sizeof(bindto));
 +                      bindto.sa.sa_family = myserver.sa.sa_family;
 +
 +                      if (SocketEngine::Bind(this->GetFd(), bindto) < 0)
 +                      {
 +                              /* Failed to bind */
 +                              ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error binding dns socket - hostnames will NOT resolve");
 +                              SocketEngine::Close(this->GetFd());
 +                              this->SetFd(-1);
 +                      }
 +                      else if (!SocketEngine::AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
 +                      {
 +                              ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Internal error starting DNS - hostnames will NOT resolve.");
 +                              SocketEngine::Close(this->GetFd());
 +                              this->SetFd(-1);
 +                      }
 +              }
 +              else
 +              {
 +                      ServerInstance->Logs->Log(MODNAME, LOG_SPARSE, "Error creating DNS socket - hostnames will NOT resolve");
 +              }
 +      }
 +};
 +
 +class ModuleDNS : public Module
 +{
 +      MyManager manager;
 +      std::string DNSServer;
 +
 +      void FindDNSServer()
 +      {
 +#ifdef _WIN32
 +              // attempt to look up their nameserver from the system
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
 +
 +              PFIXED_INFO pFixedInfo;
 +              DWORD dwBufferSize = sizeof(FIXED_INFO);
 +              pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
 +
 +              if (pFixedInfo)
 +              {
 +                      if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW)
 +                      {
 +                              HeapFree(GetProcessHeap(), 0, pFixedInfo);
 +                              pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
 +                      }
 +
 +                      if (pFixedInfo)
 +                      {
 +                              if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
 +                                      DNSServer = pFixedInfo->DnsServerList.IpAddress.String;
 +
 +                              HeapFree(GetProcessHeap(), 0, pFixedInfo);
 +                      }
 +
 +                      if (!DNSServer.empty())
 +                      {
 +                              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str());
 +                              return;
 +                      }
 +              }
 +
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
 +#else
 +              // attempt to look up their nameserver from /etc/resolv.conf
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
 +
 +              std::ifstream resolv("/etc/resolv.conf");
 +
 +              while (resolv >> DNSServer)
 +              {
 +                      if (DNSServer == "nameserver")
 +                      {
 +                              resolv >> DNSServer;
 +                              if (DNSServer.find_first_not_of("0123456789.") == std::string::npos || DNSServer.find_first_not_of("0123456789ABCDEFabcdef:") == std::string::npos)
 +                              {
 +                                      ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str());
 +                                      return;
 +                              }
 +                      }
 +              }
 +
 +              ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
 +#endif
 +              DNSServer = "127.0.0.1";
 +      }
 +
 + public:
 +      ModuleDNS() : manager(this)
 +      {
 +      }
 +
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
 +      {
 +              std::string oldserver = DNSServer;
 +              DNSServer = ServerInstance->Config->ConfValue("dns")->getString("server");
 +              if (DNSServer.empty())
 +                      FindDNSServer();
 +
 +              if (oldserver != DNSServer)
 +                      this->manager.Rehash(DNSServer);
 +      }
 +
 +      void OnUnloadModule(Module* mod)
 +      {
 +              for (unsigned int i = 0; i <= MAX_REQUEST_ID; ++i)
 +              {
 +                      DNS::Request* req = this->manager.requests[i];
 +                      if (!req)
 +                              continue;
 +
 +                      if (req->creator == mod)
 +                      {
 +                              Query rr(*req);
 +                              rr.error = ERROR_UNLOADED;
 +                              req->OnError(&rr);
 +
 +                              delete req;
 +                      }
 +              }
 +      }
 +
 +      Version GetVersion()
 +      {
 +              return Version("DNS support", VF_CORE|VF_VENDOR);
 +      }
 +};
 +
 +MODULE_INIT(ModuleDNS)
 +
index e6b41382c91ef5e6bfd29914cecb73da8c790357,0000000000000000000000000000000000000000..4a0cb2f28eb1cc380f9d4ee98aae65ab7fd19831
mode 100644,000000..100644
--- /dev/null
@@@ -1,156 -1,0 +1,156 @@@
-                                       user->dhost.c_str(),
 +/*
 + * InspIRCd -- Internet Relay Chat Daemon
 + *
 + *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 + *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
 + *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
 + *
 + * This file is part of InspIRCd.  InspIRCd is free software: you can
 + * redistribute it and/or modify it under the terms of the GNU General Public
 + * License as published by the Free Software Foundation, version 2.
 + *
 + * This program is distributed in the hope that it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 + * details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +
 +#include "inspircd.h"
 +#include "core_oper.h"
 +
 +CommandKill::CommandKill(Module* parent)
 +      : Command(parent, "KILL", 2, 2)
 +{
 +      flags_needed = 'o';
 +      syntax = "<nickname> <reason>";
 +      TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
 +}
 +
 +
 +/** Handle /KILL
 + */
 +CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
 +{
 +      /* Allow comma seperated lists of users for /KILL (thanks w00t) */
 +      if (CommandParser::LoopCall(user, this, parameters, 0))
 +      {
 +              // If we got a colon delimited list of nicks then the handler ran for each nick,
 +              // and KILL commands were broadcast for remote targets.
 +              return CMD_FAILURE;
 +      }
 +
 +      User *u = ServerInstance->FindNick(parameters[0]);
 +      if (u)
 +      {
 +              /*
 +               * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc.
 +               * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got.
 +               *
 +               * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill
 +               * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
 +               */
 +
 +              if (IS_LOCAL(user))
 +              {
 +                      /*
 +                       * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user
 +                       * and the other half not. This would be a bad thing. ;p -- w00t
 +                       */
 +                      ModResult MOD_RESULT;
 +                      FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, u, parameters[1]));
 +
 +                      if (MOD_RESULT == MOD_RES_DENY)
 +                              return CMD_FAILURE;
 +
 +                      killreason = "Killed (";
 +                      if (!ServerInstance->Config->HideKillsServer.empty())
 +                      {
 +                              // hidekills is on, use it
 +                              killreason += ServerInstance->Config->HideKillsServer;
 +                      }
 +                      else
 +                      {
 +                              // hidekills is off, do nothing
 +                              killreason += user->nick;
 +                      }
 +
 +                      killreason += " (" + parameters[1] + "))";
 +              }
 +              else
 +              {
 +                      /* Leave it alone, remote server has already formatted it */
 +                      killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit);
 +              }
 +
 +              /*
 +               * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed.
 +               * No time to fix it right now, so left a note. -- w00t
 +               */
 +              if (!IS_LOCAL(u))
 +              {
 +                      // remote kill
 +                      if (!user->server->IsULine())
 +                              ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
 +                      this->lastuuid = u->uuid;
 +              }
 +              else
 +              {
 +                      // local kill
 +                      /*
 +                       * XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill
 +                       * snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t
 +                       */
 +                      if (!user->server->IsULine())
 +                      {
 +                              if (IS_LOCAL(user))
 +                                      ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
 +                              else
 +                                      ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
 +                      }
 +
 +                      ServerInstance->Logs->Log("KILL", LOG_DEFAULT, "LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
 +
 +                      u->Write(":%s KILL %s :%s!%s!%s (%s)", ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
 +                                      u->nick.c_str(),
 +                                      ServerInstance->Config->ServerName.c_str(),
++                                      ServerInstance->Config->HideKillsServer.empty() ? user->dhost.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
 +                                      ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
 +                                      parameters[1].c_str());
 +
 +                      this->lastuuid.clear();
 +              }
 +
 +              // send the quit out
 +              ServerInstance->Users->QuitUser(u, killreason);
 +      }
 +      else
 +      {
 +              user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str());
 +              return CMD_FAILURE;
 +      }
 +
 +      return CMD_SUCCESS;
 +}
 +
 +RouteDescriptor CommandKill::GetRouting(User* user, const std::vector<std::string>& parameters)
 +{
 +      // FindNick() doesn't work here because we quit the target user in Handle() which
 +      // removes it from the nicklist, so we check lastuuid: if it's empty then this KILL
 +      // was for a local user, otherwise it contains the uuid of the user who was killed.
 +      if (lastuuid.empty())
 +              return ROUTE_LOCALONLY;
 +      return ROUTE_BROADCAST;
 +}
 +
 +
 +void CommandKill::EncodeParameter(std::string& param, int index)
 +{
 +      // Manually translate the nick -> uuid (see above), and also the reason (params[1])
 +      // because we decorate it if the oper is local and want remote servers to see the
 +      // decorated reason not the original.
 +      param = ((index == 0) ? lastuuid : killreason);
 +}
index 69aa2619ccc6f9a00c600dde581b5f3718fd2807,656be220f6c60d37469dd88f793a5b00bcc7daa2..eb24cdea0a2b0d924e8a116f6e4bbd7639c17bd5
@@@ -653,9 -818,9 +653,9 @@@ void InspIRCd::Run(
                        }
                        else if (TIME.tv_sec > OLDTIME + 2)
                        {
-                               SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)TIME.tv_sec - OLDTIME);
+                               SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)(TIME.tv_sec - OLDTIME));
                        }
 -\r
 +
                        OLDTIME = TIME.tv_sec;
  
                        if ((TIME.tv_sec % 3600) == 0)
index 8d786cc3f98e82b2758f3973fb147776a965833d,813a8ecfaa98990698339c32ac17ff15dd660ffa..6a653ddedd60f6f2dbb94e95be4097d4be8f7f94
  # include <gcrypt.h>
  #endif
  
 -enum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };
 -
 -struct SSLConfig : public refcountbase
 -{
 -      gnutls_certificate_credentials_t x509_cred;
 -      std::vector<gnutls_x509_crt_t> x509_certs;
 -      gnutls_x509_privkey_t x509_key;
 -      gnutls_dh_params_t dh_params;
 -#ifdef GNUTLS_NEW_PRIO_API
 -      gnutls_priority_t priority;
 -#endif
 -
 -      SSLConfig()
 -              : x509_cred(NULL)
 -              , x509_key(NULL)
 -              , dh_params(NULL)
 -#ifdef GNUTLS_NEW_PRIO_API
 -              , priority(NULL)
 +#ifdef _WIN32
- # pragma comment(lib, "libgnutls-28.lib")
++# pragma comment(lib, "libgnutls-30.lib")
  #endif
 -      {
 -      }
 -
 -      ~SSLConfig()
 -      {
 -              ServerInstance->Logs->Log("m_ssl_gnutls", DEBUG, "Destroying SSLConfig %p", (void*)this);
  
 -              if (x509_cred)
 -                      gnutls_certificate_free_credentials(x509_cred);
 +/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") eval("print `libgcrypt-config --cflags | tr -d \r` if `pkg-config --modversion gnutls 2>/dev/null | tr -d \r` lt '2.12'") */
 +/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") eval("print `libgcrypt-config --libs | tr -d \r` if `pkg-config --modversion gnutls 2>/dev/null | tr -d \r` lt '2.12'") */
  
 -              for (unsigned int i = 0; i < x509_certs.size(); i++)
 -                      gnutls_x509_crt_deinit(x509_certs[i]);
 -
 -              if (x509_key)
 -                      gnutls_x509_privkey_deinit(x509_key);
 -
 -              if (dh_params)
 -                      gnutls_dh_params_deinit(dh_params);
 +// These don't exist in older GnuTLS versions
 +#if INSPIRCD_GNUTLS_HAS_VERSION(2, 1, 7)
 +#define GNUTLS_NEW_PRIO_API
 +#endif
  
 -#ifdef GNUTLS_NEW_PRIO_API
 -              if (priority)
 -                      gnutls_priority_deinit(priority);
 +#if (!INSPIRCD_GNUTLS_HAS_VERSION(2, 0, 0))
 +typedef gnutls_certificate_credentials_t gnutls_certificate_credentials;
 +typedef gnutls_dh_params_t gnutls_dh_params;
  #endif
 -      }
 -};
  
 -static reference<SSLConfig> currconf;
 +enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_HANDSHAKEN };
  
 -static SSLConfig* GetSessionConfig(gnutls_session_t session);
 +#if INSPIRCD_GNUTLS_HAS_VERSION(2, 12, 0)
 +#define INSPIRCD_GNUTLS_HAS_VECTOR_PUSH
 +#define GNUTLS_NEW_CERT_CALLBACK_API
 +typedef gnutls_retr2_st cert_cb_last_param_type;
 +#else
 +typedef gnutls_retr_st cert_cb_last_param_type;
 +#endif
  
 -#if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
 -static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
 -      const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) {
 +#if INSPIRCD_GNUTLS_HAS_VERSION(3, 3, 5)
 +#define INSPIRCD_GNUTLS_HAS_RECV_PACKET
 +#endif
  
 -      st->type = GNUTLS_CRT_X509;
 +#if INSPIRCD_GNUTLS_HAS_VERSION(2, 99, 0)
 +// The second parameter of gnutls_init() has changed in 2.99.0 from gnutls_connection_end_t to unsigned int
 +// (it became a general flags parameter) and the enum has been deprecated and generates a warning on use.
 +typedef unsigned int inspircd_gnutls_session_init_flags_t;
  #else
 -static int cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs,
 -      const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st) {
 -      st->cert_type = GNUTLS_CRT_X509;
 -      st->key_type = GNUTLS_PRIVKEY_X509;
 +typedef gnutls_connection_end_t inspircd_gnutls_session_init_flags_t;
  #endif
 -      SSLConfig* conf = GetSessionConfig(session);
 -      std::vector<gnutls_x509_crt_t>& x509_certs = conf->x509_certs;
 -      st->ncerts = x509_certs.size();
 -      st->cert.x509 = &x509_certs[0];
 -      st->key.x509 = conf->x509_key;
 -      st->deinit_all = 0;
  
 -      return 0;
 -}
 +#if INSPIRCD_GNUTLS_HAS_VERSION(3, 1, 9)
 +#define INSPIRCD_GNUTLS_HAS_CORK
 +#endif
  
  class RandGen : public HandlerBase2<void, char*, size_t>
  {
index c844ef04f45b1cfbceb8b6fe5707fb06bd6cd081,2df6d7af0cdb46c9a3c42b160bbe266ce69057aa..0eb208138276a6b7d4ee3673b2d3af18d4b3b776
@@@ -86,10 -68,12 +86,15 @@@ struct CallerIDExtInfo : public Extensi
  
        void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
        {
 +              if (format == FORMAT_NETWORK)
 +                      return;
 +
+               void* old = get_raw(container);
+               if (old)
+                       this->free(old);
                callerid_data* dat = new callerid_data;
+               set_raw(container, dat);
                irc::commasepstream s(value);
                std::string tok;
                if (s.GetToken(tok))
Simple merge
index 93fddf813d83c290ccf07dd625a80e9b00b0ff05,043486283365bd3f6467c388eb8244f5989aea62..0e57896f3b78c78d7317536b745660e202277b72
@@@ -55,12 -57,9 +55,13 @@@ typedef SimpleExtItem<dccallowlist> DCC
  
  class CommandDccallow : public Command
  {
 +      DCCAllowExt& ext;
 +
   public:
 -      CommandDccallow(Module* parent) : Command(parent, "DCCALLOW", 0)
+       unsigned int maxentries;
 +      CommandDccallow(Module* parent, DCCAllowExt& Ext)
 +              : Command(parent, "DCCALLOW", 0)
 +              , ext(Ext)
        {
                syntax = "[(+|-)<nick> [<time>]]|[LIST|HELP]";
                /* XXX we need to fix this so it can work with translation stuff (i.e. move +- into a seperate param */
                                                ul.push_back(user);
                                        }
  
 -                                              user->WriteNumeric(996, "%s %s :Too many nicks on DCCALLOW list", user->nick.c_str(), user->nick.c_str());
+                                       if (dl->size() >= maxentries)
+                                       {
++                                              user->WriteNumeric(996, "%s :Too many nicks on DCCALLOW list", user->nick.c_str());
+                                               return CMD_FAILURE;
+                                       }
                                        for (dccallowlist::const_iterator k = dl->begin(); k != dl->end(); ++k)
                                        {
                                                if (k->nickname == target->nick)
@@@ -451,8 -481,8 +458,11 @@@ class ModuleDCCAllow : public Modul
                }
        }
  
 -      void ReadFileConf()
 +      void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
++              ConfigTag* tag = ServerInstance->Config->ConfValue("dccallow");
++              cmd.maxentries = tag->getInt("maxentries", 20);
++
                bfl.clear();
                ConfigTagList tags = ServerInstance->Config->ConfTags("banfile");
                for (ConfigIter i = tags.first; i != tags.second; ++i)
index 9f40d702e98acdae8edd6c35786357c7a4c1f743,9d50bd2e5342b410ef4715c492c6ffdf50502944..03f6745ee9796ed887b4ff843a3a10e9b2aa9d1b
@@@ -30,15 -35,29 +33,30 @@@ class HideOper : public SimpleUserModeH
        {
                oper = true;
        }
+       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+       {
+               if (SimpleUserModeHandler::OnModeChange(source, dest, channel, parameter, adding) == MODEACTION_DENY)
+                       return MODEACTION_DENY;
+               if (adding)
+                       opercount++;
+               else
+                       opercount--;
+               return MODEACTION_ALLOW;
+       }
  };
  
 -class ModuleHideOper : public Module
 +class ModuleHideOper : public Module, public Whois::LineEventListener
  {
        HideOper hm;
+       bool active;
   public:
        ModuleHideOper()
 -              : hm(this)
 +              : Whois::LineEventListener(this)
 +              , hm(this)
+               , active(false)
        {
        }
  
                return Version("Provides support for hiding oper status with user mode +H", VF_VENDOR);
        }
  
 -      void OnUserQuit(User* user, const std::string&, const std::string&)
++      void OnUserQuit(User* user, const std::string&, const std::string&) CXX11_OVERRIDE
+       {
 -              if (user->IsModeSet('H'))
++              if (user->IsModeSet(hm))
+                       hm.opercount--;
+       }
 -      ModResult OnNumeric(User* user, unsigned int numeric, const std::string& text)
++      ModResult OnNumeric(User* user, unsigned int numeric, const std::string& text) CXX11_OVERRIDE
+       {
+               if (numeric != 252 || active || user->HasPrivPermission("users/auspex"))
+                       return MOD_RES_PASSTHRU;
+               // If there are no visible operators then we shouldn't send the numeric.
+               size_t opercount = ServerInstance->Users->all_opers.size() - hm.opercount;
+               if (opercount)
+               {
+                       active = true;
 -                      user->WriteNumeric(252, "%s %lu :operator(s) online", user->nick.c_str(),  opercount);
++                      user->WriteNumeric(252, "%lu :operator(s) online", opercount);
+                       active = false;
+               }
+               return MOD_RES_DENY;
+       }
 -      ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text)
 +      ModResult OnWhoisLine(Whois::Context& whois, unsigned int& numeric, std::string& text) CXX11_OVERRIDE
        {
                /* Dont display numeric 313 (RPL_WHOISOPER) if they have +H set and the
                 * person doing the WHOIS is not an oper
index 5275e9bd5911b5fc95d670ee5c6c4212ad790352,b7dd0e81bbfcd1e50a00f9f5aa198ec27804d0e2..9e94e556dbc4e2052e85fe5b1edef8ce6ce65c59
@@@ -160,7 -222,7 +160,7 @@@ class ModuleIRCv3 : public Module, publ
                {
                        // Send the away notify line if the current member is local, has the away-notify cap and isn't excepted
                        User* member = IS_LOCAL(it->first);
-                       if ((member) && (cap_awaynotify.get(member)) && (last_excepts.find(member) == last_excepts.end()))
 -                      if ((member) && (cap_awaynotify.ext.get(member)) && (last_excepts.find(member) == last_excepts.end()) && (it->second != memb))
++                      if ((member) && (cap_awaynotify.get(member)) && (last_excepts.find(member) == last_excepts.end()) && (it->second != memb))
                        {
                                member->Write(line);
                        }
index 1735df924298c4bdb70e5a6ef4d19574a5dc09ef,46710946b67a7adde9193e01aa4a14733a8d93d3..617ee43b368f5a3f6f48e588e4ab15ea5f227b35
@@@ -30,11 -33,17 +30,16 @@@ static void DisplayList(User* user, Cha
                        continue;
                items << " +" << mh->name;
                if (mh->GetNumParams(true))
-                       items << " " << channel->GetModeParameter(mh);
+               {
 -                      if ((letter == 'k') && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
++                      if ((mh->name == "key") && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
+                               items << " <key>";
+                       else
 -                              items << " " << channel->GetModeParameter(letter);
++                              items << " " << channel->GetModeParameter(mh);
+               }
        }
 -      char pfx[MAXBUF];
 -      snprintf(pfx, MAXBUF, ":%s 961 %s %s", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), channel->name.c_str());
 -      user->SendText(std::string(pfx), items);
 -      user->WriteNumeric(960, "%s %s :End of mode list", user->nick.c_str(), channel->name.c_str());
 +      const std::string line = ":" + ServerInstance->Config->ServerName + " 961 " + user->nick + " " + channel->name;
 +      user->SendText(line, items);
 +      user->WriteNumeric(960, "%s :End of mode list", channel->name.c_str());
  }
  
  class CommandProp : public Command
index b3937b4a4e2adf6d430b2176ce23c32a1dadcf7b,bc90c9fad5a620ce6050cbd83430f59a8b5a3c5c..8e836c407d59e3e0be3c810b787439c1e3e0322d
@@@ -271,11 -284,19 +271,19 @@@ class ModuleNationalChars : public Modu
        {
                ConfigTag* tag = ServerInstance->Config->ConfValue("nationalchars");
                charset = tag->getString("file");
-               casemapping = tag->getString("casemapping", charset);
 -              casemapping = tag->getString("casemapping", ServerConfig::CleanFilename(charset.c_str()));
++              casemapping = tag->getString("casemapping", FileSystem::GetFileName(charset));
+               if (casemapping.find(' ') != std::string::npos)
+                       throw ModuleException("<nationalchars:casemapping> must not contain any spaces!");
+ #if defined _WIN32
 -              if (!ServerInstance->Config->StartsWithWindowsDriveLetter(charset))
++              if (!FileSystem::StartsWithWindowsDriveLetter(charset))
+                       charset.insert(0, "./locales/");
+ #else
                if(charset[0] != '/')
                        charset.insert(0, "../locales/");
+ #endif
                unsigned char * tables[8] = { m_additional, m_additionalMB, m_additionalUp, m_lower, m_upper, m_additionalUtf8, m_additionalUtf8range, m_additionalUtf8interval };
-               loadtables(charset, tables, 8, 5);
+               if (!loadtables(charset, tables, 8, 5))
+                       throw ModuleException("The locale file failed to load. Check your log file for more information.");
                forcequit = tag->getBool("forcequit");
                CheckForceQuit("National character set changed");
                CheckRehash();
        }
  
        /*so Bynets Unreal distribution stuff*/
-       void loadtables(std::string filename, unsigned char ** tables, unsigned char cnt, char faillimit)
+       bool loadtables(std::string filename, unsigned char ** tables, unsigned char cnt, char faillimit)
        {
 -              std::ifstream ifs(filename.c_str());
 +              std::ifstream ifs(ServerInstance->Config->Paths.PrependConfig(filename).c_str());
                if (ifs.fail())
                {
 -                      ServerInstance->Logs->Log("m_nationalchars",DEFAULT,"loadtables() called for missing file: %s", filename.c_str());
 +                      ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for missing file: %s", filename.c_str());
-                       return;
+                       return false;
                }
  
                for (unsigned char n=0; n< cnt; n++)
                {
                        if (loadtable(ifs, tables[n], 255) && (n < faillimit))
                        {
 -                              ServerInstance->Logs->Log("m_nationalchars",DEFAULT,"loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
 +                              ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "loadtables() called for illegal file: %s (line %d)", filename.c_str(), n+1);
-                               return;
+                               return false;
                        }
                }
  
index 3cfe9f6b8b504cf753a576879f5537c9b232a80b,10653cf74a13692c75f618c444341c463096275a..7682706965c1b2b9bfe9ca0b4770280dd85a6165
@@@ -78,11 -74,15 +78,15 @@@ install(FILES ${EXTRA_DLLS} DESTINATIO
  file(GLOB_RECURSE EXAMPLE_CONFIGS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/docs/conf/*.example")
  install(FILES ${EXAMPLE_CONFIGS} DESTINATION conf)
  
+ # Install nationalchars files
+ file(GLOB_RECURSE EXAMPLE_LOCALES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${INSPIRCD_BASE}/locales/*")
+ install(FILES ${EXAMPLE_LOCALES} DESTINATION locales)
  # Create an empty data and logs directory and install them
 -file(MAKE_DIRECTORY ${DATA_PATH})
 -install(DIRECTORY ${DATA_PATH} DESTINATION .)
 -file(MAKE_DIRECTORY ${LOG_PATH})
 -install(DIRECTORY ${LOG_PATH} DESTINATION .)
 +file(MAKE_DIRECTORY ${DATA_DIR})
 +install(DIRECTORY ${DATA_DIR} DESTINATION .)
 +file(MAKE_DIRECTORY ${LOG_DIR})
 +install(DIRECTORY ${LOG_DIR} DESTINATION .)
  
  if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
        include(InstallRequiredSystemLibraries)
index 2218d930063d0d42aa40620a5f58679e511905b3,d7ceb73aa46170a437f9468c9f958825e75bc20e..d65d4eb92f84a61486131d1fe52fc238ffd3939d
@@@ -96,14 -91,12 +97,16 @@@ CoreExport const char * insp_inet_ntop(
  #define inet_pton insp_inet_pton
  #define inet_ntop insp_inet_ntop
  
- /* Safe printf functions aren't defined in VC++ */
+ /* Safe printf functions aren't defined in VC++ releases older than v14 */
+ #if _MSC_VER <= 1800
  #define snprintf _snprintf
  #define vsnprintf _vsnprintf
+ #endif
  
 +#ifndef va_copy
 +#define va_copy(dest, src) (dest = src)
 +#endif
 +
  /* Unix-style sleep (argument is in seconds) */
  __inline void sleep(int seconds) { Sleep(seconds * 1000); }