Skip to content

Instantly share code, notes, and snippets.

@kolen
Created February 13, 2009 20:22
Show Gist options
  • Save kolen/64084 to your computer and use it in GitHub Desktop.
Save kolen/64084 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
# Directconnect bot
import asyncore, socket, re, sys, atexit, time
__revision__ = '0.0.1'
config = {'nickname': 'DoctorThanatos',
'interest': 'Stats bot',
'speed': 'DSL\x01',
'email': '[email protected]',
'sharesize': '0',
'bind_address': '',
'bind_port': 6102,
'reconnect_period': 60*20}
class dc_async_client:
def __init__(self):
self.connections = {}
self.hubs = {}
self.control_server = control_server(self)
pass
def connect(self, host, port):
self.hubs[(host,port)] = hub_record(host, port)
self.connections[host] = dc_async_connection(host, port, self)
print self.connections
def main_loop(self):
while 1:
asyncore.loop(60)
currtime = time.time()
for i in self.hubs:
chub = self.hubs[i]
if chub.reconnect_at and chub.reconnect_at <= currtime:
self.connect(chub.host, chub.port)
def notify_die(self, client):
print '[%s] died' % client.host
hub_r = self.hubs[(client.host,client.port)]
hub_r.reconnect_at = time.time() + hub_r.reconnect_period
hub_r.up = 0
def notify_up(self, client):
self.hubs[(client.host, client.port)].up = 1
class hub_record:
def __init__(self, host, port):
self.host = host
self.port = port
self.reconnect_period = config['reconnect_period']
self.reconnect_at = 0
self.up = 0
class dc_async_connection(asyncore.dispatcher):
def __init__(self, host, port, keeper, password=''):
asyncore.dispatcher.__init__(self)
self.buffer = ''
self.inbuffer = ''
self.keeper = keeper
self.host = host
self.port = port
try:
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect( (host, port) )
except socket.gaierror:
keeper.notify_die(self)
self.hubname = ''
self.users = {}
self.password = password
def handle_connect(self):
pass
def handle_read(self):
data = self.recv(1024)
self.inbuffer += data
unfin_pos = self.inbuffer.rfind('|')
if unfin_pos != -1:
process_cmds = self.inbuffer[:unfin_pos]
self.inbuffer = self.inbuffer[unfin_pos+1:]
for cmd in process_cmds.split('|'):
self.dispatch_dc_command(cmd)
def writable(self):
return (len(self.buffer) > 0)
def handle_write(self):
# print "Sent: {%s}" % self.buffer
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
def dispatch_dc_command(self, cmd):
print "Got DC cmd: [%s]" % cmd
if cmd.startswith('$Lock'):
lock = cmd[len('$Lock '):]
lock = lock[:lock.find(' ')]
print "|%s|\n*%s*" % (lock, cmd)
self.buffer += '$Key %s|' % compute_access_key(lock)
self.buffer += '$ValidateNick %s|' % config['nickname']
elif cmd.startswith('$HubName'):
self.hubname = cmd[len('$HubName')+1:]
print 'Hubname: ''%s''' % self.hubname
elif cmd.startswith('$Hello'):
interest_tag = '<DC_ASYNC_CLIENT V:%s RC4,M:S,H:0,S:0 >' % __revision__
self.buffer += '$MyINFO $ALL %s %s$ $%s$%s$%s$|' % (
config['nickname'],
config['interest']+interest_tag,
config['speed'],
config['email'],
config['sharesize'])
self.buffer += '$GetNickList|'
self.keeper.notify_up(self)
elif cmd.startswith('$MyINFO'):
match = re.match('\$MyINFO \$ALL (\S+) (.*)\$ \$(.*)\$(.*)\$(.*)\$',
cmd)
if match:
newuser = {'nick': match.group(1),
'interest': match.group(2),
'speed': match.group(3),
'email': match.group(4),
'sharesize': match.group(5)}
print newuser
self.users[newuser['nick']] = newuser
elif cmd.startswith('$Quit'):
username = cmd[len('$Quit')+1:]
if self.users.has_key(username):
del self.users[username]
print '[%s] parts' % username
elif cmd.startswith('$NickList'):
nicklist = cmd[len('$NickList')+1:]
nicklist_parsed = nicklist.split('$$')
for nick in nicklist_parsed:
if nick:
self.buffer += '$GetInfo %s %s|' % (
nick, config['nickname'])
elif cmd.startswith('$ValidateDenide'):
self.fail('$ValidateDenide')
elif cmd.startswith('$BadPass'):
self.fail('Bad password')
elif cmd.startswith('$GetPass'):
self.buffer += '$MyPass %s|' % self.password
def handle_close(self):
self.keeper.notify_die(self)
self.close()
def fail(self, message):
print "*** Error: |%s| ****" % message
self.close()
class control_server(asyncore.dispatcher):
def __init__(self, keeper):
asyncore.dispatcher.__init__(self)
self.keeper = keeper
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind((config['bind_address'], config['bind_port']))
self.listen(1)
atexit.register(self.close)
self.connections = {}
def handle_accept(self):
new_socket, address = self.accept()
print new_socket, address
self.connections[address] = control_connection(new_socket, self.keeper)
class control_connection(asyncore.dispatcher):
def __init__(self, socket, keeper):
asyncore.dispatcher.__init__(self, socket)
self.keeper = keeper
self.inbuffer = ''
self.buffer = ''
def handle_read(self):
data = self.recv(1024)
self.inbuffer += data
unfin_pos = self.inbuffer.rfind('\n')
if unfin_pos != -1:
process_cmds = self.inbuffer[:unfin_pos]
self.inbuffer = self.inbuffer[unfin_pos+1:]
for cmd in process_cmds.split('\n'):
self.dispatch_command(cmd)
def dispatch_command(self, cmd):
if cmd.startswith('servers'):
for serv_a,serv_port in self.keeper.hubs:
if self.keeper.hubs[(serv_a,serv_port)].up and self.keeper.connections.has_key(serv_a):
serv = self.keeper.connections[serv_a]
bytes = 0
for i in serv.users:
if serv.users[i]:
bytes += int(serv.users[i]['sharesize'])
self.buffer += "up$%s:%s$%s$%s$%s\n" % (
serv.host, serv.port,
serv.hubname,
len(serv.users),
bytes)
else:
self.buffer += "down$%s:%s\n" % (serv_a, serv_port)
self.buffer += "okay\n"
if cmd.startswith('quit'):
for i in self.keeper.connections:
self.keeper.connections[i].close()
asyncore.close_all()
self.close()
sys.exit(0)
if cmd.startswith('connect'):
try:
(host, port) = cmd[len('connect '):].split()
except ValueError:
self.buffer += 'invalid syntax\n'
self.keeper.connect(host, port)
def writable(self):
return (len(self.buffer) > 0)
def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
def handle_close(self):
self.close()
def special_replace(char):
if ord(char) == 0 or ord (char) == 5 or ord(char) == 96 or char == '$':
return ('/%%DCN%03u%%/' % ord(char))
else:
return char
def compute_access_key(lock):
key = ''
u = ord(lock[0]) & 255
l = ord(lock[-1]) & 255
o = ord(lock[-2]) & 255
u = u ^ l ^ o ^ 0x05
v = (((u<<8)|u)>>4)&255
key += special_replace(chr(v))
for i in range(1, len(lock)):
u = ord(lock[i])
l = ord(lock[i-1])
u = u ^ l
v = (((u<<8)|u)>>4)&255
key += special_replace(chr(v))
return key
def check_key():
'''
This function is only for testing code
'''
lock = 'EXTENDEDPROTOCOL::This_hub_was_written_by_Yoshi::CTRL[s\xc89\x03\xe6]'
key = '\xbf\xd1\xc0\x11\xb0\xa0\x10\x10A \xd1\xb1\xb1\xc0\xc00g/%DCN000%/\xe6\xc3\x10\xa1\xc2s\xd1q\xd3\x82a!\xc2\x82P\xb1\xd1/%DCN000%/\x11\xb0\x13\xd3\xb1b/%DCN096%/c\xc1\xb1\x105/%DCN000%/\x97q/%DCN096%/\xe1q\x82\xbb\x1f\xa3^\xbb'
if compute_access_key(lock) != key:
print "Mismatch: "
print "Lock: |%s|" % str(lock)
print "Key(orig): |%s|" % str(key)
print "Key(calc): |%s|" % str(compute_access_key(lock))
sys.exit (0)
check_key()
client = dc_async_client()
client.connect('xxxxxx.no-ip.org', 6100)
client.connect('xxxxxxxxxx.zapto.org', 6100)
client.connect('xxxxxxxxx.marnet.info', 411)
client.main_loop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment