From 18f6a8df00c29abc498779a8615375f08e8ef6b7 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 29 May 2014 17:19:16 -0700 Subject: [PATCH] irkerd: Close a Dispatcher after a bad-password error Instead of continually trying to reconnect. If the server didn't like your password last time, it's not likely to like it next time either. Bad-password errors aren't covered in RFC 2812 (the ERROR entry is very general [1]). Charybdis seems to use: ERROR :Closing Link: webirc. (Bad Password) and ngIRCd uses: ERROR :Access denied: Bad password? [1]: http://tools.ietf.org/html/rfc2812#section-3.7.4 --- irkerd | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/irkerd b/irkerd index 61803f4..d6686ff 100755 --- a/irkerd +++ b/irkerd @@ -969,10 +969,12 @@ class Dispatcher(list): Having multiple connections allows us to limit the number of channels each connection joins. """ - def __init__(self, target, reconnect_delay=60, **kwargs): + def __init__(self, target, reconnect_delay=60, close_callbacks=(), + **kwargs): super(Dispatcher, self).__init__() self.target = target self._reconnect_delay = reconnect_delay + self._close_callbacks = close_callbacks self._kwargs = kwargs self._channels = Channels() self._pending_connections = 0 @@ -1103,6 +1105,13 @@ class Dispatcher(list): self.remove(protocol) except ValueError: pass + for error in protocol.errors: + if 'bad password' in error.lower(): + LOG.warning( + '{}: bad password, dropping dispatcher'.format(self)) + self.close() + return + LOG.critical('schedule check reconnect {} {}'.format(self._reconnect_delay, self._check_reconnect)) loop = asyncio.get_event_loop() loop.call_later(self._reconnect_delay, self._check_reconnect) @@ -1113,6 +1122,23 @@ class Dispatcher(list): self, count)) self._create_connection() + def close(self): + for protocol in list(self): + if protocol.state != 'disconnected': + protocol.transport.close() + self.remove(protocol) + for channel in list(self._channels): + if channel.queue: + LOG.warning( + '{}: dropping {} messages queued for {}'.format( + self, channel.queued, channel)) + self._channels.discard(channel) + loop = asyncio.get_event_loop() + for callback in self._close_callbacks: + LOG.debug('{}: schedule callback {}'.format(self, callback)) + loop.call_soon(callback, self) + LOG.info('{}: closed'.format(self)) + class IrkerProtocol(LineProtocol): "Listen for JSON messages and queue them for IRC submission" @@ -1178,10 +1204,15 @@ class IrkerProtocol(LineProtocol): LOG.debug('{}: dispatch message to {}'.format(self, target)) if target.connection() not in self._dispatchers: self._dispatchers[target.connection()] = Dispatcher( - target=target, **self._kwargs) + target=target, + close_callbacks=(self._close_dispatcher,), + **self._kwargs) self._dispatchers[target.connection()].send_message( target=target, message=message) + def _close_dispatcher(self, dispatcher): + self._dispatchers.pop(dispatcher.target.connection()) + @asyncio.coroutine def _single_irker_line(line, **kwargs): -- 2.26.2