def testCheckTrIdRollover(self): """Check that the transaction id will rollover when max valuie is reached""" query = modbus_tcp.TcpQuery() tr_id_before = query._get_transaction_id() for a in range(int("ffff", 16)): query._get_transaction_id() self.assertEqual(query._get_transaction_id(), tr_id_before)
def testParseTooShortRequest(self): """Test an error is raised if the request is too short""" query = modbus_tcp.TcpQuery() self.assertRaises(modbus_tk.modbus.ModbusInvalidRequestError, query.parse_request, "") self.assertRaises(modbus_tk.modbus.ModbusInvalidRequestError, query.parse_request, "a" * 6)
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('modbus', address[0], address[1]) self.start_time = time.time() logger.info('New connection from {0}:{1}. ({2})'.format(address[0], address[1], session.id)) try: while True: request = sock.recv(7) if not request: logger.info('Client disconnected. ({0})'.format(session.id)) break if request.strip().lower() == 'quit.': logger.info('Client quit. ({0})'.format(session.id)) break tr_id, pr_id, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): new_byte = sock.recv(1) request += new_byte query = modbus_tcp.TcpQuery() # logdata is a dictionary containing request, slave_id, function_code and response response, logdata = self._databank.handle_request(query, request) logdata['request'] = request.encode('hex') session.add_event(logdata) logger.debug('Modbus traffic from {0}: {1} ({2})'.format(address[0], logdata, session.id)) if response: sock.sendall(response) except socket.timeout: logger.debug('Socket timeout, remote: {0}. ({1})'.format(address[0], session.id))
def testBuildRequest(self): """Test the mbap returned by building a request""" query = modbus_tcp.TcpQuery() request = query.build_request(to_data(""), 0) self.assertEqual( struct.pack(">HHHB", query._request_mbap.transaction_id, 0, 1, 0), request)
def testParseRequestInvalidLength(self): """Test that an error is raised if the length is not valid""" query = modbus_tcp.TcpQuery() i = 0 for pdu in ["", "a", "a" * 127, "abcdefghi"]: request = struct.pack(">HHHB", 0, 0, (len(pdu) + 2), 0) self.assertRaises(modbus_tk.modbus_tcp.ModbusInvalidMbapError, query.parse_request, request + to_data(pdu))
def testBuildRequestWithSlave(self): """Test the mbap returned by building a request with a slave""" query = modbus_tcp.TcpQuery() for i in range(0, 255): request = query.build_request(to_data(""), i) self.assertEqual( struct.pack(">HHHB", query._request_mbap.transaction_id, 0, 1, i), request)
def testIncIdOfRequest(self): """Check that the transaction id is increased when building the request""" queries = [modbus_tcp.TcpQuery() for i in range(100)] for i in range(len(queries)): queries[i].build_request(to_data(""), 0) for i in range(len(queries)-1): self.assertEqual(queries[i]._request_mbap.transaction_id+1, queries[i+1]._request_mbap.transaction_id)
def testBuildRequestWithPdu(self): """Test the mbap returned by building a request with a pdu""" query = modbus_tcp.TcpQuery() for pdu in ["", "a", "a" * 127, "abcdefghi"]: request = query.build_request(to_data(pdu), 0) self.assertEqual( struct.pack(">HHHB" + str(len(pdu)) + "s", query._request_mbap.transaction_id, 0, len(pdu) + 1, 0, to_data(pdu)), request)
def testBuildResponse(self): """Test that the response of a request is build properly""" query = modbus_tcp.TcpQuery() i = 0 for pdu in ["", "a", "a"*127, "abcdefghi"]: request = query.build_request(to_data(pdu), i) response = query.build_response(to_data(pdu)) response_pdu = query.parse_response(response) self.assertEqual(to_data(pdu), response_pdu) i += 1
def testParseRequest(self): """Test that Modbus TCP part of the request is understood""" query = modbus_tcp.TcpQuery() i = 0 for pdu in ["", "a", "a"*127, "abcdefghi"]: request = query.build_request(to_data(pdu), i) (slave, extracted_pdu) = query.parse_request(request) self.assertEqual(extracted_pdu, to_data(pdu)) self.assertEqual(slave, i) i += 1
def testParseWrongProtocolIdResponse(self): """Test an error is raised if wrong protocol id""" query = modbus_tcp.TcpQuery() pdu = "a" request = query.build_request(pdu, 0) response = struct.pack(">HHHB" + str(len(pdu)) + "s", query._request_mbap.transaction_id, query._request_mbap.protocol_id + 1, len(pdu) + 1, query._request_mbap.unit_id, pdu) self.assertRaises(modbus_tk.modbus_tcp.ModbusInvalidMbapError, query.parse_response, response)
def testParseWrongLengthResponse(self): """Test an error is raised if the length is not ok""" query = modbus_tcp.TcpQuery() pdu = to_data('a') request = query.build_request(pdu, 0) response = struct.pack(">HHHB" + str(len(pdu)) + "s", query._request_mbap.transaction_id, query._request_mbap.protocol_id + 1, len(pdu), query._request_mbap.unit_id, to_data('pdu')) self.assertRaises(modbus_tk.modbus_tcp.ModbusInvalidMbapError, query.parse_response, response)
def testParseRespone(self): """Test that Modbus TCP part of the response is understood""" query = modbus_tcp.TcpQuery() for pdu in ["", "a", "a" * 127, "abcdefghi"]: request = query.build_request(to_data(pdu), 0) response = struct.pack(">HHHB" + str(len(pdu)) + "s", query._request_mbap.transaction_id, query._request_mbap.protocol_id, len(pdu) + 1, query._request_mbap.unit_id, to_data(pdu)) extracted = query.parse_response(response) self.assertEqual(extracted, to_data(pdu))
def testIncTrIdIsThreadSafe(self): """Check that the function in charge of increasing the transaction id is thread safe""" def inc_by(): query = modbus_tcp.TcpQuery() for i in range(1000): query._get_transaction_id() query = modbus_tcp.TcpQuery() tr_id_before = query._get_transaction_id() threads = [threading.Thread(target=inc_by) for thread_nr in range(20)] for thread in threads: thread.start() for thread in threads: thread.join() self.assertEqual(1000*20+1, query._get_transaction_id()-tr_id_before)
def handle(self, sock, address): sock.settimeout(self.timeout) session_id = str(uuid.uuid4()) session_data = { 'session_id': session_id, 'remote': address, 'timestamp': datetime.utcnow(), 'data_type': 'modbus', 'data': {} } start_time = time.time() logger.info('New connection from {0}:{1}. ({2})'.format( address[0], address[1], session_id)) try: while True: request = sock.recv(7) if not request: logger.info( 'Client disconnected. ({0})'.format(session_id)) break if request.strip().lower() == 'quit.': logger.info('Client quit. ({0})'.format(session_id)) break tr_id, pr_id, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): new_byte = sock.recv(1) request += new_byte query = modbus_tcp.TcpQuery() #logdata is a dictionary containing request, slave_id, function_code and response response, logdata = self._databank.handle_request( query, request) elapse_ms = int(time.time() - start_time) * 1000 session_data['data'][elapse_ms] = logdata logger.debug('Modbus traffic from {0}: {1} ({2})'.format( address[0], logdata, session_id)) if response: sock.sendall(response) except socket.timeout: logger.debug('Socket timeout, remote: {0}. ({1})'.format( address[0], session_id)) self.log_queue.put(session_data)
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session('modbus', address[0], address[1]) self.start_time = time.time() logger.info('New Modbus connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) try: while True: request = sock.recv(7) if not request: logger.info('Modbus client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break if request.strip().lower() == 'quit.': logger.info('Modbus client quit. (%s)', session.id) session.add_event({'type': 'CONNECTION_QUIT'}) break tr_id, pr_id, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): new_byte = sock.recv(1) request += new_byte query = modbus_tcp.TcpQuery() # logdata is a dictionary containing request, slave_id, # function_code and response response, logdata = self._databank.handle_request( query, request, self.mode) logdata['request'] = request.encode('hex') session.add_event(logdata) logger.info('Modbus traffic from %s: %s (%s)', address[0], logdata, session.id) if response: sock.sendall(response) logger.info('Modbus response sent to %s', address[0]) else: # MB serial connection addressing UID=0 if (self.mode == 'serial' and logdata['slave_id'] == 0): # delay is in milliseconds time.sleep(self.delay / 1000) logger.debug( 'Modbus server\'s turnaround delay expired.') logger.info( 'Modbus connection terminated with client %s.', address[0]) session.add_event({'type': 'CONNECTION_TERMINATED'}) sock.shutdown(socket.SHUT_RDWR) sock.close() break # Invalid addressing else: logger.info( 'Modbus client ignored due to invalid addressing.' ' (%s)', session.id) session.add_event({'type': 'CONNECTION_TERMINATED'}) sock.shutdown(socket.SHUT_RDWR) sock.close() break except socket.timeout: logger.debug('Socket timeout, remote: %s. (%s)', address[0], session.id) session.add_event({'type': 'CONNECTION_LOST'})
def inc_by(): query = modbus_tcp.TcpQuery() for i in range(1000): query._get_transaction_id()
def handle(self, sock, address): sock.settimeout(self.timeout) session = conpot_core.get_session( "modbus", address[0], address[1], sock.getsockname()[0], sock.getsockname()[1], ) self.start_time = time.time() logger.info("New Modbus connection from %s:%s. (%s)", address[0], address[1], session.id) session.add_event({"type": "NEW_CONNECTION"}) try: while True: request = None try: request = sock.recv(7) except Exception as e: logger.error( "Exception occurred in ModbusServer.handle() " "at sock.recv(): %s", str(e), ) if not request: logger.info("Modbus client disconnected. (%s)", session.id) session.add_event({"type": "CONNECTION_LOST"}) break if request.strip().lower() == "quit.": logger.info("Modbus client quit. (%s)", session.id) session.add_event({"type": "CONNECTION_QUIT"}) break if len(request) < 7: logger.info( "Modbus client provided data {} but invalid.".format( session.id)) session.add_event({"type": "CONNECTION_TERMINATED"}) break _, _, length = struct.unpack(">HHH", request[:6]) while len(request) < (length + 6): try: new_byte = sock.recv(1) request += new_byte except Exception: break query = modbus_tcp.TcpQuery() # logdata is a dictionary containing request, slave_id, # function_code and response response, logdata = self._databank.handle_request( query, request, self.mode) logdata["request"] = codecs.encode(request, "hex") session.add_event(logdata) logger.info("Modbus traffic from %s: %s (%s)", address[0], logdata, session.id) if response: sock.sendall(response) logger.info("Modbus response sent to %s", address[0]) else: # TODO: # response could be None under several different cases # MB serial connection addressing UID=0 if (self.mode == "serial") and (logdata["slave_id"] == 0): # delay is in milliseconds time.sleep(self.delay / 1000) logger.debug( "Modbus server's turnaround delay expired.") logger.info( "Modbus connection terminated with client %s.", address[0]) session.add_event({"type": "CONNECTION_TERMINATED"}) sock.shutdown(socket.SHUT_RDWR) sock.close() break # Invalid addressing else: logger.info( "Modbus client ignored due to invalid addressing." " (%s)", session.id, ) session.add_event({"type": "CONNECTION_TERMINATED"}) sock.shutdown(socket.SHUT_RDWR) sock.close() break except socket.timeout: logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id) session.add_event({"type": "CONNECTION_LOST"})
def testBuildRequestWithInvalidSlave(self): """Test that an error is raised when invalid slave is passed""" query = modbus_tcp.TcpQuery() for i in [-1, 256, 257, 65536]: self.assertRaises(modbus_tk.modbus.InvalidArgumentError, query.build_request, "", i)