def load(self,path): ''' Load configuration from config file ''' conf = ConfigParser.SafeConfigParser() try: f = open(path,'r') conf.readfp(f) self.server = conf.get('General','server') self.port = int(conf.getint('General','port')) self.user = conf.get('General','user') self.name = conf.get('General','name') self.nick = conf.get('General','nick') self.ssl_enabled = conf.getboolean('General','ssl_enabled') self.control_list = conf.get('General','control_list').split(',') self.plugins = conf.get('General','plugins').split(',') for channel in conf.sections(): if channel != 'General': acl = {} item_list = conf.items(channel) for item in item_list: acl[item[0]] = item[1] self.channels[channel] = acl except: log.e(traceback.format_exc()) return False return True
def threadProc(): log.i("thread %s started!", name) try: target(*args) log.i("thread %s ended!", name) except Exception as e: log.e("thread %s crash!err=%s", name, e)
def save(self,path): ''' Save configuration to config file ''' conf = ConfigParser.SafeConfigParser() conf.add_section('General') conf.set('General','server',self.server) conf.set('General','port',str(self.port)) conf.set('General','user',self.user) conf.set('General','name',self.name) conf.set('General','nick',self.nick) conf.set('General','ssl_enabled',str(self.ssl_enabled)) conf.set('General','control_list',','.join(self.control_list)) conf.set('General','plugins',','.join(self.plugins)) for chan in self.channels.iterkeys(): conf.add_section(chan) for user in self.channels[chan].iterkeys(): conf.set(chan,user,self.channels[chan][user]) try: f = open(path,'w') conf.write(f) f.close() except: log.e(traceback.format_exc()) return False return True
def excepthook(excType, excValue, tb): '''''write the unhandle exception to log''' from log import log import traceback log.e('Unhandled Error: %s', ''.join(traceback.format_exception(excType, excValue, tb))) sys.exit(-1)
def __notify_box_close(self): with self.__notify_box_close_lock: log.v('Notify box %d close.' % self.box.uuid) try: self.box.send(Message.create_close_data()) except Exception as e: log.e('Send close message failed.')
def __handle_quit_request(self, r): c, addr = r.accept() data = c.recv(config.SOCKET_RECV_LEN) if data == RelayServer.MSG_QUIT: self.__is_quit_thread = True log.d('Get a quit request from quit-port.') else: log.e('Get an unknown message from quit-port.')
def on_msg_recv(self, msg): ''' Execute plugins on cmd recv hooks ''' for hook in self.on_msg_recv_hooks: #Try block to avoid breaking on bugged plugins try: hook(self, msg) except: log.e("Hook %s chashed" % hook.__name__) log.e(traceback.format_exc())
def on_permission_denied(self, cmd): ''' Execute plugins on cmd recv hooks ''' for hook in self.on_permission_denied_hooks: #Try block to avoid breaking on bugged plugins try: hook(self, cmd) except: log.e("Hook %s chashed" % hook.__name__) log.e(traceback.format_exc())
def on_disconnect(self): ''' Execute plugins on disconnect hooks ''' for hook in self.on_disconnect_hooks: #Try block to avoid breaking on bugged plugins try: hook(self) except: log.e("Hook %s chashed" % hook.__name__) log.e(traceback.format_exc())
def __handle_conn(self): msg = RelayManagerMessage(self.conn.recv(config.SOCKET_RECV_LEN, magic=0x4699)) if not msg.is_valid: return if msg.msg_type == RelayManagerMessage.Type.GET_TOKEN_ACK: self.__handle_get_token_ack(msg) elif msg.msg_type == RelayManagerMessage.Type.ACCESS_GRANTED: self.__handle_access_granted(msg) else: log.e('Invalid msg from RM.')
def __init__(self, data): self.data = data self.is_valid = False self.msg_type = RelayManagerMessage.Type.NONE self.content = None self.body = {'data': None} if len(data) < RelayManagerMessage.__HEADER_LEN: return header, body = data[:RelayManagerMessage.__HEADER_LEN], data[ RelayManagerMessage.__HEADER_LEN:] magic, msg_type, body_len = struct.unpack('!HHi', header) if magic != RelayManagerMessage.MAGIC or body_len != len(body): return self.msg_type = msg_type # Parse message body, MUST set 'self.is_valid' to True when body is valid. if msg_type == RelayManagerMessage.Type.ACCEPT: if body_len == 0: self.is_valid = True elif msg_type == RelayManagerMessage.Type.REFUSE: self.content = Message.get_unpacked_json_data(body) if isinstance(self.content, dict): try: self.ecode = int(self.content['reason']) self.message = str(self.content['message']) self.is_valid = True except: log.e('The content of RM refuse is invalid, %s.' % str(self.content)) elif msg_type == RelayManagerMessage.Type.GET_TOKEN_ACK: self.content = Message.get_unpacked_json_data(body) if isinstance(self.content, dict): try: self.body['serial_no'] = str(self.content['serialNo']) self.body['service'] = str(self.content['service']) if 'token' in self.content: self.body['token'] = str(self.content['token']) else: self.body['token'] = None self.is_valid = True except: log.e('The content of RM get-token-ack is invalid, %s.' % str(self.content)) elif msg_type == RelayManagerMessage.Type.ACCESS_GRANTED: self.content = Message.get_unpacked_json_data(body) if isinstance(self.content, dict): try: self.body['serial_no'] = str(self.content['serialNo']) self.body['service'] = str(self.content['service']) self.body['token'] = str(self.content['token']) self.body['expire'] = int(self.content['expire']) * 60 self.is_valid = True except: log.e('The content of RM get-token-ack is invalid, %s.' % str(self.content)) else: log.e('Unknown relay manger message type.')
def process_server_cmd(self,cmd): ''' Process a command sent by the server. It does not require checking for permissions. ''' if cmd.name not in self.server_commands: return False log.i("Command from server: %s" % cmd.name) result = getattr(self, cmd.name)(cmd) if result == False: log.e("Unable to send command response to server: '%s'" % cmd.name) return True
def bind_socket_to_port(self, s, ip, port): """ Bind a socket with ip and port and return bound port. """ count = 0 cur_port = port while True: try: cur_port = port + count s.bind((ip, cur_port)) break except socket.error as err: log.e('Bind port %s, %d failed, error number: %d' % (ip, cur_port, err.errno)) count += 1 if count > 15: count = 0 time.sleep(2) return cur_port
def recv(self): ''' Recv wrapper to handle exceptions ''' if self.socket == None: return False if self.conf.ssl_enabled == True and self.ssl_socket == None: return False try: #IRC protocol uses 512 bytes as buffer size if self.conf.ssl_enabled: return self.ssl_socket.read(512) else: return self.socket.recv(512) except Exception as e: log.e(traceback.format_exc()) return ''
def run(self): self.load_plugin_list() log.i("Plugins Loaded.") self.connect() log.i("Bot Connected.") self.on_connect() log.i("Deception locked and loaded!") while self.connected: msg = self.recv() if len(msg) == 0: self.connected = False continue msglist = msg.split('\n') for line in msglist: if len(line) == 0: continue log.d(line) cmd = self.parse_cmd(line.strip()) if cmd == None: self.on_msg_recv(line) continue log.d(cmd); if self.process_server_cmd(cmd): continue if cmd.name not in self.cmd_handlers.keys(): log.e("Command '%s' not recognized." % cmd.name) self.on_unknown_cmd(cmd) continue if not self.check_permission(cmd): self.on_permission_denied(cmd) self.process_cmd(cmd) self.on_disconnect()
def send(self,msg): ''' Send wrapper to handle exceptions ''' if self.socket == None: return False if self.conf.ssl_enabled == True and self.ssl_socket == None: return False try: if self.conf.ssl_enabled: self.ssl_socket.write(msg) else: self.socket.sendall(msg) return True except Exception as e: log.e(traceback.format_exc()) return False
def run(self): self.is_quit = False while not self.is_quit: log.v('[RM] Try connect to relay manager.') self.conn = RelayManagerConnection(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) try: self.__connect() except (socket.error, socket.timeout) as e: log.e('[RM] Send connect request to relay manager failed: %s' % str(e)) self.conn.close() time.sleep(30) continue log.v('[RM] Relay manager connected.') self.__update_network_speed() self.__send_heartbeat() self.__send_status() while not self.is_quit: try: reads, _, errors = select.select([self.conn], [], [self.conn], 1) self.__update_messages() self.__update_messages_udp() self.__update_tracers() if self.conn in reads: self.__handle_conn() if self.conn in errors: raise socket.error('connection error in select()') except (socket.error, socket.timeout) as e: log.e('[RM] Send HEARTBEAT or STATUS failed: %s' % str(e)) self.conn.close() time.sleep(1) break # Quit the connection. try: self.__send_disconnect() self.conn.close() except (socket.error, socket.timeout) as e: pass
def __handle_connection_write(self, c): log.v('Handle connection writing.') if c is self.box: log.v('Handle box writing.') try: # Check box timeout without PING from relay. # c.timeout_tracer.reset() c.send() if c.blocked: self.__close_box() except (socket.error, socket.timeout) as e: log.e('Send buffered datas to box %d failed: %s' % (c.uuid, str(e))) self.__close_box() elif c in self.clients: log.v('Handle client writing.') try: log.v( 'Send data to client %d in __handle_connection_write().' % c.uuid) c.timeout_tracer.reset() changed = c.send() if changed: if c.blocked: log.v('Client %d has blocked' % c.uuid) self.box.append_data_to_buffer( Message.create_pause_data(c.uuid)) else: log.v('Client %d has not blocked' % c.uuid) self.box.append_data_to_buffer( Message.create_resume_data(c.uuid)) else: # log.v('Client %d blocked state has not changed.' % c.uuid) pass except (socket.error, socket.timeout) as e: log.e('Send buffered datas to client failed: %s' % str(e)) self.__notify_box_client_broken(c.uuid) self.__close_client(c)
def connect(self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: self.socket.connect((self.conf.server, self.conf.port)) if (self.conf.ssl_enabled): self.ssl_socket = socket.ssl(self.socket) self.send("NICK %s\n" % self.conf.nick) self.send("USER %s 0 * :%s\n" % (self.conf.user, self.conf.name)) #TODO: wait for event 376` time.sleep(10) self.connected = True except Exception as e: log.e(traceback.format_exc()) if (self.socket != None): self.socket.close()
def __handle_connection_read(self, r): # log.v('Handle connection reading.') if r is self.s: log.v('Handle box listener reading.') c, addr = r.accept() conn = BoxConnection(c) log.d('Connected by box %d, address, %s' % (conn.uuid, repr(addr))) if self.box: conn.close() else: self.box_cache.append(conn) elif r in self.box_cache: log.v('Handle cached box reading.') try: self.__handle_cached_box_connection(r) except (socket.error, socket.timeout) as e: log.e('Cached box %d connection encounter an error: %s' % (r.uuid, str(e))) self.__close_cached_box(r) elif r is self.box: log.v('Handle box reading.') try: self.__handle_box_connection(r) except (socket.error, socket.timeout) as e: log.e('Box %d connection encounter an error: %s' % (r.uuid, str(e))) self.__close_box() elif r is self.cs: log.v('Handle client listener reading.') c, addr = r.accept() conn = ClientConnection(c) log.d('Connected by client %d, address, %s' % (conn.uuid, repr(addr))) if not self.box: log.d('Skip this client because no box connected !!!') conn.close() return self.clients.append(conn) elif r in self.clients: log.v('Handle client reading.') try: r.timeout_tracer.reset() self.__handle_client_connection(r) except (socket.error, socket.timeout) as e: log.e('Client %d connection enconter an error: %s.' % (r.uuid, str(e))) self.__notify_box_client_broken(r.uuid) self.__close_client(r) elif r is self.__qs: log.v('Handle quit-socket reading.') self.__handle_quit_request(r) else: log.d('The connection does not need handle.')
def load_plugin(self, name, sync=False): ''' Load plugin on runtime ''' log.i("Loading plugin: '%s'" % name) try: module = __import__('plugins.' + name, fromlist=["plugins"]) if sync: reload(module) #Load event hooks if hasattr(module, 'ON_CONNECT'): for hook in module.ON_CONNECT: self.on_connect_hooks.append(hook) if hasattr(module, 'ON_DISCONNECT'): for hook in module.ON_DISCONNECT: self.on_disconnect_hooks.append(hook) if hasattr(module, 'ON_MSG_RECV'): for hook in module.ON_MSG_RECV: self.on_msg_recv_hooks.append(hook) if hasattr(module, 'ON_UNKNOWN_CMD'): for hook in module.ON_UNKNOWN_CMD: self.on_unknown_cmd_hooks.append(hook) if hasattr(module, 'ON_PERMISSION_DENIED'): for hook in module.ON_PERMISSION_DENIED: self.on_permission_denied_hooks.append(hook) #Load plugin commands if hasattr(module, 'COMMAND_HANDLERS'): for handler in module.COMMAND_HANDLERS.keys(): self.cmd_handlers[handler] = module.COMMAND_HANDLERS[handler] except: log.e("Unable to load plugin '%s'" % name) log.e(traceback.format_exc())
def __handle_connection_error(self, c): # log.v('Handle connection error.') if c in self.box_cache: log.e('Cached box %d error from select().' % c.uuid) self.__close_cached_box(c) elif c is self.box: log.e('Box %d error from select().' % c.uuid) self.__close_box() elif c in self.clients: log.e('Client %d error from select().' % c.uuid) self.__close_client(c)
def process_cmd(self,cmd): ''' Execute plugins commands ''' if cmd.name not in self.cmd_handlers.keys(): return False log.i("Command from user: %s" % cmd.name) if not self.check_permission(cmd): log.e("User %s does not have enough permission for command %s" % (cmd.user, cmd.name)) return False #Try block to avoid breaking on bugged plugins try: result = self.cmd_handlers[cmd.name][0](self,cmd) if result == False: log.e("Unable to send command response to server: '%s'" % cmd.name) except: log.e("Command '%s' crashed." % cmd.name) log.e(traceback.format_exc()) return True
def __handle_box_register(self, c, msg): """ :param c: :param msg: :return: the box is valid. """ log.d('Get box %d register content: %s' % (c.uuid, msg.content)) try: sn = str(msg.content['deviceId']) service = str(msg.content['service']) token = str(msg.content['token']) except: log.e('Box register information format is not correct.') c.send(Message.create_refuse_data(ECode.RELAY_BOX_INVALID_INFO)) return False device_token = self.hub_linker.connector.verify_token( sn, service, token) if not device_token: log.e('Box register information is invalid by hub.') c.send(Message.create_refuse_data(ECode.RELAY_BOX_INVALID_INFO)) return False log.d('Box register info is valid, device_id %s, token %s.' % (str(sn), str(token))) _, _, cport, udp_port = self.upnp.get_router_info() if not cport: log.e('Can not set up client upnp port.') c.send( Message.create_refuse_data( ECode.RELAY_FAILED_CREATE_CLIENT_PORT)) return False reply = {'port': str(cport)} c.send(Message.create_accept_data(reply)) return True
def join(self, channel): ''' Join a channel ''' if channel == None: log.e("Missing parameter for JOIN message") self.send("JOIN %s\n" % channel)
def __init__(self, data, conn=None): if not data: raise socket.error('Get the EOF message') self.data = data self.is_valid = False self.type = None # Possible value: # json unpacked data. # data stream. self.content = None # Error code, ing, 4 byte in data. self.ecode = -1 self.message = '' # Client UUID, long, long, 8 byte in data self.uuid = -1 if len(data) < Message.HEADER_LEN: return header, body = data[:Message.HEADER_LEN], data[Message.HEADER_LEN:] magic, self.type, length = struct.unpack('!HHi', header) if magic != Message.MAGIC or length != len(body): return # Parse message body. # MUST set 'self.is_valid' to True when body is valid. if self.type == Type.CONNECT \ or self.type == Type.ACCEPT: self.is_valid = self.content = Message.get_unpacked_json_data(body) elif self.type == Type.REFUSE: self.content = Message.get_unpacked_json_data(body) if isinstance(self.content, dict): try: self.ecode = int(self.content['reason']) self.message = str(self.content['message']) self.is_valid = True except: log.e('The content of relay REFUSE is invalid, %s.' % str(self.content)) elif self.type == Type.DISCONNECT \ or self.type == Type.CLOSE \ or self.type == Type.PING \ or self.type == Type.PING_ACK: self.is_valid = len(body) == 0 elif self.type == Type.SERVER_DATA \ or self.type == Type.CLIENT_DATA: if len(body) <= Message.UUID_LEN: return uuid_data, self.content = body[:Message.UUID_LEN], body[Message. UUID_LEN:] self.uuid = struct.unpack('@q', uuid_data)[0] self.is_valid = True elif self.type == Type.SERVER_ERROR \ or self.type == Type.CLIENT_ERROR: self.content = Message.get_unpacked_json_data(body) if isinstance(self.content, dict): try: self.uuid = int(self.content['connection']) self.ecode = int(self.content['error']) self.is_valid = True except: log.e( 'The content of relay SERVER_ERROR or CLIENT_ERROR is invalid, %s.' % str(self.content)) elif self.type == Type.PAUSE \ or self.type == Type.RESUME: self.content = Message.get_unpacked_json_data(body) if isinstance(self.content, dict): try: self.uuid = int(self.content['connection']) self.is_valid = True except: log.e( 'The content of relay PAUSE or RESUME is invalid, %s.' % str(self.content)) else: log.e('Unknown message type.')
def pong(self, cmd): ''' Reply to ping requests ''' if len(cmd.args) >= 1: self.send("PONG : %s\r\n" % cmd.args[0]) else: log.e("Missing parameter for PING message")
def part(self, channel): ''' Part from channel ''' if channel == None: log.e("Missing parameter for PART message") self.send("PART %s\n" % channel)
def mode(self, target, mode, user): ''' Set channel mode for user ''' if target == None or mode == None or user == None: log.e("Missing parameters for MODE message") self.send("MODE %s %s %s\n" % (target, mode, user))
def privmsg(self,target,msg): ''' Send a message to channel/user ''' if target == None or msg == None: log.e("Missing parameters for PRIVMSG message") self.send("PRIVMSG %s :%s\n" % (target, msg))