Fix LIST handling and message parsing

* Always use simple LIST syntax if it succeeded once after a failed
complex query
* Reject asking for a LIST twice
* Quickly discard any ISUPPORT messages that don't contain things we
need to use
* Detect the server name and drop any messages from the server
This commit is contained in:
Mark Veidemanis 2019-10-20 16:44:33 +01:00
parent f34de8940f
commit b4fa747853
3 changed files with 64 additions and 26 deletions

View File

@ -106,22 +106,26 @@ class IRCBot(IRCClient):
self.realname = alias["realname"] self.realname = alias["realname"]
self.username = alias["nick"]+"/"+relay["net"] self.username = alias["nick"]+"/"+relay["net"]
self.password = main.config["Relay"]["Password"] self.password = main.config["Relay"]["Password"]
self.userinfo = None self.userinfo = None #
self.fingerReply = None self.fingerReply = None #
self.versionName = None self.versionName = None # don't tell anyone information about us
self.versionNum = None self.versionNum = None #
self.versionEnv = None self.versionEnv = None #
self.sourceURL = None self.sourceURL = None #
self._getWho = {} # LoopingCall objects -- needed to be able to stop them self._getWho = {} # LoopingCall objects -- needed to be able to stop them
self._tempWho = {} # Temporary storage for gathering WHO info self._tempWho = {} # temporary storage for gathering WHO info
self._tempNames = {} # Temporary storage for gathering NAMES info self._tempNames = {} # temporary storage for gathering NAMES info
self._tempList = ([], []) # Temporary storage for gathering LIST info self._tempList = ([], []) # temporary storage for gathering LIST info
self.listOngoing = False self.listOngoing = False # we are currently receiving a LIST
self.listRetried = False self.listRetried = False # we asked and got nothing so asked again
self.listAttempted = False # we asked for a list
self.listSimple = False # after asking again we got the list, so
# use the simple syntax from now on
self.chanlimit = 0 self.chanlimit = 0
self.servername = None
def joinChannels(self, channels): def joinChannels(self, channels):
sleeptime = 0.0 sleeptime = 0.0
@ -151,11 +155,13 @@ class IRCBot(IRCClient):
if cast[i] == "": # a dictionary that changes length with each iteration if cast[i] == "": # a dictionary that changes length with each iteration
del cast[i] del cast[i]
if "muser" in cast.keys(): if "muser" in cast.keys():
if cast["muser"] == self.servername:
return
if "channel" in cast.keys():
if cast["channel"] == "*":
return
if not {"nick", "ident", "host"}.issubset(set(cast.keys())):
cast["nick"], cast["ident"], cast["host"] = parsen(cast["muser"]) cast["nick"], cast["ident"], cast["host"] = parsen(cast["muser"])
# additional checks here to see if it's a server -- irc.example.net
# discard if it is
#if not cast["type"] in ["nick", "kick", "quit", "part", "join"]:
# del cast["muser"]
if set(["nick", "ident", "host", "msg"]).issubset(set(cast)): if set(["nick", "ident", "host", "msg"]).issubset(set(cast)):
if "msg" in cast.keys(): if "msg" in cast.keys():
if cast["ident"] == "znc" and cast["host"] == "znc.in": if cast["ident"] == "znc" and cast["host"] == "znc.in":
@ -166,6 +172,7 @@ class IRCBot(IRCClient):
del cast["host"] del cast["host"]
del cast["channel"] del cast["channel"]
if "Disconnected from IRC" in cast["msg"]: if "Disconnected from IRC" in cast["msg"]:
log("ZNC disconnected on %s - %i" (self.net, self.num))
self.connected = False self.connected = False
if not cast["type"] in ["query", "self", "highlight", "znc", "who"]: if not cast["type"] in ["query", "self", "highlight", "znc", "who"]:
if "channel" in cast.keys() and not cast["type"] == "mode": # don't handle modes here if "channel" in cast.keys() and not cast["type"] == "mode": # don't handle modes here
@ -323,10 +330,16 @@ class IRCBot(IRCClient):
newNicklist.append(f) newNicklist.append(f)
userinfo.initialNames(self.net, nicklist[0], newNicklist) userinfo.initialNames(self.net, nicklist[0], newNicklist)
def myInfo(self, servername, version, umodes, cmodes):
self.servername = servername
def _list(self, noargs): def _list(self, noargs):
d = Deferred() d = Deferred()
self._tempList = ([], []) self._tempList = ([], [])
self._tempList[0].append(d) self._tempList[0].append(d)
if self.listSimple:
self.sendLine("LIST")
return d
if noargs: if noargs:
self.sendLine("LIST") self.sendLine("LIST")
else: else:
@ -334,10 +347,19 @@ class IRCBot(IRCClient):
return d return d
def list(self, noargs=False): def list(self, noargs=False):
if not self.listAttempted:
self.listAttempted = True
else:
debug("List request dropped, already asked for LIST - %s - %i" % (self.net, self.num))
return
if not self.listOngoing: if not self.listOngoing:
self._list(noargs).addCallback(self.got_list) self._list(noargs).addCallback(self.got_list)
else:
debug("List request dropped, already ongoing - %s - %i" % (self.net, self.num))
return
def irc_RPL_LISTSTART(self, prefix, params): def irc_RPL_LISTSTART(self, prefix, params):
self.listAttempted = False
self.listOngoing = True self.listOngoing = True
def irc_RPL_LIST(self, prefix, params): def irc_RPL_LIST(self, prefix, params):
@ -359,12 +381,16 @@ class IRCBot(IRCClient):
if noResults: if noResults:
if not self.listRetried: if not self.listRetried:
self.list(True) self.list(True)
self.listRetried = True
else: else:
warn("List still empty after retry: %s - %i" % (net, num)) warn("List still empty after retry: %s - %i" % (net, num))
self.listRetried = False self.listRetried = False
return return
else: else:
self.listRetried = False if self.listRetried:
self.listRetried = False
debug("List received after retry - defaulting to simple list syntax")
self.listSimple = True
def got_list(self, listinfo): def got_list(self, listinfo):
if len(listinfo) == 0: # probably ngircd not supporting LIST >0 if len(listinfo) == 0: # probably ngircd not supporting LIST >0
@ -372,19 +398,33 @@ class IRCBot(IRCClient):
chankeep.initialList(self.net, self.num, listinfo, self.chanlimit) chankeep.initialList(self.net, self.num, listinfo, self.chanlimit)
def isupport(self, options): def isupport(self, options):
for i in options: interested = ("CHANLIMIT", "MAXCHANNELS")
if not any((x for x in options if any(y in x for y in interested))):
return # check if any of interested is in any of options, some networks
chanlimit = None # call isupport() more than once, so discard swiftly anything
for i in options: # we don't care about
if i.startswith("CHANLIMIT"): if i.startswith("CHANLIMIT"):
if ":" in i: if ":" in i:
split = i.split(":") split = i.split(":")
if len(split) >= 2: if len(split) >= 2:
chanlimit = split[1] chanlimit = split[1]
try: break
self.chanlimit = int(chanlimit) elif i.startswith("MAXCHANNELS"):
return if "=" in i:
except TypeError: split = i.split("=")
warn("Invalid CHANLIMIT: %s" % i) if len(split) == 2:
chanlimit = split[1]
break
if chanlimit:
try:
self.chanlimit = int(chanlimit)
except TypeError:
warn("Invalid chanlimit: %s" % i)
if self.num == 1: # Only one instance should do a list, so if self.num == 1: # Only one instance should do a list, so
self.list() # why not this one? :P if self.chanlimit:
self.list() # why not this one? :P
else:
debug("Aborting LIST due to bad chanlimit")
self.checkChannels() self.checkChannels()
#twisted sucks so i have to do this to actually get the user info #twisted sucks so i have to do this to actually get the user info

View File

@ -27,7 +27,6 @@ def getChanFree(net, new):
name = net+str(i) name = net+str(i)
chanfree[i] = main.IRCPool[name].chanlimit-len(main.IRCPool[name].channels) chanfree[i] = main.IRCPool[name].chanlimit-len(main.IRCPool[name].channels)
chanlimits.add(main.IRCPool[name].chanlimit) chanlimits.add(main.IRCPool[name].chanlimit)
if not len(chanlimits) == 1: if not len(chanlimits) == 1:
error("Network %s has servers with different CHANMAX values" % net) error("Network %s has servers with different CHANMAX values" % net)
return False return False
@ -39,7 +38,6 @@ def emptyChanAllocate(net, flist, relay, new):
return return
for i in new: for i in new:
chanfree[0][i] = chanfree[1] chanfree[0][i] = chanfree[1]
print("chanfree", chanfree)
allocated = {} allocated = {}
toalloc = len(flist) toalloc = len(flist)
if toalloc > sum(chanfree[0].values()): if toalloc > sum(chanfree[0].values()):

View File

@ -7,7 +7,7 @@ from utils.logging.debug import debug
from utils.parsing import parsen from utils.parsing import parsen
def getWhoSingle(name, query): def getWhoSingle(name, query):
result = main.r.sscan("live.who."+name, 0, query, count=-1) result = main.r.sscan("live.who."+name, 0, query, count=999999)
if result[1] == []: if result[1] == []:
return None return None
return (i.decode() for i in result[1]) return (i.decode() for i in result[1])