Created
February 13, 2009 20:22
-
-
Save kolen/64084 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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