def handle_session(self): Logger.debug(7, 'Session loop running') self.incoming_message('<?xml version="1.0" encoding="UTF-8"?><hello xmlns="parrot"/>') while True: try: Logger.debug(5,'NETCONF server ready, waiting for next message') msg = self.read_msg() except EOFError: Logger.debug(5,'EOF -- end of session') return except Exception as e: verdict = self.handle_read_exception(e) if verdict == "kill-session": return if verdict == "kill-server": Logger.info('Server terminating') sys.exit(1) # Else keep going try: if("" == msg): Logger.debug(5,'EOF ++ end of session') return if self.incoming_message(msg) == True: return except Exception as e: Logger.error('Exception in server processing: ' + str(e))
def run(self, host, port, parrot_file): Logger.info(f"Parroting {parrot_file}") try: parrot_path = pathlib.Path(parrot_file) env = Environment(loader=FileSystemLoader([parrot_path.parent], followlinks=True), autoescape=select_autoescape(['xml'])) self.server.set_template(env.get_template(str(parrot_path.name))) self.server.set_host_port(host, port) self.server.serve() except Exception as e: traceback.print_exc() Logger.fatal(f"Top level exception: {str(e)}")
def build(self): if self.target_filename.exists(): source_stat = self.source_filename.stat() target_stat = self.target_filename.stat() if source_stat.st_mtime <= target_stat.st_mtime: Logger.info( f"Parrot Builder, {self.target_filename} up to date") return str(self.target_filename) with open(self.source_filename, "r") as source_file: with open(self.target_filename, "w") as target_file: self._make_messages(source_file) self._emit_messages(target_file) return str(self.target_filename)
def process_instructions(self, reply_msg): def split_processing_instructions(reply_msg): instructions = [] pi_start_marker = '<?parrot ' pi_end_marker = '?>' while True: pi_start = reply_msg.find(pi_start_marker) if pi_start < 0: break pi_end = reply_msg[pi_start:].find(pi_end_marker) if pi_end < 0: return ('bad-processing-instruction', reply_msg[pi_start:pi_start+20]) pi = reply_msg[pi_start+len(pi_start_marker):pi_end] if pi_start: instructions += [('send', reply_msg[:pi_start])] reply_msg = reply_msg[pi_end+len(pi_end_marker):] cmd = pi.split(" ")[0] data = pi[len(cmd)+1:] instructions += [(cmd, data)] if reply_msg: instructions += [('send', reply_msg)] if not instructions: instructions = [('empty-response', None)] return instructions instructions = split_processing_instructions(reply_msg) end_session = False for (cmd, data) in instructions: if cmd == "send": Logger.debug(6, f'Sending {len(data)} bytes', payload=data) self.send_message(data) elif cmd == "ignore": Logger.debug(5, f'Not sending any response') elif cmd == "netconf11": Logger.debug(5, f'Switching to NETCONF 1.1') self.choose_netconf_ver([11]) elif cmd == "netconf10": Logger.debug(5, f'Switching to NETCONF 1.0') self.choose_netconf_ver([10]) elif cmd == "empty-response": Logger.warning(f'Template did not provide any response. Ending the session.') end_session = True elif cmd == "end-session": Logger.info(f'Ending session') end_session = True else: Logger.error(f'Unknown processing instruction "{cmd}" in template. Ending the session.') end_session = True return end_session
def send_msg(self, msg, netconf_ver=0): chunk_size = 10000 if netconf_ver == 0: netconf_ver = self.netconf_ver msg_len = len(msg) while msg != "": chunk = msg[:chunk_size].encode('utf-8') chunk_len = len(chunk) if netconf_ver == 11: header = f"\n#{chunk_len}\n".encode('utf-8') Logger.debug(10, f'Sending NC11 header', payload=header) self.channel.send(header) Logger.debug(9, f'Sending {chunk_len} bytes chunk', payload=chunk) self.channel.send(chunk) msg = msg[chunk_size:] if netconf_ver == 11: Logger.debug(10, f'Sending NC11 delimiter', payload=self.delimiter11) self.channel.send(self.delimiter11) if netconf_ver == 10: Logger.debug(10, f'Sending NC10 delimiter', payload=self.delimiter10b) self.channel.send(self.delimiter10b) Logger.info(f"Sent {msg_len} bytes in NC{netconf_ver} message", payload=msg)
def handle_connection(self, host_key): try: DoGSSAPIKeyExchange = False t = Transport(self.sock, gss_kex=DoGSSAPIKeyExchange) t.set_subsystem_handler('netconf', self.subsys) t.set_gss_host(socket.getfqdn("")) try: t.load_server_moduli() except: Logger.warning('Failed to load moduli -- gex will be unsupported.') t.add_server_key(host_key) server = Server() t.start_server(server=server) # wait for auth self.channel = t.accept(20) if self.channel is None: Logger.error('No SSH channel') return Logger.info('Waiting for message') server.event.wait(10) Logger.info('Closing') ##self.channel.close() Logger.info('Client connection closed') except ConnectionResetError as e: Logger.debug(5,'Connection reset by peer') except SSHException: Logger.error('SSH negotiation failed, client connection dropped') except Exception as e: Logger.error('Caught exception: ' + str(e.__class__) + ': ' + str(e)) traceback.print_exc() try: t.close() except: pass
def listen(self): NETCONF_Server.set_instance(self) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #sock.bind(('', self.port)) sock.bind((self.host, self.port)) except Exception as e: Logger.fatal('Bind failed: ' + str(e)) try: sock.listen(100) except Exception as e: Logger.fatal('Listen failed: ' + str(e)) Logger.info(f'Listening for connections on port {self.port}...') host_key = None try: host_key = RSAKey(filename=self.host_key_filename) except: pass if not host_key: Logger.info(f'Generating new host key') host_key = RSAKey.generate(2048) if self.host_key_filename: host_key.write_private_key_file(self.host_key_filename, password=None) Logger.info(f"Wrote host key to file, '{self.host_key_filename}'") while True: try: Logger.info(f'Waiting for client to connect') client, addr = sock.accept() except Exception as e: Logger.fatal('Accept failed: ' + str(e)) self.sock = client (ip, port) = addr Logger.info(f'Client {ip}:{port} connected') self.handle_connection(host_key) Logger.info(f'Client {ip}:{port} disconnected')