From 95c848a7201ab47e635bf54263777937998df8cb Mon Sep 17 00:00:00 2001 From: Shane Moore Date: Wed, 30 Nov 2016 23:50:20 -0800 Subject: [PATCH] Add IRC option to publish Tor address --- docs/ENV-NOTES | 7 +++-- server/env.py | 1 + server/irc.py | 79 +++++++++++++++++++++++++++++++++++--------------- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/docs/ENV-NOTES b/docs/ENV-NOTES index f51004e..32fe2b0 100644 --- a/docs/ENV-NOTES +++ b/docs/ENV-NOTES @@ -90,8 +90,11 @@ IRC - set to anything non-empty IRC_NICK - the nick to use when connecting to IRC. The default is a hash of REPORT_HOST. Either way 'E_' will be prepended. REPORT_HOST - the host to advertise. Defaults to HOST. -REPORT_SSL_PORT - the SSL port to advertise. Defaults to SSL_PORT. +REPORT_HOST_TOR - Tor .onion address to advertise. Uses TCP/SSL_PORT rather + - than REPORT_* ports. REPORT_TCP_PORT - the TCP port to advertise. Defaults to TCP_PORT. + - '0' disables publishing the port for public use. +REPORT_SSL_PORT - the SSL port to advertise. Defaults to SSL_PORT. If synchronizing from the Genesis block your performance might change by tweaking the following cache variables. Cache size is only checked @@ -124,4 +127,4 @@ FORCE_REORG - if set to a positive integer, it will simulate a reorg of the blockchain for that number of blocks on startup. Although it should fail gracefully if set to a value greater than REORG_LIMIT, I do not recommend it as I have - not tried it and there is a chance your DB might corrupt. \ No newline at end of file + not tried it and there is a chance your DB might corrupt. diff --git a/server/env.py b/server/env.py index 58b8b9d..57d931f 100644 --- a/server/env.py +++ b/server/env.py @@ -54,6 +54,7 @@ class Env(LoggedClass): self.report_tcp_port = self.integer('REPORT_TCP_PORT', self.tcp_port) self.report_ssl_port = self.integer('REPORT_SSL_PORT', self.ssl_port) self.report_host = self.default('REPORT_HOST', self.host) + self.report_host_tor = self.default('REPORT_HOST_TOR', None) self.irc_nick = self.default('IRC_NICK', None) self.irc = self.default('IRC', False) # Debugging diff --git a/server/irc.py b/server/irc.py index 7930d07..6365fd1 100644 --- a/server/irc.py +++ b/server/irc.py @@ -20,12 +20,8 @@ from lib.hash import double_sha256 from lib.util import LoggedClass -def port_text(letter, port, default): - if not port: - return '' - if port == default: - return letter - return letter + str(port) +VERSION = '1.0' +DEFAULT_PORTS = {'t': 50001, 's': 50002} class IRC(LoggedClass): @@ -37,22 +33,29 @@ class IRC(LoggedClass): def __init__(self, env): super().__init__() - tcp_text = port_text('t', env.report_tcp_port, 50001) - ssl_text = port_text('s', env.report_ssl_port, 50002) - # If this isn't something the client expects you won't appear - # in the client's network dialog box self.env = env - version = '1.0' - self.real_name = '{} v{} {} {}'.format(env.report_host, version, - tcp_text, ssl_text) + + # If this isn't something a peer or client expects + # then you won't appear in the client's network dialog box + irc_address = (env.coin.IRC_SERVER, env.coin.IRC_PORT) + self.channel = env.coin.IRC_CHANNEL self.prefix = env.coin.IRC_PREFIX + + self.clients = [] self.nick = '{}{}'.format(self.prefix, env.irc_nick if env.irc_nick else double_sha256(env.report_host.encode()) [:5].hex()) - self.channel = env.coin.IRC_CHANNEL - self.irc_server = env.coin.IRC_SERVER - self.irc_port = env.coin.IRC_PORT + self.clients.append( IrcClient(irc_address, self.nick, + env.report_host, + env.report_tcp_port, + env.report_ssl_port) ) + if env.report_host_tor: + self.clients.append( IrcClient(irc_address, self.nick + '_tor', + env.report_host_tor, + env.tcp_port, + env.ssl_port) ) + self.peer_regexp = re.compile('({}[^!]*)!'.format(self.prefix)) self.peers = {} @@ -72,20 +75,23 @@ class IRC(LoggedClass): async def join(self): import irc.client as irc_client - self.logger.info('joining IRC with nick "{}" and real name "{}"' - .format(self.nick, self.real_name)) - reactor = irc_client.Reactor() for event in ['welcome', 'join', 'quit', 'kick', 'whoreply', 'namreply', 'disconnect']: reactor.add_global_handler(event, getattr(self, 'on_' + event)) - connection = reactor.server() + # Note: Multiple nicks in same channel will trigger duplicate events + for client in self.clients: + client.connection = reactor.server() + while True: try: - connection.connect(self.irc_server, self.irc_port, - self.nick, ircname=self.real_name) - connection.set_keepalive(60) + for client in self.clients: + self.logger.info('Joining IRC in {} as "{}" with ' + 'real name "{}"' + .format(self.channel, client.nick, + client.realname)) + client.connect() while True: reactor.process_once() await asyncio.sleep(2) @@ -155,3 +161,30 @@ class IRC(LoggedClass): self.peers[nick] = peer except IndexError: pass + + +class IrcClient(LoggedClass): + + def __init__(self, irc_address, nick, host, tcp_port, ssl_port): + super().__init__() + self.irc_host, self.irc_port = irc_address + self.nick = nick + self.realname = IrcClient.create_realname(host, tcp_port, ssl_port) + self.connection = None + + + def connect(self, keepalive=60): + '''Connect this client to its IRC server''' + self.connection.connect(self.irc_host, self.irc_port, self.nick, + ircname=self.realname) + self.connection.set_keepalive(keepalive) + + + def create_realname(host, tcp_port, ssl_port): + def port_text(letter, port): + return letter if letter in DEFAULT_PORTS + and port == DEFAULT_PORTS[letter] + else letter + str(port) + tcp = ' ' + port_text('t', tcp_port) if tcp_port else '' + ssl = ' ' + port_text('s', ssl_port) if ssl_port else '' + return '{} v{}{}{}'.format(host, VERSION, tcp, ssl)