IRCP- information gathering tool for irc servers |
git clone git://git.acid.vegas/IRCP.git |
Log | Files | Refs | Archive | README | LICENSE |
ircp.py (23140B)
1 #!/usr/bin/env python 2 # internet relay chat probe for https://internetrelaychat.org/ - developed by acidvegas in python (https://git.acid.vegas/ircp) 3 4 import asyncio 5 import ipaddress 6 import json 7 import os 8 import random 9 import ssl 10 import sys 11 import tarfile 12 import time 13 14 class settings: 15 daemon = False # Run in daemon mode (24/7 throttled scanning) 16 errors = True # Show errors in console 17 errors_conn = False # Show connection errors in console 18 log_max = 5000000 # 5mb # Maximum log size (in bytes) before starting another 19 nickname = 'IRCP' # None = random 20 username = 'ircp' # None = random 21 realname = 'scan@internetrelaychat.org' # None = random 22 ns_mail = 'scan@internetrelaychat.org' # None = random@random.[com|net|org] 23 ns_pass = None # None = random 24 vhost = None # Bind to a specific IP address 25 26 class throttle: 27 channels = 5 if not settings.daemon else 3 # Maximum number of channels to scan at once 28 commands = 1.5 if not settings.daemon else 3 # Delay bewteen multiple commands send to the same target 29 connect = 15 if not settings.daemon else 60 # Delay between each connection attempt on a diffferent port 30 delay = 300 if not settings.daemon else 600 # Delay before registering nick (if enabled) & sending /LIST 31 join = 10 if not settings.daemon else 30 # Delay between channel JOINs 32 nick = 900 if not settings.daemon else 1200 # Delay between every random NICK change 33 part = 10 if not settings.daemon else 30 # Delay before PARTing a channel 34 seconds = 300 if not settings.daemon else 600 # Maximum seconds to wait when throttled for JOIN or WHOIS 35 threads = 500 if not settings.daemon else 300 # Maximum number of threads running 36 timeout = 30 if not settings.daemon else 60 # Timeout for all sockets 37 whois = 15 if not settings.daemon else 30 # Delay between WHOIS requests 38 ztimeout = 600 if not settings.daemon else 900 # Timeout for zero data from server 39 40 class bad: 41 donotscan = ( 42 'irc.dronebl.org', 'irc.alphachat.net', 43 '5.9.164.48', '45.32.74.177', '104.238.146.46', '149.248.55.130', 44 '2001:19f0:6001:1dc::1', '2001:19f0:b001:ce3::1', '2a01:4f8:160:2501:48:164:9:5', '2001:19f0:6401:17c::1' 45 ) 46 chan = { 47 '403' : 'ERR_NOSUCHCHANNEL', '405' : 'ERR_TOOMANYCHANNELS', 48 '435' : 'ERR_BANONCHAN', '442' : 'ERR_NOTONCHANNEL', 49 '448' : 'ERR_FORBIDDENCHANNEL', '470' : 'ERR_LINKCHANNEL', 50 '471' : 'ERR_CHANNELISFULL', '473' : 'ERR_INVITEONLYCHAN', 51 '474' : 'ERR_BANNEDFROMCHAN', '475' : 'ERR_BADCHANNELKEY', 52 '476' : 'ERR_BADCHANMASK', '477' : 'ERR_NEEDREGGEDNICK', 53 '479' : 'ERR_BADCHANNAME', '480' : 'ERR_THROTTLE', 54 '485' : 'ERR_CHANBANREASON', '488' : 'ERR_NOSSL', 55 '489' : 'ERR_SECUREONLYCHAN', '519' : 'ERR_TOOMANYUSERS', 56 '520' : 'ERR_OPERONLY', '926' : 'ERR_BADCHANNEL' 57 } 58 error = { 59 'install identd' : 'Identd required', 60 'trying to reconnect too fast' : 'Throttled', 61 'trying to (re)connect too fast' : 'Throttled', 62 'reconnecting too fast' : 'Throttled', 63 'access denied' : 'Access denied', 64 'not authorized to' : 'Not authorized', 65 'not authorised to' : 'Not authorized', 66 'password mismatch' : 'Password mismatch', 67 'dronebl' : 'DroneBL', 68 'dnsbl' : 'DNSBL', 69 'g:lined' : 'G:Lined', 70 'z:lined' : 'Z:Lined', 71 'timeout' : 'Timeout', 72 'closing link' : 'Banned', 73 'banned' : 'Banned', 74 'client exited' : 'QUIT', 75 'quit' : 'QUIT' 76 } 77 78 def backup(name): 79 try: 80 with tarfile.open(f'backup/{name}.tar.gz', 'w:gz') as tar: 81 for log in os.listdir('logs'): 82 tar.add('logs/' + log) 83 debug('\033[1;32mBACKUP COMPLETE\033[0m') 84 for log in os.listdir('logs'): 85 os.remove('logs/' + log) 86 except Exception as ex: 87 error('\033[1;31mBACKUP FAILED\033[0m', ex) 88 89 def debug(data): 90 print('{0} \033[1;30m|\033[0m [\033[35m~\033[0m] {1}'.format(time.strftime('%I:%M:%S'), data)) 91 92 def error(data, reason=None): 93 if settings.errors: 94 print('{0} \033[1;30m|\033[0m [\033[31m!\033[0m] {1} \033[1;30m({2})\033[0m'.format(time.strftime('%I:%M:%S'), data, str(reason))) if reason else print('{0} \033[1;30m|\033[0m [\033[31m!\033[0m] {1}'.format(time.strftime('%I:%M:%S'), data)) 95 96 def rndnick(): 97 prefix = random.choice(['st','sn','cr','pl','pr','fr','fl','qu','br','gr','sh','sk','tr','kl','wr','bl']+list('bcdfgklmnprstvwz')) 98 midfix = random.choice(('aeiou'))+random.choice(('aeiou'))+random.choice(('bcdfgklmnprstvwz')) 99 suffix = random.choice(['ed','est','er','le','ly','y','ies','iest','ian','ion','est','ing','led','inger']+list('abcdfgklmnprstvwz')) 100 return prefix+midfix+suffix 101 102 def ssl_ctx(): 103 ctx = ssl.create_default_context() 104 ctx.check_hostname = False 105 ctx.verify_mode = ssl.CERT_NONE 106 return ctx 107 108 class probe: 109 def __init__(self, semaphore, server, port, family=2): 110 self.semaphore = semaphore 111 self.server = server 112 self.port = 6697 113 self.oport = port 114 self.family = family 115 self.display = server.ljust(18)+' \033[1;30m|\033[0m unknown network \033[1;30m|\033[0m ' 116 self.nickname = None 117 self.multi = '' 118 self.snapshot = dict() 119 self.channels = {'all':list(), 'current':list(), 'users':dict()} 120 self.nicks = {'all':list(), 'check':list()} 121 self.loops = {'init':None, 'chan':None, 'nick':None, 'whois':None} 122 self.login = {'pass': settings.ns_pass if settings.ns_pass else rndnick(), 'mail': settings.ns_mail if settings.ns_mail else f'{rndnick()}@{rndnick()}.'+random.choice(('com','net','org'))} 123 self.services = {'chanserv':True, 'nickserv':True} 124 self.jthrottle = throttle.join 125 self.nthrottle = throttle.whois 126 self.reader = None 127 self.write = None 128 129 async def sendmsg(self, target, msg): 130 await self.raw(f'PRIVMSG {target} :{msg}') 131 132 async def run(self): 133 async with self.semaphore: 134 try: 135 await self.connect() # 6697 136 except Exception as ex: 137 if settings.errors_conn: 138 error(self.display + '\033[1;31mdisconnected\033[0m - failed to connect using SSL/TLS on port ' + str(self.port), ex) 139 if self.oport not in (6667,6697): 140 self.port = self.oport 141 await asyncio.sleep(throttle.connect) 142 try: 143 await self.connect() # Non-standard 144 except Exception as ex: 145 if settings.errors_conn: 146 error(self.display + '\033[1;31mdisconnected\033[0m - failed to connect using SSL/TLS on port ' + str(self.port), ex) 147 self.port = 6667 148 await asyncio.sleep(throttle.connect) 149 try: 150 await self.connect(True) # 6667 151 except Exception as ex: 152 if settings.errors_conn: 153 error(self.display + '\033[1;31mdisconnected\033[0m - failed to connect on port ' + str(self.port), ex) 154 self.port = self.oport 155 await asyncio.sleep(throttle.connect) 156 try: 157 await self.connect(True) # Non-standard 158 except Exception as ex: 159 if settings.errors_conn: 160 error(self.display + '\033[1;31mdisconnected\033[0m - failed to connect on port ' + str(self.port), ex) 161 else: 162 self.port = 6667 163 await asyncio.sleep(throttle.connect) 164 try: 165 await self.connect(True) # 6667 166 except Exception as ex: 167 if settings.errors_conn: 168 error(self.display + '\033[1;31mdisconnected\033[0m - failed to connect on port ' + str(self.port), ex) 169 170 async def raw(self, data): 171 self.writer.write(data[:510].encode('utf-8') + b'\r\n') 172 await self.writer.drain() 173 174 async def connect(self, fallback=False): 175 options = { 176 'host' : self.server, 177 'port' : self.port, 178 'limit' : 1024, 179 'ssl' : None if fallback else ssl_ctx(), 180 'family' : self.family, 181 'local_addr' : (settings.vhost, random.randint(5000,65000)) if settings.vhost else None 182 } 183 identity = { 184 'nick': settings.nickname if settings.nickname else rndnick(), 185 'user': settings.username if settings.username else rndnick(), 186 'real': settings.realname if settings.realname else rndnick() 187 } 188 self.nickname = identity['nick'] 189 self.reader, self.writer = await asyncio.wait_for(asyncio.open_connection(**options), throttle.timeout) 190 self.snapshot['port'] = options['port'] 191 del options 192 if not fallback: 193 self.snapshot['ssl'] = True 194 await self.raw('USER {0} 0 * :{1}'.format(identity['user'], identity['real'])) 195 await self.raw('NICK ' + identity['nick']) 196 del identity 197 await self.listen() 198 for item in self.loops: 199 if self.loops[item]: 200 self.loops[item].cancel() 201 with open(f'logs/{self.server}.json{self.multi}', 'w') as fp: 202 json.dump(self.snapshot, fp) 203 debug(self.display + 'finished scanning') 204 205 async def loop_initial(self): 206 try: 207 await asyncio.sleep(throttle.delay) 208 cmds = ['ADMIN', 'CAP LS', 'COMMANDS', 'HELP', 'INFO', 'IRCOPS', 'LINKS', 'MAP', 'MODULES -all', 'SERVLIST', 'STATS p', 'VERSION'] 209 random.shuffle(cmds) 210 cmds += ['PRIVMSG NickServ :REGISTER {0} {1}'.format(self.login['pass'], self.login['mail']), 'PRIVMSG ChanServ :LIST *', 'PRIVMSG NickServ :LIST *', 'LIST'] 211 for command in cmds: 212 try: 213 await self.raw(command) 214 except: 215 break 216 else: 217 await asyncio.sleep(throttle.commands) 218 if not self.channels['all']: 219 error(self.display + '\033[31merror\033[0m - no channels found') 220 await self.raw('QUIT') 221 except asyncio.CancelledError: 222 pass 223 except Exception as ex: 224 error(self.display + '\033[31merror\033[0m - loop_initial', ex) 225 226 async def loop_channels(self): 227 try: 228 while self.channels['all']: 229 while len(self.channels['current']) >= throttle.channels: 230 await asyncio.sleep(1) 231 await asyncio.sleep(self.jthrottle) 232 chan = random.choice(self.channels['all']) 233 self.channels['all'].remove(chan) 234 try: 235 if self.services['chanserv']: 236 await self.sendmsg('ChanServ', 'INFO ' + chan) 237 await asyncio.sleep(throttle.commands) 238 await self.raw('JOIN ' + chan) 239 except: 240 break 241 self.loops['nick'].cancel() 242 while self.nicks['check']: 243 await asyncio.sleep(1) 244 self.loops['whois'].cancel() 245 self.loops['nick'].cancel() 246 await self.raw('QUIT') 247 except asyncio.CancelledError: 248 pass 249 except Exception as ex: 250 error(self.display + '\033[31merror\033[0m - loop_channels', ex) 251 252 async def loop_nick(self): 253 try: 254 while True: 255 await asyncio.sleep(throttle.nick+random.randint(60,90)) 256 self.nickname = rndnick() 257 await self.raw('NICK ' + self.nickname) 258 debug(self.display + '\033[0;35mNICK\033[0m - new identity') 259 except asyncio.CancelledError: 260 pass 261 except Exception as ex: 262 error(self.display + '\033[31merror\033[0m - loop_nick', ex) 263 264 async def loop_whois(self): 265 try: 266 while True: 267 if self.nicks['check']: 268 nick = random.choice(self.nicks['check']) 269 self.nicks['check'].remove(nick) 270 try: 271 await self.raw('WHOIS ' + nick) 272 await asyncio.sleep(throttle.commands) 273 if self.services['nickserv']: 274 await self.sendmsg('NickServ', 'INFO ' + nick) 275 await asyncio.sleep(throttle.commands) 276 await self.raw(f'NOTICE {nick} \001VERSION\001') # TODO: check the database if we already have this information to speed things up 277 await asyncio.sleep(throttle.commands) 278 await self.raw(f'NOTICE {nick} \001TIME\001') 279 await asyncio.sleep(throttle.commands) 280 await self.raw(f'NOTICE {nick} \001CLIENTINFO\001') 281 await asyncio.sleep(throttle.commands) 282 await self.raw(f'NOTICE {nick} \001SOURCE\001') 283 except: 284 break 285 else: 286 del nick 287 await asyncio.sleep(throttle.whois) 288 else: 289 await asyncio.sleep(1) 290 except asyncio.CancelledError: 291 pass 292 except Exception as ex: 293 error(self.display + '\033[31merror\033[0m - loop_whois', ex) 294 295 async def db(self, event, data): 296 if event in self.snapshot: 297 if data not in self.snapshot[event]: 298 self.snapshot[event].append(data) 299 else: 300 self.snapshot[event] = [data,] 301 302 async def listen(self): 303 while True: 304 try: 305 if self.reader.at_eof(): 306 break 307 data = await asyncio.wait_for(self.reader.readuntil(b'\r\n'), throttle.ztimeout) 308 line = data.decode('utf-8').strip() 309 args = line.split() 310 event = args[1].upper() 311 if sys.getsizeof(self.snapshot) >= settings.log_max: 312 with open(f'logs/{self.server}.json{self.multi}', 'w') as fp: 313 json.dump(self.snapshot, fp) 314 self.snapshot = dict() 315 self.multi = '.1' if not self.multi else '.' + str(int(self.multi[1:])+1) 316 if args[0].upper() == 'ERROR': 317 await self.db('ERROR', line) 318 elif not event.isdigit() and event not in ('CAP','INVITE','JOIN','KICK','KILL','MODE','NICK','NOTICE','PART','PRIVMSG','QUIT','TOPIC','WHO'): 319 await self.db('RAW', line) 320 elif event != '401': 321 await self.db(event, line) 322 if event in bad.chan and len(args) >= 4: 323 chan = args[3] 324 if chan in self.channels['users']: 325 del self.channels['users'][chan] 326 error(f'{self.display}\033[31merror\033[0m - {chan}', bad.chan[event]) 327 elif line.startswith('ERROR :'): 328 check = [check for check in bad.error if check in line.lower()] 329 if check: 330 if check[0] in ('dronebl','dnsbl'): 331 self.snapshot['proxy'] = True 332 raise Exception(bad.error[check[0]]) 333 elif args[0] == 'PING': 334 await self.raw('PONG ' + args[1][1:]) 335 elif event == 'KICK' and len(args) >= 4: 336 chan = args[2] 337 kicked = args[3] 338 if kicked == self.nickname: 339 if chan in self.channels['current']: 340 self.channels['current'].remove(chan) 341 elif event == 'MODE' and len(args) == 4: 342 nick = args[2] 343 if nick == self.nickname: 344 mode = args[3][1:] 345 if mode == '+r': 346 self.snapshot['registered'] = self.login 347 elif event == '001': #RPL_WELCOME 348 host = args[0][1:] 349 self.snapshot['server'] = self.server 350 self.snapshot['host'] = host 351 if len(host) > 25: 352 self.display = f'{self.server.ljust(18)} \033[1;30m|\033[0m {host[:22]}... \033[1;30m|\033[0m ' 353 else: 354 self.display = f'{self.server.ljust(18)} \033[1;30m|\033[0m {host.ljust(25)} \033[1;30m|\033[0m ' 355 debug(self.display + f'\033[1;32mconnected\033[0m \033[1;30m(port {self.port})\033[0m') 356 self.loops['init'] = asyncio.create_task(self.loop_initial()) 357 elif event == '005': 358 for item in args: 359 if item.startswith('SSL=') and item[4:]: 360 if not self.snapshot['ssl']: 361 self.snapshot['ssl'] = item[4:] 362 break 363 elif event == '311' and len(args) >= 4: # RPL_WHOISUSER 364 nick = args[3] 365 if 'open proxy' in line.lower() or 'proxy monitor' in line.lower(): 366 self.snapshot['proxy'] = True 367 error(self.display + '\033[93mProxy Monitor detected\033[0m', nick) 368 else: 369 debug(f'{self.display}\033[34mWHOIS\033[0m {nick}') 370 elif event == 315 and len(args) >= 3: # RPL_ENDOFWHO 371 chan = args[3] 372 await self.raw(f'MODE {chan} +b') 373 await asyncio.sleep(throttle.commands) 374 await self.raw(f'MODE {chan} +e') 375 await asyncio.sleep(throttle.commands) 376 await self.raw(f'MODE {chan} +I') 377 await asyncio.sleep(throttle.commands) 378 await self.raw(f'NOTICE {chan} \001VERSION\001') 379 await asyncio.sleep(throttle.commands) 380 await self.raw(f'NOTICE {chan} \001TIME\001') 381 await asyncio.sleep(throttle.commands) 382 await self.raw(f'NOTICE {chan} \001CLIENTINFO\001') 383 await asyncio.sleep(throttle.commands) 384 await self.raw(f'NOTICE {chan} \001SOURCE\001') 385 await asyncio.sleep(throttle.part) 386 await self.raw('PART ' + chan) 387 self.channels['current'].remove(chan) 388 elif event == '322' and len(args) >= 4: # RPL_LIST 389 chan = args[3] 390 users = args[4] 391 if users != '0': # no need to JOIN empty channels... 392 self.channels['all'].append(chan) 393 self.channels['users'][chan] = users 394 elif event == '323': # RPL_LISTEND 395 if self.channels['all']: 396 debug(self.display + '\033[36mLIST\033[0m found \033[93m{0}\033[0m channel(s)'.format(str(len(self.channels['all'])))) 397 self.loops['chan'] = asyncio.create_task(self.loop_channels()) 398 self.loops['nick'] = asyncio.create_task(self.loop_nick()) 399 self.loops['whois'] = asyncio.create_task(self.loop_whois()) 400 elif event == '352' and len(args) >= 8: # RPL_WHORPL 401 nick = args[7] 402 if nick not in self.nicks['all']+[self.nickname,]: 403 self.nicks['all'].append(nick) 404 self.nicks['check'].append(nick) 405 elif event == '366' and len(args) >= 4: # RPL_ENDOFNAMES 406 chan = args[3] 407 self.channels['current'].append(chan) 408 if chan in self.channels['users']: 409 debug('{0}\033[32mJOIN\033[0m {1} \033[1;30m(found \033[93m{2}\033[1;30m users)\033[0m'.format(self.display, chan, self.channels['users'][chan])) 410 del self.channels['users'][chan] 411 await self.raw('WHO ' + chan) 412 elif event == '401' and len(args) >= 4: # ERR_NOSUCHNICK 413 nick = args[3] 414 if nick == 'ChanServ': 415 self.services['chanserv'] = False 416 elif nick == 'NickServ': 417 self.services['nickserv'] = False 418 else: 419 await self.raw('WHOWAS ' + nick) 420 elif event == '421' and len(args) >= 3: # ERR_UNKNOWNCOMMAND 421 msg = ' '.join(args[2:]) 422 if 'You must be connected for' in msg: 423 error(self.display + '\033[31merror\033[0m - delay found', msg) 424 elif event == '433': # ERR_NICKINUSE 425 self.nickname = rndnick() 426 await self.raw('NICK ' + self.nickname) 427 elif event == '439' and len(args) >= 11: # ERR_TARGETTOOFAST 428 target = args[3] 429 msg = ' '.join(args[4:])[1:] 430 seconds = args[10] 431 if target[:1] in ('#','&'): 432 self.channels['all'].append(target) 433 if seconds.isdigit(): 434 self.jthrottle = throttle.seconds if int(seconds) > throttle.seconds else int(seconds) 435 else: 436 self.nicks['check'].append(target) 437 if seconds.isdigit(): 438 self.nthrottle = throttle.seconds if int(seconds) > throttle.seconds else int(seconds) 439 error(self.display + '\033[31merror\033[0m - delay found for ' + target, msg) 440 elif event == '465' and len(args) >= 5: # ERR_YOUREBANNEDCREEP 441 check = [check for check in bad.error if check in line.lower()] 442 if check: 443 if check[0] in ('dronebl','dnsbl'): 444 self.snapshot['proxy'] = True 445 raise Exception(bad.error[check[0]]) 446 elif event == '464': # ERR_PASSWDMISMATCH 447 raise Exception('Network has a password') 448 elif event == '487': # ERR_MSGSERVICES 449 if '"/msg NickServ" is no longer supported' in line: 450 await self.raw('/NickServ REGISTER {0} {1}'.format(self.login['pass'], self.login['mail'])) 451 elif event == 'KILL': 452 nick = args[2] 453 if nick == self.nickname: 454 raise Exception('KILL') 455 elif event in ('NOTICE','PRIVMSG') and len(args) >= 4: 456 nick = args[0].split('!')[1:] 457 target = args[2] 458 msg = ' '.join(args[3:])[1:] 459 if target == self.nickname: 460 for i in ('proxy','proxys','proxies'): 461 if i in msg.lower(): 462 self.snapshot['proxy'] = True 463 check = [x for x in ('bopm','hopm') if x in line] 464 if check: 465 error(f'{self.display}\033[93m{check[0].upper()} detected\033[0m') 466 else: 467 error(self.display + '\033[93mProxy Monitor detected\033[0m') 468 for i in ('You must have been using this nick for','You must be connected for','not connected long enough','Please wait', 'You cannot list within the first'): 469 if i in msg: 470 error(self.display + '\033[31merror\033[0m - delay found', msg) 471 break 472 if msg[:8] == '\001VERSION': 473 version = random.choice(('http://www.mibbit.com ajax IRC Client','mIRC v6.35 Khaled Mardam-Bey','xchat 0.24.1 Linux 2.6.27-8-eeepc i686','rZNC Version 1.0 [02/01/11] - Built from ZNC','thelounge v3.0.0 -- https://thelounge.chat/')) 474 await self.raw(f'NOTICE {nick} \001VERSION {version}\001') 475 elif ('You are connected' in line or 'Connected securely via' in line) and ('SSL' in line or 'TLS' in line): 476 cipher = line.split()[-1:][0].replace('\'','').replace('"','') 477 self.snapshot['ssl_cipher'] = cipher 478 elif nick in ('ChanServ','NickServ'): 479 self.snapshot['services'] = True 480 if 'is now registered' in msg or f'Nickname {self.nickname} registered' in msg: 481 debug(self.display + '\033[35mNickServ\033[0m registered') 482 self.snapshot['registered'] = self.login 483 elif '!' not in args[0]: 484 if 'dronebl.org/lookup' in msg: 485 self.snapshot['proxy'] = True 486 error(self.display + '\033[93mDroneBL detected\033[0m') 487 raise Exception('DroneBL') 488 else: 489 if [i for i in ('You\'re banned','You are permanently banned','You are banned','You are not welcome','Temporary K-line') if i in msg]: 490 raise Exception('K-Lined') 491 except (UnicodeDecodeError, UnicodeEncodeError): 492 pass 493 except Exception as ex: 494 error(self.display + '\033[1;31mdisconnected\033[0m', ex) 495 break 496 497 async def main(targets): 498 sema = asyncio.BoundedSemaphore(throttle.threads) # B O U N D E D S E M A P H O R E G A N G 499 jobs = list() 500 for target in targets: 501 server = ':'.join(target.split(':')[-1:]) 502 if ':' not in target: # TODO: IPv6 addresses without a port wont get :6667 appeneded to it like this 503 port = 6697 504 else: 505 port = int(':'.join(target.split(':')[:-1])) 506 try: 507 ipaddress.IPv4Address(server) 508 jobs.append(asyncio.ensure_future(probe(sema, server, port, 2).run())) 509 except: 510 try: 511 ipaddress.IPv6Address(server) 512 jobs.append(asyncio.ensure_future(probe(sema, server, port, 10).run())) 513 except: 514 error('invalid ip address', server) 515 await asyncio.gather(*jobs) 516 517 # Main 518 print('#'*56) 519 print('#{:^54}#'.format('')) 520 print('#{:^54}#'.format('Internet Relay Chat Probe (IRCP)')) 521 print('#{:^54}#'.format('Developed by acidvegas in Python')) 522 print('#{:^54}#'.format('https://git.acid.vegas/ircp')) 523 print('#{:^54}#'.format('')) 524 print('#'*56) 525 if len(sys.argv) != 2: 526 raise SystemExit('error: invalid arguments') 527 else: 528 targets_file = sys.argv[1] 529 if not os.path.isfile(targets_file): 530 raise SystemExit('error: invalid file path') 531 else: 532 try: 533 os.mkdir('logs') 534 except FileExistsError: 535 pass 536 targets = [line.rstrip() for line in open(targets_file).readlines() if line and line not in bad.donotscan] 537 found = len(targets) 538 debug(f'loaded {found:,} targets') 539 if settings.daemon: 540 try: 541 os.mkdir('backup') 542 except FileExistsError: 543 pass 544 else: 545 targets = [target for target in targets if not os.path.isfile(f'logs/{target}.json')] # Do not scan targets we already have logged for 546 if len(targets) < found: 547 debug(f'removed {found-len(targets):,} targets we already have logs for already') 548 del found, targets_file 549 while True: 550 random.shuffle(targets) 551 asyncio.run(main(targets)) 552 debug('IRCP has finished probing!') 553 if settings.daemon: 554 backup(time.strftime('%y%m%d-%H%M%S')) 555 else: 556 break