def pack(self): if self.pdu_type not in self.pdu_mapping: raise AssembleException("s7comm", "invalid or unsupported pdu type") elif self.pdu_type in (2, 3): # type 2 and 3 feature an additional RESULT INFORMATION header return ( pack( "!BBHHHHH", self.magic, self.pdu_type, self.reserved, self.request_id, self.param_length, self.data_length, self.result_info, ) + str_to_bytes(self.parameters) + str_to_bytes(self.data) ) else: return ( pack( "!BBHHHH", self.magic, self.pdu_type, self.reserved, self.request_id, self.param_length, self.data_length, ) + str_to_bytes(self.parameters) + str_to_bytes(self.data) )
def pack(self): if self.tpdu_type == 0xf0: return pack('!BBB', self.packet_length, self.tpdu_type, self.opt_field) + str_to_bytes(self.payload) + \ str_to_bytes(self.trailer) else: return pack('!BB', self.packet_length, self.tpdu_type) + str_to_bytes(self.payload) +\ str_to_bytes(self.trailer)
def pack(self): if self.tpdu_type == 0xF0: return (pack("!BBB", self.packet_length, self.tpdu_type, self.opt_field) + str_to_bytes(self.payload) + str_to_bytes(self.trailer)) else: return (pack("!BB", self.packet_length, self.tpdu_type) + str_to_bytes(self.payload) + str_to_bytes(self.trailer))
def handle(self, sock, address): session = conpot_core.get_session( "kamstrup_management_protocol", address[0], address[1], sock.getsockname()[0], sock.getsockname()[1], ) logger.info( "New Kamstrup connection from %s:%s. (%s)", address[0], address[1], session.id, ) session.add_event({"type": "NEW_CONNECTION"}) try: sock.send( str_to_bytes( self.banner.format( conpot_core.get_databus().get_value("mac_address") ) ) ) while True: data = sock.recv(1024) if not data: logger.info("Kamstrup client disconnected. (%s)", session.id) session.add_event({"type": "CONNECTION_LOST"}) break request = data.decode() logdata = {"request": request} response = self.command_responder.respond(request) logdata["response"] = response logger.info( "Kamstrup management traffic from %s: %s (%s)", address[0], logdata, session.id, ) session.add_event(logdata) gevent.sleep(0.25) # TODO measure delay and/or RTT if response is None: session.add_event({"type": "CONNECTION_LOST"}) break # encode data before sending reply = str_to_bytes(response) sock.send(reply) except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) session.add_event({"type": "CONNECTION_LOST"}) sock.close()
def pack(self): if self.pdu_type not in self.pdu_mapping: raise AssembleException('s7comm', 'invalid or unsupported pdu type') elif self.pdu_type in (2, 3): # type 2 and 3 feature an additional RESULT INFORMATION header return pack('!BBHHHHH', self.magic, self.pdu_type, self.reserved, self.request_id, self.param_length, self.data_length, self.result_info) + str_to_bytes(self.parameters) + str_to_bytes(self.data) else: return pack('!BBHHHH', self.magic, self.pdu_type, self.reserved, self.request_id, self.param_length, self.data_length) + str_to_bytes(self.parameters) + str_to_bytes(self.data)
def pack(self): if self.type not in [1, 7]: raise S7ProtocolError("Unknown pdu type") return (pack( '!BBHHHH', 0x32, # protocol s7 magic self.type, # pdu-type 0, # reserved self.req_id, # request id len(self.parameters), # parameters length len(self.data)) + # data length str_to_bytes(self.parameters) + str_to_bytes(self.data))
def pack(self): if self.type not in [1, 7]: raise S7ProtocolError("Unknown pdu type") return (pack('!BBHHHH', 0x32, # protocol s7 magic self.type, # pdu-type 0, # reserved self.req_id, # request id len(self.parameters), # parameters length len(self.data)) + # data length str_to_bytes(self.parameters) + str_to_bytes(self.data))
def pack(self): return (pack( "!BBH", 3, # version 0, # reserved len(bytes(self.data)) + 4, # packet size ) + str_to_bytes(bytes(self.data)))
def _device_info(self, request_pdu): info_root = self.dom.xpath("//modbus/device_info")[0] vendor_name = info_root.xpath("./VendorName/text()")[0] product_code = info_root.xpath("./ProductCode/text()")[0] major_minor_revision = info_root.xpath( "./MajorMinorRevision/text()")[0] (req_device_id, _) = struct.unpack(">BB", request_pdu[2:4]) device_info = { 0: vendor_name, 1: product_code, 2: major_minor_revision } # MEI type response = struct.pack(">B", 0x0E) # requested device id response += struct.pack(">B", req_device_id) # conformity level response += struct.pack(">B", 0x01) # followup data 0x00 is False response += struct.pack(">B", 0x00) # No next object id response += struct.pack(">B", 0x00) # Number of objects response += struct.pack(">B", len(device_info)) for i in range(len(device_info)): # Object id response += struct.pack(">B", i) # Object length response += struct.pack(">B", len(device_info[i])) response += str_to_bytes(device_info[i]) return response
def _device_info(self, request_pdu): info_root = self.dom.xpath('//modbus/device_info')[0] vendor_name = info_root.xpath('./VendorName/text()')[0] product_code = info_root.xpath('./ProductCode/text()')[0] major_minor_revision = info_root.xpath('./MajorMinorRevision/text()')[0] (req_device_id, req_object_id) = struct.unpack(">BB", request_pdu[2:4]) device_info = { 0: vendor_name, 1: product_code, 2: major_minor_revision } # MEI type response = struct.pack(">B", 0x0E) # requested device id response += struct.pack(">B", req_device_id) # conformity level response += struct.pack(">B", 0x01) # followup data 0x00 is False response += struct.pack(">B", 0x00) # No next object id response += struct.pack(">B", 0x00) # Number of objects response += struct.pack(">B", len(device_info)) for i in range(len(device_info)): # Object id response += struct.pack(">B", i) # Object length response += struct.pack(">B", len(device_info[i])) response += str_to_bytes(device_info[i]) return response
def pack(self): return pack( '!BBH', 3, # version 0, # reserved len(bytes(self.data)) + 4 # packet size ) + str_to_bytes(bytes(self.data))
def do_GET(self): """Handle GET requests""" # fetch configuration dependent variables from server instance headers = [] headers.extend(self.server.global_headers) configuration = self.server.configuration docpath = self.server.docpath # retrieve GET body data # ( sticking to the HTTP protocol, there should not be any body in GET requests, # an attacker could though use the body to inject data if not flushed correctly, # which is done by accessing the data like we do now - just to be secure.. ) get_data_length = self.headers.get('content-length') get_data = None if get_data_length: get_data = self.rfile.read(int(get_data_length)) # try to find a configuration item for this GET request logger.debug('Trying to handle GET to resource <%s>, initiated by %s', self.path, self.client_address) entity_xml = configuration.xpath('//http/htdocs/node[@name="' + self.path.partition('?')[0] + '"]') if entity_xml: # A config item exists for this entity. Handle it.. (status, headers, trailers, payload, chunks) = self.load_entity(self.path, headers, configuration, docpath) else: # No config item could be found. Fall back to a standard 404.. status = 404 (status, headers, trailers, payload, chunks) = self.load_status(status, self.path, self.headers, headers, configuration, docpath, 'GET') # send initial HTTP status line to client self.send_response(status) # send all headers to client for header in headers: self.send_header(header[0], header[1]) self.end_headers() # decide upon sending content as a whole or chunked if chunks == '0': # send payload as a whole to the client self.wfile.write(str_to_bytes(payload)) else: # send payload in chunks to the client self.send_chunked(chunks, payload, trailers) # loggers self.log(self.request_version, self.command, self.client_address, (self.path, self.headers._headers, get_data), status)
def handle(self, sock, address): session = conpot_core.get_session('kamstrup_management_protocol', address[0], address[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info('New Kamstrup connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) try: sock.send( str_to_bytes(self.banner.format(conpot_core.get_databus().get_value("mac_address"))) ) while True: data = sock.recv(1024) if not data: logger.info('Kamstrup client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break request = data.decode() logdata = {'request': request} response = self.command_responder.respond(request) logdata['response'] = response logger.info('Kamstrup management traffic from %s: %s (%s)', address[0], logdata, session.id) session.add_event(logdata) gevent.sleep(0.25) # TODO measure delay and/or RTT if response is None: session.add_event({'type': 'CONNECTION_LOST'}) break # encode data before sending reply = str_to_bytes(response) sock.send(reply) except socket.timeout: logger.debug('Socket timeout, remote: %s. (%s)', address[0], session.id) session.add_event({'type': 'CONNECTION_LOST'}) sock.close()
def plc_stop_function(self): pdu_type = 1 request_id = 256 stop_func_parameter = struct.pack('!B5x10p', 0x29, # function code str_to_bytes('P_PROGRAM') # Function Name ) s7packet = S7Packet(pdu_type,request_id,stop_func_parameter).pack() cotp_packet = COTPDataPacket(s7packet).pack() tpkt_packet = TPKTPacket(cotp_packet).pack() self.s.send(tpkt_packet) reply = self.s.recv(1024) if reply: return S7Packet().unpack(COTPDataPacket().unpack(TPKTPacket().unpack(reply).data).data).data else: return None
def send_response(self, code, message=None): """Send the response header and log the response code. This function is overloaded to change the behaviour when loggers and sending default headers. """ # replace integrated loggers with conpot logger.. # self.log_request(code) if message is None: if code in self.responses: message = self.responses[code][0] else: message = '' if self.request_version != 'HTTP/0.9': msg = str_to_bytes("{} {} {}\r\n".format(self.protocol_version, code, message)) self.wfile.write(msg)
def handle(self, sock, addr): session = conpot_core.get_session('guardian_ast', addr[0], addr[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info('New GuardianAST connection from %s:%d. (%s)', addr[0], addr[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) current_time = datetime.datetime.utcnow() fill_start = self.fill_offset_time - datetime.timedelta(minutes=313) fill_stop = self.fill_offset_time - datetime.timedelta(minutes=303) # Default Product names, change based off country needs product1 = self.databus.get_value('product1').ljust(22) product1 = self.databus.get_value('product1').ljust(22) product2 = self.databus.get_value('product2').ljust(22) product3 = self.databus.get_value('product3').ljust(22) product4 = self.databus.get_value('product4').ljust(22) # Create random Numbers for the volumes # # this will crate an initial Volume and then the second value based # off the orig value. vol1 = self.databus.get_value('vol1') vol1tc = random.randint(vol1, vol1 + 200) vol2 = self.databus.get_value('vol2') vol2tc = random.randint(vol2, vol2 + 200) vol3 = self.databus.get_value('vol3') vol3tc = random.randint(vol3, vol3 + 200) vol4 = self.databus.get_value('vol4') vol4tc = random.randint(vol4, vol4 + 200) # unfilled space ULLAGE ullage1 = str(self.databus.get_value('ullage1')) ullage2 = str(self.databus.get_value('ullage2')) ullage3 = str(self.databus.get_value('ullage3')) ullage4 = str(self.databus.get_value('ullage3')) # Height of tank height1 = str(self.databus.get_value('height1')).ljust(5, '0') height2 = str(self.databus.get_value('height2')).ljust(5, '0') height3 = str(self.databus.get_value('height3')).ljust(5, '0') height4 = str(self.databus.get_value('height4')).ljust(5, '0') # Water in tank, this is a variable that needs to be low h2o1 = str(self.databus.get_value('h2o1')).ljust(4, '0') h2o2 = str(self.databus.get_value('h2o2')).ljust(4, '0') h2o3 = str(self.databus.get_value('h2o3')).ljust(4, '0') h2o4 = str(self.databus.get_value('h2o4')).ljust(4, '0') # Temperature of the tank, this will need to be between 50 - 60 temp1 = str(self.databus.get_value('temp1')).ljust(5, '0') temp2 = str(self.databus.get_value('temp2')).ljust(5, '0') temp3 = str(self.databus.get_value('temp3')).ljust(5, '0') temp4 = str(self.databus.get_value('temp4')).ljust(5, '0') station = self.databus.get_value('station_name') # This function is to set-up up the message to be sent upon a successful I20100 command being sent # The final message is sent with a current date/time stamp inside of the main loop. def I20100(): ret = '\nI20100\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nIN-TANK INVENTORY\n\n' ret += 'TANK PRODUCT VOLUME TC VOLUME ULLAGE HEIGHT WATER TEMP' ret += '\n 1 ' + product1 + str(vol1) + ' ' + str( vol1tc ) + ' ' + ullage1 + ' ' + height1 + ' ' + h2o1 + ' ' + temp1 ret += '\n 2 ' + product2 + str(vol2) + ' ' + str( vol2tc ) + ' ' + ullage2 + ' ' + height2 + ' ' + h2o2 + ' ' + temp2 ret += '\n 3 ' + product3 + str(vol3) + ' ' + str( vol3tc ) + ' ' + ullage3 + ' ' + height3 + ' ' + h2o3 + ' ' + temp3 ret += '\n 4 ' + product4 + str(vol4) + ' ' + str( vol4tc ) + ' ' + ullage4 + ' ' + height4 + ' ' + h2o4 + ' ' + temp4 ret += '\n' return ret ########################################################################### # # Only one Tank is listed currently in the I20200 command # ########################################################################### def I20200(): ret = '\nI20200\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nDELIVERY REPORT\n\n' ret += 'T 1:' + product1 + '\nINCREASE DATE / TIME GALLONS TC GALLONS WATER TEMP DEG F HEIGHT\n\n' ret += ' END: ' + str( fill_stop.strftime('%m/%d/%Y %H:%M') ) + ' ' + str(vol1 + 300) + ' ' + str( vol1tc + 300 ) + ' ' + h2o1 + ' ' + temp1 + ' ' + height1 + '\n' ret += ' START: ' + str( fill_start.strftime('%m/%d/%Y %H:%M') ) + ' ' + str(vol1 - 300) + ' ' + str( vol1tc - 300) + ' ' + h2o1 + ' ' + temp1 + ' ' + str( float(height1) - 23) + '\n' ret += ' AMOUNT: ' + str( vol1) + ' ' + str(vol1tc) + '\n\n' return ret ########################################################################### # # I20300 In-Tank Leak Detect Report # ########################################################################### def I20300(): ret = '\nI20300\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n' ret += 'TANK 1 ' + product1 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 2 ' + product2 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 3 ' + product3 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 4 ' + product4 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' return ret ########################################################################### # Shift report command I20400 only one item in report at this time, # but can always add more if needed ########################################################################### def I20400(): ret = '\nI20400\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nSHIFT REPORT\n\n' ret += 'SHIFT 1 TIME: 12:00 AM\n\nTANK PRODUCT\n\n' ret += ' 1 ' + product1 + ' VOLUME TC VOLUME ULLAGE HEIGHT WATER TEMP\n' ret += 'SHIFT 1 STARTING VALUES ' + str(vol1) + ' ' + str( vol1tc ) + ' ' + ullage1 + ' ' + height1 + ' ' + h2o1 + ' ' + temp1 + '\n' ret += ' ENDING VALUES ' + str( vol1 + 940) + ' ' + str(vol1tc + 886) + ' ' + str( int(ullage1) + 345) + ' ' + str(float( height1) + 53) + ' ' + h2o1 + ' ' + temp1 + '\n' ret += ' DELIVERY VALUE 0\n' ret += ' TOTALS 940\n\n' return ret ########################################################################### # I20500 In-Tank Status Report ########################################################################### def I20500(): ret = '\nI20500\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n\n' + station + '\n\n\n' ret += 'TANK PRODUCT STATUS\n\n' ret += ' 1 ' + product1 + ' NORMAL\n\n' ret += ' 2 ' + product2 + ' HIGH WATER ALARM\n' ret += ' HIGH WATER WARNING\n\n' ret += ' 3 ' + product3 + ' NORMAL\n\n' ret += ' 4 ' + product4 + ' NORMAL\n\n' return ret while True: try: # Get the initial data request = sock.recv(4096) # The connection has been closed if not request: break while not (b'\n' in request or b'00' in request): request += sock.recv(4096) # if first value is not ^A then do nothing # thanks John(achillean) for the help if request[:1] != b'\x01': logger.info('Non ^A command attempt %s:%d. (%s)', addr[0], addr[1], session.id) break # if request is less than 6, than do nothing if len(request) < 6: logger.info('Invalid command attempt %s:%d. (%s)', addr[0], addr[1], session.id) break cmds = { "I20100": I20100, "I20200": I20200, "I20300": I20300, "I20400": I20400, "I20500": I20500 } cmd = request[1:7].decode() # strip ^A and \n out response = None if cmd in cmds: logger.info('%s command attempt %s:%d. (%s)', cmd, addr[0], addr[1], session.id) response = cmds[cmd]() elif cmd.startswith("S6020"): # change the tank name if cmd.startswith("S60201"): # split string into two, the command, and the data TEMP = request.split(b'S60201') # if length is less than two, print error if len(TEMP) < 2: response = AST_ERROR # Else the command was entered correctly and continue else: # Strip off the carrage returns and new lines TEMP1 = TEMP[1].rstrip(b'\r\n').decode() # if Length is less than 22 if len(TEMP1) < 22: # pad the result to have 22 chars product1 = TEMP1.ljust(22) elif len(TEMP1) > 22: # else only print 22 chars if the result was longer product1 = TEMP1[:20] + " " else: # else it fits fine (22 chars) product1 = TEMP1 logger.info('S60201: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60202"): TEMP = request.split(b'S60202') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product2 = TEMP1.ljust(22) elif len(TEMP1) > 22: product2 = TEMP1[:20] + " " else: product2 = TEMP1 logger.info('S60202: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60203"): TEMP = request.split(b'S60203') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product3 = TEMP1.ljust(22) elif len(TEMP1) > 22: product3 = TEMP1[:20] + " " else: product3 = TEMP1 logger.info('S60203: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60204"): TEMP = request.split(b'S60204') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product4 = TEMP1.ljust(22) elif len(TEMP1) > 22: product4 = TEMP1[:20] + " " else: product4 = TEMP1 logger.info('S60204: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60200"): TEMP = request.split(b'S60200') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product1 = TEMP1.ljust(22) product2 = TEMP1.ljust(22) product3 = TEMP1.ljust(22) product4 = TEMP1.ljust(22) elif len(TEMP1) > 22: product1 = TEMP1[:20] + " " product2 = TEMP1[:20] + " " product3 = TEMP1[:20] + " " product4 = TEMP1[:20] + " " else: product1 = TEMP1 product2 = TEMP1 product3 = TEMP1 product4 = TEMP1 logger.info('S60200: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) else: response = AST_ERROR else: response = AST_ERROR # log what was entered logger.info('%s command attempt %s:%d. (%s)', request, addr[0], addr[1], session.id) if response: sock.send(str_to_bytes(response)) session.add_event({ "type": "AST {0}".format(cmd), "request": request, "response": response }) except Exception as e: logger.exception(('Unknown Error: {}'.format(str(e)))) logger.info('GuardianAST client disconnected %s:%d. (%s)', addr[0], addr[1], session.id) session.add_event({'type': 'CONNECTION_LOST'})
def request_ssl_17(self, data_ssl_index): # just for convenience current_ssl = S7.ssl_lists['W#16#xy11'] if data_ssl_index == 1: # 0x0001 - component identification ssl_index_description = 'Component identification' ssl_resp_data = pack('!HHHHH20sHHH', 17, # 1 WORD ( ID ) data_ssl_index, # 1 WORD ( Index ) 28, # 1 WORD ( Length of payload after element count ) 0x01, # 1 WORD ( 1 element follows ) data_ssl_index, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#0001'])), # 10 WORDS ( MLFB of component: 20 bytes => 19 chars + 1 blank (0x20) ) 0x0, # 1 WORD ( RESERVED ) 0x0, # 1 WORD ( Output state of component ) 0x0) # 1 WORD ( RESERVED ) ssl_resp_head = pack('!BBH', 0xff, # 1 BYTE ( Data Error Code. 0xFF = OK ) 0x09, # 1 BYTE ( Data Type. 0x09 = Char/String ) len(ssl_resp_data)) # 1 WORD ( Length of following data ) elif data_ssl_index == 6: # 0x0006 - hardware identification ssl_index_description = 'Hardware identification' ssl_resp_data = pack('!HHHHH20sHHH', 17, # 1 WORD ( ID ) data_ssl_index, # 1 WORD ( Index ) 28, # 1 WORD ( Length of payload after element count ) 0x01, # 1 WORD ( 1 element follows ) data_ssl_index, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#0006'])), # 10 WORDS ( MLFB of component: 20 bytes => 19 chars + 1 blank (0x20) ) 0x0, # 1 WORD ( RESERVED ) 'V3', # 1 WORD ( 'V' and first digit of version number ) 0x539) # 1 WORD ( remaining digits of version number ) ssl_resp_head = pack('!BBH', 0xff, # 1 BYTE ( Data Error Code. 0xFF = OK ) 0x09, # 1 BYTE ( Data Type. 0x09 = Char/String ) len(ssl_resp_data)) # 1 WORD ( Length of following data ) elif data_ssl_index == 7: # 0x0007 - firmware identification ssl_index_description = 'Firmware identification' ssl_resp_data = pack('!HHHHH20sHHH', 17, # 1 WORD ( ID ) data_ssl_index, # 1 WORD ( Index ) 28, # 1 WORD ( Length of payload after element count ) 0x01, # 1 WORD ( 1 element follows ) data_ssl_index, # 1 WORD ( Data Index ) str_to_bytes(str(0x0)), # 10 WORDS ( RESERVED ) 0x0, # 1 WORD ( RESERVED ) 'V3', # 1 WORD ( 'V' and first digit of version number ) 0x53A) # 1 WORD ( remaining digits of version number ) ssl_resp_head = pack('!BBH', 0xff, # 1 BYTE ( Data Error Code. 0xFF = OK ) 0x09, # 1 BYTE ( Data Type. 0x09 = Char/String ) len(ssl_resp_data)) # 1 WORD ( Length of following data ) else: ssl_index_description = 'UNKNOWN / UNDEFINED / RESERVED {0}'.format(hex(data_ssl_index)) ssl_resp_data = '' ssl_resp_head = '' ssl_resp_params = pack('!BBBBBBBB', 0x00, # SSL DIAG 0x01, # unknown 0x12, # unknown 0x08, # bytes following 0x12, # unknown, maybe 0x11 + 1 0x84, # function; response to 0x44 0x01, # subfunction; readszl 0x01) # sequence ( = sequence + 1 ) return ssl_index_description, ssl_resp_params, ssl_resp_head + ssl_resp_data
def request_ssl_28(self, data_ssl_index): # just for convenience current_ssl = S7.ssl_lists['W#16#xy1C'] # initiate header for mass component block ssl_resp_data = pack('!HHHH', 28, # 1 WORD ( ID ) data_ssl_index, # 1 WORD ( Index ) 34, # 1 WORD ( Length of payload after element count ) 0x08) # 1 WORD ( 2 elements follow ) # craft module data 0x0001 - automation system name ssl_resp_data += pack('!H24s8s', 0x01, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#0001'])), # TODO: PADDING # 'System Name ', # 12 WORDS ( Name of automation system, padded with (0x00) ) str_to_bytes('')) # 4 WORDS ( RESERVED ) # craft module data 0x0002 - component name ssl_resp_data += pack('!H24s8s', 0x02, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#0002'])), # 12 WORDS ( Name of component, padded with (0x00) ) str_to_bytes('')) # 4 WORDS ( RESERVED ) # craft module data 0x0003 - plant identification ssl_resp_data += pack('!H32s', 0x03, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#0003'])),) # 16 WORDS ( Name of plant, padded with (0x00) ) # craft module data 0x0004 - copyright ssl_resp_data += pack('!H26s6s', 0x04, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#0004'])), # 13 WORDS ( CONSTANT ) str_to_bytes('')) # 3 WORDS ( RESERVED ) # craft module data 0x0005 - module serial number ssl_resp_data += pack('!H24s8s', 0x05, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#0005'])), # 12 WORDS ( Unique Serial Number ) str_to_bytes('')) # 4 WORDS ( RESERVED ) # craft module data 0x0007 - module type name ssl_resp_data += pack('!H32s', 0x07, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#0007'])),) # 16 WORDS ( CPU type name, padded wit (0x00) ) # craft module data 0x000a - OEM ID of module ssl_resp_data += pack('!H20s6s2s4s', 0x0a, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#000A'])), # 10 WORDS ( OEM-Copyright Text, padded with (0x00) ) str_to_bytes(''), # 3 WORDS ( OEM Copyright Text padding to 26 characters ) str_to_bytes(''), # 1 WORD ( OEM ID provided by Siemens ) str_to_bytes('')) # 2 WORDS ( OEM user defined ID ) # craft module data 0x000b - location ssl_resp_data += pack('!H32s', 0x0b, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl['W#16#000B'])),) # 16 WORDS ( Location String, padded with (0x00) ) # craft leading response header ssl_resp_head = pack('!BBH', 0xff, # 1 BYTE ( Data Error Code. 0xFF = OK ) 0x09, # 1 BYTE ( Data Type. 0x09 = Char/String ) len(ssl_resp_data)) # 1 WORD ( Length of following data ) ssl_resp_packet = ssl_resp_head + ssl_resp_data ssl_resp_params = pack('!BBBBBBBB', 0x00, # SSL DIAG 0x01, # unknown 0x12, # unknown 0x08, # bytes following 0x12, # unknown, maybe 0x11 + 1 0x84, # function; response to 0x44 0x01, # subfunction; readszl 0x01) # sequence ( = sequence + 1 ) return '', ssl_resp_params, ssl_resp_packet
def pack(self): return pack('!BBH', self.version, self.reserved, self.packet_length) + str_to_bytes(self.payload)
def pack(self): return pack( '!BBB', 2, # header len 0xf0, # data packet 0x80) + str_to_bytes(bytes(self.data))
def pack(self): return pack("!BBB", 2, 0xF0, 0x80) + str_to_bytes( # header len # data packet bytes(self.data))
def request_ssl_28(self, data_ssl_index): # just for convenience current_ssl = S7.ssl_lists["W#16#xy1C"] # initiate header for mass component block ssl_resp_data = pack( "!HHHH", 28, # 1 WORD ( ID ) data_ssl_index, # 1 WORD ( Index ) 34, # 1 WORD ( Length of payload after element count ) 0x08, ) # 1 WORD ( 2 elements follow ) # craft module data 0x0001 - automation system name ssl_resp_data += pack( "!H24s8s", 0x01, # 1 WORD ( Data Index ) str_to_bytes( self.data_bus.get_value(current_ssl["W#16#0001"]) ), # TODO: PADDING # 'System Name ', # 12 WORDS ( Name of automation system, padded with (0x00) ) str_to_bytes(""), ) # 4 WORDS ( RESERVED ) # craft module data 0x0002 - component name ssl_resp_data += pack( "!H24s8s", 0x02, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl["W#16#0002"])), # 12 WORDS ( Name of component, padded with (0x00) ) str_to_bytes(""), ) # 4 WORDS ( RESERVED ) # craft module data 0x0003 - plant identification ssl_resp_data += pack( "!H32s", 0x03, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl["W#16#0003"])), ) # 16 WORDS ( Name of plant, padded with (0x00) ) # craft module data 0x0004 - copyright ssl_resp_data += pack( "!H26s6s", 0x04, # 1 WORD ( Data Index ) str_to_bytes( self.data_bus.get_value(current_ssl["W#16#0004"]) ), # 13 WORDS ( CONSTANT ) str_to_bytes(""), ) # 3 WORDS ( RESERVED ) # craft module data 0x0005 - module serial number ssl_resp_data += pack( "!H24s8s", 0x05, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl["W#16#0005"])), # 12 WORDS ( Unique Serial Number ) str_to_bytes(""), ) # 4 WORDS ( RESERVED ) # craft module data 0x0007 - module type name ssl_resp_data += pack( "!H32s", 0x07, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl["W#16#0007"])), ) # 16 WORDS ( CPU type name, padded wit (0x00) ) # craft module data 0x000a - OEM ID of module ssl_resp_data += pack( "!H20s6s2s4s", 0x0A, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl["W#16#000A"])), # 10 WORDS ( OEM-Copyright Text, padded with (0x00) ) str_to_bytes( "" ), # 3 WORDS ( OEM Copyright Text padding to 26 characters ) str_to_bytes(""), # 1 WORD ( OEM ID provided by Siemens ) str_to_bytes(""), ) # 2 WORDS ( OEM user defined ID ) # craft module data 0x000b - location ssl_resp_data += pack( "!H32s", 0x0B, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl["W#16#000B"])), ) # 16 WORDS ( Location String, padded with (0x00) ) # craft leading response header ssl_resp_head = pack( "!BBH", 0xFF, # 1 BYTE ( Data Error Code. 0xFF = OK ) 0x09, # 1 BYTE ( Data Type. 0x09 = Char/String ) len(ssl_resp_data), ) # 1 WORD ( Length of following data ) ssl_resp_packet = ssl_resp_head + ssl_resp_data ssl_resp_params = pack( "!BBBBBBBB", 0x00, # SSL DIAG 0x01, # unknown 0x12, # unknown 0x08, # bytes following 0x12, # unknown, maybe 0x11 + 1 0x84, # function; response to 0x44 0x01, # subfunction; readszl 0x01, ) # sequence ( = sequence + 1 ) return "", ssl_resp_params, ssl_resp_packet
def request_ssl_17(self, data_ssl_index): # just for convenience current_ssl = S7.ssl_lists["W#16#xy11"] if data_ssl_index == 1: # 0x0001 - component identification ssl_index_description = "Component identification" ssl_resp_data = pack( "!HHHHH20sHHH", 17, # 1 WORD ( ID ) data_ssl_index, # 1 WORD ( Index ) 28, # 1 WORD ( Length of payload after element count ) 0x01, # 1 WORD ( 1 element follows ) data_ssl_index, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl["W#16#0001"])), # 10 WORDS ( MLFB of component: 20 bytes => 19 chars + 1 blank (0x20) ) 0x0, # 1 WORD ( RESERVED ) 0x0, # 1 WORD ( Output state of component ) 0x0, ) # 1 WORD ( RESERVED ) ssl_resp_head = pack( "!BBH", 0xFF, # 1 BYTE ( Data Error Code. 0xFF = OK ) 0x09, # 1 BYTE ( Data Type. 0x09 = Char/String ) len(ssl_resp_data), ) # 1 WORD ( Length of following data ) elif data_ssl_index == 6: # 0x0006 - hardware identification ssl_index_description = "Hardware identification" ssl_resp_data = pack( "!HHHHH20sHHH", 17, # 1 WORD ( ID ) data_ssl_index, # 1 WORD ( Index ) 28, # 1 WORD ( Length of payload after element count ) 0x01, # 1 WORD ( 1 element follows ) data_ssl_index, # 1 WORD ( Data Index ) str_to_bytes(self.data_bus.get_value(current_ssl["W#16#0006"])), # 10 WORDS ( MLFB of component: 20 bytes => 19 chars + 1 blank (0x20) ) 0x0, # 1 WORD ( RESERVED ) "V3", # 1 WORD ( 'V' and first digit of version number ) 0x539, ) # 1 WORD ( remaining digits of version number ) ssl_resp_head = pack( "!BBH", 0xFF, # 1 BYTE ( Data Error Code. 0xFF = OK ) 0x09, # 1 BYTE ( Data Type. 0x09 = Char/String ) len(ssl_resp_data), ) # 1 WORD ( Length of following data ) elif data_ssl_index == 7: # 0x0007 - firmware identification ssl_index_description = "Firmware identification" ssl_resp_data = pack( "!HHHHH20sHHH", 17, # 1 WORD ( ID ) data_ssl_index, # 1 WORD ( Index ) 28, # 1 WORD ( Length of payload after element count ) 0x01, # 1 WORD ( 1 element follows ) data_ssl_index, # 1 WORD ( Data Index ) str_to_bytes(str(0x0)), # 10 WORDS ( RESERVED ) 0x0, # 1 WORD ( RESERVED ) "V3", # 1 WORD ( 'V' and first digit of version number ) 0x53A, ) # 1 WORD ( remaining digits of version number ) ssl_resp_head = pack( "!BBH", 0xFF, # 1 BYTE ( Data Error Code. 0xFF = OK ) 0x09, # 1 BYTE ( Data Type. 0x09 = Char/String ) len(ssl_resp_data), ) # 1 WORD ( Length of following data ) else: ssl_index_description = "UNKNOWN / UNDEFINED / RESERVED {0}".format( hex(data_ssl_index) ) ssl_resp_data = "" ssl_resp_head = "" ssl_resp_params = pack( "!BBBBBBBB", 0x00, # SSL DIAG 0x01, # unknown 0x12, # unknown 0x08, # bytes following 0x12, # unknown, maybe 0x11 + 1 0x84, # function; response to 0x44 0x01, # subfunction; readszl 0x01, ) # sequence ( = sequence + 1 ) return ssl_index_description, ssl_resp_params, ssl_resp_head + ssl_resp_data
def plc_stop_signal(self, current_client): # This function gets executed after plc stop signal is received the function stops the server for a while and then restarts it logger.info("Stop signal recieved from {}".format(current_client)) return str_to_bytes("0x00"), str_to_bytes("0x29")
def pack(self): return pack('!BBH', 3, # version 0, # reserved len(bytes(self.data))+4 # packet size ) + str_to_bytes(bytes(self.data))
def pack(self): return pack('!BBB', 2, # header len 0xf0, # data packet 0x80) + str_to_bytes(bytes(self.data))
def handle(self, sock, addr): session = conpot_core.get_session('guardian_ast', addr[0], addr[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info('New GuardianAST connection from %s:%d. (%s)', addr[0], addr[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) current_time = datetime.datetime.utcnow() fill_start = self.fill_offset_time - datetime.timedelta(minutes=313) fill_stop = self.fill_offset_time - datetime.timedelta(minutes=303) # Default Product names, change based off country needs product1 = self.databus.get_value('product1').ljust(22) product1 = self.databus.get_value('product1').ljust(22) product2 = self.databus.get_value('product2').ljust(22) product3 = self.databus.get_value('product3').ljust(22) product4 = self.databus.get_value('product4').ljust(22) # Create random Numbers for the volumes # # this will crate an initial Volume and then the second value based # off the orig value. vol1 = self.databus.get_value('vol1') vol1tc = random.randint(vol1, vol1+200) vol2 = self.databus.get_value('vol2') vol2tc = random.randint(vol2, vol2+200) vol3 = self.databus.get_value('vol3') vol3tc = random.randint(vol3, vol3+200) vol4 = self.databus.get_value('vol4') vol4tc = random.randint(vol4, vol4+200) # unfilled space ULLAGE ullage1 = str(self.databus.get_value('ullage1')) ullage2 = str(self.databus.get_value('ullage2')) ullage3 = str(self.databus.get_value('ullage3')) ullage4 = str(self.databus.get_value('ullage3')) # Height of tank height1 = str(self.databus.get_value('height1')).ljust(5, '0') height2 = str(self.databus.get_value('height2')).ljust(5, '0') height3 = str(self.databus.get_value('height3')).ljust(5, '0') height4 = str(self.databus.get_value('height4')).ljust(5, '0') # Water in tank, this is a variable that needs to be low h2o1 = str(self.databus.get_value('h2o1')).ljust(4, '0') h2o2 = str(self.databus.get_value('h2o2')).ljust(4, '0') h2o3 = str(self.databus.get_value('h2o3')).ljust(4, '0') h2o4 = str(self.databus.get_value('h2o4')).ljust(4, '0') # Temperature of the tank, this will need to be between 50 - 60 temp1 = str(self.databus.get_value('temp1')).ljust(5, '0') temp2 = str(self.databus.get_value('temp2')).ljust(5, '0') temp3 = str(self.databus.get_value('temp3')).ljust(5, '0') temp4 = str(self.databus.get_value('temp4')).ljust(5, '0') station = self.databus.get_value('station_name') # This function is to set-up up the message to be sent upon a successful I20100 command being sent # The final message is sent with a current date/time stamp inside of the main loop. def I20100(): ret = '\nI20100\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nIN-TANK INVENTORY\n\n' ret += 'TANK PRODUCT VOLUME TC VOLUME ULLAGE HEIGHT WATER TEMP' ret += '\n 1 ' + product1 + str(vol1) + ' ' + str(vol1tc) + ' ' + ullage1 + ' ' + height1 + ' ' + h2o1 + ' ' + temp1 ret += '\n 2 ' + product2 + str(vol2) + ' ' + str(vol2tc) + ' ' + ullage2 + ' ' + height2 + ' ' + h2o2 + ' ' + temp2 ret += '\n 3 ' + product3 + str(vol3) + ' ' + str(vol3tc) + ' ' + ullage3 + ' ' + height3 + ' ' + h2o3 + ' ' + temp3 ret += '\n 4 ' + product4 + str(vol4) + ' ' + str(vol4tc) + ' ' + ullage4 + ' ' + height4 + ' ' + h2o4 + ' ' + temp4 ret += '\n' return ret ########################################################################### # # Only one Tank is listed currently in the I20200 command # ########################################################################### def I20200(): ret = '\nI20200\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nDELIVERY REPORT\n\n' ret += 'T 1:' + product1 + '\nINCREASE DATE / TIME GALLONS TC GALLONS WATER TEMP DEG F HEIGHT\n\n' ret += ' END: ' + str(fill_stop.strftime('%m/%d/%Y %H:%M')) + ' ' + str(vol1 + 300) + ' ' + str(vol1tc + 300) + ' ' + h2o1 + ' ' + temp1 + ' ' + height1 + '\n' ret += ' START: ' + str(fill_start.strftime('%m/%d/%Y %H:%M')) + ' ' + str(vol1 - 300) + ' ' + str(vol1tc - 300) + ' ' + h2o1 + ' ' + temp1 + ' ' + str(float(height1) - 23) + '\n' ret += ' AMOUNT: ' + str(vol1) + ' ' + str(vol1tc) + '\n\n' return ret ########################################################################### # # I20300 In-Tank Leak Detect Report # ########################################################################### def I20300(): ret = '\nI20300\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n' ret += 'TANK 1 ' + product1 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 2 ' + product2 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 3 ' + product3 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' ret += 'TANK 4 ' + product4 + '\n TEST STATUS: OFF\nLEAK DATA NOT AVAILABLE ON THIS TANK\n\n' return ret ########################################################################### # Shift report command I20400 only one item in report at this time, # but can always add more if needed ########################################################################### def I20400(): ret = '\nI20400\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n' + station + '\n\n\n\nSHIFT REPORT\n\n' ret += 'SHIFT 1 TIME: 12:00 AM\n\nTANK PRODUCT\n\n' ret += ' 1 ' + product1 + ' VOLUME TC VOLUME ULLAGE HEIGHT WATER TEMP\n' ret += 'SHIFT 1 STARTING VALUES ' + str(vol1) + ' ' + str(vol1tc) + ' ' + ullage1 + ' ' + height1 + ' ' + h2o1 + ' ' + temp1 + '\n' ret += ' ENDING VALUES ' + str(vol1 + 940) + ' ' + str(vol1tc + 886) + ' ' + str(int(ullage1) + 345) + ' ' + str(float(height1) + 53) + ' ' + h2o1 + ' ' + temp1 + '\n' ret += ' DELIVERY VALUE 0\n' ret += ' TOTALS 940\n\n' return ret ########################################################################### # I20500 In-Tank Status Report ########################################################################### def I20500(): ret = '\nI20500\n' + str(current_time.strftime('%m/%d/%Y %H:%M')) ret += '\n\n\n' + station + '\n\n\n' ret += 'TANK PRODUCT STATUS\n\n' ret += ' 1 ' + product1 + ' NORMAL\n\n' ret += ' 2 ' + product2 + ' HIGH WATER ALARM\n' ret += ' HIGH WATER WARNING\n\n' ret += ' 3 ' + product3 + ' NORMAL\n\n' ret += ' 4 ' + product4 + ' NORMAL\n\n' return ret while True: try: # Get the initial data request = sock.recv(4096) # The connection has been closed if not request: break while not (b'\n' in request or b'00' in request): request += sock.recv(4096) # if first value is not ^A then do nothing # thanks John(achillean) for the help if request[:1] != b'\x01': logger.info('Non ^A command attempt %s:%d. (%s)', addr[0], addr[1], session.id) break # if request is less than 6, than do nothing if len(request) < 6: logger.info('Invalid command attempt %s:%d. (%s)', addr[0], addr[1], session.id) break cmds = {"I20100": I20100, "I20200": I20200, "I20300": I20300, "I20400": I20400, "I20500": I20500} cmd = request[1:7].decode() # strip ^A and \n out response = None if cmd in cmds: logger.info('%s command attempt %s:%d. (%s)', cmd, addr[0], addr[1], session.id) response = cmds[cmd]() elif cmd.startswith("S6020"): # change the tank name if cmd.startswith("S60201"): # split string into two, the command, and the data TEMP = request.split(b'S60201') # if length is less than two, print error if len(TEMP) < 2: response = AST_ERROR # Else the command was entered correctly and continue else: # Strip off the carrage returns and new lines TEMP1 = TEMP[1].rstrip(b'\r\n').decode() # if Length is less than 22 if len(TEMP1) < 22: # pad the result to have 22 chars product1 = TEMP1.ljust(22) elif len(TEMP1) > 22: # else only print 22 chars if the result was longer product1 = TEMP1[:20] + " " else: # else it fits fine (22 chars) product1 = TEMP1 logger.info('S60201: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60202"): TEMP = request.split(b'S60202') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product2 = TEMP1.ljust(22) elif len(TEMP1) > 22: product2 = TEMP1[:20] + " " else: product2 = TEMP1 logger.info('S60202: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60203"): TEMP = request.split(b'S60203') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product3 = TEMP1.ljust(22) elif len(TEMP1) > 22: product3 = TEMP1[:20] + " " else: product3 = TEMP1 logger.info('S60203: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60204"): TEMP = request.split(b'S60204') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product4 = TEMP1.ljust(22) elif len(TEMP1) > 22: product4 = TEMP1[:20] + " " else: product4 = TEMP1 logger.info('S60204: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) # Follows format for S60201 for comments elif cmd.startswith("S60200"): TEMP = request.split(b'S60200') if len(TEMP) < 2: response = AST_ERROR else: TEMP1 = TEMP[1].rstrip(b'\r\n').decode() if len(TEMP1) < 22: product1 = TEMP1.ljust(22) product2 = TEMP1.ljust(22) product3 = TEMP1.ljust(22) product4 = TEMP1.ljust(22) elif len(TEMP1) > 22: product1 = TEMP1[:20] + " " product2 = TEMP1[:20] + " " product3 = TEMP1[:20] + " " product4 = TEMP1[:20] + " " else: product1 = TEMP1 product2 = TEMP1 product3 = TEMP1 product4 = TEMP1 logger.info('S60200: %s command attempt %s:%d. (%s)', TEMP1, addr[0], addr[1], session.id) else: response = AST_ERROR else: response = AST_ERROR # log what was entered logger.info('%s command attempt %s:%d. (%s)', request, addr[0], addr[1], session.id) if response: sock.send(str_to_bytes(response)) session.add_event({"type": "AST {0}".format(cmd), "request": request, "response": response}) except Exception as e: logger.exception(('Unknown Error: {}'.format(str(e)))) logger.info('GuardianAST client disconnected %s:%d. (%s)', addr[0], addr[1], session.id) session.add_event({'type': 'CONNECTION_LOST'})
def do_GET(self): """Handle GET requests""" # fetch configuration dependent variables from server instance headers = [] headers.extend(self.server.global_headers) configuration = self.server.configuration docpath = self.server.docpath # retrieve GET body data # ( sticking to the HTTP protocol, there should not be any body in GET requests, # an attacker could though use the body to inject data if not flushed correctly, # which is done by accessing the data like we do now - just to be secure.. ) get_data_length = self.headers.get('content-length') get_data = None if get_data_length: get_data = self.rfile.read(int(get_data_length)) # try to find a configuration item for this GET request try: entity_xml = configuration.xpath( '//http/htdocs/node[@name="' + self.path.partition('?')[0] + '"]' ) except XPathEvalError: entity_xml = None logger.debug('Malformed HTTP:GET URN. Failed to handle <{}>. (Client: {})'.format(self.path, self.client_address)) if entity_xml: # A config item exists for this entity. Handle it.. (status, headers, trailers, payload, chunks) = self.load_entity(self.path, headers, configuration, docpath) else: # No config item could be found. Fall back to a standard 404.. status = 404 (status, headers, trailers, payload, chunks) = self.load_status(status, self.path, self.headers, headers, configuration, docpath, 'GET') # send initial HTTP status line to client self.send_response(status) # send all headers to client for header in headers: self.send_header(header[0], header[1]) self.end_headers() # decide upon sending content as a whole or chunked if chunks == '0': # send payload as a whole to the client self.wfile.write(str_to_bytes(payload)) else: # send payload in chunks to the client self.send_chunked(chunks, payload, trailers) # loggers self.log(self.request_version, self.command, self.client_address, (self.path, self.headers._headers, get_data), status)