Exemple #1
0
    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)
            )
Exemple #2
0
 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)
Exemple #3
0
 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)
Exemple #4
0
 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()
Exemple #6
0
    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)
Exemple #7
0
 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))
Exemple #8
0
 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))
Exemple #9
0
 def pack(self):
     return (pack(
         "!BBH",
         3,  # version
         0,  # reserved
         len(bytes(self.data)) + 4,  # packet size
     ) + str_to_bytes(bytes(self.data)))
Exemple #10
0
    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
Exemple #11
0
    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
Exemple #12
0
 def pack(self):
     return pack(
         '!BBH',
         3,  # version
         0,  # reserved
         len(bytes(self.data)) + 4  # packet size
     ) + str_to_bytes(bytes(self.data))
Exemple #13
0
    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()
Exemple #15
0
 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'})
Exemple #18
0
    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
Exemple #19
0
    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
Exemple #20
0
 def pack(self):
     return pack('!BBH', self.version, self.reserved,
                 self.packet_length) + str_to_bytes(self.payload)
Exemple #21
0
 def pack(self):
     return pack(
         '!BBB',
         2,  # header len
         0xf0,  # data packet
         0x80) + str_to_bytes(bytes(self.data))
Exemple #22
0
 def pack(self):
     return pack("!BBB", 2, 0xF0,
                 0x80) + str_to_bytes(  # header len  # data packet
                     bytes(self.data))
Exemple #23
0
    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
Exemple #24
0
    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
Exemple #25
0
 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")
Exemple #26
0
 def pack(self):
     return pack('!BBH', self.version, self.reserved, self.packet_length) + str_to_bytes(self.payload)
Exemple #27
0
 def pack(self):
     return pack('!BBH',
                 3,                         # version
                 0,                         # reserved
                 len(bytes(self.data))+4    # packet size
                 ) + str_to_bytes(bytes(self.data))
Exemple #28
0
 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'})
Exemple #30
0
    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)