2023-05-25 20:03:39 +00:00
#!/usr/bin/env python
# internet relay chat probe for https://internetrelaychat.org/ - developed by acidvegas in python (https://git.acid.vegas/ircp)
import asyncio
import copy
import json
import os
import random
import ssl
import sys
import time
class settings :
2023-05-30 20:59:24 +00:00
daemon = False # Run in daemon mode (24/7 throttled scanning)
2023-05-30 00:59:17 +00:00
errors = True # Show errors in console
errors_conn = False # Show connection errors in console
log_max = 5000000 # Maximum log size (in bytes) before starting another
nickname = ' IRCP ' # None = random
username = ' ircp ' # None = random
realname = ' scan@internetrelaychat.org ' # None = random
ns_mail = ' scan@internetrelaychat.org ' # None = random@random.[com|net|org]
ns_pass = ' changeme ' # None = random
vhost = None # Bind to a specific IP address
2023-05-25 20:03:39 +00:00
class throttle :
2023-05-30 20:59:24 +00:00
channels = 3 if not settings . daemon else 2 # Maximum number of channels to scan at once
delay = 300 if not settings . daemon else 600 # Delay before registering nick (if enabled) & sending /LIST
join = 10 if not settings . daemon else 30 # Delay between channel JOINs
nick = 300 if not settings . daemon else 600 # Delay between every random NICK change
part = 10 if not settings . daemon else 30 # Delay before PARTing a channel
seconds = 300 if not settings . daemon else 600 # Maximum seconds to wait when throttled for JOIN
threads = 100 if not settings . daemon else 25 # Maximum number of threads running
timeout = 30 if not settings . daemon else 60 # Timeout for all sockets
whois = 5 if not settings . daemon else 15 # Delay between WHOIS requests
ztimeout = 200 if not settings . daemon else 300 # Timeout for zero data from server
2023-05-25 20:03:39 +00:00
2023-05-27 01:31:28 +00:00
donotscan = (
2023-05-30 20:59:24 +00:00
' irc.dronebl.org ' , ' irc.alphachat.net ' ,
' 5.9.164.48 ' , ' 45.32.74.177 ' , ' 104.238.146.46 ' , ' 149.248.55.130 ' ,
' 2001:19f0:6001:1dc::1 ' , ' 2001:19f0:b001:ce3::1 ' , ' 2a01:4f8:160:2501:48:164:9:5 ' , ' 2001:19f0:6401:17c::1 '
2023-05-27 01:31:28 +00:00
)
2023-05-31 23:37:46 +00:00
badchan = {
' 403 ' : ' ERR_NOSUCHCHANNEL ' , ' 405 ' : ' ERR_TOOMANYCHANNELS ' ,
' 435 ' : ' ERR_BANONCHAN ' , ' 442 ' : ' ERR_NOTONCHANNEL ' ,
' 448 ' : ' ERR_FORBIDDENCHANNEL ' , ' 470 ' : ' ERR_LINKCHANNEL ' ,
' 471 ' : ' ERR_CHANNELISFULL ' , ' 473 ' : ' ERR_INVITEONLYCHAN ' ,
' 474 ' : ' ERR_BANNEDFROMCHAN ' , ' 475 ' : ' ERR_BADCHANNELKEY ' ,
' 476 ' : ' ERR_BADCHANMASK ' , ' 477 ' : ' ERR_NEEDREGGEDNICK ' ,
' 479 ' : ' ERR_BADCHANNAME ' , ' 480 ' : ' ERR_THROTTLE ' ,
' 485 ' : ' ERR_CHANBANREASON ' , ' 488 ' : ' ERR_NOSSL ' ,
' 489 ' : ' ERR_SECUREONLYCHAN ' , ' 519 ' : ' ERR_TOOMANYUSERS ' ,
' 520 ' : ' ERR_OPERONLY ' , ' 926 ' : ' ERR_BADCHANNEL '
2023-05-25 20:03:39 +00:00
}
2023-05-30 20:59:24 +00:00
def backup ( name ) :
try :
with tarfile . open ( f ' backup/ { name } .tar.gz ' , ' w:gz ' ) as tar :
for log in os . listdir ( ' logs ' ) :
tar . add ( ' logs/ ' + log )
debug ( ' \033 [1;32mBACKUP COMPLETE \033 [0m ' )
for log in os . listdir ( ' logs ' ) :
os . remove ( ' logs/ ' + log )
except Exception as ex :
error ( ' \033 [1;31mBACKUP FAILED \033 [0m ' , ex )
2023-05-25 20:03:39 +00:00
def debug ( data ) :
2023-05-30 19:08:52 +00:00
print ( ' {0} \033 [30m| \033 [0m [ \033 [35m~ \033 [0m] {1} ' . format ( time . strftime ( ' % I: % M: % S ' ) , data ) )
2023-05-25 20:03:39 +00:00
def error ( data , reason = None ) :
if settings . errors :
2023-05-30 22:40:41 +00:00
print ( ' {0} \033 [30m| \033 [0m [ \033 [31m! \033 [0m] {1} \033 [30m( {2} ) \033 [0m ' . format ( time . strftime ( ' % I: % M: % S ' ) , data , str ( reason ) ) ) if reason else print ( ' {0} \033 [30m| \033 [0m [ \033 [31m! \033 [0m] {1} ' . format ( time . strftime ( ' % I: % M: % S ' ) , data ) )
2023-05-25 20:03:39 +00:00
def rndnick ( ) :
prefix = random . choice ( [ ' st ' , ' sn ' , ' cr ' , ' pl ' , ' pr ' , ' fr ' , ' fl ' , ' qu ' , ' br ' , ' gr ' , ' sh ' , ' sk ' , ' tr ' , ' kl ' , ' wr ' , ' bl ' ] + list ( ' bcdfgklmnprstvwz ' ) )
midfix = random . choice ( ( ' aeiou ' ) ) + random . choice ( ( ' aeiou ' ) ) + random . choice ( ( ' bcdfgklmnprstvwz ' ) )
suffix = random . choice ( [ ' ed ' , ' est ' , ' er ' , ' le ' , ' ly ' , ' y ' , ' ies ' , ' iest ' , ' ian ' , ' ion ' , ' est ' , ' ing ' , ' led ' , ' inger ' ] + list ( ' abcdfgklmnprstvwz ' ) )
return prefix + midfix + suffix
def ssl_ctx ( ) :
ctx = ssl . create_default_context ( )
ctx . check_hostname = False
ctx . verify_mode = ssl . CERT_NONE
return ctx
class probe :
def __init__ ( self , server , semaphore ) :
self . server = server
2023-05-31 04:23:03 +00:00
self . display = server . ljust ( 18 ) + ' \033 [30m| \033 [0m unknown network \033 [30m| \033 [0m '
2023-05-25 20:03:39 +00:00
self . semaphore = semaphore
2023-05-27 22:47:43 +00:00
self . nickname = None
2023-05-31 23:37:46 +00:00
self . snapshot = dict ( )
2023-05-30 00:59:17 +00:00
self . multi = ' '
2023-05-27 22:47:43 +00:00
self . channels = { ' all ' : list ( ) , ' current ' : list ( ) , ' users ' : dict ( ) }
2023-05-30 19:08:52 +00:00
self . nicks = { ' all ' : list ( ) , ' check ' : list ( ) }
self . loops = { ' init ' : None , ' chan ' : None , ' nick ' : None , ' whois ' : None }
2023-05-29 20:25:38 +00:00
self . jthrottle = throttle . join
2023-05-25 20:03:39 +00:00
self . reader = None
2023-05-29 20:25:38 +00:00
self . write = None
2023-05-25 20:03:39 +00:00
async def run ( self ) :
async with self . semaphore :
try :
await self . connect ( )
except Exception as ex :
2023-05-30 01:27:18 +00:00
if settings . errors_conn :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [1;31mdisconnected \033 [0m - failed to connect using SSL/TLS ' , ex )
2023-05-25 20:03:39 +00:00
try :
await self . connect ( True )
except Exception as ex :
2023-05-30 01:27:18 +00:00
if settings . errors_conn :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [1;31mdisconnected \033 [0m - failed to connect ' , ex )
2023-05-25 20:03:39 +00:00
async def raw ( self , data ) :
self . writer . write ( data [ : 510 ] . encode ( ' utf-8 ' ) + b ' \r \n ' )
await self . writer . drain ( )
async def connect ( self , fallback = False ) :
options = {
' host ' : self . server ,
' port ' : 6667 if fallback else 6697 ,
' limit ' : 1024 ,
' ssl ' : None if fallback else ssl_ctx ( ) ,
' family ' : 2 , # 2 = IPv4 | 10 = IPv6 (TODO: Check for IPv6 using server DNS)
' local_addr ' : settings . vhost
}
identity = {
' nick ' : settings . nickname if settings . nickname else rndnick ( ) ,
' user ' : settings . username if settings . username else rndnick ( ) ,
' real ' : settings . realname if settings . realname else rndnick ( )
}
2023-05-28 02:16:31 +00:00
self . nickname = identity [ ' nick ' ]
2023-05-25 20:03:39 +00:00
self . reader , self . writer = await asyncio . wait_for ( asyncio . open_connection ( * * options ) , throttle . timeout )
2023-05-30 00:59:17 +00:00
del options
2023-05-26 23:59:31 +00:00
if not fallback :
self . snapshot [ ' ssl ' ] = True
2023-05-25 20:03:39 +00:00
await self . raw ( ' USER {0} 0 * : {1} ' . format ( identity [ ' user ' ] , identity [ ' real ' ] ) )
await self . raw ( ' NICK ' + identity [ ' nick ' ] )
2023-05-30 00:59:17 +00:00
del identity
2023-05-25 20:03:39 +00:00
await self . listen ( )
2023-05-26 07:26:45 +00:00
for item in self . loops :
if self . loops [ item ] :
self . loops [ item ] . cancel ( )
2023-05-30 00:59:17 +00:00
with open ( f ' logs/ { self . server } .json { self . multi } ' , ' w ' ) as fp :
2023-05-25 20:03:39 +00:00
json . dump ( self . snapshot , fp )
2023-05-27 22:47:43 +00:00
debug ( self . display + ' finished scanning ' )
2023-05-25 20:03:39 +00:00
async def loop_initial ( self ) :
try :
await asyncio . sleep ( throttle . delay )
login = {
' pass ' : settings . ns_pass if settings . ns_pass else rndnick ( ) ,
2023-05-28 19:02:13 +00:00
' mail ' : settings . ns_mail if settings . ns_mail else f ' { rndnick ( ) } @ { rndnick ( ) } . ' + random . choice ( ( ' com ' , ' net ' , ' org ' ) )
2023-05-25 20:03:39 +00:00
}
2023-05-30 19:08:52 +00:00
cmds = [ ' ADMIN ' , ' CAP LS ' , ' INFO ' , ' IRCOPS ' , ' LINKS ' , ' MAP ' , ' MODULES -all ' , ' STATS p ' , ' VERSION ' ]
2023-05-30 06:35:57 +00:00
random . shuffle ( cmds )
2023-05-30 19:08:52 +00:00
cmds + = [ ' PRIVMSG NickServ :REGISTER {0} {1} ' . format ( login [ ' pass ' ] , login [ ' mail ' ] ) , ' LIST ' ]
2023-05-30 06:35:57 +00:00
for command in cmds :
2023-05-25 20:03:39 +00:00
try :
await self . raw ( command )
except :
break
else :
await asyncio . sleep ( 1.5 )
2023-05-30 00:59:17 +00:00
del login
2023-05-25 20:03:39 +00:00
if not self . channels [ ' all ' ] :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [31merror \033 [0m - no channels found ' )
2023-05-25 20:03:39 +00:00
await self . raw ( ' QUIT ' )
except asyncio . CancelledError :
pass
2023-05-27 23:15:08 +00:00
except Exception as ex :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [31merror \033 [0m - loop_initial ' , ex )
2023-05-25 20:03:39 +00:00
async def loop_channels ( self ) :
try :
while self . channels [ ' all ' ] :
while len ( self . channels [ ' current ' ] ) > = throttle . channels :
await asyncio . sleep ( 1 )
2023-05-29 20:25:38 +00:00
await asyncio . sleep ( self . jthrottle )
2023-05-30 00:59:17 +00:00
chan = random . choice ( self . channels [ ' all ' ] )
2023-05-25 20:03:39 +00:00
self . channels [ ' all ' ] . remove ( chan )
try :
await self . raw ( ' JOIN ' + chan )
except :
break
2023-05-28 03:43:19 +00:00
self . loops [ ' nick ' ] . cancel ( )
2023-05-25 20:03:39 +00:00
while self . nicks [ ' check ' ] :
await asyncio . sleep ( 1 )
2023-05-28 03:43:19 +00:00
self . loops [ ' whois ' ] . cancel ( )
2023-05-30 00:59:17 +00:00
del self . loops [ ' whois ' ]
2023-05-25 20:03:39 +00:00
await self . raw ( ' QUIT ' )
except asyncio . CancelledError :
pass
2023-05-27 23:15:08 +00:00
except Exception as ex :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [31merror \033 [0m - loop_channels ' , ex )
2023-05-25 20:03:39 +00:00
2023-05-28 03:43:19 +00:00
async def loop_nick ( self ) :
try :
while True :
await asyncio . sleep ( throttle . nick )
self . nickname = rndnick ( )
2023-05-29 05:23:42 +00:00
await self . raw ( ' NICK ' + self . nickname )
2023-05-28 03:43:19 +00:00
except asyncio . CancelledError :
pass
except Exception as ex :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [31merror \033 [0m - loop_nick ' , ex )
2023-05-28 03:43:19 +00:00
2023-05-25 20:03:39 +00:00
async def loop_whois ( self ) :
try :
while True :
if self . nicks [ ' check ' ] :
nick = random . choice ( self . nicks [ ' check ' ] )
self . nicks [ ' check ' ] . remove ( nick )
try :
await self . raw ( ' WHOIS ' + nick )
except :
break
else :
2023-05-30 00:59:17 +00:00
del nick
2023-05-25 20:03:39 +00:00
await asyncio . sleep ( throttle . whois )
else :
await asyncio . sleep ( 1 )
except asyncio . CancelledError :
pass
2023-05-27 23:15:08 +00:00
except Exception as ex :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [31merror \033 [0m - loop_whois ' , ex )
2023-05-25 20:03:39 +00:00
async def listen ( self ) :
2023-05-27 23:15:08 +00:00
while True :
2023-05-26 07:26:45 +00:00
try :
2023-05-31 23:37:46 +00:00
if self . reader . at_eof ( ) :
2023-05-27 23:15:08 +00:00
break
2023-05-31 23:37:46 +00:00
data = await asyncio . wait_for ( self . reader . readuntil ( b ' \r \n ' ) , throttle . ztimeout )
line = data . decode ( ' utf-8 ' ) . strip ( )
args = line . split ( )
event = args [ 1 ] . upper ( )
if sys . getsizeof ( self . snapshot ) > = settings . log_max :
2023-05-30 00:59:17 +00:00
with open ( f ' logs/ { self . server } .json { self . multi } ' , ' w ' ) as fp :
json . dump ( self . snapshot , fp )
2023-05-31 23:37:46 +00:00
self . snapshot = dict ( )
2023-05-30 00:59:17 +00:00
self . multi = ' .1 ' if not self . multi else ' . ' + str ( int ( self . multi [ 1 : ] ) + 1 )
2023-05-31 23:37:46 +00:00
if args [ 0 ] . upper ( ) == ' ERROR ' :
self . snapshot [ ' ERROR ' ] = self . snapshot [ ' ERROR ' ] + [ line , ] if ' ERROR ' in self . snapshot else [ line , ]
elif not event . isdigit ( ) and event not in ( ' CAP ' , ' INVITE ' , ' JOIN ' , ' KICK ' , ' KILL ' , ' MODE ' , ' NICK ' , ' NOTICE ' , ' PART ' , ' PRIVMSG ' , ' QUIT ' , ' TOPIC ' , ' WHO ' ) :
self . snapshot [ ' RAW ' ] = self . snapshot [ ' RAW ' ] + [ line , ] if ' RAW ' in self . snapshot else [ line , ]
2023-05-29 04:27:23 +00:00
else :
2023-05-31 23:37:46 +00:00
self . snapshot [ event ] = self . snapshot [ event ] + [ line , ] if event in self . snapshot else [ line , ]
2023-06-05 20:13:26 +00:00
if event in badchan and len ( args ) > = 4 :
2023-05-29 20:25:38 +00:00
chan = args [ 3 ]
if chan in self . channels [ ' users ' ] :
del self . channels [ ' users ' ] [ chan ]
2023-06-05 20:13:26 +00:00
error ( f ' { self . display } \033 [31merror \033 [0m - { chan } ' , badchan [ event ] )
2023-05-31 23:37:46 +00:00
elif line . startswith ( ' ERROR : ' ) :
if line . startswith ( ' ERROR :Closing Link ' ) :
if ' dronebl ' in line . lower ( ) :
self . snapshot [ ' proxy ' ] = True
error ( self . display + ' \033 [93mDroneBL detected \033 [30m ' )
raise Exception ( ' DroneBL ' )
else :
raise Exception ( ' Banned ' )
elif line . startswith ( ' ERROR :Trying to reconnect too fast ' ) or line . startswith ( ' ERROR :Your host is trying to (re)connect too fast ' ) or line . startswith ( ' ERROR :Reconnecting too fast ' ) :
raise Exception ( ' Throttled ' )
elif line . startswith ( ' ERROR :Access denied ' ) :
raise Exception ( ' Access denied ' )
2023-05-25 20:03:39 +00:00
elif args [ 0 ] == ' PING ' :
await self . raw ( ' PONG ' + args [ 1 ] [ 1 : ] )
2023-05-31 23:37:46 +00:00
elif event == ' 001 ' : #RPL_WELCOME
2023-05-25 20:03:39 +00:00
host = args [ 0 ] [ 1 : ]
self . snapshot [ ' server ' ] = self . server
self . snapshot [ ' host ' ] = host
if len ( host ) > 25 :
2023-05-30 19:08:52 +00:00
self . display = f ' { self . server . ljust ( 18 ) } \033 [30m| \033 [0m { host [ : 22 ] } ... \033 [30m| \033 [0m '
2023-05-25 20:03:39 +00:00
else :
2023-05-30 19:08:52 +00:00
self . display = f ' { self . server . ljust ( 18 ) } \033 [30m| \033 [0m { host . ljust ( 25 ) } \033 [30m| \033 [0m '
debug ( self . display + ' \033 [1;32mconnected \033 [0m ' )
2023-05-25 20:03:39 +00:00
self . loops [ ' init ' ] = asyncio . create_task ( self . loop_initial ( ) )
2023-06-01 00:59:30 +00:00
elif event == ' 005 ' :
for item in args :
if item . startswith ( ' SSL= ' ) and item [ 4 : ] :
if not self . snapshot [ ' ssl ' ] :
self . snapshot [ ' ssl ' ] = item [ 4 : ]
break
2023-05-31 23:37:46 +00:00
elif event == ' 311 ' and len ( args ) > = 4 : # RPL_WHOISUSER
2023-05-30 19:08:52 +00:00
nick = args [ 3 ]
2023-05-30 21:48:13 +00:00
if ' open proxy ' in line . lower ( ) or ' proxy monitor ' in line . lower ( ) :
self . snapshot [ ' proxy ' ] = True
error ( self . display + ' \033 [93mProxy Monitor detected \033 [30m ' )
else :
debug ( f ' { self . display } \033 [34mWHOIS \033 [0m { nick } ' )
2023-05-31 23:37:46 +00:00
elif event == ' 322 ' and len ( args ) > = 4 : # RPL_LIST
2023-05-25 20:03:39 +00:00
chan = args [ 3 ]
2023-05-31 04:33:36 +00:00
users = args [ 4 ]
if users != ' 0 ' : # no need to JOIN empty channels...
self . channels [ ' all ' ] . append ( chan )
2023-05-30 00:59:17 +00:00
self . channels [ ' users ' ] [ chan ] = users
2023-05-31 23:37:46 +00:00
elif event == ' 323 ' : # RPL_LISTEND
2023-05-25 20:03:39 +00:00
if self . channels [ ' all ' ] :
2023-05-30 00:59:17 +00:00
del self . loops [ ' init ' ]
2023-05-31 04:23:03 +00:00
debug ( self . display + ' \033 [36mLIST \033 [0m found \033 [93m {0} \033 [0m channel(s) ' . format ( str ( len ( self . channels [ ' all ' ] ) ) ) )
2023-05-28 03:43:19 +00:00
self . loops [ ' chan ' ] = asyncio . create_task ( self . loop_channels ( ) )
self . loops [ ' nick ' ] = asyncio . create_task ( self . loop_nick ( ) )
self . loops [ ' whois ' ] = asyncio . create_task ( self . loop_whois ( ) )
2023-05-31 23:37:46 +00:00
elif event == ' 352 ' and len ( args ) > = 8 : # RPL_WHORPL
2023-05-25 20:03:39 +00:00
nick = args [ 7 ]
2023-05-31 05:46:53 +00:00
if nick not in self . nicks [ ' all ' ] + [ self . nickname , ] :
2023-05-25 20:03:39 +00:00
self . nicks [ ' all ' ] . append ( nick )
self . nicks [ ' check ' ] . append ( nick )
2023-05-31 23:37:46 +00:00
elif event == ' 366 ' and len ( args ) > = 4 : # RPL_ENDOFNAMES
2023-05-25 20:03:39 +00:00
chan = args [ 3 ]
self . channels [ ' current ' ] . append ( chan )
2023-05-31 04:33:36 +00:00
debug ( ' {0} \033 [32mJOIN \033 [0m {1} \033 [30m(found \033 [93m {2} \033 [30m users) \033 [0m ' . format ( self . display , chan , self . channels [ ' users ' ] [ chan ] ) )
del self . channels [ ' users ' ] [ chan ]
2023-05-25 20:03:39 +00:00
await self . raw ( ' WHO ' + chan )
await asyncio . sleep ( throttle . part )
await self . raw ( ' PART ' + chan )
self . channels [ ' current ' ] . remove ( chan )
2023-05-31 23:37:46 +00:00
elif event == ' 421 ' and len ( args ) > = 3 : # ERR_UNKNOWNCOMMAND
2023-05-25 20:03:39 +00:00
msg = ' ' . join ( args [ 2 : ] )
if ' You must be connected for ' in msg :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [31merror \033 [0m - delay found ' , msg )
2023-05-31 23:37:46 +00:00
elif event == ' 433 ' : # ERR_NICKINUSE
2023-05-25 20:03:39 +00:00
if not settings . nickname :
await self . raw ( ' NICK ' + rndnick ( ) )
else :
await self . raw ( ' NICK ' + settings . nickname + str ( random . randint ( 1000 , 9999 ) ) )
2023-05-31 23:37:46 +00:00
elif event == ' 439 ' and len ( args ) > = 5 : # ERR_TARGETTOOFAST
2023-05-29 20:25:38 +00:00
chan = args [ 3 ]
msg = ' ' . join ( args [ 4 : ] ) [ 1 : ]
self . channels [ ' all ' ] . append ( chan )
if ' Target change too fast ' in msg and len ( args ) > = 11 :
seconds = args [ 10 ]
if seconds . isdigit ( ) :
seconds = int ( seconds )
self . jthrottle = throttle . seconds if seconds > throttle . seconds else seconds
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [31merror \033 [0m - delay found ' , msg )
2023-05-31 23:37:46 +00:00
elif event == ' 465 ' : # ERR_YOUREBANNEDCREEP
2023-05-30 21:38:20 +00:00
if ' dronebl ' in line . lower ( ) :
self . snapshot [ ' proxy ' ] = True
error ( self . display + ' \033 [93mDroneBL detected \033 [30m ' )
raise Exception ( ' DroneBL ' )
else :
raise Exception ( ' K-Lined ' )
2023-05-31 23:37:46 +00:00
elif event == ' 464 ' : # ERR_PASSWDMISMATCH
2023-05-27 22:47:43 +00:00
raise Exception ( ' Network has a password ' )
2023-05-31 23:37:46 +00:00
elif event == ' 487 ' : # ERR_MSGSERVICES
2023-05-31 04:59:18 +00:00
if ' " /msg NickServ " is no longer supported ' in line :
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 ' ) )
}
await self . raw ( ' /NickServ REGISTER {0} {1} ' . format ( login [ ' pass ' ] , login [ ' mail ' ] ) )
2023-05-31 23:37:46 +00:00
elif event == ' KILL ' :
2023-05-30 19:08:52 +00:00
nick = args [ 2 ]
if nick == self . nickname :
raise Exception ( ' KILL ' )
else :
if ' KILL ' in self . snapshot :
del self . snapshot [ ' KILL ' ]
2023-05-31 23:37:46 +00:00
elif event in ( ' NOTICE ' , ' PRIVMSG ' ) and len ( args ) > = 4 :
2023-05-27 22:47:43 +00:00
nick = args [ 0 ] . split ( ' ! ' ) [ 1 : ]
target = args [ 2 ]
msg = ' ' . join ( args [ 3 : ] ) [ 1 : ]
if target == self . nickname :
2023-05-30 21:38:20 +00:00
for i in ( ' proxy ' , ' proxys ' , ' proxies ' ) :
if i in msg . lower ( ) :
self . snapshot [ ' proxy ' ] = True
2023-05-31 23:37:46 +00:00
check = [ x for x in ( ' bopm ' , ' hopm ' ) if x in line ]
2023-05-30 21:38:20 +00:00
if check :
error ( f ' { self . display } \033 [93m { check . upper ( ) } detected \033 [30m ' )
else :
error ( self . display + ' \033 [93mProxy Monitor detected \033 [30m ' )
2023-05-27 21:33:44 +00:00
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 ' ) :
if i in msg :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [31merror \033 [0m - delay found ' , msg )
2023-05-27 22:47:43 +00:00
break
if msg [ : 8 ] == ' \001 VERSION ' :
2023-05-28 02:19:37 +00:00
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/ ' ) )
2023-05-27 22:47:43 +00:00
await self . raw ( f ' NOTICE { nick } \001 VERSION { version } \001 ' )
elif nick == ' NickServ ' :
self . snapshot [ ' services ' ] = True
2023-05-31 05:21:01 +00:00
if ' is now registered ' in msg or f ' Nickname { self . nickname } registered ' in msg :
debug ( self . display + ' \033 [35mNickServ \033 [0m registered ' )
self . snapshot [ ' registered ' ] = True
2023-05-27 22:47:43 +00:00
elif ' ! ' not in args [ 0 ] :
if ' dronebl.org/lookup ' in msg :
2023-05-30 21:38:20 +00:00
self . snapshot [ ' proxy ' ] = True
2023-05-30 22:51:09 +00:00
error ( self . display + ' \033 [93mDroneBL detected \033 [0m ' )
2023-05-27 22:47:43 +00:00
raise Exception ( ' DroneBL ' )
else :
if [ i for i in ( ' You \' re banned ' , ' You are permanently banned ' , ' You are banned ' , ' You are not welcome ' ) if i in msg ] :
raise Exception ( ' K-Lined ' )
2023-05-26 07:26:45 +00:00
except ( UnicodeDecodeError , UnicodeEncodeError ) :
pass
except Exception as ex :
2023-05-30 19:08:52 +00:00
error ( self . display + ' \033 [1;31mdisconnected \033 [0m ' , ex )
2023-05-26 07:26:45 +00:00
break
2023-05-25 20:03:39 +00:00
async def main ( targets ) :
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
jobs = list ( )
for target in targets :
jobs . append ( asyncio . ensure_future ( probe ( target , sema ) . run ( ) ) )
await asyncio . gather ( * jobs )
# Main
print ( ' # ' * 56 )
print ( ' # {:^54} # ' . format ( ' ' ) )
print ( ' # {:^54} # ' . format ( ' Internet Relay Chat Probe (IRCP) ' ) )
print ( ' # {:^54} # ' . format ( ' Developed by acidvegas in Python ' ) )
print ( ' # {:^54} # ' . format ( ' https://git.acid.vegas/ircp ' ) )
print ( ' # {:^54} # ' . format ( ' ' ) )
print ( ' # ' * 56 )
if len ( sys . argv ) != 2 :
raise SystemExit ( ' error: invalid arguments ' )
else :
targets_file = sys . argv [ 1 ]
if not os . path . isfile ( targets_file ) :
raise SystemExit ( ' error: invalid file path ' )
else :
2023-05-30 00:59:17 +00:00
try :
os . mkdir ( ' logs ' )
except FileExistsError :
pass
2023-05-27 01:31:28 +00:00
targets = [ line . rstrip ( ) for line in open ( targets_file ) . readlines ( ) if line and line not in donotscan ]
2023-05-25 20:03:39 +00:00
found = len ( targets )
debug ( f ' loaded { found : , } targets ' )
2023-05-30 20:59:24 +00:00
if settings . daemon :
try :
os . mkdir ( ' backup ' )
except FileExistsError :
pass
else :
targets = [ target for target in targets if not os . path . isfile ( f ' logs/ { target } .json ' ) ] # Do not scan targets we already have logged for
2023-05-25 20:03:39 +00:00
if len ( targets ) < found :
debug ( f ' removed { found - len ( targets ) : , } targets we already have logs for already ' )
2023-05-30 00:59:17 +00:00
del found , targets_file
2023-05-30 20:59:24 +00:00
while True :
random . shuffle ( targets )
loop = asyncio . get_event_loop ( )
loop . run_until_complete ( main ( targets ) )
debug ( ' IRCP has finished probing! ' )
if settings . daemon :
backup ( time . strftime ( ' % y % m %d - % H % M % S ' ) )
else :
break