From b14ea4f051ad7ea2c5fd0951a5d005c0827ae27c Mon Sep 17 00:00:00 2001 From: James Lu Date: Fri, 3 Mar 2017 15:39:28 -0800 Subject: [PATCH 1/3] clientbot: send CAP LS before NICK/USER so that it consistently gets a response before connect Previously, SASL was failing on networks like freenode, as the connection completed before a CAP response was received. (cherry picked from commit 9420f21680058e50a8134eed7a050bb4abb9ae73) --- protocols/clientbot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/clientbot.py b/protocols/clientbot.py index 4516d79..17212c6 100644 --- a/protocols/clientbot.py +++ b/protocols/clientbot.py @@ -73,6 +73,8 @@ class ClientbotWrapperProtocol(Protocol): if sendpass: f('PASS %s' % sendpass) + f('CAP LS 302') + # This is a really gross hack to get the defined NICK/IDENT/HOST/GECOS. # But this connection stuff is done before any of the spawnClient stuff in # services_support fires. @@ -82,8 +84,6 @@ class ClientbotWrapperProtocol(Protocol): f('USER %s 8 * :%s' % (ident, # TODO: per net realnames or hostnames aren't implemented yet. conf.conf["bot"].get("realname", "PyLink Clientbot"))) - f('CAP LS 302') - # Note: clientbot clients are initialized with umode +i by default def spawnClient(self, nick, ident='unknown', host='unknown.host', realhost=None, modes={('i', None)}, server=None, ip='0.0.0.0', realname='', ts=None, opertype=None, From 84448e9803c8eaedb4aadfe1da9db16e9cbd0eb9 Mon Sep 17 00:00:00 2001 From: James Lu Date: Sat, 4 Mar 2017 23:54:16 -0800 Subject: [PATCH 2/3] clientbot: time out CAP/SASL after 5 seconds Closes #424. (cherry picked from commit 47f0b7626f8d17b99fa4dd8cfd93a89f81bb7753) --- protocols/clientbot.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/protocols/clientbot.py b/protocols/clientbot.py index 17212c6..aa2de52 100644 --- a/protocols/clientbot.py +++ b/protocols/clientbot.py @@ -75,6 +75,15 @@ class ClientbotWrapperProtocol(Protocol): f('CAP LS 302') + # Start a timer to call CAP END if registration freezes (e.g. if AUTHENTICATE for SASL is + # never replied to). + def capEnd(): + log.info('(%s) Skipping SASL due to timeout; are the IRCd and services configured ' + 'properly?', self.irc.name) + self.capEnd() + self._cap_timer = threading.Timer(5, capEnd) + self._cap_timer.start() + # This is a really gross hack to get the defined NICK/IDENT/HOST/GECOS. # But this connection stuff is done before any of the spawnClient stuff in # services_support fires. @@ -413,6 +422,14 @@ class ClientbotWrapperProtocol(Protocol): parsed_args['tags'] = tags # Add message tags to this dict. return [idsource, command, parsed_args] + def capEnd(self): + """ + Abort SASL login by sending CAP END. + """ + self.irc.send('CAP END') + log.debug("(%s) Stopping CAP END timer.", self.irc.name) + self._cap_timer.cancel() + def saslAuth(self): """ Starts an authentication attempt via SASL. This returns True if SASL @@ -483,7 +500,7 @@ class ClientbotWrapperProtocol(Protocol): logfunc = log.info if command == '903' else log.warning logfunc('(%s) %s', self.irc.name, args[-1]) if not self.has_eob: - self.irc.send('CAP END') + self.capEnd() handle_903 = handle_902 = handle_905 = handle_906 = handle_907 = handle_904 def requestNewCaps(self): @@ -521,12 +538,12 @@ class ClientbotWrapperProtocol(Protocol): # to do so. if not self.saslAuth(): if not self.has_eob: - self.irc.send('CAP END') + self.capEnd() elif subcmd == 'NAK': log.warning('(%s) Got NAK for IRCv3 capabilities %s, even though they were supposedly available', self.irc.name, args[-1]) if not self.has_eob: - self.irc.send('CAP END') + self.capEnd() elif subcmd == 'NEW': # :irc.example.com CAP modernclient NEW :batch # :irc.example.com CAP tester NEW :away-notify extended-join From 22ceb3f6995bf3709680582a5d11325093c47d02 Mon Sep 17 00:00:00 2001 From: James Lu Date: Wed, 5 Apr 2017 23:08:17 -0700 Subject: [PATCH 3/3] clientbot: make SASL timeout configurable & raise default to 15 secs (cherry picked from commit 9d50a4363be5ed2ca17b9bdf563017220ffba1bd) --- example-conf.yml | 3 +++ protocols/clientbot.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/example-conf.yml b/example-conf.yml index 80eeaa4..011db49 100644 --- a/example-conf.yml +++ b/example-conf.yml @@ -305,6 +305,9 @@ servers: #sasl_username: "mIRCsKripterz" #sasl_password: "DownLoaditn00b!!!" + # Defines the SASL timeout - this defaults to 15 seconds. + #sasl_timeout: 15 + # If this option is enabled, the bot will attempt SASL authentication even after it's # connected, as services become available throughout netsplits and reconnects. # Note: This requires an IRC server capable of IRCv3.2 cap-notify and sasl: diff --git a/protocols/clientbot.py b/protocols/clientbot.py index aa2de52..c227fea 100644 --- a/protocols/clientbot.py +++ b/protocols/clientbot.py @@ -81,7 +81,7 @@ class ClientbotWrapperProtocol(Protocol): log.info('(%s) Skipping SASL due to timeout; are the IRCd and services configured ' 'properly?', self.irc.name) self.capEnd() - self._cap_timer = threading.Timer(5, capEnd) + self._cap_timer = threading.Timer(self.irc.serverdata.get('sasl_timeout') or 15, capEnd) self._cap_timer.start() # This is a really gross hack to get the defined NICK/IDENT/HOST/GECOS.