2022-07-21 12:40:05 +00:00
|
|
|
import main
|
|
|
|
from twisted.internet.ssl import DefaultOpenSSLContextFactory
|
|
|
|
from twisted.internet import reactor
|
|
|
|
from twisted.words.protocols.irc import IRCClient
|
|
|
|
|
|
|
|
from twisted.internet.protocol import ReconnectingClientFactory
|
|
|
|
from utils.parsing import parsen
|
|
|
|
from utils.logging.log import log, error
|
|
|
|
from utils.logging.send import sendAll
|
|
|
|
from modules import userinfo
|
|
|
|
from datetime import datetime
|
|
|
|
from core.relay import sendRelayNotification
|
|
|
|
from utils.get import getRelay
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: strip out non-relay functionality
|
|
|
|
class IRCRelay(IRCClient):
|
|
|
|
def __init__(self, num, relayCommands, user, stage2):
|
|
|
|
self.isconnected = False
|
|
|
|
self.buffer = ""
|
|
|
|
if user is None:
|
|
|
|
self.user = main.config["Relay"]["User"]
|
|
|
|
else:
|
|
|
|
self.user = user.lower()
|
|
|
|
password = main.config["Relay"]["Password"]
|
|
|
|
self.nickname = "relay"
|
|
|
|
self.realname = "relay"
|
|
|
|
self.username = self.user
|
|
|
|
self.password = self.user + ":" + password
|
|
|
|
|
|
|
|
self.relayCommands = relayCommands
|
|
|
|
self.num = num
|
|
|
|
self.stage2 = stage2
|
|
|
|
self.loop = None
|
|
|
|
|
|
|
|
def privmsg(self, user, channel, msg):
|
|
|
|
nick, ident, host = parsen(user)
|
|
|
|
for i in main.ZNCErrors:
|
|
|
|
if i in msg:
|
|
|
|
error("ZNC issue:", msg)
|
|
|
|
if nick[0] == main.config["Tweaks"]["ZNC"]["Prefix"]:
|
|
|
|
nick = nick[1:]
|
|
|
|
if nick in self.relayCommands.keys():
|
|
|
|
sendAll("[%s] %s -> %s" % (self.num, nick, msg))
|
|
|
|
|
|
|
|
def irc_ERR_PASSWDMISMATCH(self, prefix, params):
|
|
|
|
log("%s: relay password mismatch" % self.num)
|
|
|
|
sendAll("%s: relay password mismatch" % self.num)
|
|
|
|
|
|
|
|
def sendStage2(self):
|
|
|
|
# [["user", {"sasl": ["message1", "message2"]}], []]
|
|
|
|
if not len(self.stage2) == 0:
|
|
|
|
user = self.stage2[0].pop(0)
|
|
|
|
commands = self.stage2[0].pop(0)
|
|
|
|
del self.stage2[0]
|
|
|
|
deliverRelayCommands(self.num, commands, user, self.stage2)
|
|
|
|
|
|
|
|
def signedOn(self):
|
|
|
|
if not self.isconnected:
|
|
|
|
self.isconnected = True
|
|
|
|
# log("signed on as a relay: %s" % self.num)
|
|
|
|
sleeptime = 0
|
|
|
|
increment = 0.8
|
|
|
|
for i in self.relayCommands.keys():
|
|
|
|
for x in self.relayCommands[i]:
|
|
|
|
reactor.callLater(
|
|
|
|
sleeptime,
|
|
|
|
self.msg,
|
|
|
|
main.config["Tweaks"]["ZNC"]["Prefix"] + i,
|
|
|
|
x,
|
|
|
|
)
|
|
|
|
sleeptime += increment
|
|
|
|
increment += 0.8
|
|
|
|
if self.stage2 is not None:
|
|
|
|
reactor.callLater(sleeptime, self.sendStage2)
|
|
|
|
reactor.callLater(sleeptime + 5, self.transport.loseConnection)
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
class IRCRelayFactory(ReconnectingClientFactory):
|
|
|
|
def __init__(self, net, num=None, relayCommands=None, user=None, stage2=None):
|
|
|
|
if net is None:
|
|
|
|
self.num = num
|
|
|
|
self.net = None
|
|
|
|
self.name = "relay - %i" % num
|
|
|
|
self.relay = True
|
|
|
|
else:
|
|
|
|
self.name = net + str(num)
|
|
|
|
self.num = num
|
|
|
|
self.net = net
|
|
|
|
self.relay = False
|
|
|
|
self.client = None
|
|
|
|
self.maxDelay = main.config["Tweaks"]["Delays"]["MaxDelay"]
|
|
|
|
self.initialDelay = main.config["Tweaks"]["Delays"]["InitialDelay"]
|
|
|
|
self.factor = main.config["Tweaks"]["Delays"]["Factor"]
|
|
|
|
self.jitter = main.config["Tweaks"]["Delays"]["Jitter"]
|
|
|
|
|
|
|
|
self.relayCommands, self.user, self.stage2 = relayCommands, user, stage2
|
|
|
|
|
|
|
|
def buildProtocol(self, addr):
|
|
|
|
|
|
|
|
entry = IRCRelay(self.num, self.relayCommands, self.user, self.stage2)
|
|
|
|
|
|
|
|
self.client = entry
|
|
|
|
return entry
|
|
|
|
|
|
|
|
def clientConnectionLost(self, connector, reason):
|
|
|
|
if not self.relay:
|
|
|
|
userinfo.delChannels(self.net, self.client.channels)
|
|
|
|
if self.client is not None:
|
|
|
|
self.client.isconnected = False
|
|
|
|
self.client.authenticated = False
|
|
|
|
self.client.channels = []
|
|
|
|
error = reason.getErrorMessage()
|
|
|
|
if not self.relay:
|
|
|
|
log("%s - %i: connection lost: %s" % (self.net, self.num, error))
|
|
|
|
sendAll("%s - %i: connection lost: %s" % (self.net, self.num, error))
|
|
|
|
ctime = str(datetime.now().isoformat())
|
|
|
|
sendRelayNotification(
|
|
|
|
{
|
|
|
|
"type": "conn",
|
|
|
|
"net": self.net,
|
|
|
|
"num": self.num,
|
|
|
|
"status": "lost",
|
|
|
|
"message": error,
|
|
|
|
"ts": ctime,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
self.retry(connector)
|
|
|
|
# ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
|
|
|
|
|
|
|
|
def clientConnectionFailed(self, connector, reason):
|
|
|
|
if self.client is not None:
|
|
|
|
self.client.isconnected = False
|
|
|
|
self.client.authenticated = False
|
|
|
|
self.client.channels = []
|
|
|
|
error = reason.getErrorMessage()
|
|
|
|
log("%s - %i: connection failed: %s" % (self.net, self.num, error))
|
|
|
|
if not self.relay:
|
|
|
|
sendAll("%s - %s: connection failed: %s" % (self.net, self.num, error))
|
|
|
|
ctime = str(datetime.now().isoformat())
|
|
|
|
sendRelayNotification(
|
|
|
|
{
|
|
|
|
"type": "conn",
|
|
|
|
"net": self.net,
|
|
|
|
"num": self.num,
|
|
|
|
"status": "failed",
|
|
|
|
"message": error,
|
|
|
|
"ts": ctime,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
self.retry(connector)
|
|
|
|
# ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
|
|
|
|
|
|
|
|
|
2022-07-21 12:40:03 +00:00
|
|
|
def deliverRelayCommands(num, relayCommands, user=None, stage2=None):
|
|
|
|
keyFN = main.certPath + main.config["Key"]
|
|
|
|
certFN = main.certPath + main.config["Certificate"]
|
|
|
|
contextFactory = DefaultOpenSSLContextFactory(keyFN.encode("utf-8", "replace"), certFN.encode("utf-8", "replace"))
|
2022-07-21 12:40:05 +00:00
|
|
|
bot = IRCRelayFactory(net=None, num=num, relayCommands=relayCommands, user=user, stage2=stage2)
|
2022-07-21 12:40:03 +00:00
|
|
|
host, port = getRelay(num)
|
2022-07-21 12:40:05 +00:00
|
|
|
reactor.connectSSL(host, port, bot, contextFactory)
|