From 980aad7f45c08b3321fc08cec5e70a2c0752a642 Mon Sep 17 00:00:00 2001 From: Georg Date: Thu, 26 Aug 2021 17:37:20 +0200 Subject: [PATCH] Init Whois from Georg/SnoParser Signed-off-by: Georg --- __pycache__/__init__.cpython-38.pyc | Bin 0 -> 637 bytes __pycache__/config.cpython-38.pyc | Bin 0 -> 2453 bytes __pycache__/plugin.cpython-38.pyc | Bin 0 -> 8505 bytes config.py | 61 +++++++++++++- plugin.py | 124 ++++++++++++++++++++++++++-- 5 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 __pycache__/__init__.cpython-38.pyc create mode 100644 __pycache__/config.cpython-38.pyc create mode 100644 __pycache__/plugin.cpython-38.pyc diff --git a/__pycache__/__init__.cpython-38.pyc b/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de1a3d350771c416dc027ef35d1d57b94047951c GIT binary patch literal 637 zcmYjOy^a$x5MF!#e{wnQj)sOUph&|?=qiK|(uo2IozO)zZmdo0Y`o{SBYT%qXn6x3 zfsR*lOO;okVw?cNl4s_dZ}iQK{b@EU0yuXcp0^=HKRx*WU;uC7+V5akKmvvsmWZIE z#{`29B_2W&qJTvt;<%6dgrp)M8Q`47ERn@fk`nlsr4U!7LIFqpjAi$+VW5!9vtdr= zC{WcAou3e&^YSZ#+BvU|KI|WvZ=h0u$_2Rqrs^-rB`^Cca>ej6Sd?F`i>1;ZTVuI- zRUf^r$1Sgy+$YSaE>ug4+dh@q;#!x!`~x0XSJ}wGo|< zc5>>3THr$n)FJxA8!mOrfR9FQ$HilpQp)t2QWx62b*Y_p`;{Jze@ItS3Nu)t74T_0 zjazM?W>WV`KPXpFdddNNPARk=jaW@16#05eCnG5}zQ2jRNxW$FS5l~rcDWdK+Kf`H zOzznbuuJ`HLUqP}wu^6MYpu&qt4)Ug2+R{N=gqH|%~lV*>9{ssBgLS!5p8o?-ZuZ< zwxLugF;eZSUEg)UXN4c!Zt;O5PPvZ#F`pHGbh^c_ z9pQcDuo5fdt_1FrGAkUq-3m%osIUrnPim~nYKPuo0rTYAwM@It>MPm})UL5(Y;&Pq z#|+00v70<@zs^p8x4*_tvQy~y!cT>#Inj0+Z8dg=on5hE4z#_voMnH5oy&Y*V&_+U zvE%(ZT3<%%G4=|(@LVgI)w%dw>#NzfonfynzG9yB5?YS2*V*M~ELSqiRo2KYtlD-O zZ~S4;s^O)6Uy4CmQ4nx#)TH62=k}r`;9^G@P=XpR1&#PL6Vhs1$UUkws6L+R;<2)Q zxAE0JF5fv=!`=NAm(+c%N~p?~zpl4J8AB@ose)F-V<8o%tzlMCw-(u3wksYqhm*8& zJEC!q(L0wh$)ET-%2I2kei0S6Y*zm&3d}`zA>R&=ZwFpnI6$TyAk$3oNr@GhZ_kU_ z9*eumHs0{l5UV45fxNEK6KfDtWZ#06+#Brze7+x^`6PFnpiG4g&WwVBo<-s@9MTL~U( zYx>r920hlyLPpkyK+q&BZkK4kFB4(X&7p!32=h4)!U$i}nO^D0Ac(Lk7i3nDgmbed zV{SsyebC-Zi{nsoofaANk{~S(Xry8KphoanFky>yR03P%KBx&9f$B*O1YyxK0wNPL zOiYt}Y9qJE7uaevA(}_n5RpSr8EczaMr2}nsQ8E)NK2Z=!w6Dm`r9+rLaqsoB00`b z(_jcC#vZf?$j%<2j49Ci8sUQspPafxhZsY8Kww0p1ZyxN1>sF-Vt6F`G$I1bSgJ=& z(y=AR9Q`GJkm3w%!KLH~-9X8hw0FPk>}-G1zL+(c78Njr%g;`&dgJX|&FlEPF}<3Z z?^@&5QWhc=v95x~kVBCAL#a&azx&Ru_ot_qqxMig^Ql-G*qG@r%O3Y;+FASf1#+0r z20{fRA=N}kg9w1g-~!epJBFa2g1(Gnb%?|8iYG{ulsYMd^K>Z79hyenG z5c0ky7t#+eKrUr@5Y8ED2=3F{lc|9s83zcXr7#r)9^2{|U?kQ#gT3o4Khi(MnV z#E^$K9}*1^XV-1QAz~zulA*6oBBS#*8k=c(Hg>bYstR@juFoeizww?>_75O+zsZK9 z+QdyZvD1_D5&T&sdo;R_*AoXo+bBEY%O6RcBUGgIg_n}aP@T5n=WW8W{T~eQnm!%P znUu}v%I#RPB!W8^&{cnr%ckqO>-Z}bHVTz$typnuM}A>xEv>sXx8!;S?{ELeFE7g* THf~}0Px;6@@-Y*7uSov@YK_bX literal 0 HcmV?d00001 diff --git a/__pycache__/plugin.cpython-38.pyc b/__pycache__/plugin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fa3b98ddaf226a44bdf694998dc954a782bd958 GIT binary patch literal 8505 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 # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -61,4 +61,63 @@ conf.registerGlobalValue(SnoParser, 'AutoVhost', conf.registerGlobalValue(SnoParser, 'preventHighlight', registry.Boolean(True, ("""Toggles in channel highlights with ZWSP"""))) +### +## WHOIS related settings below: +### +conf.registerGroup(SnoParser, 'whois') +conf.registerGlobalValue(SnoParser.whois, 'debug', + registry.Boolean('false', + """ + SnoParser: True: Very verbose console output. False: Mostly silent operation. + """ + , private=True +)) +conf.registerGlobalValue(SnoParser.whois, 'sample', + registry.String('', + """ + SnoParser: This allows to set a testing IP address, if the plugin shall be evaluated on i.e. a local network. This will override all IP addresses from SNOTICES! + """ + , private=True +)) +conf.registerGroup(SnoParser.whois, 'redis') +conf.registerGlobalValue(SnoParser.whois.redis, 'host', + registry.String('127.0.0.1', + """ + Redis: IP address or hostname. + """ + , private=True +)) +conf.registerGlobalValue(SnoParser.whois.redis, 'port', + registry.Integer('6379', + """ + Redis: Port. + """ + , private=True +)) +conf.registerGlobalValue(SnoParser.whois.redis, 'username', + registry.String('', + """ + Redis: Username. This is optional and has not been tested. It is recommended to perform initial tests on a local instance with no username and password. + """ + , private=True +)) +conf.registerGlobalValue(SnoParser.whois.redis, 'password', + registry.String('', + """ + Redis: Password. This is optional and has not been tested. It is recommended to perform initial tests on a local instance with no username and password. + """ +)) +conf.registerGlobalValue(SnoParser.whois.redis, 'db', + registry.Integer('0', + """ + Redis: Database number. It is recommended to use a dedicated, isolated, Redis instance for SnoParser instead of using an existing one with a different database. + """ +)) +conf.registerGlobalValue(SnoParser.whois.redis, 'timeout', + registry.Integer('5', + """ + Redis: Socket Timeout. The developer does not know what to recommend here, but `5` seems to yield good results. + """ +)) + # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/plugin.py b/plugin.py index 8c3717e..d47440f 100644 --- a/plugin.py +++ b/plugin.py @@ -1,5 +1,5 @@ ### -# Copyright (c) 2021, mogad0n +# Copyright (c) 2021, mogad0n and Georg Pfuetzenreuter # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -43,17 +43,124 @@ import os import sys import time import sqlite3 +import redis +import json +from datetime import timedelta +from ipwhois import IPWhois +import ipwhois class SnoParser(callbacks.Plugin): """Parses the Server Notices from ErgoIRCd""" threaded = True + def redis_connect(self) -> redis.client.Redis: + try: + redis_client = redis.Redis( + host = self.registryValue('whois.redis.host'), + port = self.registryValue('whois.redis.port'), + password = self.registryValue('whois.redis.password'), + username = self.registryValue('whois.redis.username'), + db = self.registryValue('whois.redis.db'), + socket_timeout = int(self.registryValue('whois.redis.timeout')) + ) + ping = redis_client.ping() + if ping is True: + return redis_client + except redis.AuthenticationError: + print("Could not authenticate to Redis backend.") + + def __init__(self, irc): + super().__init__(irc) + self.redis_client = self.redis_connect() + + def whois_fresh(self, sourceip: str) -> dict: + """Data from cache.""" + asn = 0 + subnet = '' + try: + whois = IPWhois(sourceip) + whoisres = whois.lookup_rdap(depth=1,retry_count=0) + results = whoisres + if self.registryValue('whois.debug'): + print(results) + asn = whoisres['asn_registry'] + country = whoisres['asn_country_code'] + description = whoisres['asn_description'] + whoisout = asn + ' ' + country + ' ' + description + except ipwhois.exceptions.IPDefinedError: + whoisout = 'RFC 4291 (Local)' + + response = whoisout + return response + + def whois_get_cache(self, key: str) -> str: + """Data from Redis.""" + + k = self.redis_client.get(key) + +# self = SnoParser() +# val = self.redis_client.get(key) + val = k + return val + + def whois_set_cache(self, key: str, value: str) -> bool: + """Data to Redis.""" + + state = self.redis_client.setex(key, timedelta(seconds=3600), value=value,) + return state + + def whois_run(self, sourceip: str) -> dict: + """Whois query router.""" + + data = self.whois_get_cache(key=sourceip) + if data is not None: + data = json.loads(data) + if self.registryValue('whois.debug'): + print("SNOPARSER DEBUG - WHOIS_RUN WITH CACHE: TRUE") + print(data) + print(sourceip) + return data + else: + data = self.whois_fresh(sourceip) + if self.registryValue('whois.debug'): + print("SNOPARSER DEBUG - WHOIS_RUN WITH CACHE: FALSE") + print(data) + print(sourceip) + if data.startswith: + if self.registryValue('whois.debug'): + print("SNOPARSER DEBUG - WHOIS_RUN WITH CACHE: FALSE AND CORRECT STARTING CHARACTER") + print(data) + data = json.dumps(data) + state = self.whois_set_cache(key=sourceip, value=data) + + if state is True: + return json.loads(data) + else: + if self.registryValue('whois.debug'): + print("SNOPARSER DEBUG _ WHOIS_RUN WITH CACHE: FALSE AND WRONG STARTING CHARACTER") + print(data) + return data + + def query(self, irc, msg, args, ipaddress): + """ + Queries the cache for an address. + """ + + data = self.whois_get_cache(key=ipaddress) + decoded = data.decode('utf-8') + ttl = self.redis_client.ttl(ipaddress) + + print('SnoParser manual query: ', data, ' ', ttl) + irc.reply(f'{decoded} - Remaining: {ttl}s') + + query = wrap(query, ['anything']) + + def doNotice(self, irc, msg): (target, text) = msg.args + if target == irc.nick: - # server notices CONNECT, KILL, XLINE, NICK, ACCOUNT - text = ircutils.stripFormatting(text) if 'CONNECT' in text: connregex = "^-CONNECT- Client connected \[(.+)\] \[u\:~(.+)\] \[h\:(.+)\] \[ip\:(.+)\] \[r\:(.+)\]$" @@ -61,12 +168,19 @@ class SnoParser(callbacks.Plugin): nickname = couple.group(1) username = couple.group(2) host = couple.group(3) - ip = couple.group(4) + if self.registryValue('whois.sample'): + ip = self.registryValue('whois.sample') + else: + ip = couple.group(4) realname = couple.group(5) ip_seen = 0 nick_seen = 0 + + whoisout = self.whois_run(sourceip=ip) DictFromSnotice = {'notice': 'connect', 'nickname': nickname, 'username': username, 'host': host, 'ip': ip, 'realname': realname, 'ipCount': ip_seen, 'nickCount': nick_seen} - repl = f"\x02\x1FNOTICE: {DictFromSnotice['notice']} \x0F\x11\x0303==>>\x0F \x02Nick:\x0F {DictFromSnotice['nickname']} \x02Username:\x0F {DictFromSnotice['username']} \x02Hostname:\x0F {DictFromSnotice['host']} \x02IP:\x0F {DictFromSnotice['ip']} \x02Realname:\x0F {DictFromSnotice['realname']} \x02IPcount:\x0F {DictFromSnotice['ipCount']} \x02NickCount:\x0F {DictFromSnotice['nickCount']}" + #repl = f"\x02\x1FNOTICE: {DictFromSnotice['notice']} \x0F\x11\x0303==>>\x0F \x02Nick:\x0F {DictFromSnotice['nickname']} \x02Username:\x0F {DictFromSnotice['username']} \x02Hostname:\x0F {DictFromSnotice['host']} \x02IP:\x0F {DictFromSnotice['ip']} \x02Realname:\x0F {DictFromSnotice['realname']} \x02IPcount:\x0F {DictFromSnotice['ipCount']} \x02NickCount:\x0F {DictFromSnotice['nickCount']}" + repl = f"\x02\x1F{DictFromSnotice['notice']} \x0F\x11\x0303==>>\x0F \x02Nick:\x0F {DictFromSnotice['nickname']} \x02Username:\x0F {DictFromSnotice['username']} \x02Hostname:\x0F {DictFromSnotice['host']} \x02IP:\x0F {DictFromSnotice['ip']} {whoisout} \x02Realname:\x0F {DictFromSnotice['realname']} \x02IPcount:\x0F {DictFromSnotice['ipCount']} \x02NickCount:\x0F {DictFromSnotice['nickCount']}" + self._sendSnotice(irc, msg, repl) if 'XLINE' in text and 'temporary' in text: xlineregex = "^-XLINE- (.+) \[(.+)\] added temporary \((.+)\) (K-Line|D-Line) for (.+)$"