Ejemplo n.º 1
0
    def __init__(self, sock, keep_alive=True):
        """
        Initializes the NI stream socket.

        @param sock: socket
        @type sock: C{socket}

        @param keep_alive: if true, the socket will automatically respond to
            keep-alive request messages. Otherwise, the keep-alive messages
            are passed to the caller in L{recv} and L{sr} calls.
        @type keep_alive: C{bool}
        """
        StreamSocket.__init__(self, sock, Raw)
        self.keep_alive = keep_alive
Ejemplo n.º 2
0
    def __init__(self, sock, keep_alive=True, base_cls=None):
        """Initializes the NI stream socket.

        @param sock: socket to wrap
        @type sock: C{socket}

        @param keep_alive: if true, the socket will automatically respond to
            keep-alive request messages. Otherwise, the keep-alive messages
            are passed to the caller in L{recv} and L{sr} calls.
        @type keep_alive: C{bool}

        @param base_cls: the base class to use when receiving packets, it uses
            L{SAPNI} as default if no class specified
        @type base_cls: L{Packet} class
        """
        StreamSocket.__init__(self, sock, Raw)
        self.keep_alive = keep_alive
        self.basecls = base_cls
Ejemplo n.º 3
0
    def send(self, packet):
        """Send a packet at the NI layer, prepending the length field.

        @param packet: packet to send
        @type packet: Packet
        """
        # Add the NI layer and send
        log_sapni.debug("To send %d bytes", len(packet) + 4)
        return StreamSocket.send(self, SAPNI() / packet)
Ejemplo n.º 4
0
    def __init__(self, sock, keep_alive=True, base_cls=None):
        """Initializes the NI stream socket.

        :param sock: socket to wrap
        :type sock: C{socket}

        :param keep_alive: if true, the socket will automatically respond to
            keep-alive request messages. Otherwise, the keep-alive messages
            are passed to the caller in :class:`recv` and :class:`sr` calls.
        :type keep_alive: ``bool``

        :param base_cls: the base class to use when receiving packets, it uses
            :class:`SAPNI` as default if no class specified
        :type base_cls: :class:`Packet` class
        """
        StreamSocket.__init__(self, sock, Raw)
        self.keep_alive = keep_alive
        self.basecls = base_cls
Ejemplo n.º 5
0
 def recv(self):
     """Receive a packet from the target host. If the talk mode in use is
     native and we've already set the route, the packet received is a raw
     packet. Otherwise, the packet received is a NI layer packet in the same
     way the :class:`SAPNIStreamSocket` works.
     """
     # If we're working on native mode and the route was accepted, we don't
     # need the NI layer anymore. Just use the plain socket inside the
     # NIStreamSockets.
     if self.routed and self.talk_mode == 1:
         return StreamSocket.recv(self)
     # If the route was not accepted yet or we're working on non-native talk
     # mode, we need the NI layer.
     return SAPNIStreamSocket.recv(self)
Ejemplo n.º 6
0
    def send(self, packet):
        """Send a packet. If the talk mode in use is native the packet sent is
        a raw packet. Otherwise, the packet is a NI layer packet in the same way
        the :class:`SAPNIStreamSocket` works.

        :param packet: packet to send
        :type packet: Packet
        """
        # If we're working on native mode and the route was accepted, we don't
        # need the NI layer anymore. Just use the plain socket inside the
        # NIStreamSockets.
        if self.routed and self.talk_mode == 1:
            return StreamSocket.send(self, packet)
        # If the route was not accepted yet or we're working on non-native talk
        # mode, we need the NI layer.
        return SAPNIStreamSocket.send(self, packet)
Ejemplo n.º 7
0
Archivo: doip.py Proyecto: netkey/scapy
 def __init__(self, ip='127.0.0.1', port=6801):
     s = socket.socket()
     s.connect((ip, port))
     StreamSocket.__init__(self, s, DoIP)
Ejemplo n.º 8
0
class Wdb2Client(Base):
    def __init__(self, name, ip, port=17185, timeout=2, mem_buff_size=300):
        '''

        :param name: Name of this targets
        :param ip: VxWorks ip
        :param port: WDB port (default: 17185)
        :param timeout: timeout of socket (default: 2)
        :param mem_buff_size: Mem buff size for memory read or write (default: 300)
        '''
        super(Wdb2Client, self).__init__(name=name)
        self._ip = ip
        self._port = port
        self._timeout = timeout
        self._connection = None
        self._target_info = {}
        self._seq = None
        self._mem_buff_size = mem_buff_size
        self.mem_dump = ''
        self.target_info = {}

    def connect(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.connect((self._ip, self._port))
        sock.settimeout(self._timeout)
        self._connection = StreamSocket(sock, Raw)
        self._seq = 1
        connect_packet = RPCReq() / WdbConnectReq()
        connect_packet[RPCReq].Procedure = 0x7a
        connect_packet[RPCReq].Seq = self._seq
        self.send_receive_wdb_packet(connect_packet)

    def reconnect(self):
        self.connect()

    def _get_seq(self):
        if self._seq >= 65535:
            self.connect()
            return self._seq
        else:
            return self._seq

    def _fix_seq(self, payload):
        if self._seq > 65535:
            self._seq = 1
        try:
            payload.Seq = self._seq
            self._seq += 1
            return payload
        except Exception as err:
            self.logger.error(err)
            return payload

    def send_packet(self, packet):
        if self._connection:
            try:
                self._connection.send(packet)

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def send_receive_packet(self, packet):
        if self._connection:
            try:
                rsp = self._connection.sr1(packet, timeout=self._timeout)
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def receive_packet(self):
        if self._connection:
            try:
                rsp = self._connection.recv()
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before receive packet!")

    def send_wdb_packet(self, packet):
        if self._connection:
            packet = self._fix_seq(packet)
            try:
                self._connection.send(packet)

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def send_receive_wdb_packet(self, packet):
        if self._connection:
            packet = self._fix_seq(packet)
            try:
                rsp = self._connection.sr1(packet, timeout=self._timeout)
                if rsp:
                    rsp = RPCRsp(str(rsp))
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def receive_wdb_packet(self):
        if self._connection:
            try:
                rsp = self._connection.recv()
                if rsp:
                    rsp = RPCRsp(str(rsp))
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None
        else:
            self.logger.error("Please create connect before receive packet!")

    def _unpack_info(self, info):
        self.target_info = {}
        info = xdrlib.Unpacker(info)
        self.target_info["Target_Type"] = info.unpack_string()  # 'VxWorks\x00'
        self.target_info["Vx_Version"] = info.unpack_string()  # '6.6\x00'
        self.target_info["Unknow1"] = info.unpack_uint()  # 80
        self.target_info["Unknow2"] = info.unpack_uint()  # 86
        self.target_info["CPU_Type"] = info.unpack_string()  # '86\x00'
        self.target_info["compiler"] = info.unpack_string()  # '86\x00'
        self.target_info["Unknow3"] = info.unpack_uint()  # 86
        self.target_info["Unknow4"] = info.unpack_uint()  # 86
        self.target_info["Unknow5"] = info.unpack_uint()  # 86
        self.target_info["Unknow6"] = info.unpack_uint()  # 86
        self.target_info["Unknow7"] = info.unpack_uint()  # 86
        self.target_info["CPU_Model"] = info.unpack_string()  # '86\x00'
        self.target_info["Unknow8"] = info.unpack_string()  # '86\x00'
        self.target_info["Unknow9"] = info.unpack_uint()  # 86
        self.target_info["Memory_Size"] = info.unpack_uint()  # 86
        self.target_info["Unknow10"] = info.unpack_uint()
        self.target_info["Unknow11"] = info.unpack_uint()
        self.target_info["Unknow12"] = info.unpack_uint()
        self.target_info["Unknow13"] = info.unpack_uint()
        self.target_info["Unknow14"] = info.unpack_uint()
        self.target_info["Unknow15"] = info.unpack_uint()
        return self.target_info

    def get_target_info(self):
        info_packet = RPCReq() / WdbGetInfoReq()
        info_packet[RPCReq].Procedure = 0x7b
        rsp = self.send_receive_wdb_packet(info_packet)
        info = rsp.load[4:]
        target_info = self._unpack_info(info)
        return target_info

    def _write_memory(self, address, data):
        '''

        :param address: offset of target memory
        :param data: data need to write to target
        :return: target response packet
        '''

        address = int(address)
        pkt = RPCReq() / WdbMemWriteReq(Offset=address, Buff=data)
        pkt[RPCReq].Procedure = 0xb
        print('start writing memory at 0x', struct.pack("!I", address).encode('hex'))
        return self.send_receive_wdb_packet(pkt)

    def write_target_memory(self, address, data):
        '''
        :param address: offset of memory
        :param data: data need to write
        :return: None
        '''
        address = int(address)
        if len(data) < 4:
            print("data length can't less than 4 byte")
        else:
            if len(data) % 4 != 0:
                data += '\x00' * (len(data) % 4)

        for i in range(0, len(data), 4):
            buff = data[i:i + 4]
            res = self._write_memory(address, buff)
            if res is None:
                print("can't write memory at 0x", struct.pack("!I", address).encode('hex'))
                return
            address += 4

    def _read_memory(self, address, length):
        '''

        :param address: offset of target memory
        :param length: length of memory to be read
        :return: Memory Data
        '''
        address = int(address)
        pkt = RPCReq() / WdbMemReadReq(Offset=address, Length=length)
        pkt[RPCReq].Procedure = 0xa
        rsp = self.send_receive_wdb_packet(pkt)
        if rsp.WdbErrorState != 0x0:
            self.logger.error("Can't read memory from %s with length %s" % (address, length))
            self.logger.error("Error Code %s" % rsp.WdbErrorState)
            return None
        buff_length = struct.unpack('!i', rsp.load[12:16])[0]
        buff = rsp.load[16:16 + buff_length]
        return buff

    def read_target_memory(self, address, length):
        self.mem_dump = ''
        address = int(address)
        if length < self._mem_buff_size:
            temp_length = length
        else:
            temp_length = self._mem_buff_size
        for offset in range(address, address + length, temp_length):
            self.logger.info('Dumping memory at %s / %s' % (offset, address + length))
            self.mem_dump += self._read_memory(offset, temp_length)
        return self.mem_dump
Ejemplo n.º 9
0
def http_request(host,
                 path="/",
                 port=80,
                 timeout=3,
                 display=False,
                 verbose=0,
                 raw=False,
                 iptables=False,
                 iface=None,
                 **headers):
    """Util to perform an HTTP request, using the TCP_client.

    :param host: the host to connect to
    :param path: the path of the request (default /)
    :param port: the port (default 80)
    :param timeout: timeout before None is returned
    :param display: display the result in the default browser (default False)
    :param raw: opens a raw socket instead of going through the OS's TCP
                socket. Scapy will then use its own TCP client.
                Careful, the OS might cancel the TCP connection with RST.
    :param iptables: when raw is enabled, this calls iptables to temporarily
                     prevent the OS from sending TCP RST to the host IP.
                     On Linux, you'll almost certainly need this.
    :param iface: interface to use. Changing this turns on "raw"
    :param headers: any additional headers passed to the request

    :returns: the HTTPResponse packet
    """
    from scapy.sessions import TCPSession
    http_headers = {
        "Accept_Encoding": b'gzip, deflate',
        "Cache_Control": b'no-cache',
        "Pragma": b'no-cache',
        "Connection": b'keep-alive',
        "Host": host,
        "Path": path,
    }
    http_headers.update(headers)
    req = HTTP() / HTTPRequest(**http_headers)
    ans = None

    # Open a socket
    if iface is not None:
        raw = True
    if raw:
        # Use TCP_client on a raw socket
        iptables_rule = "iptables -%c INPUT -s %s -p tcp --sport 80 -j DROP"
        if iptables:
            host = str(Net(host))
            assert (os.system(iptables_rule % ('A', host)) == 0)
        sock = TCP_client.tcplink(HTTP, host, port, debug=verbose, iface=iface)
    else:
        # Use a native TCP socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, port))
        sock = StreamSocket(sock, HTTP)
    # Send the request and wait for the answer
    try:
        ans = sock.sr1(req,
                       session=TCPSession(app=True),
                       timeout=timeout,
                       verbose=verbose)
    finally:
        sock.close()
        if raw and iptables:
            host = str(Net(host))
            assert (os.system(iptables_rule % ('D', host)) == 0)
    if ans:
        if display:
            if Raw not in ans:
                warning("No HTTP content returned. Cannot display")
                return ans
            # Write file
            file = get_temp_file(autoext=".html")
            with open(file, "wb") as fd:
                fd.write(ans.load)
            # Open browser
            if WINDOWS:
                os.startfile(file)
            else:
                with ContextManagerSubprocess(conf.prog.universal_open):
                    subprocess.Popen([conf.prog.universal_open, file])
        return ans
Ejemplo n.º 10
0
    def get_nisocket(cls,
                     host=None,
                     port=None,
                     route=None,
                     password=None,
                     talk_mode=None,
                     router_version=None,
                     **kwargs):
        """Helper function to obtain a :class:`SAPRoutedStreamSocket`. If no
        route is specified, it returns a plain `SAPNIStreamSocket`. If no
        route is specified and the talk mode is raw, it returns a plain
        `StreamSocket` as it's assumed that the NI layer is not desired.

        :param host: target host to connect to if not specified in the route
        :type host: C{string}

        :param port: target port to connect to if not specified in the route
        :type port: ``int``

        :param route: route to use for determining the SAP Router to connect
        :type route: C{string} or ``list`` of :class:`SAPRouterRouteHop`

        :param password: target password if not specified in the route
        :type password: C{string}

        :param talk_mode: the talk mode to use for requesting the route
        :type talk_mode: ``int``

        :param router_version: the router version to use for requesting the
            route
        :type router_version: ``int``

        :keyword kwargs: arguments to pass to :class:`SAPRoutedStreamSocket`
            constructor

        :return: connected socket through the specified route
        :rtype: :class:`SAPRoutedStreamSocket`

        :raise SAPRouteException: if the route request to the target host/port
            was not accepted by the SAP Router

        :raise socket.error: if the connection to the target host/port failed
            or the SAP Router returned an error
        """
        # If no route was provided, check the talk mode
        if route is None:
            # If talk mode is raw, create a new StreamSocket and get rid of the
            # NI layer completely and force the base class to Raw.
            if talk_mode == 1:
                sock = socket.create_connection((host, port))
                if "base_cls" in kwargs:
                    kwargs["base_cls"] = Raw
                return StreamSocket(sock, **kwargs)

            # Otherwise use the standard SAPNIStreamSocket get_nisocket method
            else:
                return SAPNIStreamSocket.get_nisocket(host, port, **kwargs)

        # If the route was provided using a route string, convert it to a
        # list of hops
        if isinstance(route, str):
            route = SAPRouterRouteHop.from_string(route)

        # If the host and port were specified, we need to add a new hop to
        # the route
        if host is not None and port is not None:
            route.append(
                SAPRouterRouteHop(hostname=host, port=port, password=password))

        # Connect to the first hop in the route (it should be the SAP Router)
        sock = socket.create_connection(
            (route[0].hostname, int(route[0].port)))

        # Create a SAPRoutedStreamSocket instance specifying the route
        return cls(sock, route, talk_mode, router_version, **kwargs)
Ejemplo n.º 11
0
class CIPClient(Base):
    def __init__(self, name, ip, port=44818, timeout=2):
        '''

        :param name: Name of this targets
        :param ip: Target ip
        :param port: CIP port (default: 44818)
        :param timeout: timeout of socket (default: 2)
        '''
        super(CIPClient, self).__init__(name=name)
        self._ip = ip
        self._port = port
        self._timeout = timeout
        self._connection = None
        self._target_info = {}
        self._session = 0x0
        self.target_info = {}

    def connect(self):
        sock = socket.socket()
        sock.settimeout(self._timeout)
        sock.connect((self._ip, self._port))
        self._connection = StreamSocket(sock, Raw)
        packet_1 = ENIPHeader(Command=0x65) / RegisterSession()
        rsp_1 = self.send_receive_cip_packet(packet_1)
        try:
            if rsp_1.haslayer(ENIPHeader):
                self._session = rsp_1.Session
        except Exception as err:
            self.logger.error(err)
            return

    def reconnect(self):
        self.connect()

    def _fix_session(self, packet):
        try:
            packet.Session = self._session
            return packet
        except Exception as err:
            self.logger.error(err)
            return packet

    def send_packet(self, packet):
        if self._connection:
            try:
                self._connection.send(packet)

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def send_receive_packet(self, packet):
        if self._connection:
            try:
                rsp = self._connection.sr1(packet, timeout=self._timeout)
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def receive_packet(self):
        if self._connection:
            try:
                rsp = self._connection.recv()
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before receive packet!")

    def send_cip_packet(self, packet):
        if self._connection:
            packet = self._fix_session(packet)
            try:
                self._connection.send(packet)

            except Exception as err:
                self.logger.error(err)
                return None
        else:
            self.logger.error("Please create connect before send packet!")

    def send_receive_cip_packet(self, packet):
        if self._connection:
            packet = self._fix_session(packet)
            # packet.show2()
            try:
                rsp = self._connection.sr1(packet, timeout=self._timeout)
                if rsp:
                    rsp = ENIPHeader(str(rsp))
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def receive_cip_packet(self):
        if self._connection:
            try:
                rsp = self._connection.recv()
                if rsp:
                    rsp = ENIPHeader(str(rsp))
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None
        else:
            self.logger.error("Please create connect before receive packet!")

    def get_target_info(self, port=0x01, port_segment=0x00):
        product_name = ''
        device_type = ''
        vendor = ''
        revision = ''
        serial_number = ''
        info_packet = ENIPHeader(Command=0x6f)/CIPCommandSpecificData()/\
                      CIPHeader(Type="Request", Service=0x52,)/\
                      CIPConnectionManager()
        info_packet[CIPCommandSpecificData].Items = [
            NullAddressItem(), UnconnectedDataItem()
        ]
        info_packet[CIPHeader].RequestPath = [
            CIPRequestPath(PathSegmentType=1, InstanceSegment=0x06),
            CIPRequestPath(PathSegmentType=1,
                           LogicalSegmentType=0x01,
                           InstanceSegment=0x01)
        ]
        info_packet[CIPConnectionManager].MessageRequest = CIPHeader(
            Type="Request",
            Service=0x01,
            RequestPath=[
                CIPRequestPath(PathSegmentType=1, InstanceSegment=0x01),
                CIPRequestPath(PathSegmentType=1,
                               LogicalSegmentType=0x01,
                               InstanceSegment=0x01)
            ])
        info_packet[CIPRoutePath].Port = port
        info_packet[CIPRoutePath].PortSegment = port_segment
        rsp = self.send_receive_cip_packet(info_packet)
        if rsp.haslayer(CIPHeader):
            if rsp[CIPHeader].GeneralStatus == 0x00:
                try:
                    if rsp.haslayer(GetAttributesAll):
                        product_name = rsp[GetAttributesAll].ProductName
                        device_type = rsp[GetAttributesAll].DeviceType
                        if device_type in DEVICE_TYPES.keys():
                            device_type = DEVICE_TYPES[device_type]
                        else:
                            device_type = "%s (%s)" % (product_name,
                                                       hex(device_type))
                        vendor = rsp[GetAttributesAll].VendorID
                        if vendor in VENDOR_IDS.keys():
                            vendor = VENDOR_IDS[vendor]
                        else:
                            vendor = "%s (%s)" % (product_name, hex(vendor))
                        revision = str(rsp[GetAttributesAll].MajorRevision) + '.'\
                                   + str(rsp[GetAttributesAll].MinorRevision)
                        serial_number = hex(rsp[GetAttributesAll].SerialNumber)
                except Exception as err:
                    pass

            else:
                self.logger.warning(
                    "Got Error Code:%s when get target info with port:%s and port_segment:%s"
                    % (port, port_segment, rsp[CIPHeader].GeneralStatus))
        return product_name, device_type, vendor, revision, serial_number
Ejemplo n.º 12
0
class S7Client(Base):
    def __init__(self,
                 name,
                 ip,
                 port=102,
                 src_tsap='\x01\x00',
                 rack=0,
                 slot=2,
                 timeout=2):
        '''

        :param name: Name of this targets
        :param ip: S7 PLC ip
        :param port: S7 PLC port (default: 102)
        :param src_tsap: src_tsap
        :param rack: cpu rack (default: 0)
        :param slot: cpu slot (default: 2)
        :param timeout: timeout of socket (default: 2)
        '''
        super(S7Client, self).__init__(name=name)
        self._ip = ip
        self._port = port
        self._slot = slot
        self._src_tsap = src_tsap
        self._dst_tsap = '\x01' + struct.pack('B', rack * 0x20 + slot)
        self._pdur = 1
        self.protect_level = None
        self._connection = None
        self._connected = False
        self._timeout = timeout
        self._pdu_length = 480
        self.readable = False
        self.writeable = False
        self.authorized = False
        self._password = None
        self._mmc_password = None
        self.is_running = False

    def connect(self):
        sock = socket.socket()
        sock.settimeout(self._timeout)
        sock.connect((self._ip, self._port))
        self._connection = StreamSocket(sock, Raw)
        packet1 = TPKT() / COTPCR()
        packet1.Parameters = [COTPOption() for i in range(3)]
        packet1.PDUType = "CR"
        packet1.Parameters[0].ParameterCode = "tpdu-size"
        packet1.Parameters[0].Parameter = "\x0a"
        packet1.Parameters[1].ParameterCode = "src-tsap"
        packet1.Parameters[2].ParameterCode = "dst-tsap"
        packet1.Parameters[1].Parameter = self._src_tsap
        packet1.Parameters[2].Parameter = self._dst_tsap
        self.send_receive_packet(packet1)
        packet2 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job", Parameters=S7SetConParameter())
        rsp2 = self.send_receive_s7_packet(packet2)
        if rsp2:
            self._connected = True
        # Todo: Need get pdu length from rsp2

    def _get_cpu_protect_level(self):
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="UserData",
            Parameters=S7ReadSZLParameterReq(),
            Data=S7ReadSZLDataReq(SZLId=0x0232, SZLIndex=0x0004))
        rsp = self.send_receive_s7_packet(packet1)
        self.protect_level = int(str(rsp)[48].encode('hex'))
        self.logger.info("CPU protect level is %s" % self.protect_level)

    def get_target_info(self):
        order_code = ''
        version = ''
        module_type_name = ''
        as_name = ''
        module_name = ''
        serial_number = ''
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="UserData",
            Parameters=S7ReadSZLParameterReq(),
            Data=S7ReadSZLDataReq(SZLId=0x0011, SZLIndex=0x0000))
        rsp1 = self.send_receive_s7_packet(packet1)
        try:
            order_code_data = rsp1[
                S7ReadSZLDataTreeRsp].Data[:rsp1[S7ReadSZLDataRsp].SZLLength]
            order_code = order_code_data[2:-7]
            version_data = rsp1[S7ReadSZLDataTreeRsp].Data[-3:]
            version = 'V {:x}.{:x}.{:x}'.format(
                int(version_data[0].encode('hex'), 16),
                int(version_data[1].encode('hex'), 16),
                int(version_data[2].encode('hex'), 16),
            )

        except Exception as err:
            self.logger.error("Can't get order code and version from target")
            return order_code, version, module_type_name, as_name, module_name, serial_number

        packet2 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="UserData",
            Parameters=S7ReadSZLParameterReq(),
            Data=S7ReadSZLDataReq(SZLId=0x001c, SZLIndex=0x0000))
        rsp2 = self.send_receive_s7_packet(packet2)
        try:
            module_name_data = rsp2[S7ReadSZLDataTreeRsp].Data[
                rsp2[S7ReadSZLDataRsp].SZLLength +
                2:rsp2[S7ReadSZLDataRsp].SZLLength * 2]
            module_name = str(
                module_name_data[:module_name_data.index('\x00')])
            self.logger.debug("module_name:%s " % module_name)
            as_name_data = rsp2[S7ReadSZLDataTreeRsp].Data[
                2:rsp2[S7ReadSZLDataRsp].SZLLength]
            as_name = str(as_name_data[:as_name_data.index('\x00')])
            self.logger.debug("as_name:%s " % as_name)
            serial_number_data = rsp2[S7ReadSZLDataTreeRsp].Data[
                rsp2[S7ReadSZLDataRsp].SZLLength * 4 +
                2:rsp2[S7ReadSZLDataRsp].SZLLength * 5]
            serial_number = str(
                serial_number_data[:serial_number_data.index('\x00')])
            self.logger.debug("serial_number:%s " % serial_number)
            module_type_name_data = rsp2[S7ReadSZLDataTreeRsp].Data[
                rsp2[S7ReadSZLDataRsp].SZLLength * 5 +
                2:rsp2[S7ReadSZLDataRsp].SZLLength * 6]
            module_type_name = str(
                module_type_name_data[:module_type_name_data.index('\x00')])
            self.logger.debug("module_type_name:%s " % module_type_name)

        except Exception as err:
            self.logger.error("Can't get module info from target")
            return order_code, version, module_type_name, as_name, module_name, serial_number

        return order_code, version, module_type_name, as_name, module_name, serial_number

    def check_privilege(self):
        self._get_cpu_protect_level()
        if self.protect_level == 1:
            self.logger.info("You have full privilege with this targets")
            self.readable = True
            self.writeable = True
        if self.protect_level == 2:
            if self.authorized is True:
                self.logger.info("You have full privilege with this targets")
                self.readable = True
                self.writeable = True
            else:
                self.logger.info(
                    "You only have read privilege with this targets")
                self.readable = True
                self.writeable = False
        if self.protect_level == 3:
            if self.authorized is True:
                self.logger.info("You have full privilege with this targets")
                self.readable = True
                self.writeable = True
            else:
                self.logger.info("You can't read or write with this targets")
                self.readable = False
                self.writeable = False

    def auth(self, password):
        """
        
        :param password: Paintext PLC password.
        """
        self.logger.info("Start authenticate with password %s" % password)
        password_hash = self._hash_password(password)
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="UserData",
            Parameters=S7PasswordParameterReq(),
            Data=S7PasswordDataReq(Data=password_hash))
        rsp1 = self.send_receive_s7_packet(packet1)
        if rsp1.haslayer(S7PasswordParameterRsp):
            if rsp1[S7PasswordParameterRsp].ErrorCode == 0:
                self.authorized = True
                self.logger.info("Authentication succeed")
            else:
                if self.authorized is True:
                    self.logger.info("Already authorized")
                else:
                    error_code = rsp1[S7PasswordParameterRsp].ErrorCode
                    if error_code in S7_ERROR_CLASS.keys():
                        self.logger.error("Got error code: %s" %
                                          S7_ERROR_CLASS[error_code])
                    else:
                        self.logger.error("Get error code: %s" %
                                          hex(error_code))
                    self.logger.error("Authentication failure")
            self.check_privilege()
        else:
            self.logger.info(
                "Receive unknown format packet, authentication failure")

    def clean_session(self):
        self.logger.info("Start clean the session")
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="UserData",
            Parameters=S7CleanSessionParameterReq(),
            Data=S7CleanSessionDataReq())
        rsp1 = self.send_receive_s7_packet(packet1)
        if rsp1.haslayer(S7CleanSessionParameterRsp):
            if rsp1[S7CleanSessionParameterRsp].ErrorCode == 0:
                self.authorized = False
                self.logger.info("session cleaned")
            else:
                error_code = rsp1[S7CleanSessionParameterRsp].ErrorCode
                if error_code in S7_ERROR_CLASS.keys():
                    self.logger.error("Got error code: %s" %
                                      S7_ERROR_CLASS[error_code])
                else:
                    self.logger.error("Get error code: %s" % hex(error_code))
        else:
            self.logger.info(
                "Receive unknown format packet, authentication failure")

    def _hash_password(self, password):
        password_hash_new = ''
        if len(password) < 1 or len(password) > 8:
            self.logger.error("Password length must between 1 to 8")
            return None
        else:
            password += bytearray.fromhex('20') * (8 - len(password))
            for i in range(8):
                if i < 2:
                    temp_data = ord(password[i])
                    temp_data ^= 0x55
                    password_hash_new += str(chr(temp_data))
                else:
                    temp_data1 = ord(password[i])
                    temp_data2 = ord(password_hash_new[i - 2])
                    temp_data1 = temp_data1 ^ 0x55 ^ temp_data2
                    password_hash_new += str(chr(temp_data1))
            return password_hash_new

    def _fix_pdur(self, payload):
        if self._pdur > 65535:
            self._pdur = 1
        try:
            payload.PDUR = self._pdur
            self._pdur += 1
            return payload
        except Exception as err:
            self.logger.error(err)
            return payload

    def send_packet(self, packet):
        if self._connection:
            try:
                self._connection.send(packet)

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def send_receive_packet(self, packet):
        if self._connection:
            try:
                rsp = self._connection.sr1(packet, timeout=self._timeout)
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def receive_packet(self):
        if self._connection:
            try:
                rsp = self._connection.recv()
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before receive packet!")

    def send_s7_packet(self, packet):
        if self._connection:
            packet = self._fix_pdur(packet)
            try:
                self._connection.send(packet)

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def send_receive_s7_packet(self, packet):
        if self._connection:
            packet = self._fix_pdur(packet)
            try:
                rsp = self._connection.sr1(packet, timeout=self._timeout)
                if rsp:
                    rsp = TPKT(str(rsp))
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def receive_s7_packet(self):
        if self._connection:
            try:
                rsp = self._connection.recv()
                if rsp:
                    rsp = TPKT(str(rsp))
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None
        else:
            self.logger.error("Please create connect before receive packet!")

    def upload_block_from_target(self, block_type, block_num, dist='A'):
        """

        :param block_type: "08": 'OB', "09": 'CMOD', "0A": 'DB', "0B": 'SDB', "0C": 'FC',
                            "0D": 'SFC', "0E": 'FB', "0F": 'SFB'
        :param block_num: Block number.
        :param dist: 'A': "Active embedded module", 'B': "Active as well as passive module",
                     'P': "Passive (copied, but not chained) module"
        :return: Block Data
        """
        if self.readable is False:
            self.logger.info("Didn't have read privilege on targets")
            return None
        block_data = ''
        if block_type in S7_BLOCK_TYPE_IN_FILE_NAME.keys():
            file_block_type = block_type
        else:
            for key, name in S7_BLOCK_TYPE_IN_FILE_NAME.iteritems():
                if name == block_type:
                    file_block_type = key
                    break
            else:
                self.logger.error(
                    "block_type: %s is incorrect please check again" %
                    block_type)
                return

        if type(block_num) != int:
            self.logger.error("block_num must be int format.")
            return

        file_block_num = "{0:05d}".format(block_num)
        file_name = '_' + file_block_type + file_block_num + dist
        self.logger.info("Start upload %s%s from target" %
                         (block_type, block_num))
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job",
            Parameters=S7RequestUploadBlockParameterReq(Filename=file_name))
        rsp1 = self.send_receive_s7_packet(packet1)
        # Todo: Might got some error
        if rsp1.ErrorClass != 0x0:
            self.logger.error("Can't upload %s%s from target" %
                              (block_type, block_num))
            self.logger.error("Error Class: %s, Error Code %s" %
                              (rsp1.ErrorClass, rsp1.ErrorCode))
            return None
        packet2 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job", Parameters=S7UploadBlockParameterReq())
        packet2[S7UploadBlockParameterReq].UploadId = rsp1[
            S7RequestUploadBlockParameterRsp].UploadId
        while True:
            rsp2 = self.send_receive_s7_packet(packet2)
            if rsp2.ErrorClass != 0x0:
                self.logger.error("Can't upload %s%s from targets" %
                                  (block_type, block_num))
                self.logger.error("Error Class: %s, Error Code %s" %
                                  (rsp1.ErrorClass, rsp1.ErrorCode))
                return None
            self.logger.debug("rsp2: %s" % str(rsp2).encode('hex'))
            block_data += rsp2.Data.Data
            if rsp2.Parameters.FunctionStatus != 1:
                break
        packet3 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job", Parameters=S7UploadBlockEndParameterReq())
        self.send_receive_s7_packet(packet3)
        self.logger.info("Upload %s%s from target succeed" %
                         (block_type, block_num))
        return block_data

    def get_info_from_block(self, block_data):
        """

        :param block_data: Block data.
        :return: mem_length, mc7_length, block_type, block_num
        """
        try:
            mem_length = struct.unpack('!i', block_data[8:12])[0]
            mc7_length = struct.unpack('!h', block_data[34:36])[0]
            block_type = S7_BLOCK_TYPE_IN_BLOCK[ord(block_data[5])]
            block_num = struct.unpack('!h', block_data[6:8])[0]
            return mem_length, mc7_length, block_type, block_num

        except Exception as err:
            self.logger.error(err)
            return None

    def download_block_to_target(self,
                                 block_data,
                                 dist='P',
                                 transfer_size=462,
                                 stop_target=False):
        """ Download block to target and active block.

        :param block_data: Block data to download.
        :param dist: 'A': "Active embedded module", 'B': "Active as well as passive module",
                     'P': "Passive (copied, but not chained) module".
        :param transfer_size: Transfer size for each packet.
        :param stop_target: Stop target PLC before download block, True or False.
        """
        if self.writeable is False:
            self.logger.info("Didn't have write privilege on targets")
            return None
        mem_length, mc7_length, block_type, block_num = self.get_info_from_block(
            block_data)
        self.logger.info("Start download %s%s to targets" %
                         (block_type, block_num))
        file_block_type = None
        for key, name in S7_BLOCK_TYPE_IN_FILE_NAME.iteritems():
            if name == block_type:
                file_block_type = key
                break
        if not file_block_type:
            self.logger.error(
                "block_type: %s is incorrect please check again" % block_type)
            return

        file_block_num = "{0:05d}".format(block_num)
        file_name = '_' + file_block_type + file_block_num + dist

        load_memory_length = '0' * (6 - len(str(mem_length))) + str(mem_length)
        mc7_length = '0' * (6 - len(str(mc7_length))) + str(mc7_length)
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job",
            Parameters=S7RequestDownloadParameterReq(
                Filename=file_name,
                LoadMemLength=load_memory_length,
                MC7Length=mc7_length))
        rsp1 = self.send_receive_s7_packet(packet1)
        if rsp1.ErrorClass != 0x0:
            self.logger.error("Can't Download %s%s to targets" %
                              (block_type, block_num))
            self.logger.error("Error Class: %s, Error Code %s" %
                              (rsp1.ErrorClass, rsp1.ErrorCode))
            return None

        if len(rsp1) > 20:
            download_req = TPKT(rsp1.load)
        else:
            download_req = self.receive_s7_packet()
        # Get pdur from download_req
        self._pdur = download_req.PDUR
        # DownloadBlock
        for i in range(0, len(block_data), transfer_size):
            if i + transfer_size <= len(block_data):
                packet2 = TPKT() / COTPDT(EOT=1) / S7Header(
                    ROSCTR="AckData",
                    Parameters=S7DownloadParameterRsp(FunctionStatus=1),
                    Data=S7DownloadDataRsp(Data=block_data[i:i +
                                                           transfer_size]))
                rsp2 = self.send_receive_s7_packet(packet2)
                self._pdur = rsp2.PDUR
            else:
                packet2 = TPKT() / COTPDT(EOT=1) / S7Header(
                    ROSCTR="AckData",
                    Parameters=S7DownloadParameterRsp(FunctionStatus=0),
                    Data=S7DownloadDataRsp(Data=block_data[i:i +
                                                           transfer_size]))
                self.send_s7_packet(packet2)
        # DownloadBlockEnd
        download_end_req = self.receive_s7_packet()
        self._pdur = download_end_req.PDUR
        packet3 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="AckData", Parameters=S7DownloadEndParameterRsp())
        self.send_s7_packet(packet3)
        # Insert block
        self.logger.debug("File_name:%s" % ('\x00' + file_name[1:]))
        packet4 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job",
            Parameters=S7PIServiceParameterReq(
                ParameterBlock=S7PIServiceParameterBlock(
                    FileNames=['\x00' + file_name[1:]]),
                PI="_INSE"))
        # Todo: Might have a better way to do this
        # packet4[S7PIServiceParameterReq].ParameterBlock = S7PIServiceParameterBlock(FileNames=[file_name[1:]])
        rsp4 = self.send_receive_s7_packet(packet4)
        if rsp4.ErrorClass != 0x0:
            self.logger.error("Can't insert %s%s to targets" %
                              (block_type, block_num))
            self.logger.error("Error Class: %s, Error Code %s" %
                              (rsp1.ErrorClass, rsp1.ErrorCode))
            return None
        self.logger.info("Download %s%s to target succeed" %
                         (block_type, block_num))

    def download_block_to_target_only(self,
                                      block_data,
                                      dist='P',
                                      transfer_size=462,
                                      stop_target=False):
        """ Download block to target only (didn't active block).

        :param block_data: Block data to download.
        :param dist: 'A': "Active embedded module", 'B': "Active as well as passive module",
                     'P': "Passive (copied, but not chained) module".
        :param transfer_size: Transfer size for each packet.
        :param stop_target: Stop target PLC before download block, True or False.
        """
        if self.writeable is False:
            self.logger.info("Didn't have write privilege on targets")
            return None
        mem_length, mc7_length, block_type, block_num = self.get_info_from_block(
            block_data)
        self.logger.info("Start download %s%s to targets" %
                         (block_type, block_num))
        file_block_type = None
        for key, name in S7_BLOCK_TYPE_IN_FILE_NAME.iteritems():
            if name == block_type:
                file_block_type = key
                break
        if not file_block_type:
            self.logger.error(
                "block_type: %s is incorrect please check again" % block_type)
            return

        file_block_num = "{0:05d}".format(block_num)
        file_name = '_' + file_block_type + file_block_num + dist

        load_memory_length = '0' * (6 - len(str(mem_length))) + str(mem_length)
        mc7_length = '0' * (6 - len(str(mc7_length))) + str(mc7_length)
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job",
            Parameters=S7RequestDownloadParameterReq(
                Filename=file_name,
                LoadMemLength=load_memory_length,
                MC7Length=mc7_length))
        rsp1 = self.send_receive_s7_packet(packet1)
        if rsp1.ErrorClass != 0x0:
            self.logger.error("Can't Download %s%s to targets" %
                              (block_type, block_num))
            self.logger.error("Error Class: %s, Error Code %s" %
                              (rsp1.ErrorClass, rsp1.ErrorCode))
            return None

        if len(rsp1) > 20:
            download_req = TPKT(rsp1.load)
        else:
            download_req = self.receive_s7_packet()
        # Get pdur from download_req
        self._pdur = download_req.PDUR
        # DownloadBlock
        for i in range(0, len(block_data), transfer_size):
            if i + transfer_size <= len(block_data):
                packet2 = TPKT() / COTPDT(EOT=1) / S7Header(
                    ROSCTR="AckData",
                    Parameters=S7DownloadParameterRsp(FunctionStatus=1),
                    Data=S7DownloadDataRsp(Data=block_data[i:i +
                                                           transfer_size]))
                rsp2 = self.send_receive_s7_packet(packet2)
                self._pdur = rsp2.PDUR
            else:
                packet2 = TPKT() / COTPDT(EOT=1) / S7Header(
                    ROSCTR="AckData",
                    Parameters=S7DownloadParameterRsp(FunctionStatus=0),
                    Data=S7DownloadDataRsp(Data=block_data[i:i +
                                                           transfer_size]))
                self.send_s7_packet(packet2)
        # DownloadBlockEnd
        download_end_req = self.receive_s7_packet()
        self._pdur = download_end_req.PDUR
        packet3 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="AckData", Parameters=S7DownloadEndParameterRsp())
        self.send_s7_packet(packet3)
        self.logger.info("Download %s%s to target succeed" %
                         (block_type, block_num))

    def get_target_status(self):
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="UserData",
            Parameters=S7ReadSZLParameterReq(),
            Data=S7ReadSZLDataReq(SZLId=0x0424, SZLIndex=0x0000))
        rsp = self.send_receive_s7_packet(packet1)
        status = str(rsp)[44]
        if status == '\x08':
            self.logger.info("Target is in run mode")
            self.is_running = True
        elif status == '\x04':
            self.logger.info("Target is in stop mode")
            self.is_running = False
        else:
            self.logger.info("Target is in unknown mode")
            self.is_running = False

    def stop_target(self):
        self.get_target_status()
        if not self.is_running:
            self.logger.info("Target is already stop")
            return
        self.logger.info("Trying to stop targets")
        packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job", Parameters=S7StopCpuParameterReq())
        rsp1 = self.send_receive_s7_packet(packet1)
        if rsp1.ErrorClass != 0x0:
            self.logger.error("Can't Stop Target")
            self.logger.error("Error Class: %s, Error Code %s" %
                              (rsp1.ErrorClass, rsp1.ErrorCode))
            return
        time.sleep(2)  # wait targets to stop
        self.get_target_status()

    def start_target(self, cold=False):
        ''' Start target PLC

        :param cold: Doing cold restart, True or False.
        '''
        self.get_target_status()
        if self.is_running:
            self.logger.info("Target is already running")
            return
        self.logger.info("Trying to start targets")

        if cold:
            packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
                ROSCTR="Job",
                Parameters=S7PIServiceParameterReq(
                    ParameterBlock=S7PIServiceParameterStringBlock()))
        else:
            packet1 = TPKT() / COTPDT(EOT=1) / S7Header(
                ROSCTR="Job", Parameters=S7PIServiceParameterReq())

        rsp1 = self.send_receive_s7_packet(packet1)
        if rsp1.ErrorClass != 0x0:
            self.logger.error("Can't Start Target")
            self.logger.error("Error Class: %s, Error Code %s" %
                              (rsp1.ErrorClass, rsp1.ErrorCode))
            return
        time.sleep(2)  # wait targets to start
        self.get_target_status()

    @staticmethod
    def get_transport_size_from_data_type(data_type):
        for key, name in S7_TRANSPORT_SIZE_IN_PARM_ITEMS.iteritems():
            if isinstance(data_type, str):
                if name.startswith(data_type.upper()):
                    return key
            elif isinstance(data_type, int):
                return data_type
        return None

    def get_item_pram_from_item(self, item):
        block_num = ''
        area_type = ''
        address = ''
        transport_size = ''
        try:
            for key in VAR_NAME_TYPES:
                if isinstance(item[0], str):
                    if item[0].startswith(key):
                        area_type = VAR_NAME_TYPES[key]

                elif isinstance(item[0], int):
                    if item[0] in VAR_NAME_TYPES.keys():
                        area_type = item[0]

            # Data block
            if area_type == 0x84:
                block_num = int(item[0][2:])
            else:
                block_num = 0

            if isinstance(item[1], str):
                address_data = item[1].split('.')
                address = int(address_data[0]) * 8 + int(address_data[1])

            elif isinstance(item[1], int):
                address = item[1]

            else:
                self.logger.error(
                    "Address: %s is not string or int format, please check again"
                    % item[1])

            transport_size = self.get_transport_size_from_data_type(item[2])

        except Exception as err:
            self.logger.error(
                "Can't get item parameter with var_name: %s with error: \r %s"
                % (item, err))
            return transport_size, block_num, area_type, address

        return transport_size, block_num, area_type, address

    @staticmethod
    def bytes_to_bit_array(bytes_data):
        bit_array = ""
        for data in bytes_data:
            bit_array += '{:08b}'.format(ord(data))
        return map(int, list(bit_array))

    def _unpack_data_with_transport_size(self, req_item, rsp_item):
        # ref http://www.plcdev.com/step_7_elementary_data_types
        if isinstance(rsp_item, S7ReadVarDataItemsRsp):
            try:
                req_type = req_item.TransportSize
                if req_type not in S7_TRANSPORT_SIZE_IN_PARM_ITEMS.keys():
                    return []
                # BIT (0x01)
                elif req_type == 0x01:
                    bit_list = self.bytes_to_bit_array(rsp_item.Data)
                    return bit_list[-1:][0]
                # BYTE (0x02)
                elif req_type == 0x02:
                    byte_list = list(rsp_item.Data)
                    return map(ord, byte_list)
                # CHAR (0x03)
                elif req_type == 0x03:
                    char_list = list(rsp_item.Data)
                    return char_list
                # WORD (0x04) 2 bytes Decimal number unsigned
                elif req_type == 0x04:
                    word_data = rsp_item.Data
                    word_list = [
                        struct.unpack('!H', word_data[i:i + 2])[0]
                        for i in range(0, len(word_data), 2)
                    ]
                    return word_list
                # INT (0x05) 2 bytes Decimal number signed
                elif req_type == 0x05:
                    int_data = rsp_item.Data
                    int_list = [
                        struct.unpack('!h', int_data[i:i + 2])[0]
                        for i in range(0, len(int_data), 2)
                    ]
                    return int_list
                # DWORD (0x06) 4 bytes Decimal number unsigned
                elif req_type == 0x06:
                    dword_data = rsp_item.Data
                    dword_list = [
                        struct.unpack('!I', dword_data[i:i + 4])[0]
                        for i in range(0, len(dword_data), 4)
                    ]
                    return dword_list
                # DINT (0x07) 4 bytes Decimal number signed
                elif req_type == 0x07:
                    dint_data = rsp_item.Data
                    dint_list = [
                        struct.unpack('!i', dint_data[i:i + 4])[0]
                        for i in range(0, len(dint_data), 4)
                    ]
                    return dint_list
                # REAL (0x08) 4 bytes IEEE Floating-point number
                elif req_type == 0x08:
                    dint_data = rsp_item.Data
                    dint_list = [
                        struct.unpack('!f', dint_data[i:i + 4])[0]
                        for i in range(0, len(dint_data), 4)
                    ]
                    return dint_list
                else:
                    return rsp_item.Data

            except Exception as err:
                return []
        return []

    @staticmethod
    def _pack_data_with_transport_size(req_item, data_list):
        # ref http://www.plcdev.com/step_7_elementary_data_types
        if isinstance(req_item, S7WriteVarItemsReq):
            try:
                req_type = req_item.TransportSize
                if req_type not in S7_TRANSPORT_SIZE_IN_PARM_ITEMS.keys():
                    return []
                # BIT (0x01)
                elif req_type == 0x01:
                    # Only support write 1 bit.
                    if isinstance(data_list, list):
                        bit_data = chr(data_list[0])
                    else:
                        bit_data = chr(data_list)
                    return bit_data
                # BYTE (0x02)
                elif req_type == 0x02:
                    byte_data = ''.join(chr(x) for x in data_list)
                    return byte_data
                # CHAR (0x03)
                elif req_type == 0x03:
                    char_data = ''.join(x for x in data_list)
                    return char_data
                # WORD (0x04) 2 bytes Decimal number unsigned
                elif req_type == 0x04:
                    word_data = ''.join(
                        struct.pack('!H', x) for x in data_list)
                    return word_data
                # INT (0x05) 2 bytes Decimal number signed
                elif req_type == 0x05:
                    int_data = ''.join(struct.pack('!h', x) for x in data_list)
                    return int_data
                # DWORD (0x06) 4 bytes Decimal number unsigned
                elif req_type == 0x06:
                    dword_data = ''.join(
                        struct.pack('!I', x) for x in data_list)
                    return dword_data
                # DINT (0x07) 4 bytes Decimal number signed
                elif req_type == 0x07:
                    dint_data = ''.join(
                        struct.pack('!i', x) for x in data_list)
                    return dint_data
                # REAL (0x08) 4 bytes IEEE Floating-point number
                elif req_type == 0x08:
                    real_data = ''.join(
                        struct.pack('!f', x) for x in data_list)
                    return real_data
                # Other data
                else:
                    other_data = ''.join(x for x in data_list)
                    return other_data

            except Exception as err:
                return ''
        return ''

    @staticmethod
    def _convert_transport_size_from_parm_to_data(parm_transport_size):
        if parm_transport_size not in S7_TRANSPORT_SIZE_IN_PARM_ITEMS.keys():
            return None
        else:
            # BIT (0x03)
            if parm_transport_size == 0x01:
                return 0x03
            # BYTE/WORD/DWORD (0x04)
            elif parm_transport_size in (0x02, 0x04, 0x06):
                return 0x04
            # INTEGER (0x05)
            elif parm_transport_size in (0x05, 0x07):
                return 0x05
            # REAL (0x07)
            elif parm_transport_size == 0x08:
                return 0x07
            # OCTET STRING (0x09)
            else:
                return 0x09

    def read_var(self, items):
        '''

        :param items:
        :return: Return data list of read_var items.
        '''
        read_items = []
        items_data = []

        if isinstance(items, list):
            for i in range(len(items)):
                try:
                    transport_size, block_num, area_type, address = self.get_item_pram_from_item(
                        items[i])
                    length = int(items[i][3])
                    if transport_size:
                        read_items.append(
                            S7ReadVarItemsReq(TransportSize=transport_size,
                                              GetLength=length,
                                              BlockNum=block_num,
                                              AREAType=area_type,
                                              Address=address))
                except Exception as err:
                    self.logger.error(
                        "Can't create read var packet because of: \r %s" % err)
                    return None
        else:
            self.logger.error("items is not list please check again")
            return None

        packet = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job", Parameters=S7ReadVarParameterReq(Items=read_items))
        rsp = self.send_receive_s7_packet(packet)
        if rsp.ErrorClass != 0x0:
            self.logger.error("Can't Read var from Target")
            self.logger.error("Error Class: %s, Error Code %s" %
                              (rsp.ErrorClass, rsp.ErrorCode))
            return None
        if rsp.haslayer(S7ReadVarDataItemsRsp):
            for i in range(len(rsp[S7ReadVarDataRsp].Items)):
                req_item = read_items[i][S7ReadVarItemsReq]
                rsp_item = rsp[S7ReadVarDataRsp].Items[i]
                if rsp_item.ReturnCode == 0xff:
                    rsp_item_data = self._unpack_data_with_transport_size(
                        req_item, rsp_item)
                    items_data.append(rsp_item_data)
                else:
                    items_data.append('')
        return items_data

    def write_var(self, items):
        """

        :param items:
        :return:
        """
        write_items = []
        items_data = []
        write_data_rsp = []
        if isinstance(items, list):
            for i in range(len(items)):
                try:
                    transport_size, block_num, area_type, address = self.get_item_pram_from_item(
                        items[i])
                    length = len(items[i][3])
                    if transport_size:
                        write_items.append(
                            S7WriteVarItemsReq(TransportSize=transport_size,
                                               ItemCount=length,
                                               BlockNum=block_num,
                                               AREAType=area_type,
                                               BitAddress=address))
                        write_data = self._pack_data_with_transport_size(
                            write_items[i], items[i][3])
                        items_data.append(
                            S7WriteVarDataItemsReq(
                                TransportSize=self.
                                _convert_transport_size_from_parm_to_data(
                                    transport_size),
                                Data=write_data))
                except Exception as err:
                    self.logger.error(
                        "Can't create write var packet because of: \r %s" %
                        err)
                    return None
        else:
            self.logger.error("items is not list please check again")
            return None

        packet = TPKT() / COTPDT(EOT=1) / S7Header(
            ROSCTR="Job",
            Parameters=S7WriteVarParameterReq(Items=write_items),
            Data=S7WriteVarDataReq(Items=items_data))
        rsp = self.send_receive_s7_packet(packet)
        if rsp.ErrorClass != 0x0:
            self.logger.error("Can't write var to Target.")
            self.logger.error("Error Class: %s, Error Code %s" %
                              (rsp.ErrorClass, rsp.ErrorCode))
            return None
        if rsp.haslayer(S7WriteVarDataRsp):
            for rsp_items in rsp[S7WriteVarDataRsp].Items:
                write_data_rsp.append(rsp_items.ReturnCode)
            return write_data_rsp
        else:
            self.logger.error("Unknown response packet format.")
            return None
Ejemplo n.º 13
0
 def __init__(self, ip='127.0.0.1', port=6801):
     self.ip = ip
     self.port = port
     s = socket.socket()
     s.connect((self.ip, self.port))
     StreamSocket.__init__(self, s, ENET)
Ejemplo n.º 14
0
class ModbusClient(Base):
    def __init__(self, name, ip, port=502, timeout=2):
        '''

        :param name: Name of this targets
        :param ip: Modbus Target ip
        :param port: Modbus TCP port (default: 502)
        :param timeout: timeout of socket (default: 2)
        '''
        super(ModbusClient, self).__init__(name=name)
        self._ip = ip
        self._port = port
        self._connection = None
        self._connected = False
        self._timeout = timeout

    def connect(self):
        sock = socket.socket()
        sock.connect((self._ip, self._port))
        sock.settimeout(self._timeout)
        self._connection = StreamSocket(sock, Raw)

    def send_packet(self, packet):
        if self._connection:
            try:
                self._connection.send(packet)

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def send_receive_packet(self, packet):
        if self._connection:
            try:
                rsp = self._connection.sr1(packet, timeout=self._timeout)
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None
        else:
            self.logger.error("Please create connect before send packet!")

    def receive_packet(self):
        if self._connection:
            try:
                rsp = self._connection.recv()
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before receive packet!")

    def send_modbus_packet(self, packet):
        if self._connection:
            try:
                self._connection.send(packet)

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def send_receive_modbus_packet(self, packet):
        func_code = packet.func_code
        if self._connection:
            try:
                rsp = self._connection.sr1(packet, timeout=self._timeout)
                if rsp:
                    rsp = ModbusHeaderResponse(str(rsp))
                    if rsp.haslayer(modbus_response_classes[func_code]):
                        return rsp
                    elif rsp.haslayer(GenericError):
                        self.logger.error("Got error with error code:%s" %
                                          rsp.exceptCode)
                return None

            except Exception as err:
                self.logger.error(err)
                return None

        else:
            self.logger.error("Please create connect before send packet!")

    def receive_modbus_packet(self):
        if self._connection:
            try:
                rsp = self._connection.recv()
                if rsp:
                    rsp = ModbusHeaderResponse(str(rsp))
                return rsp

            except Exception as err:
                self.logger.error(err)
                return None
        else:
            self.logger.error("Please create connect before receive packet!")

    @staticmethod
    def bytes_list_to_bit_array(coils_bytes):
        bit_array = ""
        for data in coils_bytes:
            bit_array += BitArray(
                bytes=chr(data), length=8
            ).bin[::-1]  # Bit values (least significant bit is first coil!)
        return list(bit_array)

    def read_coils(self, address, count):
        '''

        :param address: Reference Number of coils
        :param count: Bit Count for read
        :return: Coil Status in list, if got some error return None.
        '''
        packet = ModbusHeaderRequest(func_code=0x01) / ReadCoilsRequest(
            ReferenceNumber=address, BitCount=count)
        rsp = self.send_receive_modbus_packet(packet)
        if rsp:
            coils = rsp.CoilStatus
            coils = self.bytes_list_to_bit_array(coils)
            return coils[:count]
        else:
            return None

    def read_discrete_inputs(self, address, count):
        '''

        :param address: Reference Number of discrete inputs
        :param count: Bit Count for read
        :return: InputStatus in list, if got some error return None.
        '''
        packet = ModbusHeaderRequest(
            func_code=0x02) / ReadDiscreteInputsRequest(
                ReferenceNumber=address, BitCount=count)
        rsp = self.send_receive_modbus_packet(packet)
        if rsp:
            inputStatus = rsp.InputStatus
            inputStatus = self.bytes_list_to_bit_array(inputStatus)
            return inputStatus[:count]
        else:
            return None

    def read_holding_registers(self, address, count):
        '''

        :param address: Reference Number of holding registers
        :param count: Word count for read
        :return: Registers in list
        '''
        packet = ModbusHeaderRequest(func_code=0x03) / \
                 ReadHoldingRegistersRequest(ReferenceNumber=address, WordCount=count)
        rsp = self.send_receive_modbus_packet(packet)
        registers = rsp.RegisterValue
        return registers

    def read_input_registers(self, address, count):
        '''

        :param address: Reference Number of input registers
        :param count: Word count for read
        :return: Registers in list
        '''
        packet = ModbusHeaderRequest(func_code=0x04) / \
                 ReadInputRegistersRequest(ReferenceNumber=address, WordCount=count)
        rsp = self.send_receive_modbus_packet(packet)
        registers = rsp.RegisterValue
        return registers

    def write_single_coil(self, address, value):
        '''

        :param address: Reference Number of coil
        :param value: coil's value(True or False)
        :return: Response packet
        '''
        # TODO: Need return only value.
        if value is True:
            data = 0xFF00
        else:
            data = 0x0000
        packet = ModbusHeaderRequest(func_code=0x05) / WriteSingleCoilRequest(
            ReferenceNumber=address, Value=data)
        rsp = self.send_receive_modbus_packet(packet)
        return rsp

    def write_single_register(self, address, value):
        '''

        :param address: Reference Number of register
        :param value: value of register (0x0-0xffff)
        :return: Response packet
        '''
        packet = ModbusHeaderRequest(
            func_code=0x06) / WriteSingleRegisterRequest(
                ReferenceNumber=address, Value=value)
        rsp = self.send_receive_modbus_packet(packet)
        return rsp

    def write_multiple_coils(self, address, values):
        '''

        :param address: Reference Number of coils
        :param values: values to write in list must in multiples of 8. example: values = [0, 0, 0, 0, 1, 1, 1, 1]
        :return: Response packet
        '''
        values = values[::-1]  # least significant bit = first coil
        packet = ModbusHeaderRequest(
            func_code=0x0F) / WriteMultipleCoilsRequest(
                ReferenceNumber=address, Values=values)
        rsp = self.send_receive_modbus_packet(packet)
        return rsp

    def write_multiple_registers(self, address, values):
        '''

        :param address: address: Reference Number of register
        :param values: values to write in list. example: values = [0x01, 0x02, 0x03, 0x04]
        :return: Response packet
        '''
        packet = ModbusHeaderRequest(func_code=0x10) / \
                 WriteMultipleRegistersRequest(ReferenceNumber=address, Values=values)
        rsp = self.send_receive_modbus_packet(packet)
        return rsp

    def read_file_record(self, file_number, offset, length):
        '''

        :param file_number: File number
        :param offset: offset of file
        :param length: length to read
        :return: Response packet
        '''
        packet = ModbusHeaderRequest(func_code=0x14) / ReadFileRecordRequest()
        packet[ReadFileRecordRequest].Groups = ReadFileSubRequest(
            FileNumber=file_number, Offset=offset, Length=length)
        rsp = self.send_receive_modbus_packet(packet)
        return rsp

    def write_file_record(self, file_number, offset, data):
        '''

        :param file_number: File number
        :param offset: offset of file
        :param data: data to write
        :return: Response packet
        '''
        data_list = []
        for i in range(0, len(data), 0x02):
            data1 = struct.unpack("!H", data[i:i + 2])[0]
            data_list.append(data1)
        packet = ModbusHeaderRequest(func_code=0x15) / WriteFileRecordRequest()
        packet[WriteFileRecordRequest].Groups = WriteFileSubRequest(
            FileNumber=file_number, Offset=offset, Data=data_list)
        rsp = self.send_receive_modbus_packet(packet)
        return rsp

    def mask_write_register(self, address, and_mask=0xffff, or_mask=0x0000):
        '''

        :param address: Reference Number of register
        :param and_mask: And mask of register
        :param or_mask: Or mask of register
        :return: Response packet
        '''
        packet = ModbusHeaderRequest(func_code=0x16) / \
                 MaskWriteRegisterRequest(ReferenceNumber=address, AndMask=and_mask, OrMask=or_mask),
        rsp = self.send_receive_modbus_packet(packet)
        return rsp

    def read_write_multiple_registers(self, read_address, read_count,
                                      write_address, values):
        '''

        :param read_address: Reference Number of register to read
        :param read_count: Word count for read
        :param write_address: Reference Number of register to write
        :param values: values to write in list. example: values = [0x01, 0x02, 0x03, 0x04]
        :return: Response packet
        '''
        packet = ModbusHeaderRequest(func_code=0x17) / \
                 ReadWriteMultipleRegistersRequest(ReadReferenceNumber=read_address, ReadWordCount=read_count,
                                                   WriteReferenceNumber=write_address, RegisterValues=values)
        rsp = self.send_receive_modbus_packet(packet)
        return rsp

    def read_fifo_queue(self, address):
        '''

        :param address: Reference Number of fifo
        :return: Response packet
        '''
        packet = ModbusHeaderRequest(func_code=0x17) / ReadFIFOQueueRequest(
            ReferenceNumber=address)
        rsp = self.send_receive_modbus_packet(packet)
        return rsp
Ejemplo n.º 15
0
 def connect(self):
     sock = socket.socket()
     sock.connect((self._ip, self._port))
     sock.settimeout(self._timeout)
     self._connection = StreamSocket(sock, Raw)