channel halfoperator to remove a user. A removed user will part with
a message stating they were removed from the channel and by whom.">
+<helpop key="rmode" value="/RMODE [channel] [modeletter] {[pattern]}
+
+Removes listmodes from a channel.
+E.g. /RMODE #Chan b m:* will remove all mute extbans.">
+
<helpop key="fpart" value="/FPART [channel] [nick] {[reason]}
This behaves identically to /REMOVE, the only difference is that that
c Blocks private messages and notices from users who do
not share a common channel with you (requires
commonchans module).
- d Deaf mode. User will not recieve any messages or notices
+ d Deaf mode. User will not receive any messages or notices
from channels they are in (requires deaf module).
g In combination with /allow, provides for server side
ignore (requires callerid module).
v [nickname] Gives voice to [nickname], allowing them to speak
while the channel is +m.
- h [nickname] Gives halfop status to [nickname] (this mode can
- be disabled).
+ h [nickname] Gives halfop status to [nickname] (requires
+ customprefix module).
o [nickname] Gives op status to [nickname].
a [nickname] Gives protected status to [nickname], preventing
them from them from being kicked (+q only,
- requires chanprotect module).
+ requires customprefix module).
q [nickname] Gives owner status to [nickname], preventing them
from being kicked (Services or only, requires
- chanprotect module).
+ customprefix module).
b [hostmask] Bans [hostmask] from the channel.
e [hostmask] Excepts [hostmask] from bans (requires
module).
D Delays join messages from users until they
message the channel (requires delayjoin module).
+ E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similiar messages.
+ Kicks as default, blocks with ~ and bans with *
+ The last two parameters are optional.
F [changes]:[sec] Blocks nick changes when they equal or exceed the
specified rate (requires nickflood module).
G Censors messages to the channel based on the
c Blocks private messages and notices from users who do
not share a common channel with you (requires
commonchans module).
- d Deaf mode. User will not recieve any messages or notices
+ d Deaf mode. User will not receive any messages or notices
from channels they are in (requires deaf module).
g In combination with /allow, provides for server side
ignore (requires callerid module).
v [nickname] Gives voice to [nickname], allowing them to speak
while the channel is +m.
- h [nickname] Gives halfop status to [nickname] (this mode can
- be disabled).
+ h [nickname] Gives halfop status to [nickname] (requires
+ customprefix module).
o [nickname] Gives op status to [nickname].
a [nickname] Gives protected status to [nickname], preventing
them from them from being kicked (+q only,
- requires chanprotect module).
+ requires customprefix module).
q [nickname] Gives owner status to [nickname], preventing them
from being kicked (Services or only, requires
- chanprotect module).
+ customprefix module).
b [hostmask] Bans [hostmask] from the channel.
e [hostmask] Excepts [hostmask] from bans (requires
module).
D Delays join messages from users until they
message the channel (requires delayjoin module).
+ E [~*][lines]:[sec]{[:difference]}{[:backlog]} Allows blocking of similiar messages.
+ Kicks as default, blocks with ~ and bans with *
+ The last two parameters are optional.
F [changes]:[sec] Blocks nick changes when they equal or exceed the
specified rate (requires nickflood module).
G Censors messages to the channel based on the
# #
########################################################################
+#-#-#-#-#-#-#-#-#-# CONFIGURATION FORMAT #-#-#-#-#-#-#-#-#-#-#-#-#-#-
+# #
+# In order to maintain compatibility with older configuration files, #
+# you can change the configuration parser to parse as it did in #
+# previous releases. When using the "compat" format, you need to use #
+# C++ escape sequences (e.g. \n) instead of XML ones (e.g. &nl;) and #
+# can not use <define> to create macros. #
+#<config format="compat">
+
#-#-#-#-#-#-#-#-#-# INCLUDE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#-#
# #
# This optional tag allows you to include another config file #
# #
# Variables may be redefined and may reference other variables. #
# Value expansion happens at the time the tag is read. #
-# #
-# Using variable definitions REQUIRES that the config format be #
-# changed to "xml" from the default "compat" that uses escape #
-# sequences such as "\"" and "\n", and does not support <define> #
-<config format="xml">
<define name="bindip" value="1.2.2.3">
<define name="localips" value="&bindip;/24">
# |_| \_\___|\__,_|\__,_| |_| |_| |_|_|___/ |____/|_|\__(_) #
# #
# If you want to link servers to InspIRCd you must load the #
- # m_spanningtree.so module! Please see the modules list below for #
+ # m_spanningtree.so module! Please see the modules list for #
# information on how to load this module! If you do not load this #
- # module, server ports will NOT be bound! #
- # #
- # PLEASE NOTE: If you have build InspIRCd with IPv6 support, you MUST #
- # specify a bind address if you want the IRCd to bind to a IPv4 IP. #
+ # module, server ports will NOT work! #
<bind
# address: IP address to bind to if the box that you are hosting
# for ssl to work. If you do not want this bind section to support ssl,
# just remove or comment out this option.
ssl="gnutls"
+
+ # defer: When this is non-zero, connections will not be handed over to
+ # the daemon from the operating system before data is ready.
+ # In Linux, the value indicates the number of seconds we'll wait for a
+ # connection to come up with data. Don't set it too low!
+ # In BSD the value is ignored; only zero and non-zero is possible.
+ # Windows ignores this parameter completely.
+ # Note: This does not take effect on rehash.
+ # To change it on a running bind, you'll have to comment it out,
+ # rehash, comment it in and rehash again.
+ defer="0"
>
<bind address="" port="6660-6669" type="clients">
# #
<power
- # hash: what hash these passwords are hashed with. requires the module
- # for selected hash (m_md5.so, m_sha256.so or m_ripemd160.so) be
- # loaded and the password hashing module (m_password_hash.so)
- # loaded. Options here are: "md5", "sha256" and "ripemd160".
+ # hash: what hash these passwords are hashed with.
+ # Requires the module for selected hash (m_md5.so, m_sha256.so
+ # or m_ripemd160.so) be loaded and the password hashing module
+ # (m_password_hash.so) loaded.
+ # Options here are: "md5", "sha256" and "ripemd160", or one of
+ # these prefixed with "hmac-", e.g.: "hmac-sha256".
# Optional, but recommended. Create hashed password with:
# /mkpasswd <hash> <password>
#hash="sha256"
# maxconnwarn: Enable warnings when localmax or globalmax is hit (defaults to on)
maxconnwarn="off"
+ # resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
+ # in this class. This can save a lot of resources on very busy servers.
+ resolvehostnames="yes"
+
# usednsbl: Defines whether or not users in this class are subject to DNSBL. Default is yes.
# This setting only has effect when m_dnsbl is loaded.
#usednsbl="yes"
# globalmax: Maximum global (network-wide) connections per IP.
globalmax="3"
+ # resolvehostnames: If disabled, no DNS lookups will be performed on connecting users
+ # in this class. This can save a lot of resources on very busy servers.
+ resolvehostnames="yes"
+
# useident: Defines if users in this class must respond to a ident query or not.
useident="no"
# This file has all the information about oper classes, types and o:lines.
# You *MUST* edit it.
-<include file="conf/examples/opers.conf.example">
+<include file="examples/opers.conf.example">
# This file has all the information about server links and ulined servers.
# You *MUST* edit it if you intend to link servers.
-<include file="conf/examples/links.conf.example">
+<include file="examples/links.conf.example">
#-#-#-#-#-#-#-#-#-#- MISCELLANEOUS CONFIGURATION -#-#-#-#-#-#-#-#-#-#
# #
# motd - displayed on connect and when a user executes /MOTD
# rules - displayed when the user executes /RULES
# Modules can also define their own files
-<files motd="conf/examples/motd.txt.example" rules="conf/examples/rules.txt.example">
+<files motd="examples/motd.txt.example" rules="examples/rules.txt.example">
# Example of an executable file include. Note this will be read on rehash,
# not when the command is run.
# the correct parameters are.
syntaxhints="no"
- # cyclehosts: If enabled, when a user gets a host set, it will cycle
- # them in all their channels. If not, it will simply change their host
- # without cycling them.
- cyclehosts="yes"
-
# cyclehostsfromuser: If enabled, the source of the mode change for
# cyclehosts will be the user who cycled. This can look nicer, but
# triggers anti-takeover mechanisms of some obsolete bots.
# defaultmodes: What modes are set on a empty channel when a user
# joins it and it is unregistered. This is similar to Asuka's
# autochanmodes.
- defaultmodes="nt"
+ defaultmodes="not"
- # moronbanner: This is the text that is sent to a user when they are
+ # xlinemessage: This is the text that is sent to a user when they are
# banned from the server.
- moronbanner="You're banned! Email haha@abuse.com with the ERROR line below for help."
+ xlinemessage="You're banned! Email irc@example.com with the ERROR line below for help."
# exemptchanops: exemptions for channel access restrictions based on prefix.
exemptchanops="nonick:v flood:o"
# nosnoticestack: This prevents snotices from 'stacking' and giving you
# the message saying '(last message repeated X times)'. Defaults to no.
- nosnoticestack="no"
-
- # welcomenotice: When turned on, this sends a NOTICE to connecting users
- # with the text Welcome to <networkname>! after successful registration.
- # Defaults to yes.
- welcomenotice="yes">
+ nosnoticestack="no">
#-#-#-#-#-#-#-#-#-#-#-# PERFORMANCE CONFIGURATION #-#-#-#-#-#-#-#-#-#-#
# #
<performance
- # netbuffersize: Size of the buffer used to recieve data from clients.
+ # netbuffersize: Size of the buffer used to receive data from clients.
# The ircd may only read this amount of text in 1 go at any time.
netbuffersize="10240"
- # maxwho: Maximum number of results to show in a /who query.
- maxwho="4096"
-
# somaxconn: The maximum number of connections that may be waiting
# in the accept queue. This is *NOT* the total maximum number of
# connections per server. Some systems may only allow this to be up
# to 5, while others (such as linux and *BSD) default to 128.
+ # Setting this above the limit imposed by your OS can have undesired
+ # effects.
somaxconn="128"
- # limitsomaxconn: By default, somaxconn (see above) is limited to a
- # safe maximum value in the 2.0 branch for compatibility reasons.
- # This setting can be used to disable this limit, forcing InspIRCd
- # to use the value specifed above.
- limitsomaxconn="true"
-
# softlimit: This optional feature allows a defined softlimit for
# connections. If defined, it sets a soft max connections value.
softlimit="12800"
+ # clonesonconnect: If this is set to false, we won't check for clones
+ # on initial connection, but only after the DNS check is done.
+ # This can be useful where your main class is more restrictive
+ # than some other class a user can be assigned after DNS lookup is complete.
+ # Turning this option off will make the server spend more time on users we may
+ # potentially not want. Normally this should be neglible, though.
+ # Default value is true
+ clonesonconnect="true"
+
# quietbursts: When syncing or splitting from a network, a server
# can generate a lot of connect and quit messages to opers with
# +C and +Q snomasks. Setting this to yes squelches those messages,
# which makes it easier for opers, but degrades the functionality of
# bots like BOPM during netsplits.
- quietbursts="yes"
-
- # nouserdns: If enabled, no DNS lookups will be performed on
- # connecting users. This can save a lot of resources on very busy servers.
- nouserdns="no">
+ quietbursts="yes">
#-#-#-#-#-#-#-#-#-#-#-# SECURITY CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#
# #
<security
+ # allowcoreunload: If this value is set to yes, Opers will be able to
+ # unload core modules (e.g. cmd_privmsg.so).
+ allowcoreunload="no"
# announceinvites: This option controls which members of the channel
# receive an announcement when someone is INVITEd. Available values:
# (Commands like /notice, /privmsg, /kick, etc)
maxtargets="20"
- # customversion: Displays a custom string when a user /version's
- # the ircd. This may be set for security reasons or vanity reasons.
+ # customversion: A custom message to be displayed in the comments field
+ # of the VERSION command response. This does not hide the InspIRCd version.
customversion=""
# operspywhois: show opers (users/auspex) the +s channels a user is in. Values:
# maxaway: Maximum length of an away message.
maxaway="200">
+#-#-#-#-#-#-#-#-#-#-#-#-# PATHS CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
+# #
+# This configuration tag defines the location that InspIRCd stores #
+# various types of files such as configuration files, log files and #
+# modules. You will probably not need to change these from the values #
+# set when InspIRCd was built unless you are using a binary package #
+# where you do not have the ability to set build time configuration. #
+#<path configdir="conf" datadir="data" logdir="logs" moduledir="modules">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Logging
# provide almost all the features of InspIRCd. :) #
# #
# The default does nothing -- we include it for simplicity for you. #
-<include file="conf/examples/modules.conf.example">
+<include file="examples/modules.conf.example">
# Here are some pre-built modules.conf files that closely match the
# default configurations of some popular IRCd's. You still may want to
# recommended that you make your own modules file based on modules.conf.example.
# Settings similar to UnrealIRCd defaults.
-#<include file="conf/examples/modules/unrealircd.conf.example">
+#<include file="examples/modules/unrealircd.conf.example">
# Settings similar to Charybdis IRCd defaults.
-#<include file="conf/examples/modules/charybdis.conf.example">
+#<include file="examples/modules/charybdis.conf.example">
#########################################################################
# failover (see above).
timeout="300"
- # ssl: If defined, this states extra modules that will be used when
+ # ssl: If defined, this states the SSL module that will be used when
# making an outbound connection to the server. Options are: "openssl"
- # and "gnutls" for encryption (they are compatible with each other).
- # You must use the same (or a compatible) transport on both sides of the link.
+ # and "gnutls" (they are compatible with each other).
#
# You will need to load the m_ssl_openssl.so module for openssl,
# m_ssl_gnutls.so for gnutls. The server port that you connect to
# Simple autoconnect block. This enables automatic connection of a server
# Recommended setup is to have leaves connect to the hub, and have no
# automatic connections started by the hub.
-<autoconnect period="300" server="hub.penguin.org">
+<autoconnect period="10m" server="hub.penguin.org">
# Failover autoconnect block. If you have multiple hubs, or want your network
# to automatically link even if the hub is down, you can specify multiple
- # space seperated servers to autoconnect; they will be tried in a round
+ # space separated servers to autoconnect; they will be tried in a round
# robin fashion until one succeeds. Period defines the time for restarting
# a single loop.
<autoconnect period="120"
# Allow opers (channels/auspex) to see see all joins/parts/kicks in the channel
#
# Exemptchanops can be used to adjust the level at which users become visible or
- # the level at which they can see the full membe list of the channel.
+ # the level at which they can see the full member list of the channel.
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Autoop module: Adds basic channel access controls via the +w listmode.
# capsmap="ABCDEFGHIJKLMNOPQRSTUVWXYZ! ">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Block color module: Blocking color-coded messages with cmode +c
+ # Block color module: Blocking color-coded messages with chan mode +c
#<module name="m_blockcolor.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# CallerID module: Adds usermode +g which activates hybrid-style
- # callerid (== block all private messages unless you /accept first)
+ # callerid: block all private messages unless you /accept first
#<module name="m_callerid.so">
#
#-#-#-#-#-#-#-#-#-#-#- CALLERID CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# specify some censor tags. See also: #
# http://wiki.inspircd.org/Modules/censor #
#
-#<include file="conf/examples/censor.conf.example">
+#<include file="examples/censor.conf.example">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# CGI:IRC module: Adds support for automatic host changing in CGI:IRC
# This is the hard limit for 'X'.
# If notice is set to yes, joining users will get a NOTICE before playback
# telling them about the following lines being the pre-join history.
-#<chanhistory maxlines="20" notice="yes">
+# If bots is set to yes, it will also send to users marked with +B
+#<chanhistory maxlines="20" notice="yes" bots="yes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Channel logging module: used to send snotice output to channels, to
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Channel Names module: Allows disabling channels which have certain
- # characters in the channel name such as bold, colorcodes, etc which
+ # characters in the channel name such as bold, colorcodes, etc. which
# can be quite annoying and allow users to on occasion have a channel
# that looks like the name of another channel on the network.
#<module name="m_channames.so">
# in a channel matching a mask 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 a 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="&"
-
- # 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">
-
-
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Check module: gives /check
# Check is useful for looking up information on channels,
# cloak prefix as shown below. The cloak key must be shared across #
# the network for correct cloaking. #
# #
-# There are four methods of cloaking: #
+# There are two methods of cloaking: #
# #
# half Cloak only the "unique" portion of a host; show #
# the last 2 parts of the domain, /16 subnet of IPv4 #
# full Cloak the users completely, using three slices for #
# common CIDR bans (IPv4: /16, /24; IPv6: /48, /64) #
# #
-# These methods use a single key that can be any length of text. #
+# The methods use a single key that can be any length of text. #
# An optional prefix may be specified to mark cloaked hosts. #
-# #
-# The following methods are maintained for backwards compatibility; #
-# they are slightly less secure, and always hide unresolved IPs #
-# #
-# compat-host InspIRCd 1.2-compatible host-based cloaking #
-# compat-ip InspIRCd 1.2-compatible ip-always cloaking #
-# #
-# If you use a compat cloaking mode then you must specify key1, key2, #
-# key3, key4; the values must be less than 0x80000000 and should be #
-# picked at random. Prefix is mandatory, will default to network name #
-# if not specified, and will always have a "-" appended. #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
#
#<cloak mode="half"
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Clones module: Adds an oper command /CLONES for detecting cloned
- # users. Warning: This module may be resource intensive when its
- # command is issued, use with care.
+ # users. Warning: This command may be resource intensive when it is
+ # issued, use with care.
# This module is oper-only.
# To use, CLONES must be in one of your oper class blocks.
#<module name="m_clones.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Channel cycle module. Server side /hop, with +ilk etc bypass.
+ # Channel cycle module. Server side /hop, with +ilk etc. bypass.
#<module name="m_cycle.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# quitmsg="Throttled" bootwait="10">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Custom prefixes: allows for channel prefixes to be added.
+ # Custom prefixes: allows for channel prefixes to be added.
-# This replaces m_chanprotect and m_halfop.
#<module name="m_customprefix.so">
#
# name The name of the mode, must be unique from other modes
#<customprefix name="admin" letter="a" prefix="&" rank="40000" ranktoset="50000">
#<customprefix name="halfop" letter="h" prefix="%" rank="20000" ranktoset="30000">
#<customprefix name="halfvoice" letter="V" prefix="-" rank="1" ranktoset="20000">
+ #
+ # Do /reloadmodule m_customprefix.so after changing the settings of this module.
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Custom title module: Adds the /TITLE command which allows for trusted
# Glob masks are accepted here also.
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Devoice Module: Let users devoice themselves.
+ # Devoice Module: Let users devoice themselves using /devoice #chan.
#<module name="m_devoice.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# specfiy below the path to the filter.conf file, or define some #
# <filter> tags. #
# #
-#<include file="conf/examples/filter.conf.example">
+#<include file="examples/filter.conf.example">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Gecosban: Implements extended ban r:, which stops anyone matching
# must be in one of your oper class blocks.
#<module name="m_globalload.so">
-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
-# Halfop module: Provides the +h (halfops) channel status mode.
-#<module name="m_halfop.so">
-
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# HELPOP module: Provides the /HELPOP command
#<module name="m_helpop.so">
# specify below the path to the helpop.conf file, or if you like to #
# make a mess, define your helpop tags in this conf. #
# #
-#<include file="conf/examples/inspircd.helpop-full.example">
+#<include file="examples/inspircd.helpop-full.example">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # HIDECHANS module: Allows opers to hide their channels list from non-
+ # HIDECHANS module: Allows users to hide their channels list from non-
# opers by setting user mode +I on themselves.
#<module name="m_hidechans.so">
#
#<hostchange mask="a@b.com" action="set" value="blah.blah.blah">
#<hostchange mask="localhost" ports="7000,7001,7005-7007" action="set" value="blahblah.foo">
+# hostcycle: If loaded, when a user gets a host or ident set, it will
+# cycle them in all their channels. If not loaded it will simply change
+# their host/ident without cycling them.
+#<module name="m_hostcycle.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# httpd module: Provides http server support for InspIRCd
#<module name="m_httpd.so">
#<module name="m_ircv3.so">
# The following block can be used to control which extensions are
# enabled.
- #<ircv3 accoutnotify="on" awaynotify="on" extendedjoin="on">
+ #<ircv3 accountnotify="on" awaynotify="on" extendedjoin="on">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Join flood module: Adds support for join flood protection (+j)
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Anti-Auto-Rejoin: Adds support for prevention of auto-rejoin (+J)
#<module name="m_kicknorejoin.so">
-# Set the maximum time that is accepted as a parameter for +J here.
-#<kicknorejoin maxtime="1m">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Knock module: adds the /KNOCK command and +K channel mode
# are specified in a <class> tag that the oper is part of. This is so #
# you can control who has access to this possible dangerous command. #
# If your server is locked and you get disconnected, do a REHASH from #
- # shell to open up again.
+ # shell to open up again. #
#
# This module is oper-only.
#
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Map hiding module: replaces /MAP and /LINKS output to users with a #
# message to see a website, set by maphide="http://link.to/site" in #
- # the security tag, instead. #
+ # the <security> tag, instead. #
#<module name="m_maphide.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
#<module name="m_nokicks.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # No nicks module: Adds the +N channel mode, as well as the +b N:
- # extended bantype. +N stops all users from changing their nick,
- # the +b N: extban stops anyone from matching a +b N:nick!user@host
- # mask from changing their nick.
+ # No nicks module: Adds the +N channel mode, as well as the N extban.
+ # +N stops all users from changing their nick, the N extban stops
+ # anyone from matching a +b N:nick!user@host mask from changing their
+ # nick.
#<module name="m_nonicks.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
#<module name="m_nonotice.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Network buisness join module
+ # Network business join module
# Allows an oper to join a channel using /OJOIN, giving them +Y on the
# channel which makes them immune to kick/deop/etc.
#<module name="m_ojoin.so">
# If you are using the m_operjoin.so module, specify options here: #
# #
# channel - The channel name to join, can also be a comma #
- # seperated list eg. "#channel1,#channel2". #
+ # separated list eg. "#channel1,#channel2". #
# #
# override - Lets the oper join walking thru any modes that #
# might be set, even bans. Use "yes" or "no". #
#<operprefix prefix="!">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper MOTD module: Provides support for seperate message of the day
+ # Oper MOTD module: Provides support for separate message of the day
# on oper-up
# This module is oper-only.
#<module name="m_opermotd.so">
# Read the comment above <connect:allowmotdcolors> in #
# inspircd.conf.example for details. #
# #
-#<opermotd file="conf/examples/opermotd.txt.example" onoper="yes" processcolors="false">
+#<opermotd file="examples/opermotd.txt.example" onoper="yes" processcolors="false">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Override module: Adds support for oper override
# Oper levels module: Gives each oper a level and prevents
# actions being taken against higher level opers
# Specify the level as the 'level' parameter of the <type> tag
- # # This module is oper-only.
+ # This module is oper-only.
#<module name="m_operlevels.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# You can also use $user for the user ident string.
forwardmsg="NOTICE $nick :*** Forwarding PASS to $nickrequired"
- # cmd: Command for the nick to run when it recieves a connect
+ # cmd: Command for the nick to run when it receives a connect
# password.
cmd="PRIVMSG $nickrequired :IDENTIFY $pass">
# whenever +P is set, unset, or the topic/modes on a +P channel is changed.
# If you want to do this, set the filename below, and uncomment the include.
#
- #<permchanneldb filename="data/permchannels.conf">
+ # If 'listmodes' is true then all list modes (+b, +I, +e, +g...) will be
+ # saved. Defaults to false.
+ #<permchanneldb filename="data/permchannels.conf" listmodes="true">
#<include file="data/permchannels.conf">
#
# You may also create channels on startup by using the <permchannels> block.
# Optional - If you specify to use the m_randquote.so module, then #
# specify below the path to the randquotes.conf file. #
# #
- #<randquote file="randquotes.conf">
+ #<randquote file="quotes.txt">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Redirect module: Adds channel redirection (mode +L) #
# m_rline.
#<module name="m_regex_pcre.so">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# Regular Expression Provider for RE2 Regular Expressions.
+# You need libre2 installed and in your include/library paths in order
+# to compile and load this module.
+#<module name="m_regex_re2.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Regular Expression Provider for POSIX Regular Expressions.
# You shouldn't need any additional libraries on a POSIX-compatible
- # system (ie: any Linux, BSD, but not Windows). You must have at least
- # 1 provider loaded to use m_filter or m_rline.
+ # system (i.e.: any Linux, BSD, but not Windows). You must have at
+ # least 1 provider loaded to use m_filter or m_rline.
# On POSIX-compliant systems, regex syntax can be found by using the
# command: 'man 7 regex'.
#<module name="m_regex_posix.so">
# alternative to /KICK
#<module name="m_remove.so">
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# A module to block, kick or ban upon similiar messages being uttered several times.
+# Syntax [~*][lines]:[sec]{[:difference]}{[:matchlines]}
+# ~ is to block, * is to ban, default is kick.
+# lines - In mode 1 the amount of lines that has to match consecutively - In mode 2 the size of the backlog to keep for matching
+# seconds - How old the message has to be before it's invalidated.
+# distance - Edit distance, in percent, between two strings to trigger on.
+# matchlines - When set, the function goes into mode 2. In this mode the function will trigger if this many of the last <lines> matches.
+#
+# As this module can be rather CPU-intensive, it comes with some options.
+# maxbacklog - Maximum size that can be specified for backlog. 0 disables multiline matching.
+# maxdistance - Max percentage of difference between two lines we'll allow to match. Set to 0 to disable edit-distance matching.
+# maxlines - Max lines of backlog to match against.
+# maxsecs - Maximum value of seconds a user can set. 0 to allow any.
+# size - Maximum number of characters to check for, can be used to truncate messages
+# before they are checked, resulting in less CPU usage. Increasing this beyond 512
+# doesn't have any effect, as the maximum length of a message on IRC cannot exceed that.
+#<repeat maxbacklog="20" maxlines="20" maxdistance="50" maxsecs="0" size="512">
+#<module name="m_repeat.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Restricted channels module: Allows only opers to create channels.
#
# use glob. For this reason, is recommended to use a real regex engine
# so that at least \s or [[:space:]] is available.
+#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
+# RMODE module: Adds the /RMODE command
+# Allows channel mods to remove list modes en masse.
+# Syntax: /rmode <channel> <mode> [pattern]
+# E.g. '/rmode #Channel b m:*' will remove all mute-extbans on the channel.
+#<module name="m_rmode.so">
+
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SAJOIN module: Adds the /SAJOIN command
# This module is oper-only.
# and is similar in operation to the way asuka and ircu handle services.
#
# At the same time, this offers +r for users and channels to mark them
- # as identified seperately from the idea of a master account, which
+ # as identified separately from the idea of a master account, which
# can be useful for services which are heavily nick-as-account centric.
#
# This replaces m_services from 1.1 and earlier.
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SETNAME module: Adds the /SETNAME command
- # This module is oper-only.
- # To use, SETNAME must be in one of your oper class blocks.
#<module name="m_setname.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# it directly affects the mode object itself.
#<showwhois opersonly="yes"
#
- # You may also set whether or not users should recieve whois notices, should
+ # You may also set whether or not users should receive whois notices, should
# they be /whois'd by an oper.
# showfromopers="yes">
#<module name="m_sslmodes.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # GnuTLS ssl module: Adds support for client-server SSL using GnuTLS,
- # if enabled. You must copy the source for this module from the directory
- # src/modules/extra, or answer 'yes' in ./configure when asked if you
- # want to enable this, or it will not load.
+ # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
+ # if enabled. You must answer 'yes' in ./configure when asked or
+ # manually symlink the source for this module from the directory
+ # src/modules/extra, if you want to enable this, or it will not load.
#<module name="m_ssl_gnutls.so">
#
#-#-#-#-#-#-#-#-#-#-#- GNUTLS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
# m_ssl_gnutls.so is too complex it describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_gnutls #
- # #
- # NOTE: If you want to use this module to encrypt and sign your #
- # server to server traffic, you MUST load it before m_spanningtree in #
- # your configuration file! #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# SSL Info module: Allows users to retrieve information about other
#<module name="m_sslinfo.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # OpenSSL ssl module: Adds support for client-server SSL using OpenSSL,
- # if enabled. You must copy the source for this module from the directory
- # src/modules/extra, or answer 'yes' in ./configure when asked if you
- # want to enable this, or it will not load.
+ # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
+ # if enabled. You must answer 'yes' in ./configure when asked or symlink
+ # the source for this module from the directory src/modules/extra, if
+ # you want to enable this, or it will not load.
#<module name="m_ssl_openssl.so">
#
#-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
# m_ssl_openssl.so is too complex it describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_openssl #
- # #
- # NOTE: If you want to use this module to encrypt and sign your #
- # server to server traffic, you MUST load it before m_spanningtree in #
- # your configuration file! #
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Strip color module: Adds the channel mode +S
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Test line module: Adds the /TLINE command, used to test how many
- # users a /GLINE or /ZLINE etc would match.
+ # users a /GLINE or /ZLINE etc. would match.
# This module is oper-only.
# To use, TLINE must be in one of your oper class blocks.
#<module name="m_tline.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# UHNAMES support module: Adds support for the IRCX style UHNAMES
# extension, which displays ident and hostname in the names list for
- # each user, saving clients from doing a WHO on the channel. Note that
- # this module is not widely supported yet. If a client does not support
- # UHNAMES it will not enable it, this will not break incompatible
- # clients.
+ # each user, saving clients from doing a WHO on the channel.
+ # If a client does not support UHNAMES it will not enable it, this will
+ # not break incompatible clients.
#<module name="m_uhnames.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Userip module: Adds the /USERIP command
- # This module is oper-only.
- # To use, USERIP must be in one of your oper class blocks.
+ # Allows users to query their own IP, also allows opers to query the IP
+ # of anyone else.
#<module name="m_userip.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
<module name="m_chanfilter.so">
<chanfilter hidemask="yes">
-<module name="m_chanprotect.so">
-
-<chanprotect
- noservices="no"
- qprefix="~"
- aprefix="&"
- deprotectself="yes"
- deprotectothers="yes">
-
<module name="m_check.so">
<module name="m_chghost.so">
<hostname charmap="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_/0123456789">
# If you are using the m_operjoin.so module, specify options here: #
# #
# channel - The channel name to join, can also be a comma #
- # seperated list eg. "#channel1,#channel2". #
+ # separated list eg. "#channel1,#channel2". #
# #
# override - Lets the oper join walking thru any modes that #
# might be set, even bans. Use "yes" or "no". #
<module name="m_operlog.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # Oper MOTD module: Provides support for seperate message of the day
+ # Oper MOTD module: Provides support for separate message of the day
# on oper-up
# This module is oper-only.
#<module name="m_opermotd.so">
<module name="m_sslmodes.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # GnuTLS ssl module: Adds support for client-server SSL using GnuTLS,
- # if enabled. You must copy the source for this module from the directory
- # src/modules/extra, or answer 'yes' in ./configure when asked if you
- # want to enable this, or it will not load.
+ # GnuTLS SSL module: Adds support for SSL connections using GnuTLS,
+ # if enabled. You must answer 'yes' in ./configure when asked or symlink
+ # the source for this module from the directory src/modules/extra, if
+ # you want to enable this, or it will not load.
#<module name="m_ssl_gnutls.so">
#
#-#-#-#-#-#-#-#-#-#-#- GNUTLS CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
# m_ssl_gnutls.so is too complex it describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_gnutls #
- # #
- # NOTE: If you want to use this module to encrypt and sign your #
- # server to server traffic, you MUST load it before m_spanningtree in #
- # your configuration file! #
<module name="m_sslinfo.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
- # OpenSSL ssl module: Adds support for client-server SSL using OpenSSL,
- # if enabled. You must copy the source for this module from the directory
- # src/modules/extra, or answer 'yes' in ./configure when asked if you
- # want to enable this, or it will not load.
+ # OpenSSL SSL module: Adds support for SSL connections using OpenSSL,
+ # if enabled. You must answer 'yes' in ./configure when asked or symlink
+ # the source for this module from the directory src/modules/extra, if
+ # you want to enable this, or it will not load.
#<module name="m_ssl_openssl.so">
#
#-#-#-#-#-#-#-#-#-#-#- OPENSSL CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#-#
# #
# m_ssl_openssl.so is too complex it describe here, see the wiki: #
# http://wiki.inspircd.org/Modules/ssl_openssl #
- # #
- # NOTE: If you want to use this module to encrypt and sign your #
- # server to server traffic, you MUST load it before m_spanningtree in #
- # your configuration file! #
<module name="m_stripcolor.so">
<module name="m_svshold.so">
# - servers/auspex: allows opers with this priv to see more detail about server information than normal users.
# ACTIONS:
# - users/mass-message: allows opers with this priv to PRIVMSG and NOTICE to a server mask (e.g. NOTICE $*)
+ # - users/samode-usermodes: allows opers with this priv to change the user modes of any other user using /SAMODE
# - channels/high-join-limit: allows opers with this priv to join <channels:opers> total channels instead of <channels:users> total channels.
# PERMISSIONS:
# - users/flood/no-throttle: allows opers with this priv to send commands without being throttled (*NOTE)
- # - users/flood/increased-buffers: allows opers with this priv to send and recieve data without worrying about being disconnected for exceeding limits (*NOTE)
+ # - users/flood/increased-buffers: allows opers with this priv to send and receive data without worrying about being disconnected for exceeding limits (*NOTE)
#
- # *NOTE: These privs are potantially dangerous, as they grant users with them the ability to hammer your server's CPU/RAM as much as they want, essentially.
+ # *NOTE: These privs are potentially dangerous, as they grant users with them the ability to hammer your server's CPU/RAM as much as they want, essentially.
privs="users/auspex channels/auspex servers/auspex users/mass-message channels/high-join-limit users/flood/no-throttle users/flood/increased-buffers"
# usermodes: Oper-only usermodes that opers with this class can use.
# Remember: This is case sensitive
name="Brain"
- # hash: what hash this password is hashed with. requires the module
- # for selected hash (m_md5.so, m_sha256.so or m_ripemd160.so) be
- # loaded and the password hashing module (m_password_hash.so)
- # loaded. Options here are: "md5", "sha256" and "ripemd160".
+ # hash: what hash this password is hashed with.
+ # Requires the module for selected hash (m_md5.so, m_sha256.so
+ # or m_ripemd160.so) be loaded and the password hashing module
+ # (m_password_hash.so) loaded.
+ # Options here are: "md5", "sha256" and "ripemd160", or one of
+ # these prefixed with "hmac-", e.g.: "hmac-sha256".
# Create hashed password with: /mkpasswd <hash> <password>
hash="sha256"
*/
-#ifndef BASE_H
-#define BASE_H
+#pragma once
#include <map>
#include <deque>
};
template <typename T>
- class CoreExport reference
+ class reference
{
T* value;
public:
virtual ~ServiceProvider();
};
-
-#endif
*/
-#ifndef SOCKETENGINE_H
-#define SOCKETENGINE_H
+#pragma once
#include <vector>
#include <string>
#include <map>
-#include "inspircd_config.h"
+#include "config.h"
#include "socket.h"
#include "base.h"
/** Add a trial write. During the next DispatchEvents invocation, this
* will call HandleEvent with EVENT_WRITE unless writes are known to be
* blocking.
- *
+ *
* This could be used to group several writes together into a single
* send() syscall, or to ensure that writes are blocking when attempting
* to use FD_WANT_FAST_WRITE.
/** Assert that writes are known to block. This cancels FD_ADD_TRIAL_WRITE.
* Reset by SE before running EVENT_WRITE
*/
- FD_WRITE_WILL_BLOCK = 0x8000,
+ FD_WRITE_WILL_BLOCK = 0x8000,
/** Mask for trial read/trial write */
FD_TRIAL_NOTE_MASK = 0x5000
* @param buf The buffer in which the data that is sent is stored.
* @param len The size of the buffer.
* @param flags A flag value that controls the sending of the data.
- * @param to The remote IP address and port.
+ * @param to The remote IP address and port.
* @param tolen The size of the to parameter.
* @return This method should return exactly the same values as the system call it emulates.
*/
* Checks EAGAIN and WSAEWOULDBLOCK
*/
static bool IgnoreError();
+
+ /** Return the last socket related error. strrerror(errno) on *nix
+ */
+ static std::string LastError();
+
+ /** Returns the error for the given error num, strerror(errnum) on *nix
+ */
+ static std::string GetError(int errnum);
};
inline bool SocketEngine::IgnoreError()
}
SocketEngine* CreateSocketEngine();
-
-#endif
-
#include "inspircd.h"
#include "commands/cmd_whowas.h"
-WhoWasMaintainTimer * timer;
-
-CommandWhowas::CommandWhowas( Module* parent) : Command(parent, "WHOWAS", 1)
+CommandWhowas::CommandWhowas( Module* parent)
+ : Command(parent, "WHOWAS", 1)
+ , GroupSize(0), MaxGroups(0), MaxKeep(0)
{
syntax = "<nick>{,<nick>}";
Penalty = 2;
- timer = new WhoWasMaintainTimer(3600);
- ServerInstance->Timers->AddTimer(timer);
}
CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
{
/* if whowas disabled in config */
- if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
+ if (this->GroupSize == 0 || this->MaxGroups == 0)
{
user->WriteNumeric(421, "%s %s :This command has been disabled.",user->nick.c_str(),name.c_str());
return CMD_FAILURE;
if (i == whowas.end())
{
user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
}
else
{
whowas_set* grp = i->second;
- if (grp->size())
+ if (!grp->empty())
{
for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++)
{
WhoWasGroup* u = *ux;
- time_t rawtime = u->signon;
- tm *timeinfo;
- char b[25];
-
- timeinfo = localtime(&rawtime);
-
- strncpy(b,asctime(timeinfo),24);
- b[24] = 0;
user->WriteNumeric(314, "%s %s %s %s * :%s",user->nick.c_str(),parameters[0].c_str(),
u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
user->WriteNumeric(379, "%s %s :was connecting from *@%s",
user->nick.c_str(), parameters[0].c_str(), u->host.c_str());
- if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), ServerInstance->Config->HideWhoisServer.c_str(), signon.c_str());
- else
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), u->server.c_str(), signon.c_str());
+ std::string signon = ServerInstance->TimeString(u->signon);
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(), parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), b);
+ bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
++ user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(), parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
}
}
else
{
user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
}
}
{
int whowas_size = 0;
int whowas_bytes = 0;
- whowas_users_fifo::iterator iter;
- for (iter = whowas_fifo.begin(); iter != whowas_fifo.end(); iter++)
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- whowas_set* n = (whowas_set*)whowas.find(iter->second)->second;
- if (n->size())
- {
- whowas_size += n->size();
- whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
- }
+ whowas_set* n = i->second;
+ whowas_size += n->size();
+ whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
}
return "Whowas entries: " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)";
}
void CommandWhowas::AddToWhoWas(User* user)
{
/* if whowas disabled */
- if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
+ if (this->GroupSize == 0 || this->MaxGroups == 0)
{
return;
}
- whowas_users::iterator iter = whowas.find(irc::string(user->nick.c_str()));
+ // Insert nick if it doesn't exist
+ // 'first' will point to the newly inserted element or to the existing element with an equivalent key
+ std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(irc::string(user->nick.c_str()), static_cast<whowas_set*>(NULL)));
- if (iter == whowas.end())
+ if (ret.second) // If inserted
{
+ // This nick is new, create a list for it and add the first record to it
whowas_set* n = new whowas_set;
- WhoWasGroup *a = new WhoWasGroup(user);
- n->push_back(a);
- whowas[user->nick.c_str()] = n;
- whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick.c_str()));
+ n->push_back(new WhoWasGroup(user));
+ ret.first->second = n;
- if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups)
+ // Add this nick to the fifo too
+ whowas_fifo.push_back(std::make_pair(ServerInstance->Time(), ret.first->first));
+
+ if (whowas.size() > this->MaxGroups)
{
- whowas_users::iterator iter2 = whowas.find(whowas_fifo[0].second);
- if (iter2 != whowas.end())
+ // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
+ whowas_users::iterator it = whowas.find(whowas_fifo.front().second);
+ if (it != whowas.end())
{
- whowas_set* n2 = (whowas_set*)iter2->second;
-
- if (n2->size())
- {
- while (n2->begin() != n2->end())
- {
- WhoWasGroup *a2 = *(n2->begin());
- delete a2;
- n2->pop_front();
- }
- }
-
- delete n2;
- whowas.erase(iter2);
+ whowas_set* set = it->second;
+ for (whowas_set::iterator i = set->begin(); i != set->end(); ++i)
+ delete *i;
+
+ delete set;
+ whowas.erase(it);
}
whowas_fifo.pop_front();
}
}
else
{
- whowas_set* group = (whowas_set*)iter->second;
- WhoWasGroup *a = new WhoWasGroup(user);
- group->push_back(a);
+ // We've met this nick before, add a new record to the list
+ whowas_set* set = ret.first->second;
+ set->push_back(new WhoWasGroup(user));
- if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize)
+ // If there are too many records for this nick, remove the oldest (front)
+ if (set->size() > this->GroupSize)
{
- WhoWasGroup *a2 = (WhoWasGroup*)*(group->begin());
- delete a2;
- group->pop_front();
+ delete set->front();
+ set->pop_front();
}
}
}
/* on rehash, refactor maps according to new conf values */
-void CommandWhowas::PruneWhoWas(time_t t)
+void CommandWhowas::Prune()
{
- /* config values */
- int groupsize = ServerInstance->Config->WhoWasGroupSize;
- int maxgroups = ServerInstance->Config->WhoWasMaxGroups;
- int maxkeep = ServerInstance->Config->WhoWasMaxKeep;
+ time_t min = ServerInstance->Time() - this->MaxKeep;
/* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
- whowas_users::iterator iter;
- int fifosize;
- while ((fifosize = (int)whowas_fifo.size()) > 0)
+ while (!whowas_fifo.empty())
{
- if (fifosize > maxgroups || whowas_fifo[0].first < t - maxkeep)
+ if ((whowas_fifo.size() > this->MaxGroups) || (whowas_fifo.front().first < min))
{
- iter = whowas.find(whowas_fifo[0].second);
+ whowas_users::iterator iter = whowas.find(whowas_fifo.front().second);
/* hopefully redundant integrity check, but added while debugging r6216 */
if (iter == whowas.end())
{
/* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (1)");
+ ServerInstance->Logs->Log("WHOWAS", LOG_DEFAULT, "BUG: Whowas maps got corrupted! (1)");
return;
}
- whowas_set* n = (whowas_set*)iter->second;
-
- if (n->size())
- {
- while (n->begin() != n->end())
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- }
- }
+ whowas_set* set = iter->second;
+ for (whowas_set::iterator i = set->begin(); i != set->end(); ++i)
+ delete *i;
- delete n;
+ delete set;
whowas.erase(iter);
whowas_fifo.pop_front();
}
}
/* Then cut the whowas sets to new size (groupsize) */
- fifosize = (int)whowas_fifo.size();
- for (int i = 0; i < fifosize; i++)
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- iter = whowas.find(whowas_fifo[0].second);
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
+ whowas_set* n = i->second;
+ while (n->size() > this->GroupSize)
{
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (2)");
- return;
- }
- whowas_set* n = (whowas_set*)iter->second;
- if (n->size())
- {
- int nickcount = n->size();
- while (n->begin() != n->end() && nickcount > groupsize)
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- nickcount--;
- }
+ delete n->front();
+ n->pop_front();
}
}
}
/* call maintain once an hour to remove expired nicks */
-void CommandWhowas::MaintainWhoWas(time_t t)
+void CommandWhowas::Maintain()
{
- for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++)
+ time_t min = ServerInstance->Time() - this->MaxKeep;
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- whowas_set* n = (whowas_set*)iter->second;
- if (n->size())
+ whowas_set* set = i->second;
+ while (!set->empty() && set->front()->signon < min)
{
- while ((n->begin() != n->end()) && ((*n->begin())->signon < t - ServerInstance->Config->WhoWasMaxKeep))
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->erase(n->begin());
- }
+ delete set->front();
+ set->pop_front();
}
}
}
CommandWhowas::~CommandWhowas()
{
- if (timer)
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- ServerInstance->Timers->DelTimer(timer);
- }
+ whowas_set* set = i->second;
+ for (whowas_set::iterator j = set->begin(); j != set->end(); ++j)
+ delete *j;
- whowas_users::iterator iter;
- int fifosize;
- while ((fifosize = (int)whowas_fifo.size()) > 0)
- {
- iter = whowas.find(whowas_fifo[0].second);
-
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (3)");
- return;
- }
-
- whowas_set* n = (whowas_set*)iter->second;
-
- if (n->size())
- {
- while (n->begin() != n->end())
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- }
- }
-
- delete n;
- whowas.erase(iter);
- whowas_fifo.pop_front();
+ delete set;
}
}
{
}
-WhoWasGroup::~WhoWasGroup()
-{
-}
-
-/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */
-void WhoWasMaintainTimer::Tick(time_t)
-{
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest(whowas, whowas, WhowasRequest::WHOWAS_MAINTAIN).Send();
- }
-}
-
class ModuleWhoWas : public Module
{
CommandWhowas cmd;
+
public:
ModuleWhoWas() : cmd(this)
{
void init()
{
ServerInstance->Modules->AddService(cmd);
+ OnRehash(NULL);
}
- void OnRequest(Request& request)
+ void OnGarbageCollect()
{
- WhowasRequest& req = static_cast<WhowasRequest&>(request);
- switch (req.type)
- {
- case WhowasRequest::WHOWAS_ADD:
- cmd.AddToWhoWas(req.user);
- break;
- case WhowasRequest::WHOWAS_STATS:
- req.value = cmd.GetStats();
- break;
- case WhowasRequest::WHOWAS_PRUNE:
- cmd.PruneWhoWas(ServerInstance->Time());
- break;
- case WhowasRequest::WHOWAS_MAINTAIN:
- cmd.MaintainWhoWas(ServerInstance->Time());
- break;
- }
+ // Remove all entries older than MaxKeep
+ cmd.Maintain();
+ }
+
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+ {
+ cmd.AddToWhoWas(user);
+ }
+
+ ModResult OnStats(char symbol, User* user, string_list &results)
+ {
+ if (symbol == 'z')
+ results.push_back(ServerInstance->Config->ServerName+" 249 "+user->nick+" :"+cmd.GetStats());
+
+ return MOD_RES_PASSTHRU;
+ }
+
+ void OnRehash(User* user)
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("whowas");
+ unsigned int NewGroupSize = tag->getInt("groupsize", 10, 0, 10000);
+ unsigned int NewMaxGroups = tag->getInt("maxgroups", 10240, 0, 1000000);
+ unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
+
+ if ((NewGroupSize == cmd.GroupSize) && (NewMaxGroups == cmd.MaxGroups) && (NewMaxKeep == cmd.MaxKeep))
+ return;
+
+ cmd.GroupSize = NewGroupSize;
+ cmd.MaxGroups = NewMaxGroups;
+ cmd.MaxKeep = NewMaxKeep;
+ cmd.Prune();
}
Version GetVersion()
{
- return Version("WHOWAS Command", VF_VENDOR);
+ return Version("WHOWAS", VF_VENDOR);
}
};
*/
-/* $Core */
-
#ifdef _WIN32
#define _CRT_RAND_S
#include <stdlib.h>
{
std::string description;
- FOREACH_MOD(I_OnGetServerDescription,OnGetServerDescription(servername,description));
+ FOREACH_MOD(OnGetServerDescription, (servername,description));
if (!description.empty())
{
return iter->second;
}
-User* InspIRCd::FindNick(const char* nick)
-{
- if (isdigit(*nick))
- return FindUUID(nick);
-
- user_hash::iterator iter = this->Users->clientlist->find(nick);
-
- if (iter == this->Users->clientlist->end())
- return NULL;
-
- return iter->second;
-}
-
User* InspIRCd::FindNickOnly(const std::string &nick)
{
user_hash::iterator iter = this->Users->clientlist->find(nick);
return iter->second;
}
-User* InspIRCd::FindNickOnly(const char* nick)
-{
- user_hash::iterator iter = this->Users->clientlist->find(nick);
-
- if (iter == this->Users->clientlist->end())
- return NULL;
-
- return iter->second;
-}
-
User *InspIRCd::FindUUID(const std::string &uid)
{
user_hash::iterator finduuid = this->Users->uuidlist->find(uid);
return finduuid->second;
}
-
-User *InspIRCd::FindUUID(const char *uid)
-{
- return FindUUID(std::string(uid));
-}
-
/* find a channel record by channel name and return a pointer to it */
-Channel* InspIRCd::FindChan(const char* chan)
-{
- chan_hash::iterator iter = chanlist->find(chan);
-
- if (iter == chanlist->end())
- /* Couldn't find it */
- return NULL;
-
- return iter->second;
-}
Channel* InspIRCd::FindChan(const std::string &chan)
{
User* u = *i;
if (u->registered == REG_ALL)
{
- u->WriteServ("NOTICE %s :%s",u->nick.c_str(),s.c_str());
- }
+ u->WriteNotice(s);
+ }
else
{
/* Unregistered connections receive ERROR, not a NOTICE */
}
}
-/* return channel count */
-long InspIRCd::ChannelCount()
-{
- return chanlist->size();
-}
-
bool InspIRCd::IsValidMask(const std::string &mask)
{
const char* dest = mask.c_str();
}
/* true for valid channel name, false else */
-bool IsChannelHandler::Call(const char *chname, size_t max)
+bool IsChannelHandler::Call(const std::string& chname)
{
- const char *c = chname + 1;
+ if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
+ return false;
- /* check for no name - don't check for !*chname, as if it is empty, it won't be '#'! */
- if (!chname || *chname != '#')
- {
+ if (chname[0] != '#')
return false;
- }
- while (*c)
+ for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
{
- switch (*c)
+ switch (*i)
{
case ' ':
case ',':
case 7:
return false;
}
-
- c++;
- }
-
- size_t len = c - chname;
- /* too long a name - note funky pointer arithmetic here. */
- if (len > max)
- {
- return false;
}
return true;
}
/* true for valid nickname, false else */
-bool IsNickHandler::Call(const char* n, size_t max)
+bool IsNickHandler::Call(const std::string& n)
{
- if (!n || !*n)
+ if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
return false;
- unsigned int p = 0;
- for (const char* i = n; *i; i++, p++)
+ for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
{
if ((*i >= 'A') && (*i <= '}'))
{
continue;
}
- if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i > n))
+ if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
{
/* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
continue;
return false;
}
- /* too long? or not */
- return (p <= max);
+ return true;
}
/* return true for good ident, false else */
-bool IsIdentHandler::Call(const char* n)
+bool IsIdentHandler::Call(const std::string& n)
{
- if (!n || !*n)
+ if (n.empty())
return false;
- for (const char* i = n; *i; i++)
+ for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
{
if ((*i >= 'A') && (*i <= '}'))
{
return true;
}
-bool IsSIDHandler::Call(const std::string &str)
+bool InspIRCd::IsSID(const std::string &str)
{
/* Returns true if the string given is exactly 3 characters long,
* starts with a digit, and the other two characters are A-Z or digits
((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
}
-/* open the proper logfile */
-bool InspIRCd::OpenLog(char**, int)
-{
- if (!Config->cmdline.writelog) return true; // Skip opening default log if -nolog
-
- if (Config->cmdline.startup_log.empty())
- Config->cmdline.startup_log = LOG_PATH "/startup.log";
- FILE* startup = fopen(Config->cmdline.startup_log.c_str(), "a+");
-
- if (!startup)
- {
- return false;
- }
-
- FileWriter* fw = new FileWriter(startup);
- FileLogStream *f = new FileLogStream((Config->cmdline.forcedebug ? DEBUG : DEFAULT), fw);
-
- this->Logs->AddLogType("*", f, true);
-
- return true;
-}
-
void InspIRCd::CheckRoot()
{
#ifndef _WIN32
if (geteuid() == 0)
{
std::cout << "ERROR: You are running an irc server as root! DO NOT DO THIS!" << std::endl << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Can't start as root");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Can't start as root");
Exit(EXIT_STATUS_ROOT);
}
#endif
void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...)
{
- char textbuffer[MAXBUF];
- va_list argsPtr;
- va_start (argsPtr, format);
- vsnprintf(textbuffer, MAXBUF, format, argsPtr);
- va_end(argsPtr);
-
- this->SendWhoisLine(user, dest, numeric, std::string(textbuffer));
+ std::string textbuffer;
+ VAFORMAT(textbuffer, format, format)
+ this->SendWhoisLine(user, dest, numeric, textbuffer);
}
/** Refactored by Brain, Jun 2009. Much faster with some clever O(1) array
* lookups and pointer maths.
*/
-long InspIRCd::Duration(const std::string &str)
+unsigned long InspIRCd::Duration(const std::string &str)
{
unsigned char multiplier = 0;
long total = 0;
return total + subtotal;
}
+const char* InspIRCd::Format(va_list &vaList, const char* formatString)
+{
+ static std::vector<char> formatBuffer(1024);
+
+ while (true)
+ {
+ va_list dst;
+ va_copy(dst, vaList);
+
+ int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
+ if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
+ {
+ return &formatBuffer[0];
+ }
+
+ formatBuffer.resize(formatBuffer.size() * 2);
+ }
+
+ throw CoreException();
+}
+
+const char* InspIRCd::Format(const char* formatString, ...)
+{
+ const char* ret;
+ VAFORMAT(ret, formatString, formatString);
+ return ret;
+}
+
bool InspIRCd::ULine(const std::string& sserver)
{
if (sserver.empty())
std::string InspIRCd::TimeString(time_t curtime)
{
- return std::string(ctime(&curtime),24);
+ #ifdef _WIN32
+ if (curtime < 0)
+ curtime = 0;
+ #endif
+
+ struct tm* timeinfo = localtime(&curtime);
+ if (!timeinfo)
+ {
+ curtime = 0;
+ timeinfo = localtime(&curtime);
+ }
+
+ // If the calculated year exceeds four digits or is less than the year 1000,
+ // the behavior of asctime() is undefined
+ if (timeinfo->tm_year + 1900 > 9999)
+ timeinfo->tm_year = 9999 - 1900;
+ else if (timeinfo->tm_year + 1900 < 1000)
+ timeinfo->tm_year = 0;
+
+ return std::string(asctime(timeinfo),24);
}
-// You should only pass a single character to this.
-void InspIRCd::AddExtBanChar(char c)
-{
- std::string &tok = Config->data005;
- std::string::size_type ebpos = tok.find(" EXTBAN=,");
-
- if (ebpos == std::string::npos)
- {
- tok.append(" EXTBAN=,");
- tok.push_back(c);
- }
- else
- {
- ebpos += 9;
- while (isalpha(tok[ebpos]) && tok[ebpos] < c)
- ebpos++;
- tok.insert(ebpos, 1, c);
- }
-}
-
std::string InspIRCd::GenRandomStr(int length, bool printable)
{
char* buf = new char[length];
*/
-/* $Core */
#include "inspircd.h"
-#include "inspircd_version.h"
#include <signal.h>
#ifndef _WIN32
#include "testsuite.h"
InspIRCd* ServerInstance = NULL;
-int* mysig = NULL;
/** Seperate from the other casemap tables so that code *can* still exclusively rely on RFC casemapping
* if it must.
*/
const char* ExitCodes[] =
{
- "No error", /* 0 */
- "DIE command", /* 1 */
- "execv() failed", /* 2 */
- "Internal error", /* 3 */
- "Config file error", /* 4 */
- "Logfile error", /* 5 */
- "POSIX fork failed", /* 6 */
- "Bad commandline parameters", /* 7 */
- "No ports could be bound", /* 8 */
- "Can't write PID file", /* 9 */
- "SocketEngine could not initialize", /* 10 */
- "Refusing to start up as root", /* 11 */
- "Found a <die> tag!", /* 12 */
- "Couldn't load module on startup", /* 13 */
- "Could not create windows forked process", /* 14 */
- "Received SIGTERM", /* 15 */
- "Bad command handler loaded", /* 16 */
- "RegisterServiceCtrlHandler failed", /* 17 */
- "UpdateSCMStatus failed", /* 18 */
- "CreateEvent failed" /* 19 */
+ "No error", // 0
+ "DIE command", // 1
+ "Config file error", // 2
+ "Logfile error", // 3
+ "POSIX fork failed", // 4
+ "Bad commandline parameters", // 5
+ "Can't write PID file", // 6
+ "SocketEngine could not initialize", // 7
+ "Refusing to start up as root", // 8
+ "Couldn't load module on startup", // 9
+ "Received SIGTERM" // 10
};
template<typename T> static void DeleteZero(T*&n)
void InspIRCd::Cleanup()
{
+ // Close all listening sockets
for (unsigned int i = 0; i < ports.size(); i++)
{
- /* This calls the constructor and closes the listening socket */
ports[i]->cull();
delete ports[i];
}
/* Must be deleted before modes as it decrements modelines */
if (FakeClient)
FakeClient->cull();
- if (Res)
- Res->cull();
DeleteZero(this->FakeClient);
DeleteZero(this->Users);
DeleteZero(this->Modes);
DeleteZero(this->BanCache);
DeleteZero(this->SNO);
DeleteZero(this->Config);
- DeleteZero(this->Res);
DeleteZero(this->chanlist);
DeleteZero(this->PI);
DeleteZero(this->Threads);
DeleteZero(this->Timers);
DeleteZero(this->SE);
- /* Close logging */
- this->Logs->CloseLogs();
+ Logs->CloseLogs();
DeleteZero(this->Logs);
}
-void InspIRCd::Restart(const std::string &reason)
-{
- /* SendError flushes each client's queue,
- * regardless of writeability state
- */
- this->SendError(reason);
-
- /* Figure out our filename (if theyve renamed it, we're boned) */
- std::string me;
-
- char** argv = Config->cmdline.argv;
-
-#ifdef _WIN32
- char module[MAX_PATH];
- if (GetModuleFileNameA(NULL, module, MAX_PATH))
- me = module;
-#else
- me = argv[0];
-#endif
-
- this->Cleanup();
-
- if (execv(me.c_str(), argv) == -1)
- {
- /* Will raise a SIGABRT if not trapped */
- throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
- }
-}
-
-void InspIRCd::ResetMaxBans()
-{
- for (chan_hash::const_iterator i = chanlist->begin(); i != chanlist->end(); i++)
- i->second->ResetMaxBans();
-}
-
-/** Because hash_map doesn't free its buckets when we delete items, we occasionally
- * recreate the hash to free them up.
- * We do this by copying the entries from the old hash to a new hash, causing all
- * empty buckets to be weeded out of the hash.
- * Since this is quite expensive, it's not done very often.
- */
-void InspIRCd::RehashUsersAndChans()
-{
- user_hash* old_users = Users->clientlist;
- Users->clientlist = new user_hash;
- for (user_hash::const_iterator n = old_users->begin(); n != old_users->end(); n++)
- Users->clientlist->insert(*n);
- delete old_users;
-
- user_hash* old_uuid = Users->uuidlist;
- Users->uuidlist = new user_hash;
- for (user_hash::const_iterator n = old_uuid->begin(); n != old_uuid->end(); n++)
- Users->uuidlist->insert(*n);
- delete old_uuid;
-
- chan_hash* old_chans = chanlist;
- chanlist = new chan_hash;
- for (chan_hash::const_iterator n = old_chans->begin(); n != old_chans->end(); n++)
- chanlist->insert(*n);
- delete old_chans;
-
- // Reset the already_sent IDs so we don't wrap it around and drop a message
- LocalUser::already_sent_id = 0;
- for (LocalUserList::const_iterator i = Users->local_users.begin(); i != Users->local_users.end(); i++)
- {
- (**i).already_sent = 0;
- (**i).RemoveExpiredInvites();
- }
-}
-
void InspIRCd::SetSignals()
{
#ifndef _WIN32
void InspIRCd::QuickExit(int status)
{
- exit(0);
+ exit(status);
}
bool InspIRCd::DaemonSeed()
#else
signal(SIGTERM, InspIRCd::QuickExit);
- int childpid;
- if ((childpid = fork ()) < 0)
+ int childpid = fork();
+ if (childpid < 0)
return false;
else if (childpid > 0)
{
rlimit rl;
if (getrlimit(RLIMIT_CORE, &rl) == -1)
{
- this->Logs->Log("STARTUP",DEFAULT,"Failed to getrlimit()!");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to getrlimit()!");
return false;
}
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_CORE, &rl) == -1)
- this->Logs->Log("STARTUP",DEFAULT,"setrlimit() failed, cannot increase coredump size.");
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setrlimit() failed, cannot increase coredump size.");
return true;
#endif
#ifndef _WIN32
std::string fname(filename);
if (fname.empty())
- fname = DATA_PATH "/inspircd.pid";
+ fname = ServerInstance->Config->Paths.PrependData("inspircd.pid");
std::ofstream outfile(fname.c_str());
if (outfile.is_open())
{
else
{
std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Failed to write PID-file '%s', exiting.",fname.c_str());
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s', exiting.",fname.c_str());
Exit(EXIT_STATUS_PID);
}
#endif
* THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
* themselves within the class.
*/
- NICKForced("NICKForced", NULL),
- OperQuit("OperQuit", NULL),
+ OperQuit("operquit", NULL),
GenRandom(&HandleGenRandom),
IsChannel(&HandleIsChannel),
- IsSID(&HandleIsSID),
Rehash(&HandleRehash),
IsNick(&HandleIsNick),
IsIdent(&HandleIsIdent),
- FloodQuitUser(&HandleFloodQuitUser),
OnCheckExemption(&HandleOnCheckExemption)
{
ServerInstance = this;
- Extensions.Register(&NICKForced);
Extensions.Register(&OperQuit);
FailedPortList pl;
int do_version = 0, do_nofork = 0, do_debug = 0,
do_nolog = 0, do_root = 0, do_testsuite = 0; /* flag variables */
- int c = 0;
// Initialize so that if we exit before proper initialization they're not deleted
this->Logs = 0;
this->Parser = 0;
this->XLines = 0;
this->Modes = 0;
- this->Res = 0;
this->ConfigThread = NULL;
this->FakeClient = NULL;
/* Default implementation does nothing */
this->PI = new ProtocolInterface;
- this->s_signal = 0;
-
// Create base manager classes early, so nothing breaks
this->Users = new UserManager;
- this->Users->clientlist = new user_hash();
- this->Users->uuidlist = new user_hash();
this->chanlist = new chan_hash();
this->Config = new ServerConfig;
this->SNO = new SnomaskManager;
this->BanCache = new BanCacheManager;
this->Modules = new ModuleManager();
+ dynamic_reference_base::reset_all();
this->stats = new serverstats();
this->Timers = new TimerManager;
this->Parser = new CommandParser;
struct option longopts[] =
{
{ "nofork", no_argument, &do_nofork, 1 },
- { "logfile", required_argument, NULL, 'f' },
{ "config", required_argument, NULL, 'c' },
{ "debug", no_argument, &do_debug, 1 },
{ "nolog", no_argument, &do_nolog, 1 },
{ 0, 0, 0, 0 }
};
+ int c;
int index;
- while ((c = getopt_long(argc, argv, ":c:f:", longopts, &index)) != -1)
+ while ((c = getopt_long(argc, argv, ":c:", longopts, &index)) != -1)
{
switch (c)
{
- case 'f':
- /* Log filename was set */
- Config->cmdline.startup_log = optarg;
- break;
case 'c':
/* Config filename was set */
ConfigFileName = optarg;
default:
/* Fall through to handle other weird values too */
std::cout << "Unknown parameter '" << argv[optind-1] << "'" << std::endl;
- std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--logfile <filename>] " << std::endl <<
- std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version] [--config <config>] [--testsuite]" << std::endl;
+ std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--config <config>]" << std::endl <<
+ std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version] [--testsuite]" << std::endl;
Exit(EXIT_STATUS_ARGV);
break;
}
if (do_version)
{
- std::cout << std::endl << VERSION << " r" << REVISION << std::endl;
+ std::cout << std::endl << VERSION << " " << REVISION << std::endl;
Exit(EXIT_STATUS_NOERROR);
}
if (do_debug)
{
FileWriter* fw = new FileWriter(stdout);
- FileLogStream* fls = new FileLogStream(RAWIO, fw);
+ FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw);
Logs->AddLogTypes("*", fls, true);
}
- else if (!this->OpenLog(argv, argc))
- {
- std::cout << "ERROR: Could not open initial logfile " << Config->cmdline.startup_log << ": " << strerror(errno) << std::endl << std::endl;
- Exit(EXIT_STATUS_LOG);
- }
if (!ServerConfig::FileExists(ConfigFileName.c_str()))
{
#endif
{
std::cout << "ERROR: Cannot open config file: " << ConfigFileName << std::endl << "Exiting..." << std::endl;
- this->Logs->Log("STARTUP",DEFAULT,"Unable to open config file %s", ConfigFileName.c_str());
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Unable to open config file %s", ConfigFileName.c_str());
Exit(EXIT_STATUS_CONFIG);
}
}
std::cout << con_green << "(C) InspIRCd Development Team." << con_reset << std::endl << std::endl;
std::cout << "Developers:" << std::endl;
std::cout << con_green << "\tBrain, FrostyCoolSlug, w00t, Om, Special, peavey" << std::endl;
- std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl;\r
+ std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl;
std::cout << "\tAttila" << con_reset << std::endl << std::endl;
std::cout << "Others:\t\t\t" << con_green << "See /INFO Output" << con_reset << std::endl;
this->CheckRoot();
else
{
- std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl\r
- << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl\r
- << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl\r
- << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl\r
- << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl\r
- << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl\r
- << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl\r
- << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;\r
+ std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl
+ << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl
+ << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl
+ << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl
+ << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl
+ << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl
+ << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl
+ << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl;
sleep(20);
}
#endif
if (!this->DaemonSeed())
{
std::cout << "ERROR: could not go into daemon mode. Shutting down." << std::endl;
- Logs->Log("STARTUP", DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "ERROR: could not go into daemon mode. Shutting down.");
Exit(EXIT_STATUS_FORK);
}
}
SE->RecoverFromFork();
- /* During startup we don't actually initialize this
- * in the thread engine.
+ /* During startup we read the configuration now, not in
+ * a seperate thread
*/
this->Config->Read();
this->Config->Apply(NULL, "");
Logs->OpenFileLogs();
+ ModeParser::InitBuiltinModes();
- this->Res = new DNS();
-
- /*
- * Initialise SID/UID.
- * For an explanation as to exactly how this works, and why it works this way, see GetUID().
- * -- w00t
- */
+ // If we don't have a SID, generate one based on the server name and the server description
if (Config->sid.empty())
- {
- // Generate one
- unsigned int sid = 0;
- char sidstr[4];
-
- for (const char* x = Config->ServerName.c_str(); *x; ++x)
- sid = 5 * sid + *x;
- for (const char* y = Config->ServerDesc.c_str(); *y; ++y)
- sid = 5 * sid + *y;
- sprintf(sidstr, "%03d", sid % 1000);
+ Config->sid = UIDGenerator::GenerateSID(Config->ServerName, Config->ServerDesc);
- Config->sid = sidstr;
- }
+ // Initialize the UID generator with our sid
+ this->UIDGen.init(Config->sid);
- /* set up fake client again this time with the correct uid */
+ // Create the server user for this server
this->FakeClient = new FakeUser(Config->sid, Config->ServerName);
- // Get XLine to do it's thing.
- this->XLines->CheckELines();
+ // This is needed as all new XLines are marked pending until ApplyLines() is called
this->XLines->ApplyLines();
int bounditems = BindPorts(pl);
this->Modules->LoadAll();
- /* Just in case no modules were loaded - fix for bug #101 */
- this->BuildISupport();
+ // Build ISupport as ModuleManager::LoadAll() does not do it
+ this->ISupport.Build();
Config->ApplyDisabledCommands(Config->DisabledCommands);
if (!pl.empty())
if (kill(getppid(), SIGTERM) == -1)
{
std::cout << "Error killing parent process: " << strerror(errno) << std::endl;
- Logs->Log("STARTUP", DEFAULT, "Error killing parent process: %s",strerror(errno));
+ Logs->Log("STARTUP", LOG_DEFAULT, "Error killing parent process: %s",strerror(errno));
}
}
fclose(stdout);
if (dup2(fd, STDIN_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdin.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdin.");
if (dup2(fd, STDOUT_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stdout.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdout.");
if (dup2(fd, STDERR_FILENO) < 0)
- Logs->Log("STARTUP", DEFAULT, "Failed to dup /dev/null to stderr.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stderr.");
close(fd);
}
else
{
- Logs->Log("STARTUP", DEFAULT,"Keeping pseudo-tty open as we are running in the foreground.");
+ Logs->Log("STARTUP", LOG_DEFAULT, "Keeping pseudo-tty open as we are running in the foreground.");
}
#else
/* Set win32 service as running, if we are running as a service */
QueryPerformanceFrequency(&stats->QPFrequency);
#endif
- Logs->Log("STARTUP", DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SE->GetMaxFds());
+ Logs->Log("STARTUP", LOG_DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SE->GetMaxFds());
#ifndef _WIN32
std::string SetUser = Config->ConfValue("security")->getString("runasuser");
if (ret == -1)
{
- this->Logs->Log("SETGROUPS", DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
this->QuickExit(0);
}
if (!g)
{
- this->Logs->Log("SETGUID", DEFAULT, "getgrnam() failed (bad user?): %s", strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam() failed (bad user?): %s", strerror(errno));
this->QuickExit(0);
}
if (ret == -1)
{
- this->Logs->Log("SETGUID", DEFAULT, "setgid() failed (bad user?): %s", strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid() failed (bad user?): %s", strerror(errno));
this->QuickExit(0);
}
}
if (!u)
{
- this->Logs->Log("SETGUID", DEFAULT, "getpwnam() failed (bad user?): %s", strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam() failed (bad user?): %s", strerror(errno));
this->QuickExit(0);
}
if (ret == -1)
{
- this->Logs->Log("SETGUID", DEFAULT, "setuid() failed (bad user?): %s", strerror(errno));
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid() failed (bad user?): %s", strerror(errno));
this->QuickExit(0);
}
}
#endif
}
-int InspIRCd::Run()
+void InspIRCd::Run()
{
/* See if we're supposed to be running the test suite rather than entering the mainloop */
if (Config->cmdline.TestSuite)
{
TestSuite* ts = new TestSuite;
delete ts;
- Exit(0);
+ return;
}
UpdateTime();
if (this->ConfigThread && this->ConfigThread->IsDone())
{
/* Rehash has completed */
- this->Logs->Log("CONFIG",DEBUG,"Detected ConfigThread exiting, tidying up...");
+ this->Logs->Log("CONFIG", LOG_DEBUG, "Detected ConfigThread exiting, tidying up...");
this->ConfigThread->Finish();
{
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)
{
- this->RehashUsersAndChans();
- FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
+ Users->GarbageCollect();
+ FOREACH_MOD(OnGarbageCollect, ());
}
Timers->TickTimers(TIME.tv_sec);
- this->DoBackgroundUserStuff();
+ Users->DoBackgroundUserStuff();
if ((TIME.tv_sec % 5) == 0)
{
- FOREACH_MOD(I_OnBackgroundTimer,OnBackgroundTimer(TIME.tv_sec));
+ FOREACH_MOD(OnBackgroundTimer, (TIME.tv_sec));
SNO->FlushSnotices();
}
}
GlobalCulls.Apply();
AtomicActions.Run();
- if (this->s_signal)
+ if (s_signal)
{
this->SignalHandler(s_signal);
- this->s_signal = 0;
+ s_signal = 0;
}
}
-
- return 0;
}
-/**********************************************************************************/
-
-/**
- * An ircd in five lines! bwahahaha. ahahahahaha. ahahah *cough*.
- */
-
-/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
- * (until this returns true, a user will block in the waiting state, waiting to connect up to the
- * registration timeout maximum seconds)
- */
-bool InspIRCd::AllModulesReportReady(LocalUser* user)
-{
- ModResult res;
- FIRST_MOD_RESULT(OnCheckReady, res, (user));
- return (res == MOD_RES_PASSTHRU);
-}
+sig_atomic_t InspIRCd::s_signal = 0;
void InspIRCd::SetSignal(int signal)
{
- *mysig = signal;
+ s_signal = signal;
}
/* On posix systems, the flow of the program starts right here, with
ENTRYPOINT
{
new InspIRCd(argc, argv);
- mysig = &ServerInstance->s_signal;
ServerInstance->Run();
delete ServerInstance;
return 0;
#include "socket.h"
#include "inspstring.h"
#include "socketengine.h"
+#include "iohook.h"
#ifndef DISABLE_WRITEV
#include <sys/uio.h>
if (err != I_ERR_NONE)
{
state = I_ERROR;
- SetError(strerror(errno));
+ SetError(SocketEngine::LastError());
OnError(err);
}
}
irc::sockets::sockaddrs addr, bind;
if (!irc::sockets::aptosa(ipaddr, aport, addr))
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: Hostname passed to BufferedSocket, rather than an IP address!");
return I_ERR_CONNECT;
}
ServerInstance->SE->NonBlocking(fd);
- if (ServerInstance->SE->Connect(this, &dest.sa, sa_size(dest)) == -1)
+ if (ServerInstance->SE->Connect(this, &dest.sa, dest.sa_size()) == -1)
{
if (errno != EINPROGRESS)
return I_ERR_CONNECT;
this->Timeout = new SocketTimeout(this->GetFd(), this, timeout, ServerInstance->Time());
ServerInstance->Timers->AddTimer(this->Timeout);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"BufferedSocket::DoConnect success");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BufferedSocket::DoConnect success");
return I_ERR_NONE;
}
{
// final chance, dump as much of the sendq as we can
DoWrite();
- if (IOHook)
+ if (GetIOHook())
{
try
{
- IOHook->OnStreamSocketClose(this);
+ GetIOHook()->OnStreamSocketClose(this);
}
catch (CoreException& modexcept)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT,"%s threw an exception: %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
modexcept.GetSource(), modexcept.GetReason());
}
- IOHook = NULL;
+ DelIOHook();
}
ServerInstance->SE->Shutdown(this, 2);
ServerInstance->SE->DelFd(this);
void StreamSocket::DoRead()
{
- if (IOHook)
+ if (GetIOHook())
{
int rv = -1;
try
{
- rv = IOHook->OnStreamSocketRead(this, recvq);
+ rv = GetIOHook()->OnStreamSocketRead(this, recvq);
}
catch (CoreException& modexcept)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "%s threw an exception: %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
modexcept.GetSource(), modexcept.GetReason());
return;
}
}
else
{
- error = strerror(errno);
+ error = SocketEngine::LastError();
ServerInstance->SE->ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
}
}
return;
if (!error.empty() || fd < 0 || fd == INT_MAX)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "DoWrite on errored or closed socket");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DoWrite on errored or closed socket");
return;
}
#ifndef DISABLE_WRITEV
- if (IOHook)
+ if (GetIOHook())
#endif
{
int rv = -1;
// The length limit of 1024 is to prevent merging strings
// more than once when writes begin to block.
std::string tmp;
- tmp.reserve(sendq_len);
- for(unsigned int i=0; i < sendq.size(); i++)
- tmp.append(sendq[i]);
- sendq.clear();
- sendq.push_back(tmp);
+ tmp.reserve(1280);
+ while (!sendq.empty() && tmp.length() < 1024)
+ {
+ tmp.append(sendq.front());
+ sendq.pop_front();
+ }
+ sendq.push_front(tmp);
}
std::string& front = sendq.front();
int itemlen = front.length();
- if (IOHook)
+ if (GetIOHook())
{
- rv = IOHook->OnStreamSocketWrite(this, front);
+ rv = GetIOHook()->OnStreamSocketWrite(this, front);
if (rv > 0)
{
// consumed the entire string, and is ready for more
if (errno == EINTR || SocketEngine::IgnoreError())
ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK);
else
- SetError(strerror(errno));
+ SetError(SocketEngine::LastError());
return;
}
else if (rv < itemlen)
}
catch (CoreException& modexcept)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG,"%s threw an exception: %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "%s threw an exception: %s",
modexcept.GetSource(), modexcept.GetReason());
}
}
}
else
{
- error = strerror(errno);
+ error = SocketEngine::LastError();
}
}
if (!error.empty())
{
if (fd < 0)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Attempt to write data to dead socket: %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to write data to dead socket: %s",
data.c_str());
return;
}
ServerInstance->SE->ChangeEventMask(this, FD_ADD_TRIAL_WRITE);
}
-void SocketTimeout::Tick(time_t)
+bool SocketTimeout::Tick(time_t)
{
- ServerInstance->Logs->Log("SOCKET", DEBUG,"SocketTimeout::Tick");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SocketTimeout::Tick");
if (ServerInstance->SE->GetRef(this->sfd) != this->sock)
- return;
+ return false;
if (this->sock->state == I_CONNECTING)
{
}
this->sock->Timeout = NULL;
+ return false;
}
void BufferedSocket::OnConnected() { }
this->Close();
if (Timeout)
{
- ServerInstance->Timers->DelTimer(Timeout);
- Timeout = NULL;
+ // The timer is removed from the TimerManager in Timer::~Timer()
+ delete Timeout;
}
}
if (errornum == 0)
SetError("Connection closed");
else
- SetError(strerror(errornum));
+ SetError(SocketEngine::GetError(errornum));
switch (errornum)
{
case ETIMEDOUT:
}
catch (CoreException& ex)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Caught exception in socket processing on FD %d - '%s'",
fd, ex.GetReason());
SetError(ex.GetReason());
}
if (!error.empty())
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error on FD %d - '%s'", fd, error.c_str());
OnError(errcode);
}
}
#include <gcrypt.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
-#include "ssl.h"
-#include "m_cap.h"
+#include "modules/ssl.h"
+#include "modules/cap.h"
+
+#if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 9) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 9 && GNUTLS_VERSION_PATCH >= 8))
+#define GNUTLS_HAS_MAC_GET_ID
+#include <gnutls/crypto.h>
+#endif
#ifdef _WIN32
# pragma comment(lib, "libgnutls.lib")
# pragma comment(lib, "gdi32.lib")
#endif
-/* $ModDesc: Provides SSL support for clients */
-/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") exec("libgcrypt-config --cflags") */
+/* $CompileFlags: pkgconfincludes("gnutls","/gnutls/gnutls.h","") exec("libgcrypt-config --cflags") -Wno-pedantic */
/* $LinkerFlags: rpath("pkg-config --libs gnutls") pkgconflibs("gnutls","/libgnutls.so","-lgnutls") exec("libgcrypt-config --libs") */
-/* $NoPedantic */
+ #ifndef GNUTLS_VERSION_MAJOR
+ #define GNUTLS_VERSION_MAJOR LIBGNUTLS_VERSION_MAJOR
+ #define GNUTLS_VERSION_MINOR LIBGNUTLS_VERSION_MINOR
+ #define GNUTLS_VERSION_PATCH LIBGNUTLS_VERSION_PATCH
+ #endif
+
// These don't exist in older GnuTLS versions
- #if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 1) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_MICRO >= 7))
+ #if ((GNUTLS_VERSION_MAJOR > 2) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR > 1) || (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_PATCH >= 7))
#define GNUTLS_NEW_PRIO_API
#endif
issl_session() : socket(NULL), sess(NULL) {}
};
-class CommandStartTLS : public SplitCommand
+class GnuTLSIOHook : public SSLIOHook
{
- public:
- bool enabled;
- CommandStartTLS (Module* mod) : SplitCommand(mod, "STARTTLS")
+ private:
+ void InitSession(StreamSocket* user, bool me_server)
{
- enabled = true;
- works_before_reg = true;
+ issl_session* session = &sessions[user->GetFd()];
+
+ gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
+ session->socket = user;
+
+ #ifdef GNUTLS_NEW_PRIO_API
+ gnutls_priority_set(session->sess, priority);
++ #else
++ gnutls_set_default_priority(session->sess);
+ #endif
+ gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_dh_set_prime_bits(session->sess, dh_bits);
+ gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(session));
+ gnutls_transport_set_push_function(session->sess, gnutls_push_wrapper);
+ gnutls_transport_set_pull_function(session->sess, gnutls_pull_wrapper);
+
+ if (me_server)
+ gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
+
+ Handshake(session, user);
}
- CmdResult HandleLocal(const std::vector<std::string> ¶meters, LocalUser *user)
+ void CloseSession(issl_session* session)
{
- if (!enabled)
+ if (session->sess)
{
- user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str());
- return CMD_FAILURE;
+ gnutls_bye(session->sess, GNUTLS_SHUT_WR);
+ gnutls_deinit(session->sess);
}
+ session->socket = NULL;
+ session->sess = NULL;
+ session->cert = NULL;
+ session->status = ISSL_NONE;
+ }
- if (user->registered == REG_ALL)
- {
- user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str());
- }
- else
+ bool Handshake(issl_session* session, StreamSocket* user)
+ {
+ int ret = gnutls_handshake(session->sess);
+
+ if (ret < 0)
{
- if (!user->eh.GetIOHook())
+ if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
{
- user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
- /* We need to flush the write buffer prior to adding the IOHook,
- * otherwise we'll be sending this line inside the SSL session - which
- * won't start its handshake until the client gets this line. Currently,
- * we assume the write will not block here; this is usually safe, as
- * STARTTLS is sent very early on in the registration phase, where the
- * user hasn't built up much sendq. Handling a blocked write here would
- * be very annoying.
- */
- user->eh.DoWrite();
- user->eh.AddIOHook(creator);
- creator->OnStreamSocketAccept(&user->eh, NULL, NULL);
+ // Handshake needs resuming later, read() or write() would have blocked.
+
+ if(gnutls_record_get_direction(session->sess) == 0)
+ {
+ // gnutls_handshake() wants to read() again.
+ session->status = ISSL_HANDSHAKING_READ;
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ }
+ else
+ {
+ // gnutls_handshake() wants to write() again.
+ session->status = ISSL_HANDSHAKING_WRITE;
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ }
}
else
- user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
+ {
+ user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
+ CloseSession(session);
+ session->status = ISSL_CLOSING;
+ }
+
+ return false;
}
+ else
+ {
+ // Change the seesion state
+ session->status = ISSL_HANDSHAKEN;
- return CMD_FAILURE;
+ VerifyCertificate(session,user);
+
+ // Finish writing, if any left
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+
+ return true;
+ }
}
-};
-class ModuleSSLGnuTLS : public Module
-{
- issl_session* sessions;
+ void VerifyCertificate(issl_session* session, StreamSocket* user)
+ {
+ if (!session->sess || !user)
+ return;
- gnutls_certificate_credentials_t x509_cred;
- gnutls_dh_params_t dh_params;
- gnutls_digest_algorithm_t hash;
- #ifdef GNUTLS_NEW_PRIO_API
- gnutls_priority_t priority;
- #endif
+ unsigned int status;
+ const gnutls_datum_t* cert_list;
+ int ret;
+ unsigned int cert_list_size;
+ gnutls_x509_crt_t cert;
+ char str[512];
+ unsigned char digest[512];
+ size_t digest_size = sizeof(digest);
+ size_t name_size = sizeof(str);
+ ssl_cert* certinfo = new ssl_cert;
+ session->cert = certinfo;
- std::string sslports;
- int dh_bits;
+ /* This verification function uses the trusted CAs in the credentials
+ * structure. So you must have installed one or more CA certificates.
+ */
+ ret = gnutls_certificate_verify_peers2(session->sess, &status);
- bool cred_alloc;
- bool dh_alloc;
+ if (ret < 0)
+ {
+ certinfo->error = std::string(gnutls_strerror(ret));
+ return;
+ }
- RandGen randhandler;
- CommandStartTLS starttls;
+ certinfo->invalid = (status & GNUTLS_CERT_INVALID);
+ certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND);
+ certinfo->revoked = (status & GNUTLS_CERT_REVOKED);
+ certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA);
- GenericCap capHandler;
- ServiceProvider iohook;
+ /* Up to here the process is the same for X.509 certificates and
+ * OpenPGP keys. From now on X.509 certificates are assumed. This can
+ * be easily extended to work with openpgp keys as well.
+ */
+ if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
+ {
+ certinfo->error = "No X509 keys sent";
+ return;
+ }
+
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0)
+ {
+ certinfo->error = gnutls_strerror(ret);
+ return;
+ }
+
+ cert_list_size = 0;
+ cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
+ if (cert_list == NULL)
+ {
+ certinfo->error = "No certificate was found";
+ goto info_done_dealloc;
+ }
+
+ /* This is not a real world example, since we only check the first
+ * certificate in the given chain.
+ */
+
+ ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ {
+ certinfo->error = gnutls_strerror(ret);
+ goto info_done_dealloc;
+ }
+
+ gnutls_x509_crt_get_dn(cert, str, &name_size);
+ certinfo->dn = str;
+
+ gnutls_x509_crt_get_issuer_dn(cert, str, &name_size);
+ certinfo->issuer = str;
+
+ if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0)
+ {
+ certinfo->error = gnutls_strerror(ret);
+ }
+ else
+ {
+ certinfo->fingerprint = BinToHex(digest, digest_size);
+ }
+
+ /* Beware here we do not check for errors.
+ */
+ if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
+ {
+ certinfo->error = "Not activated, or expired certificate";
+ }
+
+info_done_dealloc:
+ gnutls_x509_crt_deinit(cert);
+ }
- inline static const char* UnknownIfNULL(const char* str)
+ static const char* UnknownIfNULL(const char* str)
{
return str ? str : "UNKNOWN";
}
}
public:
+ issl_session* sessions;
+ gnutls_certificate_credentials_t x509_cred;
- ModuleSSLGnuTLS()
- : starttls(this), capHandler(this, "tls"), iohook(this, "ssl/gnutls", SERVICE_IOHOOK)
- {
- gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+ gnutls_digest_algorithm_t hash;
+ #ifdef GNUTLS_NEW_PRIO_API
+ gnutls_priority_t priority;
+ #endif
+ int dh_bits;
+ GnuTLSIOHook(Module* parent)
+ : SSLIOHook(parent, "ssl/gnutls")
+ {
sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
+ }
- gnutls_global_init(); // This must be called once in the program
- gnutls_x509_privkey_init(&x509_key);
-
- #ifdef GNUTLS_NEW_PRIO_API
- // Init this here so it's always initialized, avoids an extra boolean
- gnutls_priority_init(&priority, "NORMAL", NULL);
- #endif
-
- cred_alloc = false;
- dh_alloc = false;
+ ~GnuTLSIOHook()
+ {
+ delete[] sessions;
}
- void init()
+ void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
{
- // Needs the flag as it ignores a plain /rehash
- OnModuleRehash(NULL,"ssl");
+ issl_session* session = &sessions[user->GetFd()];
- ServerInstance->GenRandom = &randhandler;
+ /* For STARTTLS: Don't try and init a session on a socket that already has a session */
+ if (session->sess)
+ return;
- // Void return, guess we assume success
- gnutls_certificate_set_dh_params(x509_cred, dh_params);
- Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnUserConnect,
- I_OnEvent, I_OnHookIO };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ InitSession(user, true);
+ }
- ServerInstance->Modules->AddService(iohook);
- ServerInstance->Modules->AddService(starttls);
+ void OnStreamSocketConnect(StreamSocket* user) CXX11_OVERRIDE
+ {
+ InitSession(user, false);
}
- void OnRehash(User* user)
+ void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
{
- sslports.clear();
+ CloseSession(&sessions[user->GetFd()]);
+ }
- ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
- starttls.enabled = Conf->getBool("starttls", true);
+ int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
+ {
+ issl_session* session = &sessions[user->GetFd()];
- if (Conf->getBool("showports", true))
+ if (!session->sess)
{
- sslports = Conf->getString("advertisedports");
- if (!sslports.empty())
- return;
-
- for (size_t i = 0; i < ServerInstance->ports.size(); i++)
- {
- ListenSocket* port = ServerInstance->ports[i];
- if (port->bind_tag->getString("ssl") != "gnutls")
- continue;
+ CloseSession(session);
+ user->SetError("No SSL session");
+ return -1;
+ }
- const std::string& portid = port->bind_desc;
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %s", portid.c_str());
+ if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
+ {
+ // The handshake isn't finished, try to finish it.
+
+ if(!Handshake(session, user))
+ {
+ if (session->status != ISSL_CLOSING)
+ return 0;
+ return -1;
+ }
+ }
+
+ // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
+
+ if (session->status == ISSL_HANDSHAKEN)
+ {
+ char* buffer = ServerInstance->GetReadBuffer();
+ size_t bufsiz = ServerInstance->Config->NetBufferSize;
+ int ret = gnutls_record_recv(session->sess, buffer, bufsiz);
+ if (ret > 0)
+ {
+ recvq.append(buffer, ret);
+ return 1;
+ }
+ else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+ {
+ return 0;
+ }
+ else if (ret == 0)
+ {
+ user->SetError("Connection closed");
+ CloseSession(session);
+ return -1;
+ }
+ else
+ {
+ user->SetError(gnutls_strerror(ret));
+ CloseSession(session);
+ return -1;
+ }
+ }
+ else if (session->status == ISSL_CLOSING)
+ return -1;
+
+ return 0;
+ }
+
+ int OnStreamSocketWrite(StreamSocket* user, std::string& sendq) CXX11_OVERRIDE
+ {
+ issl_session* session = &sessions[user->GetFd()];
+
+ if (!session->sess)
+ {
+ CloseSession(session);
+ user->SetError("No SSL session");
+ return -1;
+ }
+
+ if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
+ {
+ // The handshake isn't finished, try to finish it.
+ Handshake(session, user);
+ if (session->status != ISSL_CLOSING)
+ return 0;
+ return -1;
+ }
+
+ int ret = 0;
+
+ if (session->status == ISSL_HANDSHAKEN)
+ {
+ ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
+
+ if (ret == (int)sendq.length())
+ {
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_WRITE);
+ return 1;
+ }
+ else if (ret > 0)
+ {
+ sendq = sendq.substr(ret);
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
+ {
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
+ return 0;
+ }
+ else // (ret < 0)
+ {
+ user->SetError(gnutls_strerror(ret));
+ CloseSession(session);
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ ssl_cert* GetCertificate(StreamSocket* sock) CXX11_OVERRIDE
+ {
+ int fd = sock->GetFd();
+ issl_session* session = &sessions[fd];
+ return session->cert;
+ }
+
+ void TellCiphersAndFingerprint(LocalUser* user)
+ {
+ const gnutls_session_t& sess = sessions[user->eh.GetFd()].sess;
+ if (sess)
+ {
+ std::string text = "*** You are connected using SSL cipher '";
+
+ text += UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
+ text.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
+ text.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess)))).append("'");
+
+ ssl_cert* cert = sessions[user->eh.GetFd()].cert;
+ if (!cert->fingerprint.empty())
+ text += " and your SSL fingerprint is " + cert->fingerprint;
+
+ user->WriteNotice(text);
+ }
+ }
+};
+
+class CommandStartTLS : public SplitCommand
+{
+ IOHook& hook;
+
+ public:
+ bool enabled;
+ CommandStartTLS(Module* mod, IOHook& Hook)
+ : SplitCommand(mod, "STARTTLS")
+ , hook(Hook)
+ {
+ enabled = true;
+ works_before_reg = true;
+ }
+
+ CmdResult HandleLocal(const std::vector<std::string> ¶meters, LocalUser *user)
+ {
+ if (!enabled)
+ {
+ user->WriteNumeric(691, "%s :STARTTLS is not enabled", user->nick.c_str());
+ return CMD_FAILURE;
+ }
+
+ if (user->registered == REG_ALL)
+ {
+ user->WriteNumeric(691, "%s :STARTTLS is not permitted after client registration is complete", user->nick.c_str());
+ }
+ else
+ {
+ if (!user->eh.GetIOHook())
+ {
+ user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
+ /* We need to flush the write buffer prior to adding the IOHook,
+ * otherwise we'll be sending this line inside the SSL session - which
+ * won't start its handshake until the client gets this line. Currently,
+ * we assume the write will not block here; this is usually safe, as
+ * STARTTLS is sent very early on in the registration phase, where the
+ * user hasn't built up much sendq. Handling a blocked write here would
+ * be very annoying.
+ */
+ user->eh.DoWrite();
+ user->eh.AddIOHook(&hook);
+ hook.OnStreamSocketAccept(&user->eh, NULL, NULL);
+ }
+ else
+ user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
+ }
+
+ return CMD_FAILURE;
+ }
+};
+
+class ModuleSSLGnuTLS : public Module
+{
+ GnuTLSIOHook iohook;
+
+ gnutls_dh_params_t dh_params;
+
+ std::string sslports;
+
+ bool cred_alloc;
+ bool dh_alloc;
+
+ RandGen randhandler;
+ CommandStartTLS starttls;
+
+ GenericCap capHandler;
+
+ public:
+ ModuleSSLGnuTLS()
+ : iohook(this), starttls(this, iohook), capHandler(this, "tls")
+ {
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+ gnutls_global_init(); // This must be called once in the program
+ gnutls_x509_privkey_init(&x509_key);
+
+ #ifdef GNUTLS_NEW_PRIO_API
+ // Init this here so it's always initialized, avoids an extra boolean
+ gnutls_priority_init(&iohook.priority, "NORMAL", NULL);
+ #endif
+
+ cred_alloc = false;
+ dh_alloc = false;
+ }
+
+ void init() CXX11_OVERRIDE
+ {
+ // Needs the flag as it ignores a plain /rehash
+ OnModuleRehash(NULL,"ssl");
+
+ ServerInstance->GenRandom = &randhandler;
+
+ // Void return, guess we assume success
+ gnutls_certificate_set_dh_params(iohook.x509_cred, dh_params);
+
+ ServerInstance->Modules->AddService(iohook);
+ ServerInstance->Modules->AddService(starttls);
+ }
+
+ void OnRehash(User* user) CXX11_OVERRIDE
+ {
+ sslports.clear();
+
+ ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
+ starttls.enabled = Conf->getBool("starttls", true);
+
+ if (Conf->getBool("showports", true))
+ {
+ sslports = Conf->getString("advertisedports");
+ if (!sslports.empty())
+ return;
+
+ for (size_t i = 0; i < ServerInstance->ports.size(); i++)
+ {
+ ListenSocket* port = ServerInstance->ports[i];
+ if (port->bind_tag->getString("ssl") != "gnutls")
+ continue;
+
+ const std::string& portid = port->bind_desc;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Enabling SSL for port %s", portid.c_str());
if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
{
}
}
- void OnModuleRehash(User* user, const std::string ¶m)
+ void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE
{
if(param != "ssl")
return;
ConfigTag* Conf = ServerInstance->Config->ConfValue("gnutls");
- cafile = Conf->getString("cafile", CONFIG_PATH "/ca.pem");
- crlfile = Conf->getString("crlfile", CONFIG_PATH "/crl.pem");
- certfile = Conf->getString("certfile", CONFIG_PATH "/cert.pem");
- keyfile = Conf->getString("keyfile", CONFIG_PATH "/key.pem");
- dh_bits = Conf->getInt("dhbits");
+ cafile = ServerInstance->Config->Paths.PrependConfig(Conf->getString("cafile", "ca.pem"));
+ crlfile = ServerInstance->Config->Paths.PrependConfig(Conf->getString("crlfile", "crl.pem"));
+ certfile = ServerInstance->Config->Paths.PrependConfig(Conf->getString("certfile", "cert.pem"));
+ keyfile = ServerInstance->Config->Paths.PrependConfig(Conf->getString("keyfile", "key.pem"));
+ int dh_bits = Conf->getInt("dhbits");
std::string hashname = Conf->getString("hash", "md5");
// The GnuTLS manual states that the gnutls_set_default_priority()
if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))
dh_bits = 1024;
+ iohook.dh_bits = dh_bits;
+
+ // As older versions of gnutls can't do this, let's disable it where needed.
+#ifdef GNUTLS_HAS_MAC_GET_ID
+ // As gnutls_digest_algorithm_t and gnutls_mac_algorithm_t are mapped 1:1, we can do this
+ // There is no gnutls_dig_get_id() at the moment, but it may come later
+ iohook.hash = (gnutls_digest_algorithm_t)gnutls_mac_get_id(hashname.c_str());
+ if (iohook.hash == GNUTLS_DIG_UNKNOWN)
+ throw ModuleException("Unknown hash type " + hashname);
+
+ // Check if the user is walking around with their head in the ass,
+ // giving us something that is a valid MAC but not digest
+ gnutls_hash_hd_t is_digest;
+ if (gnutls_hash_init(&is_digest, iohook.hash) < 0)
+ throw ModuleException("Unknown hash type " + hashname);
+ gnutls_hash_deinit(is_digest, NULL);
+#else
if (hashname == "md5")
- hash = GNUTLS_DIG_MD5;
+ iohook.hash = GNUTLS_DIG_MD5;
else if (hashname == "sha1")
- hash = GNUTLS_DIG_SHA1;
+ iohook.hash = GNUTLS_DIG_SHA1;
else
throw ModuleException("Unknown hash type " + hashname);
-
+#endif
int ret;
if (cred_alloc)
{
// Deallocate the old credentials
- gnutls_certificate_free_credentials(x509_cred);
+ gnutls_certificate_free_credentials(iohook.x509_cred);
for(unsigned int i=0; i < x509_certs.size(); i++)
gnutls_x509_crt_deinit(x509_certs[i]);
x509_certs.clear();
}
- ret = gnutls_certificate_allocate_credentials(&x509_cred);
+ ret = gnutls_certificate_allocate_credentials(&iohook.x509_cred);
cred_alloc = (ret >= 0);
if (!cred_alloc)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to allocate certificate credentials: %s", gnutls_strerror(ret));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Failed to allocate certificate credentials: %s", gnutls_strerror(ret));
- if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
+ if((ret =gnutls_certificate_set_x509_trust_file(iohook.x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
- if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEBUG, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
+ if((ret = gnutls_certificate_set_x509_crl_file (iohook.x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));
FileReader reader;
- reader.LoadFile(certfile);
- std::string cert_string = reader.Contents();
+ reader.Load(certfile);
+ std::string cert_string = reader.GetString();
gnutls_datum_t cert_datum = { (unsigned char*)cert_string.data(), static_cast<unsigned int>(cert_string.length()) };
- reader.LoadFile(keyfile);
- std::string key_string = reader.Contents();
+ reader.Load(keyfile);
+ std::string key_string = reader.GetString();
gnutls_datum_t key_datum = { (unsigned char*)key_string.data(), static_cast<unsigned int>(key_string.length()) };
// If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
if((ret = gnutls_x509_privkey_import(x509_key, &key_datum, GNUTLS_X509_FMT_PEM)) < 0)
throw ModuleException("Unable to load GnuTLS server private key (" + keyfile + "): " + std::string(gnutls_strerror(ret)));
- if((ret = gnutls_certificate_set_x509_key(x509_cred, &x509_certs[0], certcount, x509_key)) < 0)
+ if((ret = gnutls_certificate_set_x509_key(iohook.x509_cred, &x509_certs[0], certcount, x509_key)) < 0)
throw ModuleException("Unable to set GnuTLS cert/key pair: " + std::string(gnutls_strerror(ret)));
#ifdef GNUTLS_NEW_PRIO_API
// It's safe to call this every time as we cannot have this uninitialized, see constructor and below.
- gnutls_priority_deinit(priority);
+ gnutls_priority_deinit(iohook.priority);
// Try to set the priorities for ciphers, kex methods etc. to the user supplied string
// If the user did not supply anything then the string is already set to "NORMAL"
const char* priocstr = priorities.c_str();
const char* prioerror;
- if ((ret = gnutls_priority_init(&priority, priocstr, &prioerror)) < 0)
+ if ((ret = gnutls_priority_init(&iohook.priority, priocstr, &prioerror)) < 0)
{
// gnutls did not understand the user supplied string, log and fall back to the default priorities
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to set priorities to \"%s\": %s Syntax error at position %u, falling back to default (NORMAL)", priorities.c_str(), gnutls_strerror(ret), (unsigned int) (prioerror - priocstr));
- gnutls_priority_init(&priority, "NORMAL", NULL);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Failed to set priorities to \"%s\": %s Syntax error at position %u, falling back to default (NORMAL)", priorities.c_str(), gnutls_strerror(ret), (unsigned int) (prioerror - priocstr));
+ gnutls_priority_init(&iohook.priority, "NORMAL", NULL);
}
#else
if (priorities != "NORMAL")
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: You've set <gnutls:priority> to a value other than the default, but this is only supported with GnuTLS v2.1.7 or newer. Your GnuTLS version is older than that so the option will have no effect.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "You've set <gnutls:priority> to a value other than the default, but this is only supported with GnuTLS v2.1.7 or newer. Your GnuTLS version is older than that so the option will have no effect.");
#endif
#if(GNUTLS_VERSION_MAJOR < 2 || ( GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 12 ) )
- gnutls_certificate_client_set_retrieve_function (x509_cred, cert_callback);
+ gnutls_certificate_client_set_retrieve_function (iohook.x509_cred, cert_callback);
#else
- gnutls_certificate_set_retrieve_function (x509_cred, cert_callback);
+ gnutls_certificate_set_retrieve_function (iohook.x509_cred, cert_callback);
#endif
ret = gnutls_dh_params_init(&dh_params);
dh_alloc = (ret >= 0);
if (!dh_alloc)
{
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Failed to initialise DH parameters: %s", gnutls_strerror(ret));
return;
}
if (!dhfile.empty())
{
// Try to load DH params from file
- reader.LoadFile(dhfile);
- std::string dhstring = reader.Contents();
+ reader.Load(dhfile);
+ std::string dhstring = reader.GetString();
gnutls_datum_t dh_datum = { (unsigned char*)dhstring.data(), static_cast<unsigned int>(dhstring.length()) };
if ((ret = gnutls_dh_params_import_pkcs3(dh_params, &dh_datum, GNUTLS_X509_FMT_PEM)) < 0)
{
// File unreadable or GnuTLS was unhappy with the contents, generate the DH primes now
- ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT, "m_ssl_gnutls.so: Generating DH parameters because I failed to load them from file '%s': %s", dhfile.c_str(), gnutls_strerror(ret));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Generating DH parameters because I failed to load them from file '%s': %s", dhfile.c_str(), gnutls_strerror(ret));
GenerateDHParams();
}
}
int ret;
- if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)
- ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));
+ if((ret = gnutls_dh_params_generate2(dh_params, iohook.dh_bits)) < 0)
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Failed to generate DH parameters (%d bits): %s", iohook.dh_bits, gnutls_strerror(ret));
}
~ModuleSSLGnuTLS()
gnutls_x509_privkey_deinit(x509_key);
#ifdef GNUTLS_NEW_PRIO_API
- gnutls_priority_deinit(priority);
+ gnutls_priority_deinit(iohook.priority);
#endif
if (dh_alloc)
gnutls_dh_params_deinit(dh_params);
if (cred_alloc)
- gnutls_certificate_free_credentials(x509_cred);
+ gnutls_certificate_free_credentials(iohook.x509_cred);
gnutls_global_deinit();
- delete[] sessions;
ServerInstance->GenRandom = &ServerInstance->HandleGenRandom;
}
- void OnCleanup(int target_type, void* item)
+ void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
{
if(target_type == TYPE_USER)
{
LocalUser* user = IS_LOCAL(static_cast<User*>(item));
- if (user && user->eh.GetIOHook() == this)
+ if (user && user->eh.GetIOHook() == &iohook)
{
// User is using SSL, they're a local user, and they're using one of *our* SSL ports.
// Potentially there could be multiple SSL modules loaded at once on different ports.
}
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides SSL support for clients", VF_VENDOR);
}
-
- void On005Numeric(std::string &output)
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
if (!sslports.empty())
- output.append(" SSL=" + sslports);
+ tokens["SSL"] = sslports;
if (starttls.enabled)
- output.append(" STARTTLS");
+ tokens["STARTTLS"];
}
- void OnHookIO(StreamSocket* user, ListenSocket* lsb)
+ void OnHookIO(StreamSocket* user, ListenSocket* lsb) CXX11_OVERRIDE
{
if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "gnutls")
{
/* Hook the user with our module */
- user->AddIOHook(this);
+ user->AddIOHook(&iohook);
}
}
- void OnRequest(Request& request)
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
{
- if (strcmp("GET_SSL_CERT", request.id) == 0)
- {
- SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
- int fd = req.sock->GetFd();
- issl_session* session = &sessions[fd];
-
- req.cert = session->cert;
- }
- }
-
- void InitSession(StreamSocket* user, bool me_server)
- {
- issl_session* session = &sessions[user->GetFd()];
-
- gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT);
- session->socket = user;
-
- #ifdef GNUTLS_NEW_PRIO_API
- gnutls_priority_set(session->sess, priority);
- #else
- gnutls_set_default_priority(session->sess);
- #endif
- gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
- gnutls_dh_set_prime_bits(session->sess, dh_bits);
- gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(session));
- gnutls_transport_set_push_function(session->sess, gnutls_push_wrapper);
- gnutls_transport_set_pull_function(session->sess, gnutls_pull_wrapper);
-
- if (me_server)
- gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.
-
- Handshake(session, user);
- }
-
- void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
- {
- issl_session* session = &sessions[user->GetFd()];
-
- /* For STARTTLS: Don't try and init a session on a socket that already has a session */
- if (session->sess)
- return;
-
- InitSession(user, true);
- }
-
- void OnStreamSocketConnect(StreamSocket* user)
- {
- InitSession(user, false);
- }
-
- void OnStreamSocketClose(StreamSocket* user)
- {
- CloseSession(&sessions[user->GetFd()]);
- }
-
- int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
- {
- issl_session* session = &sessions[user->GetFd()];
-
- if (!session->sess)
- {
- CloseSession(session);
- user->SetError("No SSL session");
- return -1;
- }
-
- if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE)
- {
- // The handshake isn't finished, try to finish it.
-
- if(!Handshake(session, user))
- {
- if (session->status != ISSL_CLOSING)
- return 0;
- return -1;
- }
- }
-
- // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.
-
- if (session->status == ISSL_HANDSHAKEN)
- {
- char* buffer = ServerInstance->GetReadBuffer();
- size_t bufsiz = ServerInstance->Config->NetBufferSize;
- int ret = gnutls_record_recv(session->sess, buffer, bufsiz);
- if (ret > 0)
- {
- recvq.append(buffer, ret);
- return 1;
- }
- else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
- {
- return 0;
- }
- else if (ret == 0)
- {
- user->SetError("Connection closed");
- CloseSession(session);
- return -1;
- }
- else
- {
- user->SetError(gnutls_strerror(ret));
- CloseSession(session);
- return -1;
- }
- }
- else if (session->status == ISSL_CLOSING)
- return -1;
-
- return 0;
- }
-
- int OnStreamSocketWrite(StreamSocket* user, std::string& sendq)
- {
- issl_session* session = &sessions[user->GetFd()];
-
- if (!session->sess)
- {
- CloseSession(session);
- user->SetError("No SSL session");
- return -1;
- }
-
- if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
- {
- // The handshake isn't finished, try to finish it.
- Handshake(session, user);
- if (session->status != ISSL_CLOSING)
- return 0;
- return -1;
- }
-
- int ret = 0;
-
- if (session->status == ISSL_HANDSHAKEN)
- {
- ret = gnutls_record_send(session->sess, sendq.data(), sendq.length());
-
- if (ret == (int)sendq.length())
- {
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_WRITE);
- return 1;
- }
- else if (ret > 0)
- {
- sendq = sendq.substr(ret);
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
- return 0;
- }
- else if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED || ret == 0)
- {
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
- return 0;
- }
- else // (ret < 0)
- {
- user->SetError(gnutls_strerror(ret));
- CloseSession(session);
- return -1;
- }
- }
-
- return 0;
- }
-
- bool Handshake(issl_session* session, StreamSocket* user)
- {
- int ret = gnutls_handshake(session->sess);
-
- if (ret < 0)
- {
- if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
- {
- // Handshake needs resuming later, read() or write() would have blocked.
-
- if(gnutls_record_get_direction(session->sess) == 0)
- {
- // gnutls_handshake() wants to read() again.
- session->status = ISSL_HANDSHAKING_READ;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
- }
- else
- {
- // gnutls_handshake() wants to write() again.
- session->status = ISSL_HANDSHAKING_WRITE;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
- }
- }
- else
- {
- user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret)));
- CloseSession(session);
- session->status = ISSL_CLOSING;
- }
-
- return false;
- }
- else
- {
- // Change the seesion state
- session->status = ISSL_HANDSHAKEN;
-
- VerifyCertificate(session,user);
-
- // Finish writing, if any left
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
-
- return true;
- }
- }
-
- void OnUserConnect(LocalUser* user)
- {
- if (user->eh.GetIOHook() == this)
- {
- if (sessions[user->eh.GetFd()].sess)
- {
- const gnutls_session_t& sess = sessions[user->eh.GetFd()].sess;
- std::string cipher = UnknownIfNULL(gnutls_kx_get_name(gnutls_kx_get(sess)));
- cipher.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-");
- cipher.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess))));
-
- ssl_cert* cert = sessions[user->eh.GetFd()].cert;
- if (cert->fingerprint.empty())
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), cipher.c_str());
- else
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
- " and your SSL fingerprint is %s", user->nick.c_str(), cipher.c_str(), cert->fingerprint.c_str());
- }
- }
- }
-
- void CloseSession(issl_session* session)
- {
- if (session->sess)
- {
- gnutls_bye(session->sess, GNUTLS_SHUT_WR);
- gnutls_deinit(session->sess);
- }
- session->socket = NULL;
- session->sess = NULL;
- session->cert = NULL;
- session->status = ISSL_NONE;
- }
-
- void VerifyCertificate(issl_session* session, StreamSocket* user)
- {
- if (!session->sess || !user)
- return;
-
- unsigned int status;
- const gnutls_datum_t* cert_list;
- int ret;
- unsigned int cert_list_size;
- gnutls_x509_crt_t cert;
- char name[MAXBUF];
- unsigned char digest[MAXBUF];
- size_t digest_size = sizeof(digest);
- size_t name_size = sizeof(name);
- ssl_cert* certinfo = new ssl_cert;
- session->cert = certinfo;
-
- /* This verification function uses the trusted CAs in the credentials
- * structure. So you must have installed one or more CA certificates.
- */
- ret = gnutls_certificate_verify_peers2(session->sess, &status);
-
- if (ret < 0)
- {
- certinfo->error = std::string(gnutls_strerror(ret));
- return;
- }
-
- certinfo->invalid = (status & GNUTLS_CERT_INVALID);
- certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND);
- certinfo->revoked = (status & GNUTLS_CERT_REVOKED);
- certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA);
-
- /* Up to here the process is the same for X.509 certificates and
- * OpenPGP keys. From now on X.509 certificates are assumed. This can
- * be easily extended to work with openpgp keys as well.
- */
- if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)
- {
- certinfo->error = "No X509 keys sent";
- return;
- }
-
- ret = gnutls_x509_crt_init(&cert);
- if (ret < 0)
- {
- certinfo->error = gnutls_strerror(ret);
- return;
- }
-
- cert_list_size = 0;
- cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);
- if (cert_list == NULL)
- {
- certinfo->error = "No certificate was found";
- goto info_done_dealloc;
- }
-
- /* This is not a real world example, since we only check the first
- * certificate in the given chain.
- */
-
- ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
- if (ret < 0)
- {
- certinfo->error = gnutls_strerror(ret);
- goto info_done_dealloc;
- }
-
- gnutls_x509_crt_get_dn(cert, name, &name_size);
- certinfo->dn = name;
-
- gnutls_x509_crt_get_issuer_dn(cert, name, &name_size);
- certinfo->issuer = name;
-
- if ((ret = gnutls_x509_crt_get_fingerprint(cert, hash, digest, &digest_size)) < 0)
- {
- certinfo->error = gnutls_strerror(ret);
- }
- else
- {
- certinfo->fingerprint = irc::hex(digest, digest_size);
- }
-
- /* Beware here we do not check for errors.
- */
- if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
- {
- certinfo->error = "Not activated, or expired certificate";
- }
-
-info_done_dealloc:
- gnutls_x509_crt_deinit(cert);
+ if (user->eh.GetIOHook() == &iohook)
+ iohook.TellCiphersAndFingerprint(user);
}
- void OnEvent(Event& ev)
+ void OnEvent(Event& ev) CXX11_OVERRIDE
{
if (starttls.enabled)
capHandler.HandleEvent(ev);
# define __AVAILABILITYMACROS__
# define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
#endif
-
+
#include "inspircd.h"
+#include "iohook.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
-#include "ssl.h"
+#include "modules/ssl.h"
#ifdef _WIN32
# pragma comment(lib, "libcrypto.lib")
# define MAX_DESCRIPTORS 10000
#endif
-/* $ModDesc: Provides SSL support for clients */
-
-/* $LinkerFlags: if("USE_FREEBSD_BASE_SSL") -lssl -lcrypto */
-/* $CompileFlags: if(!"USE_FREEBSD_BASE_SSL") pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
-/* $LinkerFlags: if(!"USE_FREEBSD_BASE_SSL") rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
-
-/* $NoPedantic */
-
+/* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") -Wno-pedantic */
+/* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto") */
enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
return 1;
}
-class ModuleSSLOpenSSL : public Module
+class OpenSSLIOHook : public SSLIOHook
{
- issl_session* sessions;
-
- SSL_CTX* ctx;
- SSL_CTX* clictx;
-
- std::string sslports;
- bool use_sha;
-
- ServiceProvider iohook;
- public:
-
- ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
+ private:
+ bool Handshake(StreamSocket* user, issl_session* session)
{
- sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
+ int ret;
- /* Global SSL library initialization*/
- SSL_library_init();
- SSL_load_error_strings();
+ if (session->outbound)
+ ret = SSL_connect(session->sess);
+ else
+ ret = SSL_accept(session->sess);
- /* Build our SSL contexts:
- * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
- */
- ctx = SSL_CTX_new( SSLv23_server_method() );
- clictx = SSL_CTX_new( SSLv23_client_method() );
+ if (ret < 0)
+ {
+ int err = SSL_get_error(session->sess, ret);
- SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- SSL_CTX_set_mode(clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ if (err == SSL_ERROR_WANT_READ)
+ {
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ session->status = ISSL_HANDSHAKING;
+ return true;
+ }
+ else if (err == SSL_ERROR_WANT_WRITE)
+ {
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
+ session->status = ISSL_HANDSHAKING;
+ return true;
+ }
+ else
+ {
+ CloseSession(session);
+ }
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
- SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
- }
+ return false;
+ }
+ else if (ret > 0)
+ {
+ // Handshake complete.
+ VerifyCertificate(session, user);
- void init()
- {
- // Needs the flag as it ignores a plain /rehash
- OnModuleRehash(NULL,"ssl");
- Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnHookIO, I_OnUserConnect };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- ServerInstance->Modules->AddService(iohook);
- }
+ session->status = ISSL_OPEN;
- void OnHookIO(StreamSocket* user, ListenSocket* lsb)
- {
- if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+
+ return true;
+ }
+ else if (ret == 0)
{
- /* Hook the user with our module */
- user->AddIOHook(this);
+ CloseSession(session);
+ return true;
}
+
+ return true;
}
- void OnRehash(User* user)
+ void CloseSession(issl_session* session)
{
- sslports.clear();
-
- ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
-
- if (Conf->getBool("showports", true))
+ if (session->sess)
{
- sslports = Conf->getString("advertisedports");
- if (!sslports.empty())
- return;
-
- for (size_t i = 0; i < ServerInstance->ports.size(); i++)
- {
- ListenSocket* port = ServerInstance->ports[i];
- if (port->bind_tag->getString("ssl") != "openssl")
- continue;
-
- const std::string& portid = port->bind_desc;
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
-
- if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
- {
- /*
- * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
- * the IP:port in ISUPPORT.
- *
- * We used to advertise all ports seperated by a ';' char that matched the above criteria,
- * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
- * To solve this by default we now only display the first IP:port found and let the user
- * configure the exact value for the 005 token, if necessary.
- */
- sslports = portid;
- break;
- }
- }
+ SSL_shutdown(session->sess);
+ SSL_free(session->sess);
}
+
+ session->sess = NULL;
+ session->status = ISSL_NONE;
+ errno = EIO;
}
- void OnModuleRehash(User* user, const std::string ¶m)
+ void VerifyCertificate(issl_session* session, StreamSocket* user)
{
- if (param != "ssl")
+ if (!session->sess || !user)
return;
- std::string keyfile;
- std::string certfile;
- std::string cafile;
- std::string dhfile;
- OnRehash(user);
-
- ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
-
- cafile = conf->getString("cafile", CONFIG_PATH "/ca.pem");
- certfile = conf->getString("certfile", CONFIG_PATH "/cert.pem");
- keyfile = conf->getString("keyfile", CONFIG_PATH "/key.pem");
- dhfile = conf->getString("dhfile", CONFIG_PATH "/dhparams.pem");
- std::string hash = conf->getString("hash", "md5");
- if (hash != "sha1" && hash != "md5")
- throw ModuleException("Unknown hash type " + hash);
- use_sha = (hash == "sha1");
+ X509* cert;
+ ssl_cert* certinfo = new ssl_cert;
+ session->cert = certinfo;
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
- std::string ciphers = conf->getString("ciphers", "");
+ cert = SSL_get_peer_certificate((SSL*)session->sess);
- if (!ciphers.empty())
+ if (!cert)
{
- if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str())))
- {
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't set cipher list to %s.", ciphers.c_str());
- ERR_print_errors_cb(error_callback, this);
- }
+ certinfo->error = "Could not get peer certificate: "+std::string(get_error());
+ return;
}
- /* Load our keys and certificates
- * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
- */
- if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
- {
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
- }
+ certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
- if (SelfSigned)
- if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
++ if (!SelfSigned)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
+ certinfo->unknownsigner = false;
+ certinfo->trusted = true;
}
-
- /* Load the CAs we trust*/
- if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
+ else
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", cafile.c_str(), strerror(errno));
- ERR_print_errors_cb(error_callback, this);
+ certinfo->unknownsigner = true;
+ certinfo->trusted = false;
}
- FILE* dhpfile = fopen(dhfile.c_str(), "r");
- DH* ret;
+ certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
+ certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
- if (dhpfile == NULL)
+ if (!X509_digest(cert, digest, md, &n))
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
- throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
+ certinfo->error = "Out of memory generating fingerprint";
}
else
{
- ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
- if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
- {
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
- ERR_print_errors_cb(error_callback, this);
- }
+ certinfo->fingerprint = BinToHex(md, n);
}
- fclose(dhpfile);
- }
-
- void On005Numeric(std::string &output)
- {
- if (!sslports.empty())
- output.append(" SSL=" + sslports);
- }
-
- ~ModuleSSLOpenSSL()
- {
- SSL_CTX_free(ctx);
- SSL_CTX_free(clictx);
- delete[] sessions;
- }
-
- void OnUserConnect(LocalUser* user)
- {
- if (user->eh.GetIOHook() == this)
+ if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), ServerInstance->Time()) == 0))
{
- if (sessions[user->eh.GetFd()].sess)
- {
- if (!sessions[user->eh.GetFd()].cert->fingerprint.empty())
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
- " and your SSL fingerprint is %s", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess), sessions[user->eh.GetFd()].cert->fingerprint.c_str());
- else
- user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess));
- }
+ certinfo->error = "Not activated, or expired certificate";
}
- }
-
- void OnCleanup(int target_type, void* item)
- {
- if (target_type == TYPE_USER)
- {
- LocalUser* user = IS_LOCAL((User*)item);
- if (user && user->eh.GetIOHook() == this)
- {
- // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
- // Potentially there could be multiple SSL modules loaded at once on different ports.
- ServerInstance->Users->QuitUser(user, "SSL module unloading");
- }
- }
+ X509_free(cert);
}
- Version GetVersion()
+ public:
+ issl_session* sessions;
+ SSL_CTX* ctx;
+ SSL_CTX* clictx;
+ const EVP_MD *digest;
+
+ OpenSSLIOHook(Module* mod)
+ : SSLIOHook(mod, "ssl/openssl")
{
- return Version("Provides SSL support for clients", VF_VENDOR);
+ sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
}
- void OnRequest(Request& request)
+ ~OpenSSLIOHook()
{
- if (strcmp("GET_SSL_CERT", request.id) == 0)
- {
- SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
- int fd = req.sock->GetFd();
- issl_session* session = &sessions[fd];
-
- req.cert = session->cert;
- }
+ delete[] sessions;
}
- void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
+ void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
{
int fd = user->GetFd();
if (SSL_set_fd(session->sess, fd) == 0)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Can't set fd with SSL_set_fd: %d", fd);
return;
}
Handshake(user, session);
}
- void OnStreamSocketConnect(StreamSocket* user)
+ void OnStreamSocketConnect(StreamSocket* user) CXX11_OVERRIDE
{
int fd = user->GetFd();
/* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
if (SSL_set_fd(session->sess, fd) == 0)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Can't set fd with SSL_set_fd: %d", fd);
return;
}
Handshake(user, session);
}
- void OnStreamSocketClose(StreamSocket* user)
+ void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
{
int fd = user->GetFd();
/* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
CloseSession(&sessions[fd]);
}
- int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
+ int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
{
int fd = user->GetFd();
/* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
return 0;
}
- int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
+ int OnStreamSocketWrite(StreamSocket* user, std::string& buffer) CXX11_OVERRIDE
{
int fd = user->GetFd();
return 0;
}
- bool Handshake(StreamSocket* user, issl_session* session)
+ ssl_cert* GetCertificate(StreamSocket* sock) CXX11_OVERRIDE
{
- int ret;
-
- if (session->outbound)
- ret = SSL_connect(session->sess);
- else
- ret = SSL_accept(session->sess);
+ int fd = sock->GetFd();
+ issl_session* session = &sessions[fd];
+ return session->cert;
+ }
- if (ret < 0)
+ void TellCiphersAndFingerprint(LocalUser* user)
+ {
+ issl_session& s = sessions[user->eh.GetFd()];
+ if (s.sess)
{
- int err = SSL_get_error(session->sess, ret);
-
- if (err == SSL_ERROR_WANT_READ)
- {
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
- session->status = ISSL_HANDSHAKING;
- return true;
- }
- else if (err == SSL_ERROR_WANT_WRITE)
- {
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
- session->status = ISSL_HANDSHAKING;
- return true;
- }
- else
- {
- CloseSession(session);
- }
+ std::string text = "*** You are connected using SSL cipher '" + std::string(SSL_get_cipher(s.sess)) + "'";
+ const std::string& fingerprint = s.cert->fingerprint;
+ if (!fingerprint.empty())
+ text += " and your SSL fingerprint is " + fingerprint;
- return false;
+ user->WriteNotice(text);
}
- else if (ret > 0)
- {
- // Handshake complete.
- VerifyCertificate(session, user);
+ }
+};
- session->status = ISSL_OPEN;
+class ModuleSSLOpenSSL : public Module
+{
+ std::string sslports;
+ OpenSSLIOHook iohook;
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
+ public:
+ ModuleSSLOpenSSL() : iohook(this)
+ {
+ /* Global SSL library initialization*/
+ SSL_library_init();
+ SSL_load_error_strings();
- return true;
- }
- else if (ret == 0)
- {
- CloseSession(session);
- return true;
- }
+ /* Build our SSL contexts:
+ * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
+ */
+ iohook.ctx = SSL_CTX_new( SSLv23_server_method() );
+ iohook.clictx = SSL_CTX_new( SSLv23_client_method() );
- return true;
+ SSL_CTX_set_mode(iohook.ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ SSL_CTX_set_mode(iohook.clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ SSL_CTX_set_verify(iohook.ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+ SSL_CTX_set_verify(iohook.clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
}
- void CloseSession(issl_session* session)
+ ~ModuleSSLOpenSSL()
{
- if (session->sess)
+ SSL_CTX_free(iohook.ctx);
+ SSL_CTX_free(iohook.clictx);
+ }
+
+ void init() CXX11_OVERRIDE
+ {
+ // Needs the flag as it ignores a plain /rehash
+ OnModuleRehash(NULL,"ssl");
+ ServerInstance->Modules->AddService(iohook);
+ }
+
+ void OnHookIO(StreamSocket* user, ListenSocket* lsb) CXX11_OVERRIDE
+ {
+ if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
{
- SSL_shutdown(session->sess);
- SSL_free(session->sess);
+ /* Hook the user with our module */
+ user->AddIOHook(&iohook);
}
+ }
- session->sess = NULL;
- session->status = ISSL_NONE;
- errno = EIO;
+ void OnRehash(User* user) CXX11_OVERRIDE
+ {
+ sslports.clear();
+
+ ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
+
+ if (Conf->getBool("showports", true))
+ {
+ sslports = Conf->getString("advertisedports");
+ if (!sslports.empty())
+ return;
+
+ for (size_t i = 0; i < ServerInstance->ports.size(); i++)
+ {
+ ListenSocket* port = ServerInstance->ports[i];
+ if (port->bind_tag->getString("ssl") != "openssl")
+ continue;
+
+ const std::string& portid = port->bind_desc;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Enabling SSL for port %s", portid.c_str());
+
+ if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
+ {
+ /*
+ * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
+ * the IP:port in ISUPPORT.
+ *
+ * We used to advertise all ports seperated by a ';' char that matched the above criteria,
+ * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
+ * To solve this by default we now only display the first IP:port found and let the user
+ * configure the exact value for the 005 token, if necessary.
+ */
+ sslports = portid;
+ break;
+ }
+ }
+ }
}
- void VerifyCertificate(issl_session* session, StreamSocket* user)
+ void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE
{
- if (!session->sess || !user)
+ if (param != "ssl")
return;
- X509* cert;
- ssl_cert* certinfo = new ssl_cert;
- session->cert = certinfo;
- unsigned int n;
- unsigned char md[EVP_MAX_MD_SIZE];
- const EVP_MD *digest = use_sha ? EVP_sha1() : EVP_md5();
+ std::string keyfile;
+ std::string certfile;
+ std::string cafile;
+ std::string dhfile;
+ OnRehash(user);
- cert = SSL_get_peer_certificate((SSL*)session->sess);
+ ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
- if (!cert)
+ cafile = ServerInstance->Config->Paths.PrependConfig(conf->getString("cafile", "ca.pem"));
+ certfile = ServerInstance->Config->Paths.PrependConfig(conf->getString("certfile", "cert.pem"));
+ keyfile = ServerInstance->Config->Paths.PrependConfig(conf->getString("keyfile", "key.pem"));
+ dhfile = ServerInstance->Config->Paths.PrependConfig(conf->getString("dhfile", "dhparams.pem"));
+ std::string hash = conf->getString("hash", "md5");
+
+ iohook.digest = EVP_get_digestbyname(hash.c_str());
+ if (iohook.digest == NULL)
+ throw ModuleException("Unknown hash type " + hash);
+
+ std::string ciphers = conf->getString("ciphers", "");
+
+ SSL_CTX* ctx = iohook.ctx;
+ SSL_CTX* clictx = iohook.clictx;
+
+ if (!ciphers.empty())
{
- certinfo->error = "Could not get peer certificate: "+std::string(get_error());
- return;
+ if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str())))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't set cipher list to %s.", ciphers.c_str());
+ ERR_print_errors_cb(error_callback, this);
+ }
}
- certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
+ /* Load our keys and certificates
+ * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
+ */
+ if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
+ ERR_print_errors_cb(error_callback, this);
+ }
- if (!SelfSigned)
+ if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
{
- certinfo->unknownsigner = false;
- certinfo->trusted = true;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
+ ERR_print_errors_cb(error_callback, this);
}
- else
+
+ /* Load the CAs we trust*/
+ if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
{
- certinfo->unknownsigner = true;
- certinfo->trusted = false;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", cafile.c_str(), strerror(errno));
+ ERR_print_errors_cb(error_callback, this);
}
- certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
- certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
+ FILE* dhpfile = fopen(dhfile.c_str(), "r");
+ DH* ret;
- if (!X509_digest(cert, digest, md, &n))
+ if (dhpfile == NULL)
{
- certinfo->error = "Out of memory generating fingerprint";
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
+ throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
}
else
{
- certinfo->fingerprint = irc::hex(md, n);
+ ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
+ if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
+ {
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
+ ERR_print_errors_cb(error_callback, this);
+ }
}
- if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), ServerInstance->Time()) == 0))
+ fclose(dhpfile);
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+ {
+ if (!sslports.empty())
+ tokens["SSL"] = sslports;
+ }
+
+ void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
+ {
+ if (user->eh.GetIOHook() == &iohook)
+ iohook.TellCiphersAndFingerprint(user);
+ }
+
+ void OnCleanup(int target_type, void* item) CXX11_OVERRIDE
+ {
+ if (target_type == TYPE_USER)
{
- certinfo->error = "Not activated, or expired certificate";
+ LocalUser* user = IS_LOCAL((User*)item);
+
+ if (user && user->eh.GetIOHook() == &iohook)
+ {
+ // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
+ // Potentially there could be multiple SSL modules loaded at once on different ports.
+ ServerInstance->Users->QuitUser(user, "SSL module unloading");
+ }
}
+ }
- X509_free(cert);
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides SSL support for clients", VF_VENDOR);
}
};
static int error_callback(const char *str, size_t len, void *u)
{
- ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "SSL error: " + std::string(str, len - 1));
//
// XXX: Remove this line, it causes valgrind warnings...
#include "inspircd.h"
#include "xline.h"
-
-/* $ModDesc: Provides handling of DNS blacklists */
+#include "modules/dns.h"
/* Class holding data for a single entry */
- class DNSBLConfEntry
+ class DNSBLConfEntry : public refcountbase
{
public:
enum EnumBanaction { I_UNKNOWN, I_KILL, I_ZLINE, I_KLINE, I_GLINE, I_MARK };
unsigned char records[256];
unsigned long stats_hits, stats_misses;
DNSBLConfEntry(): type(A_BITMASK),duration(86400),bitmask(0),stats_hits(0), stats_misses(0) {}
- ~DNSBLConfEntry() { }
};
/** Resolver for CGI:IRC hostnames encoded in ident/GECOS
*/
-class DNSBLResolver : public Resolver
+class DNSBLResolver : public DNS::Request
{
std::string theiruid;
LocalStringExt& nameExt;
LocalIntExt& countExt;
- DNSBLConfEntry *ConfEntry;
+ reference<DNSBLConfEntry> ConfEntry;
public:
- DNSBLResolver(DNS::Manager *mgr, Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, DNSBLConfEntry *conf)
- DNSBLResolver(Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf, bool &cached)
- : Resolver(hostname, DNS_QUERY_A, cached, me), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
++ DNSBLResolver(DNS::Manager *mgr, Module *me, LocalStringExt& match, LocalIntExt& ctr, const std::string &hostname, LocalUser* u, reference<DNSBLConfEntry> conf)
+ : DNS::Request(mgr, me, hostname, DNS::QUERY_A, true), theiruid(u->uuid), nameExt(match), countExt(ctr), ConfEntry(conf)
{
}
/* Note: This may be called multiple times for multiple A record results */
- virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+ void OnLookupComplete(const DNS::Query *r) CXX11_OVERRIDE
{
/* Check the user still exists */
LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
- if (them)
+ if (!them)
+ return;
+
+ const DNS::ResourceRecord &ans_record = r->answers[0];
+
+ int i = countExt.get(them);
+ if (i)
+ countExt.set(them, i - 1);
+
+ // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
+
+ unsigned int bitmask = 0, record = 0;
+ bool match = false;
+ in_addr resultip;
+
+ inet_aton(ans_record.rdata.c_str(), &resultip);
+
+ switch (ConfEntry->type)
+ {
+ case DNSBLConfEntry::A_BITMASK:
+ bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
+ bitmask &= ConfEntry->bitmask;
+ match = (bitmask != 0);
+ break;
+ case DNSBLConfEntry::A_RECORD:
+ record = resultip.s_addr >> 24; /* Last octet */
+ match = (ConfEntry->records[record] == 1);
+ break;
+ }
+
+ if (match)
{
- int i = countExt.get(them);
- if (i)
- countExt.set(them, i - 1);
- // Now we calculate the bitmask: 256*(256*(256*a+b)+c)+d
- if(result.length())
+ std::string reason = ConfEntry->reason;
+ std::string::size_type x = reason.find("%ip%");
+ while (x != std::string::npos)
{
- unsigned int bitmask = 0, record = 0;
- bool match = false;
- in_addr resultip;
+ reason.erase(x, 4);
+ reason.insert(x, them->GetIPString());
+ x = reason.find("%ip%");
+ }
- inet_aton(result.c_str(), &resultip);
+ ConfEntry->stats_hits++;
- switch (ConfEntry->type)
+ switch (ConfEntry->banaction)
+ {
+ case DNSBLConfEntry::I_KILL:
{
- case DNSBLConfEntry::A_BITMASK:
- bitmask = resultip.s_addr >> 24; /* Last octet (network byte order) */
- bitmask &= ConfEntry->bitmask;
- match = (bitmask != 0);
- break;
- case DNSBLConfEntry::A_RECORD:
- record = resultip.s_addr >> 24; /* Last octet */
- match = (ConfEntry->records[record] == 1);
+ ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
break;
}
-
- if (match)
+ case DNSBLConfEntry::I_MARK:
{
- std::string reason = ConfEntry->reason;
- std::string::size_type x = reason.find("%ip%");
- while (x != std::string::npos)
+ if (!ConfEntry->ident.empty())
{
- reason.erase(x, 4);
- reason.insert(x, them->GetIPString());
- x = reason.find("%ip%");
+ them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
+ them->ChangeIdent(ConfEntry->ident);
}
- ConfEntry->stats_hits++;
-
- switch (ConfEntry->banaction)
+ if (!ConfEntry->host.empty())
{
- case DNSBLConfEntry::I_KILL:
- {
- ServerInstance->Users->QuitUser(them, "Killed (" + reason + ")");
- break;
- }
- case DNSBLConfEntry::I_MARK:
- {
- if (!ConfEntry->ident.empty())
- {
- them->WriteServ("304 " + them->nick + " :Your ident has been set to " + ConfEntry->ident + " because you matched " + reason);
- them->ChangeIdent(ConfEntry->ident.c_str());
- }
-
- if (!ConfEntry->host.empty())
- {
- them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason);
- them->ChangeDisplayedHost(ConfEntry->host.c_str());
- }
-
- nameExt.set(them, ConfEntry->name);
- break;
- }
- case DNSBLConfEntry::I_KLINE:
- {
- KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- "*", them->GetIPString());
- if (ServerInstance->XLines->AddLine(kl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(kl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- delete kl;
- break;
- }
- case DNSBLConfEntry::I_GLINE:
- {
- GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- "*", them->GetIPString());
- if (ServerInstance->XLines->AddLine(gl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(gl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- delete gl;
- break;
- }
- case DNSBLConfEntry::I_ZLINE:
- {
- ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
- them->GetIPString());
- if (ServerInstance->XLines->AddLine(zl,NULL))
- {
- std::string timestr = ServerInstance->TimeString(zl->expiry);
- ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s",
- them->GetIPString(), timestr.c_str(), reason.c_str());
- ServerInstance->XLines->ApplyLines();
- }
- else
- delete zl;
- break;
- }
- case DNSBLConfEntry::I_UNKNOWN:
- {
- break;
- }
- break;
+ them->WriteServ("304 " + them->nick + " :Your host has been set to " + ConfEntry->host + " because you matched " + reason);
+ them->ChangeDisplayedHost(ConfEntry->host);
}
- ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
+ nameExt.set(them, ConfEntry->name);
+ break;
+ }
+ case DNSBLConfEntry::I_KLINE:
+ {
+ KLine* kl = new KLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ "*", them->GetIPString());
+ if (ServerInstance->XLines->AddLine(kl,NULL))
+ {
+ std::string timestr = ServerInstance->TimeString(kl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"K:line added due to DNSBL match on *@%s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete kl;
+ break;
+ }
+ case DNSBLConfEntry::I_GLINE:
+ {
+ GLine* gl = new GLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ "*", them->GetIPString());
+ if (ServerInstance->XLines->AddLine(gl,NULL))
+ {
+ std::string timestr = ServerInstance->TimeString(gl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"G:line added due to DNSBL match on *@%s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete gl;
+ break;
}
- else
- ConfEntry->stats_misses++;
+ case DNSBLConfEntry::I_ZLINE:
+ {
+ ZLine* zl = new ZLine(ServerInstance->Time(), ConfEntry->duration, ServerInstance->Config->ServerName.c_str(), reason.c_str(),
+ them->GetIPString());
+ if (ServerInstance->XLines->AddLine(zl,NULL))
+ {
+ std::string timestr = ServerInstance->TimeString(zl->expiry);
+ ServerInstance->SNO->WriteGlobalSno('x',"Z:line added due to DNSBL match on *@%s to expire on %s: %s",
+ them->GetIPString().c_str(), timestr.c_str(), reason.c_str());
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete zl;
+ break;
+ }
+ case DNSBLConfEntry::I_UNKNOWN:
+ default:
+ break;
}
- else
- ConfEntry->stats_misses++;
+
+ ServerInstance->SNO->WriteGlobalSno('a', "Connecting user %s%s detected as being on a DNS blacklist (%s) with result %d", them->nick.empty() ? "<unknown>" : "", them->GetFullRealHost().c_str(), ConfEntry->domain.c_str(), (ConfEntry->type==DNSBLConfEntry::A_BITMASK) ? bitmask : record);
}
+ else
+ ConfEntry->stats_misses++;
}
- virtual void OnError(ResolverError e, const std::string &errormessage)
+ void OnError(const DNS::Query *q) CXX11_OVERRIDE
{
LocalUser* them = (LocalUser*)ServerInstance->FindUUID(theiruid);
- if (them)
- {
- int i = countExt.get(them);
- if (i)
- countExt.set(them, i - 1);
- }
- }
+ if (!them)
+ return;
- virtual ~DNSBLResolver()
- {
+ int i = countExt.get(them);
+ if (i)
+ countExt.set(them, i - 1);
+
+ if (q->error == DNS::ERROR_NO_RECORDS || q->error == DNS::ERROR_DOMAIN_NOT_FOUND)
+ ConfEntry->stats_misses++;
}
};
class ModuleDNSBL : public Module
{
- std::vector<DNSBLConfEntry *> DNSBLConfEntries;
+ std::vector<reference<DNSBLConfEntry> > DNSBLConfEntries;
+ dynamic_reference<DNS::Manager> DNS;
LocalStringExt nameExt;
LocalIntExt countExt;
return DNSBLConfEntry::I_UNKNOWN;
}
public:
- ModuleDNSBL() : nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
+ ModuleDNSBL() : DNS(this, "DNS"), nameExt("dnsbl_match", this), countExt("dnsbl_pending", this) { }
- void init()
+ void init() CXX11_OVERRIDE
{
ReadConf();
ServerInstance->Modules->AddService(nameExt);
ServerInstance->Modules->AddService(countExt);
- Implementation eventlist[] = { I_OnRehash, I_OnSetUserIP, I_OnStats, I_OnSetConnectClass, I_OnCheckReady };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- ~ModuleDNSBL()
- {
- ClearEntries();
- }
-
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides handling of DNS blacklists", VF_VENDOR);
}
- /** Clear entries and free the mem it was using
- */
- void ClearEntries()
- {
- for (std::vector<DNSBLConfEntry *>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
- delete *i;
- DNSBLConfEntries.clear();
- }
-
/** Fill our conf vector with data
*/
void ReadConf()
{
- ClearEntries();
+ DNSBLConfEntries.clear();
ConfigTagList dnsbls = ServerInstance->Config->ConfTags("dnsbl");
for(ConfigIter i = dnsbls.first; i != dnsbls.second; ++i)
{
ConfigTag* tag = i->second;
- DNSBLConfEntry *e = new DNSBLConfEntry();
+ reference<DNSBLConfEntry> e = new DNSBLConfEntry();
e->name = tag->getString("name");
e->ident = tag->getString("ident");
}
e->banaction = str2banaction(tag->getString("action"));
- e->duration = ServerInstance->Duration(tag->getString("duration", "60"));
+ e->duration = tag->getDuration("duration", 60, 1);
/* Use portparser for record replies */
std::string location = tag->getTagLocation();
ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid banaction", location.c_str());
}
- else if (e->duration <= 0)
- {
- std::string location = tag->getTagLocation();
- ServerInstance->SNO->WriteGlobalSno('a', "DNSBL(%s): Invalid duration", location.c_str());
- }
else
{
if (e->reason.empty())
/* add it, all is ok */
DNSBLConfEntries.push_back(e);
- continue;
}
-
- /* delete and drop it, error somewhere */
- delete e;
}
}
- void OnRehash(User* user)
+ void OnRehash(User* user) CXX11_OVERRIDE
{
ReadConf();
}
- void OnSetUserIP(LocalUser* user)
+ void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE
{
- if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET))
+ if ((user->exempt) || (user->client_sa.sa.sa_family != AF_INET) || !DNS)
return;
if (user->MyClass)
return;
}
else
- ServerInstance->Logs->Log("m_dnsbl", DEBUG, "User has no connect class in OnSetUserIP");
-
- unsigned char a, b, c, d;
- char reversedipbuf[128];
- std::string reversedip;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User has no connect class in OnSetUserIP");
- d = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
- c = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
- b = (unsigned char) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
- a = (unsigned char) user->client_sa.in4.sin_addr.s_addr & 0xFF;
+ unsigned int a, b, c, d;
+ d = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 24) & 0xFF;
+ c = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 16) & 0xFF;
+ b = (unsigned int) (user->client_sa.in4.sin_addr.s_addr >> 8) & 0xFF;
+ a = (unsigned int) user->client_sa.in4.sin_addr.s_addr & 0xFF;
- snprintf(reversedipbuf, 128, "%d.%d.%d.%d", d, c, b, a);
- reversedip = std::string(reversedipbuf);
+ const std::string reversedip = ConvToStr(d) + "." + ConvToStr(c) + "." + ConvToStr(b) + "." + ConvToStr(a);
countExt.set(user, DNSBLConfEntries.size());
// For each DNSBL, we will run through this lookup
- unsigned int i = 0;
- while (i < DNSBLConfEntries.size())
+ for (unsigned i = 0; i < DNSBLConfEntries.size(); ++i)
{
// Fill hostname with a dnsbl style host (d.c.b.a.domain.tld)
std::string hostname = reversedip + "." + DNSBLConfEntries[i]->domain;
/* now we'd need to fire off lookups for `hostname'. */
- bool cached;
- DNSBLResolver *r = new DNSBLResolver(this, nameExt, countExt, hostname, user, DNSBLConfEntries[i], cached);
- ServerInstance->AddResolver(r, cached);
+ DNSBLResolver *r = new DNSBLResolver(*this->DNS, this, nameExt, countExt, hostname, user, DNSBLConfEntries[i]);
+ try
+ {
+ this->DNS->Process(r);
+ }
+ catch (DNS::Exception &ex)
+ {
+ delete r;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, std::string(ex.GetReason()));
+ }
+
if (user->quitting)
break;
- i++;
}
}
- ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
+ ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
{
std::string dnsbl;
if (!myclass->config->readString("dnsbl", dnsbl))
return MOD_RES_PASSTHRU;
return MOD_RES_DENY;
}
-
- ModResult OnCheckReady(LocalUser *user)
+
+ ModResult OnCheckReady(LocalUser *user) CXX11_OVERRIDE
{
if (countExt.get(user))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
}
- ModResult OnStats(char symbol, User* user, string_list &results)
+ ModResult OnStats(char symbol, User* user, string_list &results) CXX11_OVERRIDE
{
if (symbol != 'd')
return MOD_RES_PASSTHRU;
unsigned long total_hits = 0, total_misses = 0;
- for (std::vector<DNSBLConfEntry*>::iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); i++)
+ for (std::vector<reference<DNSBLConfEntry> >::const_iterator i = DNSBLConfEntries.begin(); i != DNSBLConfEntries.end(); ++i)
{
total_hits += (*i)->stats_hits;
total_misses += (*i)->stats_misses;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* $ModDesc: Provides support for extended-join, away-notify and account-notify CAP capabilities */
-
#include "inspircd.h"
-#include "account.h"
-#include "m_cap.h"
+#include "modules/account.h"
+#include "modules/cap.h"
class ModuleIRCv3 : public Module
{
UserChanList chans(user->chans);
std::map<User*, bool> exceptions;
- FOREACH_MOD(I_OnBuildNeighborList, OnBuildNeighborList(user, chans, exceptions));
+ FOREACH_MOD(OnBuildNeighborList, (user, chans, exceptions));
// Send it to all local users who were explicitly marked as neighbours by modules and have the required ext
for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
OnRehash(NULL);
- Implementation eventlist[] = { I_OnUserJoin, I_OnPostJoin, I_OnSetAway, I_OnEvent, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- void OnRehash(User* user)
+ void OnRehash(User* user) CXX11_OVERRIDE
{
ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
- accountnotify = conf->getBool("accoutnotify", true);
- accountnotify = conf->getBool("accountnotify", conf->getBool("accoutnotify", true));
++ accountnotify = conf->getBool("accountnotify", true);
awaynotify = conf->getBool("awaynotify", true);
extendedjoin = conf->getBool("extendedjoin", true);
}
- void OnEvent(Event& ev)
+ void OnEvent(Event& ev) CXX11_OVERRIDE
{
if (awaynotify)
cap_awaynotify.HandleEvent(ev);
}
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+ void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
{
// Remember who is not going to see the JOIN because of other modules
- if ((awaynotify) && (IS_AWAY(memb->user)))
+ if ((awaynotify) && (memb->user->IsAway()))
last_excepts = excepts;
if (!extendedjoin)
}
}
- ModResult OnSetAway(User* user, const std::string &awaymsg)
+ ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE
{
if (awaynotify)
{
return MOD_RES_PASSTHRU;
}
- void OnPostJoin(Membership *memb)
+ void OnPostJoin(Membership *memb) CXX11_OVERRIDE
{
- if ((!awaynotify) || (!IS_AWAY(memb->user)))
+ if ((!awaynotify) || (!memb->user->IsAway()))
return;
std::string line = ":" + memb->user->GetFullHost() + " AWAY :" + memb->user->awaymsg;
ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST);
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for extended-join, away-notify and account-notify CAP capabilities", VF_VENDOR);
}
#include "inspircd.h"
++#include "listmode.h"
+#include <fstream>
-/* $ModDesc: Provides support for channel mode +P to provide permanent channels */
-// Not in a class due to circular dependancy hell.
-static std::string permchannelsconf;
-static bool WriteDatabase(Module* mod, bool save_listmodes)
+ struct ListModeData
+ {
+ std::string modes;
+ std::string params;
+ };
+
+/** Handles the +P channel mode
+ */
+class PermChannel : public ModeHandler
{
- FILE *f;
-
- if (permchannelsconf.empty())
+ public:
+ PermChannel(Module* Creator)
+ : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL)
{
- // Fake success.
- return true;
+ oper = true;
}
- std::string tempname = permchannelsconf + ".tmp";
+ ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+ {
+ if (adding == channel->IsModeSet(this))
+ return MODEACTION_DENY;
+
+ channel->SetMode(this, adding);
+ if (!adding)
+ channel->CheckDestroy();
+
+ return MODEACTION_ALLOW;
+ }
+};
- static bool WriteDatabase(PermChannel& permchanmode)
+// Not in a class due to circular dependancy hell.
+static std::string permchannelsconf;
++static bool WriteDatabase(PermChannel& permchanmode, Module* mod, bool save_listmodes)
+{
++ ChanModeReference ban(mod, "ban");
/*
* We need to perform an atomic write so as not to fuck things up.
- * So, let's write to a temporary file, flush and sync the FD, then rename the file..
- * -- w00t
+ * So, let's write to a temporary file, flush it, then rename the file..
+ * -- w00t
*/
- f = fopen(tempname.c_str(), "w");
- if (!f)
+
+ // If the user has not specified a configuration file then we don't write one.
+ if (permchannelsconf.empty())
+ return true;
+
+ std::string permchannelsnewconf = permchannelsconf + ".tmp";
+ std::ofstream stream(permchannelsnewconf.c_str());
+ if (!stream.is_open())
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot create database! %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot create database! %s (%d)", strerror(errno), errno);
ServerInstance->SNO->WriteToSnoMask('a', "database: cannot create new db: %s (%d)", strerror(errno), errno);
return false;
}
+
+ stream << "# This file is automatically generated by m_permchannels. Any changes will be overwritten." << std::endl
+ << "<config format=\"xml\">" << std::endl;
- fputs("# Permchannels DB\n# This file is autogenerated; any changes will be overwritten!\n<config format=\"compat\">\n", f);
- // Now, let's write.
- std::string line;
for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)
{
Channel* chan = i->second;
- if (!chan->IsModeSet('P'))
+ if (!chan->IsModeSet(permchanmode))
continue;
- lm.modes = std::string(chan->bans.size(), 'b');
- for (BanList::const_iterator j = chan->bans.begin(); j != chan->bans.end(); ++j)
- {
- lm.params += j->data;
- lm.params += ' ';
- }
+ std::string chanmodes = chan->ChanModes(true);
+ if (save_listmodes)
+ {
+ ListModeData lm;
+
+ // Bans are managed by the core, so we have to process them separately
- FOREACH_MOD(I_OnSyncChannel, OnSyncChannel(chan, mod, &lm));
++ static_cast<ListModeBase*>(*ban)->DoSyncChannel(chan, mod, &lm);
+
+ // All other listmodes are managed by modules, so we need to ask them (call their
+ // OnSyncChannel() handler) to give our ProtoSendMode() a list of modes that are
+ // set on the channel. The ListModeData struct is passed as an opaque pointer
+ // that will be passed back to us by the module handling the mode.
- std::string chants = ConvToStr(chan->age);
- std::string topicts = ConvToStr(chan->topicset);
- const char* items[] =
- {
- "<permchannels channel=",
- chan->name.c_str(),
- " ts=",
- chants.c_str(),
- " topic=",
- chan->topic.c_str(),
- " topicts=",
- topicts.c_str(),
- " topicsetby=",
- chan->setby.c_str(),
- " modes=",
- chanmodes.c_str(),
- ">\n"
- };
-
- line.clear();
- int item = 0, ipos = 0;
- while (item < 13)
- {
- char c = items[item][ipos++];
- if (c == 0)
- {
- // end of this string; hop to next string, insert a quote
- item++;
- ipos = 0;
- c = '"';
- }
- else if (c == '\\' || c == '"')
- {
- line += '\\';
- }
- line += c;
- }
-
- // Erase last '"'
- line.erase(line.end()-1);
- fputs(line.c_str(), f);
++ FOREACH_MOD(OnSyncChannel, (chan, mod, &lm));
+
+ if (!lm.modes.empty())
+ {
+ // Remove the last space
+ lm.params.erase(lm.params.end()-1);
+
+ // If there is at least a space in chanmodes (that is, a non-listmode has a parameter)
+ // insert the listmode mode letters before the space. Otherwise just append them.
+ std::string::size_type p = chanmodes.find(' ');
+ if (p == std::string::npos)
+ chanmodes += lm.modes;
+ else
+ chanmodes.insert(p, lm.modes);
+
+ // Append the listmode parameters (the masks themselves)
+ chanmodes += ' ';
+ chanmodes += lm.params;
+ }
+ }
+
- << "\" modes=\"" << ServerConfig::Escape(chan->ChanModes(true))
+ stream << "<permchannels channel=\"" << ServerConfig::Escape(chan->name)
++ << "\" ts=\"" << chan->age
+ << "\" topic=\"" << ServerConfig::Escape(chan->topic)
++ << "\" topicts=\"" << chan->topicset
++ << "\" topicsetby=\"" << ServerConfig::Escape(chan->setby)
++ << "\" modes=\"" << ServerConfig::Escape(chanmodes)
+ << "\">" << std::endl;
}
- int write_error = 0;
- write_error = ferror(f);
- write_error |= fclose(f);
- if (write_error)
+ if (stream.fail())
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot write to new database! %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot write to new database! %s (%d)", strerror(errno), errno);
ServerInstance->SNO->WriteToSnoMask('a', "database: cannot write to new db: %s (%d)", strerror(errno), errno);
return false;
}
+ stream.close();
#ifdef _WIN32
if (remove(permchannelsconf.c_str()))
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot remove old database! %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot remove old database! %s (%d)", strerror(errno), errno);
ServerInstance->SNO->WriteToSnoMask('a', "database: cannot remove old database: %s (%d)", strerror(errno), errno);
return false;
}
#endif
// Use rename to move temporary to new db - this is guarenteed not to fuck up, even in case of a crash.
- if (rename(tempname.c_str(), permchannelsconf.c_str()) < 0)
+ if (rename(permchannelsnewconf.c_str(), permchannelsconf.c_str()) < 0)
{
- ServerInstance->Logs->Log("m_permchannels",DEFAULT, "permchannels: Cannot move new to old database! %s (%d)", strerror(errno), errno);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Cannot move new to old database! %s (%d)", strerror(errno), errno);
ServerInstance->SNO->WriteToSnoMask('a', "database: cannot replace old with new db: %s (%d)", strerror(errno), errno);
return false;
}
return true;
}
-
-
-/** Handles the +P channel mode
- */
-class PermChannel : public ModeHandler
-{
- public:
- PermChannel(Module* Creator) : ModeHandler(Creator, "permanent", 'P', PARAM_NONE, MODETYPE_CHANNEL) { oper = true; }
-
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
- {
- if (adding)
- {
- if (!channel->IsModeSet('P'))
- {
- channel->SetMode('P',true);
- return MODEACTION_ALLOW;
- }
- }
- else
- {
- if (channel->IsModeSet('P'))
- {
- channel->SetMode(this,false);
- if (channel->GetUserCounter() == 0)
- {
- channel->DelUser(ServerInstance->FakeClient);
- }
- return MODEACTION_ALLOW;
- }
- }
-
- return MODEACTION_DENY;
- }
-};
-
class ModulePermanentChannels : public Module
{
PermChannel p;
bool dirty;
+ bool save_listmodes;
public:
ModulePermanentChannels() : p(this), dirty(false)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->Modules->AddService(p);
- Implementation eventlist[] = { I_OnChannelPreDelete, I_OnPostTopicChange, I_OnRawMode, I_OnRehash, I_OnBackgroundTimer };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
OnRehash(NULL);
}
{
chan_hash::iterator at = iter;
iter++;
- FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(c));
+ FOREACH_MOD(OnChannelDelete, (c));
ServerInstance->chanlist->erase(at);
ServerInstance->GlobalCulls.AddItem(c);
}
return Module::cull();
}
- virtual void OnRehash(User *user)
+ void OnRehash(User *user) CXX11_OVERRIDE
{
- permchannelsconf = ServerInstance->Config->ConfValue("permchanneldb")->getString("filename");
+ ConfigTag* tag = ServerInstance->Config->ConfValue("permchanneldb");
+ permchannelsconf = tag->getString("filename");
+ save_listmodes = tag->getBool("listmodes");
}
void LoadDatabase()
{
ConfigTag* tag = i->second;
std::string channel = tag->getString("channel");
-- std::string topic = tag->getString("topic");
std::string modes = tag->getString("modes");
- if (channel.empty())
+ if ((channel.empty()) || (channel.length() > ServerInstance->Config->Limits.ChanMax))
{
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Malformed permchannels tag with empty channel name.");
- ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
++ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")");
continue;
}
if (!c)
{
- c = new Channel(channel, ServerInstance->Time());
- if (!topic.empty())
- time_t TS = tag->getInt("ts");
- c = new Channel(channel, ((TS > 0) ? TS : ServerInstance->Time()));
++ time_t TS = tag->getInt("ts", ServerInstance->Time(), 1);
++ c = new Channel(channel, TS);
+
- c->SetTopic(NULL, topic, true);
- c->setby = tag->getString("topicsetby");
- if (c->setby.empty())
- c->setby = ServerInstance->Config->ServerName;
+ unsigned int topicset = tag->getInt("topicts");
- // SetTopic() sets the topic TS to now, if there was no topicts saved then don't overwrite that with a 0
- if (topicset > 0)
++ c->topic = tag->getString("topic");
++
++ if ((topicset != 0) || (!c->topic.empty()))
+ {
- c->SetTopic(ServerInstance->FakeClient, topic);
-
- /*
- * Due to the way protocol works in 1.2, we need to hack the topic TS in such a way that this
- * topic will always win over others.
- *
- * This is scheduled for (proper) fixing in a later release, and can be removed at a later date.
- */
- c->topicset = 42;
++ if (topicset == 0)
++ topicset = ServerInstance->Time();
+ c->topicset = topicset;
++ c->setby = tag->getString("topicsetby");
++ if (c->setby.empty())
++ c->setby = ServerInstance->Config->ServerName;
+ }
- ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str());
+
- ServerInstance->Logs->Log("m_permchannels", DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str());
++ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), c->topic.c_str());
if (modes.empty())
continue;
}
}
- virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string ¶m, bool adding, int pcnt)
+ ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string ¶m, bool adding, int pcnt) CXX11_OVERRIDE
{
- if (chan && (chan->IsModeSet('P') || mode == 'P'))
+ if (chan && (chan->IsModeSet(p) || mode == p.GetModeChar()))
dirty = true;
return MOD_RES_PASSTHRU;
}
- virtual void OnPostTopicChange(User*, Channel *c, const std::string&)
+ void OnPostTopicChange(User*, Channel *c, const std::string&) CXX11_OVERRIDE
{
- if (c->IsModeSet('P'))
+ if (c->IsModeSet(p))
dirty = true;
}
- void OnBackgroundTimer(time_t)
+ void OnBackgroundTimer(time_t) CXX11_OVERRIDE
{
if (dirty)
- WriteDatabase(p);
- WriteDatabase(this, save_listmodes);
++ WriteDatabase(p, this, save_listmodes);
dirty = false;
}
// Load only when there are no linked servers - we set the TS of the channels we
// create to the current time, this can lead to desync because spanningtree has
// no way of knowing what we do
- ProtoServerList serverlist;
+ ProtocolInterface::ServerList serverlist;
ServerInstance->PI->GetServerList(serverlist);
if (serverlist.size() < 2)
{
}
catch (CoreException& e)
{
- ServerInstance->Logs->Log("m_permchannels", DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error loading permchannels database: " + std::string(e.GetReason()));
}
}
}
- virtual Version GetVersion()
+ void ProtoSendMode(void* opaque, TargetTypeFlags type, void* target, const std::vector<std::string>& modes, const std::vector<TranslateType>& translate)
+ {
+ // We never pass an empty modelist but better be sure
+ if (modes.empty())
+ return;
+
+ ListModeData* lm = static_cast<ListModeData*>(opaque);
+
+ // Append the mode letters without the trailing '+' (for example "IIII", "gg")
+ lm->modes.append(modes[0].begin()+1, modes[0].end());
+
+ // Append the parameters
+ for (std::vector<std::string>::const_iterator i = modes.begin()+1; i != modes.end(); ++i)
+ {
+ lm->params += *i;
+ lm->params += ' ';
+ }
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for channel mode +P to provide permanent channels",VF_VENDOR);
}
- virtual ModResult OnChannelPreDelete(Channel *c)
+ ModResult OnChannelPreDelete(Channel *c) CXX11_OVERRIDE
{
- if (c->IsModeSet('P'))
+ if (c->IsModeSet(p))
return MOD_RES_DENY;
return MOD_RES_PASSTHRU;
#include "treesocket.h"
/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */
-CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *srcuser)
+CmdResult CommandFJoin::Handle(User* srcuser, std::vector<std::string>& params)
{
- SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
- /* 1.1 FJOIN works as follows:
+ /* 1.1+ FJOIN works as follows:
*
* Each FJOIN is sent along with a timestamp, and the side with the lowest
* timestamp 'wins'. From this point on we will refer to this side as the
* The winning side on the other hand will ignore all user modes from the
* losing side, so only its own modes get applied. Life is simple for those
* who succeed at internets. :-)
+ *
+ * Syntax:
+ * :<sid> FJOIN <chan> <TS> <modes> :[[modes,]<uuid> [[modes,]<uuid> ... ]]
+ * The last parameter is a list consisting of zero or more (modelist, uuid)
+ * pairs (permanent channels may have zero users). The mode list for each
+ * user is a concatenation of the mode letters the user has on the channel
+ * (e.g.: "ov" if the user is opped and voiced). The order of the mode letters
+ * are not important but if a server ecounters an unknown mode letter, it will
+ * drop the link to avoid desync.
+ *
+ * InspIRCd 2.0 and older required a comma before the uuid even if the user
+ * had no prefix modes on the channel, InspIRCd 2.2 and later does not require
+ * a comma in this case anymore.
+ *
*/
- irc::modestacker modestack(true); /* Modes to apply from the users in the user list */
- User* who = NULL; /* User we are currently checking */
- std::string channel = params[0]; /* Channel name, as a string */
- time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */
- irc::tokenstream users((params.size() > 3) ? params[params.size() - 1] : ""); /* users from the user list */
- bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
- Channel* chan = ServerInstance->FindChan(channel); /* The channel we're sending joins to */
- bool created = !chan; /* True if the channel doesnt exist here yet */
- std::string item; /* One item in the list of nicks */
-
- TreeSocket* src_socket = Utils->FindServer(srcuser->server)->GetRoute()->GetSocket();
-
+ time_t TS = ConvToInt(params[1]);
if (!TS)
{
- ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", srcuser->server.c_str());
return CMD_INVALID;
}
- if (created)
+ const std::string& channel = params[0];
+ Channel* chan = ServerInstance->FindChan(channel);
+ bool apply_other_sides_modes = true;
+
+ if (!chan)
{
chan = new Channel(channel, TS);
- ServerInstance->SNO->WriteToSnoMask('d', "Creation FJOIN received for %s, timestamp: %lu", chan->name.c_str(), (unsigned long)TS);
}
else
{
time_t ourTS = chan->age;
-
if (TS != ourTS)
+ {
ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %lu",
chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (unsigned long)(ourTS - TS));
- /* If our TS is less than theirs, we dont accept their modes */
- if (ourTS < TS)
- {
- ServerInstance->SNO->WriteToSnoMask('d', "NOT Applying modes from other side");
- apply_other_sides_modes = false;
- }
- else if (ourTS > TS)
- {
- /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
- ServerInstance->SNO->WriteToSnoMask('d', "Removing our modes, accepting remote");
- parameterlist param_list;
- if (Utils->AnnounceTSChange)
- chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS);
- // while the name is equal in case-insensitive compare, it might differ in case; use the remote version
- chan->name = channel;
- chan->age = TS;
- chan->ClearInvites();
- param_list.push_back(channel);
- this->RemoveStatus(ServerInstance->FakeClient, param_list);
-
- // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
- // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
- // deleted later) as soon as the permchan mode is removed from them.
- if (ServerInstance->FindChan(channel) == NULL)
+ /* If our TS is less than theirs, we dont accept their modes */
+ if (ourTS < TS)
{
- chan = new Channel(channel, TS);
+ apply_other_sides_modes = false;
+ }
+ else if (ourTS > TS)
+ {
+ /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
+ if (Utils->AnnounceTSChange)
+ chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS);
+
+ // while the name is equal in case-insensitive compare, it might differ in case; use the remote version
+ chan->name = channel;
+ chan->age = TS;
+ chan->ClearInvites();
+
+ CommandFJoin::RemoveStatus(chan);
+
+ // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
+ // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
+ // deleted later) as soon as the permchan mode is removed from them.
+ if (ServerInstance->FindChan(channel) == NULL)
+ {
+ chan = new Channel(channel, TS);
+ }
}
}
- // The silent case here is ourTS == TS, we don't need to remove modes here, just to merge them later on.
}
- /* First up, apply their modes if they won the TS war */
+ /* First up, apply their channel modes if they won the TS war */
if (apply_other_sides_modes)
{
+ // Need to use a modestacker here due to maxmodes
+ irc::modestacker stack(true);
+ std::vector<std::string>::const_iterator paramit = params.begin() + 3;
+ const std::vector<std::string>::const_iterator lastparamit = ((params.size() > 3) ? (params.end() - 1) : params.end());
+ for (std::string::const_iterator i = params[2].begin(); i != params[2].end(); ++i)
+ {
+ ModeHandler* mh = ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL);
+ if (!mh)
+ continue;
+
+ std::string modeparam;
+ if ((paramit != lastparamit) && (mh->GetNumParams(true)))
+ {
+ modeparam = *paramit;
+ ++paramit;
+ }
+
+ stack.Push(*i, modeparam);
+ }
+
std::vector<std::string> modelist;
- modelist.push_back(channel);
- /* Remember, params[params.size() - 1] is userlist, and we don't want to apply *that* */
- modelist.insert(modelist.end(), params.begin()+2, params.end()-1);
- ServerInstance->Modes->Process(modelist, srcuser, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
+ // Mode parser needs to know what channel to act on.
+ modelist.push_back(params[0]);
+
+ while (stack.GetStackedLine(modelist))
+ {
- ServerInstance->Modes->Process(modelist, srcuser, true);
++ ServerInstance->Modes->Process(modelist, srcuser, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
+ modelist.erase(modelist.begin() + 1, modelist.end());
+ }
-
- ServerInstance->Modes->Process(modelist, srcuser, true);
}
- /* Now, process every 'modes,nick' pair */
+ irc::modestacker modestack(true);
+ TreeSocket* src_socket = Utils->FindServer(srcuser->server)->GetSocket();
+
+ /* Now, process every 'modes,uuid' pair */
+ irc::tokenstream users(*params.rbegin());
+ std::string item;
+ irc::modestacker* modestackptr = (apply_other_sides_modes ? &modestack : NULL);
while (users.GetToken(item))
{
- const char* usr = item.c_str();
- if (usr && *usr)
- {
- const char* unparsedmodes = usr;
- std::string modes;
-
-
- /* Iterate through all modes for this user and check they are valid. */
- while ((*unparsedmodes) && (*unparsedmodes != ','))
- {
- ModeHandler *mh = ServerInstance->Modes->FindMode(*unparsedmodes, MODETYPE_CHANNEL);
- if (!mh)
- {
- ServerInstance->Logs->Log("m_spanningtree", SPARSE, "Unrecognised mode %c, dropping link", *unparsedmodes);
- return CMD_INVALID;
- }
-
- modes += *unparsedmodes;
- usr++;
- unparsedmodes++;
- }
+ if (!ProcessModeUUIDPair(item, src_socket, chan, modestackptr))
+ return CMD_INVALID;
+ }
- /* Advance past the comma, to the nick */
- usr++;
+ /* Flush mode stacker if we lost the FJOIN or had equal TS */
+ if (apply_other_sides_modes)
+ CommandFJoin::ApplyModeStack(srcuser, chan, modestack);
- /* Check the user actually exists */
- who = ServerInstance->FindUUID(usr);
- if (who)
- {
- /* Check that the user's 'direction' is correct */
- TreeServer* route_back_again = Utils->BestRouteTo(who->server);
- if ((!route_back_again) || (route_back_again->GetSocket() != src_socket))
- continue;
+ return CMD_SUCCESS;
+}
- /* Add any modes this user had to the mode stack */
- for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
- modestack.Push(*x, who->nick);
+bool CommandFJoin::ProcessModeUUIDPair(const std::string& item, TreeSocket* src_socket, Channel* chan, irc::modestacker* modestack)
+{
+ std::string::size_type comma = item.find(',');
- Channel::JoinUser(who, channel.c_str(), true, "", route_back_again->bursting, TS);
- }
- else
- {
- ServerInstance->Logs->Log("m_spanningtree",SPARSE, "Ignored nonexistant user %s in fjoin to %s (probably quit?)", usr, channel.c_str());
- continue;
- }
- }
+ // Comma not required anymore if the user has no modes
+ std::string uuid = ((comma == std::string::npos) ? item : item.substr(comma+1));
+ User* who = ServerInstance->FindUUID(uuid);
+ if (!who)
+ {
+ // Probably KILLed, ignore
+ return true;
}
- /* Flush mode stacker if we lost the FJOIN or had equal TS */
- if (apply_other_sides_modes)
+ /* Check that the user's 'direction' is correct */
+ TreeServer* route_back_again = Utils->BestRouteTo(who->server);
+ if ((!route_back_again) || (route_back_again->GetSocket() != src_socket))
{
- parameterlist stackresult;
- stackresult.push_back(channel);
+ return true;
+ }
- while (modestack.GetStackedLine(stackresult))
+ /* Check if the user received at least one mode */
+ if ((modestack) && (comma > 0) && (comma != std::string::npos))
+ {
+ /* Iterate through the modes and see if they are valid here, if so, apply */
+ std::string::const_iterator commait = item.begin()+comma;
+ for (std::string::const_iterator i = item.begin(); i != commait; ++i)
{
- ServerInstance->SendMode(stackresult, srcuser);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
+ if (!ServerInstance->Modes->FindMode(*i, MODETYPE_CHANNEL))
+ {
+ ServerInstance->SNO->WriteToSnoMask('d', "Unrecognised mode '%c' for a user in FJOIN, dropping link", *i);
+ return false;
+ }
+
+ /* Add any modes this user had to the mode stack */
+ modestack->Push(*i, who->nick);
}
}
- return CMD_SUCCESS;
+
+ chan->ForceJoin(who, NULL, route_back_again->bursting);
+ return true;
}
-void CommandFJoin::RemoveStatus(User* srcuser, parameterlist ¶ms)
+void CommandFJoin::RemoveStatus(Channel* c)
{
- if (params.size() < 1)
- return;
-
- Channel* c = ServerInstance->FindChan(params[0]);
+ irc::modestacker stack(false);
- if (c)
+ for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter)
{
- irc::modestacker stack(false);
- parameterlist stackresult;
- stackresult.push_back(c->name);
+ ModeHandler* mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
+
+ /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack,
+ * rather than applied immediately. Module unloads require this to be done immediately,
+ * for this function we require tidyness instead. Fixes bug #493
+ */
+ if (mh)
+ mh->RemoveMode(c, stack);
+ }
- for (char modeletter = 'A'; modeletter <= 'z'; ++modeletter)
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
+ ApplyModeStack(ServerInstance->FakeClient, c, stack);
+}
- /* Passing a pointer to a modestacker here causes the mode to be put onto the mode stack,
- * rather than applied immediately. Module unloads require this to be done immediately,
- * for this function we require tidyness instead. Fixes bug #493
- */
- if (mh)
- mh->RemoveMode(c, &stack);
- }
+void CommandFJoin::ApplyModeStack(User* srcuser, Channel* c, irc::modestacker& stack)
+{
+ parameterlist stackresult;
+ stackresult.push_back(c->name);
- while (stack.GetStackedLine(stackresult))
- {
- ServerInstance->SendMode(stackresult, srcuser);
- stackresult.erase(stackresult.begin() + 1, stackresult.end());
- }
+ while (stack.GetStackedLine(stackresult))
+ {
+ ServerInstance->Modes->Process(stackresult, srcuser, ModeParser::MODE_LOCALONLY);
+ stackresult.erase(stackresult.begin() + 1, stackresult.end());
}
}
-
*/
-/* $ModDesc: Provides a spanning tree server link protocol */
-
#include "inspircd.h"
#include "socket.h"
#include "xline.h"
+#include "iohook.h"
-#include "cachetimer.h"
#include "resolvers.h"
#include "main.h"
#include "utils.h"
#include "protocolinterface.h"
ModuleSpanningTree::ModuleSpanningTree()
+ : commands(NULL), DNS(this, "DNS")
{
- Utils = new SpanningTreeUtilities(this);
- commands = new SpanningTreeCommands(this);
- RefreshTimer = NULL;
}
SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
- : rconnect(module, module->Utils), rsquit(module, module->Utils),
+ : rconnect(module), rsquit(module), map(module),
svsjoin(module), svspart(module), svsnick(module), metadata(module),
- uid(module), opertype(module), fjoin(module), fmode(module), ftopic(module),
- fhost(module), fident(module), fname(module)
+ uid(module), opertype(module), fjoin(module), ijoin(module), resync(module),
+ fmode(module), ftopic(module), fhost(module), fident(module), fname(module),
+ away(module), addline(module), delline(module), encap(module), idle(module),
+ nick(module), ping(module), pong(module), push(module), save(module),
+ server(module), squit(module), snonotice(module), version(module),
+ burst(module), endburst(module)
{
}
void ModuleSpanningTree::init()
{
+ ServerInstance->SNO->EnableSnomask('l', "LINK");
+
+ Utils = new SpanningTreeUtilities(this);
+ Utils->TreeRoot = new TreeServer;
+ commands = new SpanningTreeCommands(this);
ServerInstance->Modules->AddService(commands->rconnect);
ServerInstance->Modules->AddService(commands->rsquit);
- ServerInstance->Modules->AddService(commands->svsjoin);
- ServerInstance->Modules->AddService(commands->svspart);
- ServerInstance->Modules->AddService(commands->svsnick);
- ServerInstance->Modules->AddService(commands->metadata);
- ServerInstance->Modules->AddService(commands->uid);
- ServerInstance->Modules->AddService(commands->opertype);
- ServerInstance->Modules->AddService(commands->fjoin);
- ServerInstance->Modules->AddService(commands->fmode);
- ServerInstance->Modules->AddService(commands->ftopic);
- ServerInstance->Modules->AddService(commands->fhost);
- ServerInstance->Modules->AddService(commands->fident);
- ServerInstance->Modules->AddService(commands->fname);
- RefreshTimer = new CacheRefreshTimer(Utils);
- ServerInstance->Timers->AddTimer(RefreshTimer);
-
- Implementation eventlist[] =
- {
- I_OnPreCommand, I_OnGetServerDescription, I_OnUserInvite, I_OnPostTopicChange,
- I_OnWallops, I_OnUserNotice, I_OnUserMessage, I_OnBackgroundTimer, I_OnUserJoin,
- I_OnChangeHost, I_OnChangeName, I_OnChangeIdent, I_OnUserPart, I_OnUnloadModule,
- I_OnUserQuit, I_OnUserPostNick, I_OnUserKick, I_OnRemoteKill, I_OnRehash, I_OnPreRehash,
- I_OnOper, I_OnAddLine, I_OnDelLine, I_OnMode, I_OnLoadModule, I_OnStats,
- I_OnSetAway, I_OnPostCommand, I_OnUserConnect, I_OnAcceptConnection
- };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ ServerInstance->Modules->AddService(commands->map);
delete ServerInstance->PI;
- ServerInstance->PI = new SpanningTreeProtocolInterface(Utils);
+ ServerInstance->PI = new SpanningTreeProtocolInterface;
loopCall = false;
// update our local user count
- Utils->TreeRoot->SetUserCount(ServerInstance->Users->local_users.size());
+ Utils->TreeRoot->UserCount = ServerInstance->Users->local_users.size();
}
void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
{
Parent = Current->GetParent()->GetName();
}
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+
+ const TreeServer::ChildServers& children = Current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
- if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName()))))
+ TreeServer* server = *i;
+ if ((server->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(server->GetName()))))
{
- if (IS_OPER(user))
+ if (user->IsOper())
{
- ShowLinks(Current->GetChild(q),user,hops+1);
+ ShowLinks(server, user, hops+1);
}
}
else
{
- ShowLinks(Current->GetChild(q),user,hops+1);
+ ShowLinks(server, user, hops+1);
}
}
/* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
- if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!IS_OPER(user)))
+ if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!user->IsOper()))
return;
/* Or if the server is hidden and they're not an oper */
- else if ((Current->Hidden) && (!IS_OPER(user)))
+ else if ((Current->Hidden) && (!user->IsOper()))
return;
- std::string servername = Current->GetName();
- user->WriteNumeric(364, "%s %s %s :%d %s", user->nick.c_str(), servername.c_str(),
- (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
- (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
+ user->WriteNumeric(364, "%s %s %s :%d %s", user->nick.c_str(), Current->GetName().c_str(),
+ (Utils->FlatLinks && (!user->IsOper())) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
+ (Utils->FlatLinks && (!user->IsOper())) ? 0 : hops,
Current->GetDesc().c_str());
}
-int ModuleSpanningTree::CountServs()
-{
- return Utils->serverlist.size();
-}
-
void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
{
ShowLinks(Utils->TreeRoot,user,0);
user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
- return;
}
std::string ModuleSpanningTree::TimeToStr(time_t secs)
{
TreeServer *s = i->second;
- if (s->GetSocket() && s->GetSocket()->GetLinkState() == DYING)
+ // Skip myself
+ if (s->IsRoot())
+ continue;
+
+ if (s->GetSocket()->GetLinkState() == DYING)
{
s->GetSocket()->Close();
goto restart;
}
- // Fix for bug #792, do not ping servers that are not connected yet!
- // Remote servers have Socket == NULL and local connected servers have
- // Socket->LinkState == CONNECTED
- if (s->GetSocket() && s->GetSocket()->GetLinkState() != CONNECTED)
+ // Do not ping servers that are not fully connected yet!
+ // Servers which are connected to us have IsLocal() == true and if they're fully connected
+ // then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected.
+ if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED)
continue;
// Now do PING checks on all servers
- TreeServer *mts = Utils->BestRouteTo(s->GetID());
-
- if (mts)
+ // Only ping if this server needs one
+ if (curtime >= s->NextPingTime())
{
- // Only ping if this server needs one
- if (curtime >= s->NextPingTime())
+ // And if they answered the last
+ if (s->AnsweredLastPing())
{
- // And if they answered the last
- if (s->AnsweredLastPing())
- {
- // They did, send a ping to them
- s->SetNextPingTime(curtime + Utils->PingFreq);
- TreeSocket *tsock = mts->GetSocket();
-
- // ... if we can find a proper route to them
- if (tsock)
- {
- tsock->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " +
- ServerInstance->Config->GetSID() + " " + s->GetID());
- s->LastPingMsec = ts;
- }
- }
- else
+ // They did, send a ping to them
+ s->SetNextPingTime(curtime + Utils->PingFreq);
+ s->GetSocket()->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " + s->GetID());
+ s->LastPingMsec = ts;
+ }
+ else
+ {
+ // They didn't answer the last ping, if they are locally connected, get rid of them.
+ if (s->IsLocal())
{
- // They didn't answer the last ping, if they are locally connected, get rid of them.
- TreeSocket *sock = s->GetSocket();
- if (sock)
- {
- sock->SendError("Ping timeout");
- sock->Close();
- goto restart;
- }
+ TreeSocket* sock = s->GetSocket();
+ sock->SendError("Ping timeout");
+ sock->Close();
+ goto restart;
}
}
+ }
- // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
- if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
- {
- /* The server hasnt responded, send a warning to opers */
- std::string servername = s->GetName();
- ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", servername.c_str(), Utils->PingWarnTime);
- s->Warned = true;
- }
+ // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
+ if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
+ {
+ /* The server hasnt responded, send a warning to opers */
+ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime);
+ s->Warned = true;
}
}
}
return;
}
- QueryType start_type = DNS_QUERY_AAAA;
+ DNS::QueryType start_type = DNS::QUERY_AAAA;
if (strchr(x->IPAddr.c_str(),':'))
{
in6_addr n;
if (ipvalid)
{
/* Gave a hook, but it wasnt one we know */
- TreeSocket* newsocket = new TreeSocket(Utils, x, y, x->IPAddr);
+ TreeSocket* newsocket = new TreeSocket(x, y, x->IPAddr);
if (newsocket->GetFd() > -1)
{
/* Handled automatically on success */
ServerInstance->GlobalCulls.AddItem(newsocket);
}
}
+ else if (!DNS)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and m_dns.so is not loaded, unable to resolve.", x->Name.c_str());
+ }
else
{
+ ServernameResolver* snr = new ServernameResolver(*DNS, x->IPAddr, x, start_type, y);
try
{
- bool cached = false;
- ServernameResolver* snr = new ServernameResolver(Utils, x->IPAddr, x, cached, start_type, y);
- ServerInstance->AddResolver(snr, cached);
+ DNS->Process(snr);
}
- catch (ModuleException& e)
+ catch (DNS::Exception& e)
{
+ delete snr;
ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
ConnectServer(y, false);
}
TreeServer* found = Utils->FindServerMask(parameters[0]);
if (found)
{
- std::string Version = found->GetVersion();
- user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
if (found == Utils->TreeRoot)
{
- ServerInstance->Config->Send005(user);
+ // Pass to default VERSION handler.
+ return MOD_RES_PASSTHRU;
}
+ std::string Version = found->GetVersion();
+ user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
}
else
{
*/
void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
{
- char text[MAXBUF];
- va_list argsPtr;
-
- va_start(argsPtr, format);
- vsnprintf(text, MAXBUF, format, argsPtr);
- va_end(argsPtr);
+ std::string text;
+ VAFORMAT(text, format, format);
if (IS_LOCAL(user))
- user->WriteServ("NOTICE %s :%s", user->nick.c_str(), text);
+ user->WriteNotice(text);
else
ServerInstance->PI->SendUserNotice(user, text);
}
}
else
{
- std::string servername = CheckDupe->GetParent()->GetName();
- RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), servername.c_str());
+ RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str());
return MOD_RES_DENY;
}
}
return MOD_RES_DENY;
}
+void ModuleSpanningTree::On005Numeric(std::map<std::string, std::string>& tokens)
+{
+ tokens["MAP"];
+}
+
void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
{
TreeServer* s = Utils->FindServer(servername);
{
if (IS_LOCAL(source))
{
- parameterlist params;
+ CmdBuilder params(source, "INVITE");
params.push_back(dest->uuid);
params.push_back(channel->name);
params.push_back(ConvToStr(expiry));
- Utils->DoOneToMany(source->uuid,"INVITE",params);
+ params.Broadcast();
}
}
if (!IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(chan->name);
- params.push_back(":"+topic);
- Utils->DoOneToMany(user->uuid,"TOPIC",params);
+ CommandFTopic::Builder(user, chan).Broadcast();
}
-void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
+void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype)
{
- if (IS_LOCAL(user))
- {
- parameterlist params;
- params.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"WALLOPS",params);
- }
-}
-
-void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
-{
- /* Server origin */
- if (user == NULL)
- return;
-
- if (target_type == TYPE_USER)
- {
- User* d = (User*)dest;
- if (!IS_LOCAL(d) && IS_LOCAL(user))
- {
- parameterlist params;
- params.push_back(d->uuid);
- params.push_back(":"+text);
- Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
- }
- }
- else if (target_type == TYPE_CHANNEL)
- {
- if (IS_LOCAL(user))
- {
- Channel *c = (Channel*)dest;
- if (c)
- {
- std::string cname = c->name;
- if (status)
- cname = status + cname;
- TreeServerList list;
- Utils->GetListOfServersForChannel(c,list,status,exempt_list);
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (Sock)
- Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
- }
- }
- }
- }
- else if (target_type == TYPE_SERVER)
- {
- if (IS_LOCAL(user))
- {
- char* target = (char*)dest;
- parameterlist par;
- par.push_back(target);
- par.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"NOTICE",par);
- }
- }
-}
-
-void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
-{
- /* Server origin */
- if (user == NULL)
+ if (!IS_LOCAL(user))
return;
+ const char* message_type = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
if (target_type == TYPE_USER)
{
- // route private messages which are targetted at clients only to the server
- // which needs to receive them
- User* d = (User*)dest;
- if (!IS_LOCAL(d) && (IS_LOCAL(user)))
+ User* d = (User*) dest;
+ if (!IS_LOCAL(d))
{
- parameterlist params;
+ CmdBuilder params(user, message_type);
params.push_back(d->uuid);
- params.push_back(":"+text);
- Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
+ params.push_last(text);
+ params.Unicast(d);
}
}
else if (target_type == TYPE_CHANNEL)
{
- if (IS_LOCAL(user))
- {
- Channel *c = (Channel*)dest;
- if (c)
- {
- std::string cname = c->name;
- if (status)
- cname = status + cname;
- TreeServerList list;
- Utils->GetListOfServersForChannel(c,list,status,exempt_list);
- for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
- {
- TreeSocket* Sock = i->second->GetSocket();
- if (Sock)
- Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
- }
- }
- }
+ Utils->SendChannelMessage(user->uuid, (Channel*)dest, text, status, exempt_list, message_type);
}
else if (target_type == TYPE_SERVER)
{
- if (IS_LOCAL(user))
- {
- char* target = (char*)dest;
- parameterlist par;
- par.push_back(target);
- par.push_back(":"+text);
- Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
- }
+ char* target = (char*) dest;
+ CmdBuilder par(user, message_type);
+ par.push_back(target);
+ par.push_last(text);
+ par.Broadcast();
}
}
if (user->quitting)
return;
- parameterlist params;
- params.push_back(user->uuid);
- params.push_back(ConvToStr(user->age));
- params.push_back(user->nick);
- params.push_back(user->host);
- params.push_back(user->dhost);
- params.push_back(user->ident);
- params.push_back(user->GetIPString());
- params.push_back(ConvToStr(user->signon));
- params.push_back("+"+std::string(user->FormatModes(true)));
- params.push_back(":"+user->fullname);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
+ CommandUID::Builder(user).Broadcast();
- if (IS_OPER(user))
- {
- params.clear();
- params.push_back(user->oper->name);
- Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
- }
+ if (user->IsOper())
+ CommandOpertype::Builder(user).Broadcast();
for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
{
ServerInstance->PI->SendMetaData(user, item->name, value);
}
- Utils->TreeRoot->SetUserCount(1); // increment by 1
+ Utils->TreeRoot->UserCount++;
}
-void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts)
{
// Only do this for local users
- if (IS_LOCAL(memb->user))
+ if (!IS_LOCAL(memb->user))
+ return;
+
+ if (created_by_local)
{
- parameterlist params;
- // set up their permissions and the channel TS with FJOIN.
- // All users are FJOINed now, because a module may specify
- // new joining permissions for the user.
+ CmdBuilder params("FJOIN");
params.push_back(memb->chan->name);
params.push_back(ConvToStr(memb->chan->age));
- params.push_back(std::string("+") + memb->chan->ChanModes(true));
- params.push_back(memb->modes+","+memb->user->uuid);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
+ params.push_raw(" +").push_raw(memb->chan->ChanModes(true));
+ params.push(memb->modes).push_raw(',').push_raw(memb->user->uuid);
+ params.Broadcast();
+ }
+ else
+ {
+ CmdBuilder params(memb->user, "IJOIN");
+ params.push_back(memb->chan->name);
+ if (!memb->modes.empty())
+ {
+ params.push_back(ConvToStr(memb->chan->age));
+ params.push_back(memb->modes);
+ }
+ params.Broadcast();
}
}
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(newhost);
- Utils->DoOneToMany(user->uuid,"FHOST",params);
+ CmdBuilder(user, "FHOST").push(newhost).Broadcast();
}
void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(gecos);
- Utils->DoOneToMany(user->uuid,"FNAME",params);
+ CmdBuilder(user, "FNAME").push(gecos).Broadcast();
}
void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
if ((user->registered != REG_ALL) || (!IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(ident);
- Utils->DoOneToMany(user->uuid,"FIDENT",params);
+ CmdBuilder(user, "FIDENT").push(ident).Broadcast();
}
void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
{
if (IS_LOCAL(memb->user))
{
- parameterlist params;
+ CmdBuilder params(memb->user, "PART");
params.push_back(memb->chan->name);
if (!partmessage.empty())
- params.push_back(":"+partmessage);
- Utils->DoOneToMany(memb->user->uuid,"PART",params);
+ params.push_last(partmessage);
+ params.Broadcast();
}
}
{
if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
{
- parameterlist params;
-
if (oper_message != reason)
- {
- params.push_back(":"+oper_message);
- Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
- }
- params.clear();
- params.push_back(":"+reason);
- Utils->DoOneToMany(user->uuid,"QUIT",params);
+ ServerInstance->PI->SendMetaData(user, "operquit", oper_message);
+
+ CmdBuilder(user, "QUIT").push_last(reason).Broadcast();
}
// Regardless, We need to modify the user Counts..
TreeServer* SourceServer = Utils->FindServer(user->server);
if (SourceServer)
{
- SourceServer->SetUserCount(-1); // decrement by 1
+ SourceServer->UserCount--;
}
}
{
if (IS_LOCAL(user))
{
- parameterlist params;
+ CmdBuilder params(user, "NICK");
params.push_back(user->nick);
/** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
user->age = ServerInstance->Time();
params.push_back(ConvToStr(user->age));
- Utils->DoOneToMany(user->uuid,"NICK",params);
+ params.Broadcast();
}
else if (!loopCall && user->nick == user->uuid)
{
- parameterlist params;
+ CmdBuilder params("SAVE");
params.push_back(user->uuid);
params.push_back(ConvToStr(user->age));
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
+ params.Broadcast();
}
}
void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
{
- parameterlist params;
+ if ((!IS_LOCAL(source) || source != ServerInstance->FakeClient))
+ return;
+
+ CmdBuilder params(source, "KICK");
params.push_back(memb->chan->name);
params.push_back(memb->user->uuid);
- params.push_back(":"+reason);
- if (IS_LOCAL(source))
- {
- Utils->DoOneToMany(source->uuid,"KICK",params);
- }
- else if (source == ServerInstance->FakeClient)
- {
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params);
- }
-}
-
-void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason)
-{
- if (!IS_LOCAL(source))
- return; // Only start routing if we're origin.
-
- ServerInstance->OperQuit.set(dest, operreason);
- parameterlist params;
- params.push_back(":"+operreason);
- Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
- params.clear();
- params.push_back(dest->uuid);
- params.push_back(":"+reason);
- Utils->DoOneToMany(source->uuid,"KILL",params);
+ params.push_last(reason);
+ params.Broadcast();
}
void ModuleSpanningTree::OnPreRehash(User* user, const std::string ¶meter)
if (loopCall)
return; // Don't generate a REHASH here if we're in the middle of processing a message that generated this one
- ServerInstance->Logs->Log("remoterehash", DEBUG, "called with param %s", parameter.c_str());
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnPreRehash called with param %s", parameter.c_str());
// Send out to other servers
if (!parameter.empty() && parameter[0] != '-')
{
- parameterlist params;
+ CmdBuilder params((user ? user->uuid : ServerInstance->Config->GetSID()), "REHASH");
params.push_back(parameter);
- Utils->DoOneToAllButSender(user ? user->uuid : ServerInstance->Config->GetSID(), "REHASH", params, user ? user->server : ServerInstance->Config->ServerName);
+ params.Forward(user ? Utils->BestRouteTo(user->server) : NULL);
}
}
void ModuleSpanningTree::OnUnloadModule(Module* mod)
{
+ if (!Utils)
+ return;
ServerInstance->PI->SendMetaData(NULL, "modules", "-" + mod->ModuleSourceFile);
- unsigned int items = Utils->TreeRoot->ChildCount();
- for(unsigned int x = 0; x < items; x++)
+ // Close all connections which use an IO hook provided by this module
+ const TreeServer::ChildServers& list = Utils->TreeRoot->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = list.begin(); i != list.end(); ++i)
{
- TreeServer* srv = Utils->TreeRoot->GetChild(x);
- TreeSocket* sock = srv->GetSocket();
- if (sock && sock->GetIOHook() == mod)
+ TreeSocket* sock = (*i)->GetSocket();
+ if (sock && sock->GetIOHook() && sock->GetIOHook()->creator == mod)
{
sock->SendError("SSL module unloaded");
sock->Close();
}
}
- if (sock->GetIOHook() == mod)
+
+ for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i)
+ {
+ TreeSocket* sock = i->first;
++ if (sock->GetIOHook() && sock->GetIOHook()->creator == mod)
+ sock->Close();
+ }
}
// note: the protocol does not allow direct umode +o except
{
if (user->registered != REG_ALL || !IS_LOCAL(user))
return;
- parameterlist params;
- params.push_back(opertype);
- Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
+ CommandOpertype::Builder(user).Broadcast();
}
void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
{
- if (!x->IsBurstable() || loopCall)
+ if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(x->type);
- params.push_back(x->Displayable());
- params.push_back(ServerInstance->Config->ServerName);
- params.push_back(ConvToStr(x->set_time));
- params.push_back(ConvToStr(x->duration));
- params.push_back(":" + x->reason);
-
if (!user)
- {
- /* Server-set lines */
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
- }
- else if (IS_LOCAL(user))
- {
- /* User-set lines */
- Utils->DoOneToMany(user->uuid, "ADDLINE", params);
- }
+ user = ServerInstance->FakeClient;
+
+ CommandAddLine::Builder(x, user).Broadcast();
}
void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
{
- if (!x->IsBurstable() || loopCall)
+ if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
return;
- parameterlist params;
- params.push_back(x->type);
- params.push_back(x->Displayable());
-
if (!user)
- {
- /* Server-unset lines */
- Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
- }
- else if (IS_LOCAL(user))
- {
- /* User-unset lines */
- Utils->DoOneToMany(user->uuid, "DELLINE", params);
- }
-}
+ user = ServerInstance->FakeClient;
-void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const parameterlist &text, const std::vector<TranslateType> &translate)
-{
- if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
- {
- parameterlist params;
- std::string output_text;
-
- ServerInstance->Parser->TranslateUIDs(translate, text, output_text);
-
- if (target_type == TYPE_USER)
- {
- User* u = (User*)dest;
- params.push_back(u->uuid);
- params.push_back(output_text);
- Utils->DoOneToMany(user->uuid, "MODE", params);
- }
- else
- {
- Channel* c = (Channel*)dest;
- params.push_back(c->name);
- params.push_back(ConvToStr(c->age));
- params.push_back(output_text);
- Utils->DoOneToMany(user->uuid, "FMODE", params);
- }
- }
+ CmdBuilder params(user, "DELLINE");
+ params.push_back(x->type);
+ params.push_back(x->Displayable());
+ params.Broadcast();
}
ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
{
if (IS_LOCAL(user))
- {
- parameterlist params;
- if (!awaymsg.empty())
- {
- params.push_back(ConvToStr(user->awaytime));
- params.push_back(":" + awaymsg);
- }
- Utils->DoOneToMany(user->uuid, "AWAY", params);
- }
+ CommandAway::Builder(user, awaymsg).Broadcast();
return MOD_RES_PASSTHRU;
}
void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate)
{
TreeSocket* s = (TreeSocket*)opaque;
- std::string output_text;
-
- ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text);
+ std::string output_text = CommandParser::TranslateUIDs(translate, modeline);
if (target)
{
if (u)
s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
else if (c)
- s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
+ s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+ConvToStr(c->age)+" "+extname+" :"+extdata);
else if (!target)
s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
}
CullResult ModuleSpanningTree::cull()
{
- Utils->cull();
- ServerInstance->Timers->DelTimer(RefreshTimer);
+ if (Utils)
+ Utils->cull();
return this->Module::cull();
}
*/
-#ifndef M_SPANNINGTREE_UTILS_H
-#define M_SPANNINGTREE_UTILS_H
+#pragma once
#include "inspircd.h"
+#include "cachetimer.h"
/* Foward declarations */
class TreeServer;
class Autoconnect;
class ModuleSpanningTree;
class SpanningTreeUtilities;
+class CmdBuilder;
+
+extern SpanningTreeUtilities* Utils;
/* This hash_map holds the hash equivalent of the server
* tree, used for rapid linear lookups.
*/
-#ifdef HASHMAP_DEPRECATED
- typedef nspace::hash_map<std::string, TreeServer*, nspace::insensitive, irc::StrHashComp> server_hash;
-#else
- typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<std::string>, irc::StrHashComp> server_hash;
-#endif
-
-typedef std::map<TreeServer*,TreeServer*> TreeServerList;
+typedef TR1NS::unordered_map<std::string, TreeServer*, irc::insensitive, irc::StrHashComp> server_hash;
/** Contains helper functions and variables for this module,
* and keeps them out of the global namespace
*/
class SpanningTreeUtilities : public classbase
{
+ CacheRefreshTimer RefreshTimer;
+
public:
+ typedef std::set<TreeSocket*> TreeSocketSet;
+ typedef std::map<TreeSocket*, std::pair<std::string, int> > TimeoutList;
/** Creator module
*/
server_hash sidlist;
/** List of all outgoing sockets and their timeouts
*/
- std::map<TreeSocket*, std::pair<std::string, int> > timeoutlist;
+ TimeoutList timeoutlist;
/** Holds the data from the <link> tags in the conf
*/
std::vector<reference<Link> > LinkBlocks;
*/
~SpanningTreeUtilities();
- void RouteCommand(TreeServer*, const std::string&, const parameterlist&, User*);
+ void RouteCommand(TreeServer* origin, CommandBase* cmd, const parameterlist& parameters, User* user);
/** Send a message from this server to one other local or remote
*/
- bool DoOneToOne(const std::string &prefix, const std::string &command, const parameterlist ¶ms, const std::string& target);
+ bool DoOneToOne(const CmdBuilder& params, const std::string& target);
/** Send a message from this server to all but one other, local or remote
*/
- bool DoOneToAllButSender(const std::string &prefix, const std::string &command, const parameterlist ¶ms, const std::string& omit);
+ void DoOneToAllButSender(const CmdBuilder& params, TreeServer* omit);
/** Send a message from this server to all others
*/
- bool DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist ¶ms);
+ void DoOneToMany(const CmdBuilder& params);
/** Read the spanningtree module's tags from the config file
*/
void ReadConfiguration();
- /** Add a server to the server list for GetListOfServersForChannel
+ /** Handle nick collision
*/
- void AddThisServer(TreeServer* server, TreeServerList &list);
+ int DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid);
/** Compile a list of servers which contain members of channel c
*/
- void GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list);
+ void GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list);
/** Find a server by name
*/
/** Refresh the IP cache used for allowing inbound connections
*/
void RefreshIPCache();
+
+ /** Sends a PRIVMSG or a NOTICE to a channel obeying an exempt list and an optional prefix
+ */
+ void SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const CUList& exempt_list, const char* message_type, TreeSocket* omit = NULL);
};
-#endif
+inline void SpanningTreeUtilities::DoOneToMany(const CmdBuilder& params)
+{
+ DoOneToAllButSender(params, NULL);
+}
#include "inspircd.h"
-/* $ModDesc: Provides support for USERIP command */
-
/** Handle /USERIP
*/
class CommandUserip : public Command
std::string retbuf = "340 " + user->nick + " :";
int nicks = 0;
bool checked_privs = false;
- bool has_privs;
+ bool has_privs = false;
for (int i = 0; i < (int)parameters.size(); i++)
{
continue;
}
- retbuf = retbuf + u->nick + (IS_OPER(u) ? "*" : "") + "=";
- if (IS_AWAY(u))
+ retbuf = retbuf + u->nick + (u->IsOper() ? "*" : "") + "=";
+ if (u->IsAway())
retbuf += "-";
else
retbuf += "+";
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ServerInstance->Modules->AddService(cmd);
- Implementation eventlist[] = { I_On005Numeric };
- ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
- }
-
- virtual void On005Numeric(std::string &output)
- {
- output = output + " USERIP";
}
- virtual ~ModuleUserIP()
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
+ tokens["USERIP"];
}
- virtual Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("Provides support for USERIP command",VF_VENDOR);
}
-
};
MODULE_INIT(ModuleUserIP)
-
new_m &= ~FD_WANT_READ_MASK;
if (change & FD_WANT_WRITE_MASK)
new_m &= ~FD_WANT_WRITE_MASK;
-
+
// if adding a trial read/write, insert it into the set
if (change & FD_TRIAL_NOTE_MASK && !(old_m & FD_TRIAL_NOTE_MASK))
trials.insert(eh->GetFd());
kbitpersec_in = in_kbit / 1024;
kbitpersec_out = out_kbit / 1024;
}
+
+ std::string SocketEngine::LastError()
+ {
+ #ifndef _WIN32
+ return strerror(errno);
+ #else
+ char szErrorString[500];
+ DWORD dwErrorCode = WSAGetLastError();
+ if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)szErrorString, _countof(szErrorString), NULL) == 0)
+ sprintf_s(szErrorString, _countof(szErrorString), "Error code: %u", dwErrorCode);
+ return szErrorString;
+ #endif
+ }
+
+ std::string SocketEngine::GetError(int errnum)
+ {
+ #ifndef _WIN32
+ return strerror(errnum);
+ #else
+ WSASetLastError(errnum);
+ return LastError();
+ #endif
+ }
}
else
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+ ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
// This is not a maximum, just a hint at the eventual number of sockets that may be polled.
if (EngineHandle == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now.");
std::cout << "ERROR: Could not initialize epoll socket engine: " << strerror(errno) << std::endl;
std::cout << "ERROR: Your kernel probably does not have the proper features. This is a fatal error, exiting now." << std::endl;
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+ ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
ref = new EventHandler* [GetMaxFds()];
int fd = eh->GetFd();
if ((fd < 0) || (fd > GetMaxFds() - 1))
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
return false;
}
if (ref[fd])
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
return false;
}
int i = epoll_ctl(EngineHandle, EPOLL_CTL_ADD, fd, &ev);
if (i < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Error adding fd: %d to socketengine: %s", fd, strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Error adding fd: %d to socketengine: %s", fd, strerror(errno));
return false;
}
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
ref[fd] = eh;
SocketEngine::SetEventMask(eh, event_mask);
int fd = eh->GetFd();
if ((fd < 0) || (fd > GetMaxFds() - 1))
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
return;
}
if (i < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"epoll_ctl can't remove socket: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "epoll_ctl can't remove socket: %s", strerror(errno));
}
ref[fd] = NULL;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
CurrentSetSize--;
}
EventHandler* eh = ref[events[j].data.fd];
if (!eh)
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Got event on unknown fd: %d", events[j].data.fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Got event on unknown fd: %d", events[j].data.fd);
epoll_ctl(EngineHandle, EPOLL_CTL_DEL, events[j].data.fd, &events[j]);
continue;
}
sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
if (MAX_DESCRIPTORS <= 0)
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+ ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
this->RecoverFromFork();
EngineHandle = kqueue();
if (EngineHandle == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
- ServerInstance->Logs->Log("SOCKET",DEFAULT, "ERROR: this is a fatal error, exiting now.");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features.");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: this is a fatal error, exiting now.");
std::cout << "ERROR: Could not initialize socket engine. Your kernel probably does not have the proper features." << std::endl;
std::cout << "ERROR: this is a fatal error, exiting now." << std::endl;
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+ ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
CurrentSetSize = 0;
}
int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
if (i == -1)
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to add fd: %d %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to add fd: %d %s",
fd, strerror(errno));
return false;
}
OnSetEvent(eh, 0, event_mask);
CurrentSetSize++;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
return true;
}
if ((fd < 0) || (fd > GetMaxFds() - 1))
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"DelFd() on invalid fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "DelFd() on invalid fd: %d", fd);
return;
}
if (j < 0)
{
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to remove fd: %d %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to remove fd: %d %s",
fd, strerror(errno));
}
CurrentSetSize--;
ref[fd] = NULL;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
void KQueueEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD, 0, 0, NULL);
int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
eh->GetFd(), strerror(errno));
}
}
EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
eh->GetFd(), strerror(errno));
}
}
EV_SET(&ke, eh->GetFd(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
int i = kevent(EngineHandle, &ke, 1, 0, 0, NULL);
if (i < 0) {
- ServerInstance->Logs->Log("SOCKET",DEFAULT,"Failed to mark for writing: %d %s",
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to mark for writing: %d %s",
eh->GetFd(), strerror(errno));
}
}
*/
-#include "inspircd.h"
-#include "exitcodes.h"
-
#ifndef SOCKETENGINE_POLL
#define SOCKETENGINE_POLL
#include <vector>
#include <string>
#include <map>
-#include "inspircd_config.h"
+#include "exitcodes.h"
#include "inspircd.h"
#include "socketengine.h"
#ifndef _WIN32
- #ifndef __USE_XOPEN
- #define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
- #endif
- #include <poll.h>
- #include <sys/poll.h>
+ # ifndef __USE_XOPEN
+ # define __USE_XOPEN /* fuck every fucking OS ever made. needed by poll.h to work.*/
+ # endif
+ # include <poll.h>
+ # include <sys/poll.h>
+ # include <sys/resource.h>
#else
- /* *grumble* */
- #define struct pollfd WSAPOLLFD
- #define poll WSAPoll
+ # define struct pollfd WSAPOLLFD
+ # define poll WSAPoll
#endif
class InspIRCd;
#endif
- #ifdef BSD
- #include <sys/sysctl.h>
- #else
- #include <ulimit.h>
- #endif
-
PollEngine::PollEngine()
{
CurrentSetSize = 0;
- #ifdef BSD
- int mib[2];
- size_t len;
-
- mib[0] = CTL_KERN;
- #ifdef KERN_MAXFILESPERPROC
- mib[1] = KERN_MAXFILESPERPROC;
- #else
- mib[1] = KERN_MAXFILES;
- #endif
- len = sizeof(MAX_DESCRIPTORS);
- sysctl(mib, 2, &MAX_DESCRIPTORS, &len, NULL, 0);
- #else
- int max = ulimit(4, 0);
- if (max > 0)
+ struct rlimit limits;
+ if (!getrlimit(RLIMIT_NOFILE, &limits))
{
- MAX_DESCRIPTORS = max;
+ MAX_DESCRIPTORS = limits.rlim_cur;
}
else
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets: %s", strerror(errno));
std::cout << "ERROR: Can't determine maximum number of open sockets: " << strerror(errno) << std::endl;
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+ ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
- #endif
ref = new EventHandler* [GetMaxFds()];
events = new struct pollfd[GetMaxFds()];
int fd = eh->GetFd();
if ((fd < 0) || (fd > GetMaxFds() - 1))
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "AddFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
return false;
}
if (fd_mappings.find(fd) != fd_mappings.end())
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Attempt to add duplicate fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Attempt to add duplicate fd: %d", fd);
return false;
}
events[index].fd = fd;
events[index].events = mask_to_poll(event_mask);
- ServerInstance->Logs->Log("SOCKET", DEBUG,"New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d (%d; index %d)", fd, events[fd].events, index);
SocketEngine::SetEventMask(eh, event_mask);
CurrentSetSize++;
return true;
std::map<int, unsigned int>::iterator it = fd_mappings.find(eh->GetFd());
if (it == fd_mappings.end())
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"SetEvents() on unknown fd: %d", eh->GetFd());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "SetEvents() on unknown fd: %d", eh->GetFd());
return;
}
int fd = eh->GetFd();
if ((fd < 0) || (fd > MAX_DESCRIPTORS))
{
- ServerInstance->Logs->Log("SOCKET", DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd out of range: (fd: %d, max: %d)", fd, GetMaxFds());
return;
}
std::map<int, unsigned int>::iterator it = fd_mappings.find(fd);
if (it == fd_mappings.end())
{
- ServerInstance->Logs->Log("SOCKET",DEBUG,"DelFd() on unknown fd: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DelFd() on unknown fd: %d", fd);
return;
}
CurrentSetSize--;
- ServerInstance->Logs->Log("SOCKET", DEBUG, "Remove file descriptor: %d (index: %d) "
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d (index: %d) "
"(Filled gap with: %d (index: %d))", fd, index, last_fd, last_index);
}
// whoops, deleted out from under us
continue;
}
-
+
if (events[index].revents & POLLOUT)
{
int mask = eh->GetEventMask();
#include <vector>
#include <string>
#include <map>
-#include "inspircd_config.h"
#include "inspircd.h"
#include "socketengine.h"
#include <port.h>
}
else
{
- ServerInstance->Logs->Log("SOCKET", DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
+ ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "ERROR: Can't determine maximum number of open sockets!");
std::cout << "ERROR: Can't determine maximum number of open sockets!" << std::endl;
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+ ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
EngineHandle = port_create();
if (EngineHandle == -1)
{
- ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: Could not initialize socket engine: %s", strerror(errno));
- ServerInstance->Logs->Log("SOCKET",SPARSE,"ERROR: This is a fatal error, exiting now.");
+ ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: Could not initialize socket engine: %s", strerror(errno));
+ ServerInstance->Logs->Log("SOCKET", LOG_SPARSE, "ERROR: This is a fatal error, exiting now.");
std::cout << "ERROR: Could not initialize socket engine: " << strerror(errno) << std::endl;
std::cout << "ERROR: This is a fatal error, exiting now." << std::endl;
- ServerInstance->Exit(EXIT_STATUS_SOCKETENGINE);
+ ServerInstance->QuickExit(EXIT_STATUS_SOCKETENGINE);
}
CurrentSetSize = 0;
SocketEngine::SetEventMask(eh, event_mask);
port_associate(EngineHandle, PORT_SOURCE_FD, fd, mask_to_events(event_mask), eh);
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
CurrentSetSize++;
return true;
}
CurrentSetSize--;
ref[fd] = NULL;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
int PortsEngine::DispatchEvents()
*/
-#include "inspircd_config.h"
-
#include "inspircd.h"
#include "socketengine.h"
CurrentSetSize++;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"New file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "New file descriptor: %d", fd);
return true;
}
if (fd == MaxFD)
--MaxFD;
- ServerInstance->Logs->Log("SOCKET",DEBUG,"Remove file descriptor: %d", fd);
+ ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "Remove file descriptor: %d", fd);
}
void SelectEngine::OnSetEvent(EventHandler* eh, int old_mask, int new_mask)
if (has_write)
{
WriteEvents++;
- SetEventMask(ev, ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
+ int newmask = (ev->GetEventMask() & ~(FD_WRITE_WILL_BLOCK | FD_WANT_SINGLE_WRITE));
+ this->OnSetEvent(ev, ev->GetEventMask(), newmask);
+ SetEventMask(ev, newmask);
ev->HandleEvent(EVENT_WRITE);
}
}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
#include "inspircd_win32wrapper.h"
#include "inspircd.h"
#include "configreader.h"
}
return 1;
}
-
+
return 0;
}
// optind++; // Trash this next argument, we won't be needing it.
par = ___argv[optind-1];
}
- }
+ }
// increment the argument for next time
// optind++;
{
if (__longopts[i].val == -1 || par == 0)
return 1;
-
+
return __longopts[i].val;
- }
+ }
break;
}
}
{
return dwErrorCode;
}
-
- #include "../src/modules/m_spanningtree/link.h"
- #include "modules/ssl.h"
- template class reference<Link>;
- template class reference<Autoconnect>;
- template class reference<ssl_cert>;
- template class reference<OperInfo>;