def read_msg_11(self): chunk_size = 10000 max_header_len = 15 frame_len = -1 msg = b"" while True: Logger.debug(8,f'Read data, indata queue length {len(self.indata)} bytes') if frame_len >= 0: # We know the frame length # Read frame body until frame_len bytes available len_indata = len(self.indata) if len_indata >= frame_len: # We have the entire frame msg += self.indata[:frame_len] self.indata = self.indata[frame_len:] frame_len = -1 else: # We got one more chunk of the frame, but not the entire frame msg += self.indata frame_len -= len_indata self.indata = self.channel.recv(chunk_size) if self.indata == b"": Logger.debug(8, f"Message11 EOF") ##self.channel.close() return b"" if frame_len < 0: # Have not the frame length header yet nl0 = self.indata.find(b"\n") nl1 = self.indata.find(b"\n", nl0 + 1) Logger.debug(9, f"Header nl0={nl0}, nl1={nl1}, h={self.indata[1] if len(self.indata) >= 2 else None}") if nl0 == 0 and nl1 > nl0 and nl1 <= max_header_len and self.indata[1] == ord(b'#'): # Found header frame_len_str = self.indata[2:nl1] next_frame_start = nl1 + 1 self.indata = self.indata[next_frame_start:] if frame_len_str == b"#": # We got the entire message return msg try: frame_len = int(frame_len_str.decode('utf-8')) except: Logger.warning(f'Framing error, invalid frame length value: """{frame_len_str}"""') return b"" else: if (nl0 == 0 and nl1 > nl0) or (len(self.indata) > max_header_len): # Definitely should have a complete header, something is wrong Logger.warning(f'Framing error, invalid frame header', payload = self.indata) return b"" # No header in sight, better read some more data = self.channel.recv(chunk_size) self.indata += data if data == b"": Logger.debug(8, f"Message11 header EOF") ##self.channel.close() return b""
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 read_msg(self, netconf_ver=0): if netconf_ver == 0: netconf_ver = self.netconf_ver if netconf_ver == 11: msg = self.read_msg_11() else: msg = self.read_msg_10() try: if isinstance(msg, bytes): decoded_msg = msg.decode("utf-8") else: decoded_msg = msg Logger.debug(8,f'Received {len(decoded_msg)} byte message', payload=decoded_msg) except Exception as e: Logger.warning(f"Could not UTF-8 decode message", payload=msg) raise return decoded_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