Esempio n. 1
0
 def __init__(self, server, nodeid):
     self.server = server
     self.nodeid = None
     if isinstance(nodeid, Node):
         self.nodeid = nodeid.nodeid
     elif isinstance(nodeid, ua.NodeId):
         self.nodeid = nodeid
     elif type(nodeid) in (str, bytes):
         self.nodeid = ua.NodeId.from_string(nodeid)
     elif isinstance(nodeid, int):
         self.nodeid = ua.NodeId(nodeid, 0)
     else:
         raise ua.UaError(
             "argument to node must be a NodeId object or a string defining a nodeid found {0} of type {1}"
             .format(nodeid, type(nodeid)))
     self.basenodeid = None
Esempio n. 2
0
 def __init__(self,
              security_policy,
              body=b'',
              msg_type=ua.MessageType.SecureMessage,
              chunk_type=ua.ChunkType.Single):
     self.MessageHeader = ua.Header(msg_type, chunk_type)
     if msg_type in (ua.MessageType.SecureMessage,
                     ua.MessageType.SecureClose):
         self.SecurityHeader = ua.SymmetricAlgorithmHeader()
     elif msg_type == ua.MessageType.SecureOpen:
         self.SecurityHeader = ua.AsymmetricAlgorithmHeader()
     else:
         raise ua.UaError("Unsupported message type: {0}".format(msg_type))
     self.SequenceHeader = ua.SequenceHeader()
     self.Body = body
     self.security_policy = security_policy
Esempio n. 3
0
    def historize_event(self, source, period=timedelta(days=7)):
        """
        subscribe to the source nodes' events and store the data in the active storage; custom event properties included
        """
        if not self._sub:
            self._sub = self._create_subscription(SubHandler(self.storage))
        if source in self._handlers:
            raise ua.UaError("Events from {} are already historized".format(source))

        # get the event types the source node generates and a list of all possible event fields
        event_types, ev_fields = self._get_source_event_data(source)

        self.storage.new_historized_event(source.nodeid, ev_fields, period)

        handler = self._sub.subscribe_events(source)  # FIXME supply list of event types when master is fixed
        self._handlers[source] = handler
Esempio n. 4
0
    def create_session(self):
        """
        send a CreateSessionRequest to server with reasonable parameters.
        If you want o modify settings look at code of this methods
        and make your own
        """
        desc = ua.ApplicationDescription()
        desc.ApplicationUri = self.application_uri
        desc.ProductUri = self.product_uri
        desc.ApplicationName = ua.LocalizedText(self.name)
        desc.ApplicationType = ua.ApplicationType.Client

        params = ua.CreateSessionParameters()
        # at least 32 random bytes for server to prove possession of private key (specs part 4, 5.6.2.2)
        nonce = utils.create_nonce(32)
        params.ClientNonce = nonce
        params.ClientCertificate = self.security_policy.client_certificate
        params.ClientDescription = desc
        params.EndpointUrl = self.server_url.geturl()
        params.SessionName = self.description + " Session" + str(
            self._session_counter)
        params.RequestedSessionTimeout = 3600000
        params.MaxResponseMessageSize = 0  # means no max size
        response = self.uaclient.create_session(params)
        if self.security_policy.client_certificate is None:
            data = nonce
        else:
            data = self.security_policy.client_certificate + nonce
        self.security_policy.asymmetric_cryptography.verify(
            data, response.ServerSignature.Signature)
        self._server_nonce = response.ServerNonce
        if not self.security_policy.server_certificate:
            self.security_policy.server_certificate = response.ServerCertificate
        elif self.security_policy.server_certificate != response.ServerCertificate:
            raise ua.UaError("Server certificate mismatch")
        # remember PolicyId's: we will use them in activate_session()
        ep = Client.find_endpoint(response.ServerEndpoints,
                                  self.security_policy.Mode,
                                  self.security_policy.URI)
        self._policy_ids = ep.UserIdentityTokens
        self.session_timeout = response.RevisedSessionTimeout
        self.keepalive = KeepAlive(
            self,
            min(self.session_timeout, self.secure_channel_timeout) *
            0.7)  # 0.7 is from spec
        self.keepalive.start()
        return response
Esempio n. 5
0
def get_base_data_type(datatype):
    """
    Looks up the base datatype of the provided datatype Node
    The base datatype is either:
    A primitive type (ns=0, i<=21) or a complex one (ns=0 i>21 and i<=30) like Enum and Struct.
    
    Args:
        datatype: NodeId of a datype of a variable
    Returns:
        NodeId of datatype base or None in case base datype can not be determined
    """
    base = datatype
    while base:
        if base.nodeid.NamespaceIndex == 0 and isinstance(base.nodeid.Identifier, int) and base.nodeid.Identifier <= 30:
            return base
        base = get_node_supertype(base)
    raise ua.UaError("Datatype must be a subtype of builtin types {0!s}".format(datatype))
Esempio n. 6
0
 def _receive(self, msg):
     self._check_incoming_chunk(msg)
     self._incoming_parts.append(msg)
     if msg.MessageHeader.ChunkType == ua.ChunkType.Intermediate:
         return None
     if msg.MessageHeader.ChunkType == ua.ChunkType.Abort:
         err = struct_from_binary(ua.ErrorMessage, ua.utils.Buffer(msg.Body))
         logger.warning("Message %s aborted: %s", msg, err)
         # specs Part 6, 6.7.3 say that aborted message shall be ignored
         # and SecureChannel should not be closed
         self._incoming_parts = []
         return None
     elif msg.MessageHeader.ChunkType == ua.ChunkType.Single:
         message = ua.Message(self._incoming_parts)
         self._incoming_parts = []
         return message
     else:
         raise ua.UaError("Unsupported chunk type: {0}".format(msg))
Esempio n. 7
0
 def set_security_string(self, string):
     """
     Set SecureConnection mode. String format:
     Policy,Mode,certificate,private_key[,server_private_key]
     where Policy is Basic128Rsa15, Basic256 or Basic256Sha256,
         Mode is Sign or SignAndEncrypt
         certificate, private_key and server_private_key are
             paths to .pem or .der files
     Call this before connect()
     """
     if not string:
         return
     parts = string.split(',')
     if len(parts) < 4:
         raise ua.UaError('Wrong format: `{0}`, expected at least 4 comma-separated values'.format(string))
     policy_class = getattr(security_policies, 'SecurityPolicy' + parts[0])
     mode = getattr(ua.MessageSecurityMode, parts[1])
     return self.set_security(policy_class, parts[2], parts[3],
                              parts[4] if len(parts) >= 5 else None, mode)
Esempio n. 8
0
def data_type_to_variant_type(dtype_node):
    """
    Given a Node datatype, find out the variant type to encode
    data. This is not exactly straightforward...
    """
    base = get_base_data_type(dtype_node)

    if base.nodeid.Identifier != 29:
        return ua.VariantType(base.nodeid.Identifier)
    else:
        # we have an enumeration, we need to look at child to find type
        descs = dtype_node.get_children_descriptions()
        bnames = [d.BrowseName.Name for d in descs]
        if "EnumStrings" in bnames:
            return ua.VariantType.LocalizedText
        elif "EnumValues" in bnames:
            return ua.VariantType.ExtensionObject
        else:
            raise ua.UaError(
                "Enumeration must have a child node describing its type and values"
            )
Esempio n. 9
0
    def receive_from_header_and_body(self, header, body):
        """
        Convert MessageHeader and binary body to OPC UA TCP message (see OPC UA
        specs Part 6, 7.1: Hello, Acknowledge or ErrorMessage), or a Message
        object, or None (if intermediate chunk is received)
        """
        if header.MessageType == ua.MessageType.SecureOpen:
            data = body.copy(header.body_size)
            security_header = struct_from_binary(ua.AsymmetricAlgorithmHeader,
                                                 data)
            self.select_policy(security_header.SecurityPolicyURI,
                               security_header.SenderCertificate)
        elif header.MessageType in (ua.MessageType.SecureMessage,
                                    ua.MessageType.SecureClose):
            data = body.copy(header.body_size)
            security_header = struct_from_binary(ua.SymmetricAlgorithmHeader,
                                                 data)
            self._check_sym_header(security_header)

        if header.MessageType in (ua.MessageType.SecureMessage,
                                  ua.MessageType.SecureOpen,
                                  ua.MessageType.SecureClose):
            chunk = MessageChunk.from_header_and_body(self.security_policy,
                                                      header, body)
            return self._receive(chunk)
        elif header.MessageType == ua.MessageType.Hello:
            msg = struct_from_binary(ua.Hello, body)
            self._max_chunk_size = msg.ReceiveBufferSize
            return msg
        elif header.MessageType == ua.MessageType.Acknowledge:
            msg = struct_from_binary(ua.Acknowledge, body)
            self._max_chunk_size = msg.SendBufferSize
            return msg
        elif header.MessageType == ua.MessageType.Error:
            msg = struct_from_binary(ua.ErrorMessage, body)
            logger.warning("Received an error: %s", msg)
            return msg
        else:
            raise ua.UaError("Unsupported message type {0}".format(
                header.MessageType))
Esempio n. 10
0
 def _call_callback(self, request_id, body):
     with self._lock:
         future = self._callbackmap.pop(request_id, None)
         if future is None:
             raise ua.UaError("No future object found for request: {0}, callbacks in list are {1}".format(request_id, self._callbackmap.keys()))
     future.set_result(body)