1
mirror of git://git.acid.vegas/IRCP.git synced 2024-11-22 16:06:41 +00:00

Added multi-logs & memory management has been improved for performance

This commit is contained in:
Dionysus 2023-05-29 20:59:17 -04:00
parent cfa0e3c2bb
commit 751b28eac5
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
2 changed files with 51 additions and 32 deletions

View File

@ -24,15 +24,17 @@ The IRC networks we scanned are PUBLIC networks...any person can freely connect
## Config ## Config
###### Settings ###### Settings
| Setting | Default Value | Description | | Setting | Default Value | Description |
| ---------- | ------------------------------ | ------------------------------------------ | | ------------- | ------------------------------ | ----------------------------------------------------- |
| `errors` | `False` | Show errors in console | | `errors` | `True` | Show errors in console |
| `nickname` | `"IRCP"` | IRC nickname *(`None` = random)* | | `errors_conn` | `False` | Show connection errors in console |
| `username` | `"ircp"` | IRC username *(`None` = random)* | | `log_max` | `5000000` | Maximum log size *(in bytes)* before starting another |
| `realname` | `"internetrelaychat.org"` | IRC realname *(`None` = random)* | | `nickname` | `"IRCP"` | IRC nickname *(`None` = random)* |
| `ns_mail` | `"scan@internetrelaychat.org"` | NickServ email address *(`None` = random)* | | `username` | `"ircp"` | IRC username *(`None` = random)* |
| `ns_pass` | `"changeme"` | NickServ password *(None = random)* | | `realname` | `"internetrelaychat.org"` | IRC realname *(`None` = random)* |
| `vhost` | `None` | Bind to a specific IP address | | `ns_mail` | `"scan@internetrelaychat.org"` | NickServ email address *(`None` = random)* |
| `ns_pass` | `"changeme"` | NickServ password *(None = random)* |
| `vhost` | `None` | Bind to a specific IP address |
###### Throttle ###### Throttle
| Setting | Default Value | Description | | Setting | Default Value | Description |

63
ircp.py
View File

@ -11,13 +11,15 @@ import sys
import time import time
class settings: class settings:
errors = True # Show errors in console errors = True # Show errors in console
nickname = 'IRCP' # None = random errors_conn = False # Show connection errors in console
username = 'ircp' # None = random log_max = 5000000 # Maximum log size (in bytes) before starting another
realname = 'scan@internetrelaychat.org' # None = random nickname = 'IRCP' # None = random
ns_mail = 'scan@internetrelaychat.org' # None = random@random.[com|net|org] username = 'ircp' # None = random
ns_pass = 'changeme' # None = random realname = 'scan@internetrelaychat.org' # None = random
vhost = None # Bind to a specific IP address ns_mail = 'scan@internetrelaychat.org' # None = random@random.[com|net|org]
ns_pass = 'changeme' # None = random
vhost = None # Bind to a specific IP address
class throttle: class throttle:
channels = 3 # Maximum number of channels to scan at once channels = 3 # Maximum number of channels to scan at once
@ -137,7 +139,8 @@ class probe:
self.display = server.ljust(18)+' | ' self.display = server.ljust(18)+' | '
self.semaphore = semaphore self.semaphore = semaphore
self.nickname = None self.nickname = None
self.snapshot = copy.deepcopy(snapshot) # <--- GET FUCKED PYTHON self.snapshot = {'raw':list()}
self.multi = ''
self.channels = {'all':list(), 'current':list(), 'users':dict()} self.channels = {'all':list(), 'current':list(), 'users':dict()}
self.nicks = {'all':list(), 'check':list()} self.nicks = {'all':list(), 'check':list()}
self.loops = {'init':None,'chan':None,'nick':None,'whois':None} self.loops = {'init':None,'chan':None,'nick':None,'whois':None}
@ -150,11 +153,13 @@ class probe:
try: try:
await self.connect() await self.connect()
except Exception as ex: except Exception as ex:
error(self.display + 'failed to connect using SSL/TLS', ex) if settings.error_conn:
error(self.display + 'failed to connect using SSL/TLS', ex)
try: try:
await self.connect(True) await self.connect(True)
except Exception as ex: except Exception as ex:
error(self.display + 'failed to connect', ex) if settings.error_conn:
error(self.display + 'failed to connect', ex)
async def raw(self, data): async def raw(self, data):
self.writer.write(data[:510].encode('utf-8') + b'\r\n') self.writer.write(data[:510].encode('utf-8') + b'\r\n')
@ -176,17 +181,17 @@ class probe:
} }
self.nickname = identity['nick'] self.nickname = identity['nick']
self.reader, self.writer = await asyncio.wait_for(asyncio.open_connection(**options), throttle.timeout) self.reader, self.writer = await asyncio.wait_for(asyncio.open_connection(**options), throttle.timeout)
del options
if not fallback: if not fallback:
self.snapshot['ssl'] = True self.snapshot['ssl'] = True
await self.raw('USER {0} 0 * :{1}'.format(identity['user'], identity['real'])) await self.raw('USER {0} 0 * :{1}'.format(identity['user'], identity['real']))
await self.raw('NICK ' + identity['nick']) await self.raw('NICK ' + identity['nick'])
del identity
await self.listen() await self.listen()
for item in self.loops: for item in self.loops:
if self.loops[item]: if self.loops[item]:
self.loops[item].cancel() self.loops[item].cancel()
for item in [rm for rm in self.snapshot if not self.snapshot[rm]]: with open(f'logs/{self.server}.json{self.multi}', 'w') as fp:
del self.snapshot[item]
with open(f'logs/{self.server}.json', 'w') as fp:
json.dump(self.snapshot, fp) json.dump(self.snapshot, fp)
debug(self.display + 'finished scanning') debug(self.display + 'finished scanning')
@ -204,6 +209,7 @@ class probe:
break break
else: else:
await asyncio.sleep(1.5) await asyncio.sleep(1.5)
del login
if not self.channels['all']: if not self.channels['all']:
error(self.display + 'no channels found') error(self.display + 'no channels found')
await self.raw('QUIT') await self.raw('QUIT')
@ -217,8 +223,8 @@ class probe:
while self.channels['all']: while self.channels['all']:
while len(self.channels['current']) >= throttle.channels: while len(self.channels['current']) >= throttle.channels:
await asyncio.sleep(1) await asyncio.sleep(1)
chan = random.choice(self.channels['all'])
await asyncio.sleep(self.jthrottle) await asyncio.sleep(self.jthrottle)
chan = random.choice(self.channels['all'])
self.channels['all'].remove(chan) self.channels['all'].remove(chan)
try: try:
await self.raw('JOIN ' + chan) await self.raw('JOIN ' + chan)
@ -228,6 +234,7 @@ class probe:
while self.nicks['check']: while self.nicks['check']:
await asyncio.sleep(1) await asyncio.sleep(1)
self.loops['whois'].cancel() self.loops['whois'].cancel()
del self.loops['whois']
await self.raw('QUIT') await self.raw('QUIT')
except asyncio.CancelledError: except asyncio.CancelledError:
pass pass
@ -257,6 +264,7 @@ class probe:
except: except:
break break
else: else:
del nick
await asyncio.sleep(throttle.whois) await asyncio.sleep(throttle.whois)
else: else:
await asyncio.sleep(1) await asyncio.sleep(1)
@ -275,8 +283,13 @@ class probe:
args = line.split() args = line.split()
numeric = args[1] numeric = args[1]
#debug(line) #debug(line)
if numeric in self.snapshot: if sys.getsizeof(self.snapshot) >= settings.log_max:
if not self.snapshot[numeric]: with open(f'logs/{self.server}.json{self.multi}', 'w') as fp:
json.dump(self.snapshot, fp)
self.snapshot = {'raw':list()}
self.multi = '.1' if not self.multi else '.' + str(int(self.multi[1:])+1)
if numeric in snapshot:
if numeric not in self.snapshot:
self.snapshot[numeric] = line self.snapshot[numeric] = line
elif line not in self.snapshot[numeric]: elif line not in self.snapshot[numeric]:
if type(self.snapshot[numeric]) == list: if type(self.snapshot[numeric]) == list:
@ -308,13 +321,15 @@ class probe:
self.display = f'{self.server.ljust(18)} | {host.ljust(25)} | ' self.display = f'{self.server.ljust(18)} | {host.ljust(25)} | '
debug(self.display + 'connected') debug(self.display + 'connected')
self.loops['init'] = asyncio.create_task(self.loop_initial()) self.loops['init'] = asyncio.create_task(self.loop_initial())
elif numeric == '322' and len(args) >= 5: # RPL_LIST elif numeric == '322' and len(args) >= 4: # RPL_LIST
chan = args[3] chan = args[3]
users = args[4]
self.channels['all'].append(chan) self.channels['all'].append(chan)
self.channels['users'][chan] = users if len(args) >= 5:
users = args[4]
self.channels['users'][chan] = users
elif numeric == '323': # RPL_LISTEND elif numeric == '323': # RPL_LISTEND
if self.channels['all']: if self.channels['all']:
del self.loops['init']
debug(self.display + 'found {0} channel(s)'.format(str(len(self.channels['all'])))) debug(self.display + 'found {0} channel(s)'.format(str(len(self.channels['all']))))
self.loops['chan'] = asyncio.create_task(self.loop_channels()) self.loops['chan'] = asyncio.create_task(self.loop_channels())
self.loops['nick'] = asyncio.create_task(self.loop_nick()) self.loops['nick'] = asyncio.create_task(self.loop_nick())
@ -329,6 +344,7 @@ class probe:
self.channels['current'].append(chan) self.channels['current'].append(chan)
if chan in self.channels['users']: if chan in self.channels['users']:
debug('{0}scanning {1} users in {2}'.format(self.display, self.channels['users'][chan].ljust(4), chan)) debug('{0}scanning {1} users in {2}'.format(self.display, self.channels['users'][chan].ljust(4), chan))
del self.channels['users'][chan]
else: else:
debug(f'{self.display}scanning users in {chan}') debug(f'{self.display}scanning users in {chan}')
await self.raw('WHO ' + chan) await self.raw('WHO ' + chan)
@ -406,17 +422,18 @@ else:
if not os.path.isfile(targets_file): if not os.path.isfile(targets_file):
raise SystemExit('error: invalid file path') raise SystemExit('error: invalid file path')
else: else:
try:
os.mkdir('logs')
except FileExistsError:
pass
targets = [line.rstrip() for line in open(targets_file).readlines() if line and line not in donotscan] targets = [line.rstrip() for line in open(targets_file).readlines() if line and line not in donotscan]
found = len(targets) found = len(targets)
debug(f'loaded {found:,} targets') debug(f'loaded {found:,} targets')
targets = [target for target in targets if not os.path.isfile(f'logs/{target}.json')] # Do not scan targets we already have logged for targets = [target for target in targets if not os.path.isfile(f'logs/{target}.json')] # Do not scan targets we already have logged for
if len(targets) < found: if len(targets) < found:
debug(f'removed {found-len(targets):,} targets we already have logs for already') debug(f'removed {found-len(targets):,} targets we already have logs for already')
del found, targets_file
random.shuffle(targets) random.shuffle(targets)
try:
os.mkdir('logs')
except FileExistsError:
pass
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
loop.run_until_complete(main(targets)) loop.run_until_complete(main(targets))
debug('IRCP has finished probing!') debug('IRCP has finished probing!')