From 0c4c4c4aa836221112ccccaf09c7b0d25ba2297b Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 14 Mar 2024 10:54:39 +0100 Subject: [PATCH] fix(irc-puppet): pick a random IPv6 address to connect to IRC hosts --- dibridge/irc_puppet.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/dibridge/irc_puppet.py b/dibridge/irc_puppet.py index a86e6df..6dd2090 100644 --- a/dibridge/irc_puppet.py +++ b/dibridge/irc_puppet.py @@ -1,6 +1,7 @@ import asyncio import irc.client_aio import logging +import random import socket @@ -135,14 +136,28 @@ async def reclaim_nick(self): self._client.nick(self._nickname) async def connect(self): - self._log.info("Connecting to IRC from %s ...", self._ipv6_address) - local_addr = (str(self._ipv6_address), 0) while self._reconnect: + # As per RFC, libc sorts IPv6 results in some complicated way. In result, + # even if the IRC host has multiple IPv6 addresses listed, we will pick + # almost always the same one. This gives unneeded pressure on a single + # host, instead of distributing the load. So instead, we do the lookup + # ourselves, and pick a random one. + ipv6s = await self.loop.getaddrinfo(self._irc_host, None, socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP) + if not ipv6s: + self._log.warning("Failed DNS lookup, retrying in 5 seconds") + # When we can't connect, try again in 5 seconds. + await asyncio.sleep(5) + continue + + irc_host_ipv6 = random.choice(ipv6s)[4][0] + + self._log.info("Connecting to IRC from %s to %s (%s) ...", self._ipv6_address, self._irc_host, irc_host_ipv6) + try: await self.connection.connect( - self._irc_host, + irc_host_ipv6, self._irc_port, self._nickname, username=self._username,