uaprotocol_hand.py
Go to the documentation of this file.
00001 import struct
00002 import logging
00003 import hashlib
00004 from datetime import datetime
00005 
00006 from opcua.ua import uaprotocol_auto as auto
00007 from opcua.ua import uatypes
00008 from opcua.ua import ua_binary as uabin
00009 from opcua.ua import UaError
00010 from opcua.common import utils
00011 
00012 logger = logging.getLogger('opcua.uaprotocol')
00013 
00014 OPC_TCP_SCHEME = 'opc.tcp'
00015 
00016 
00017 class Hello(uatypes.FrozenClass):
00018 
00019     def __init__(self):
00020         self.ProtocolVersion = 0
00021         self.ReceiveBufferSize = 65536
00022         self.SendBufferSize = 65536
00023         self.MaxMessageSize = 0
00024         self.MaxChunkCount = 0
00025         self.EndpointUrl = ""
00026         self._freeze = True
00027 
00028     def to_binary(self):
00029         b = []
00030         b.append(uabin.Primitives.UInt32.pack(self.ProtocolVersion))
00031         b.append(uabin.Primitives.UInt32.pack(self.ReceiveBufferSize))
00032         b.append(uabin.Primitives.UInt32.pack(self.SendBufferSize))
00033         b.append(uabin.Primitives.UInt32.pack(self.MaxMessageSize))
00034         b.append(uabin.Primitives.UInt32.pack(self.MaxChunkCount))
00035         b.append(uabin.Primitives.String.pack(self.EndpointUrl))
00036         return b"".join(b)
00037 
00038     @staticmethod
00039     def from_binary(data):
00040         hello = Hello()
00041         hello.ProtocolVersion = uabin.Primitives.UInt32.unpack(data)
00042         hello.ReceiveBufferSize = uabin.Primitives.UInt32.unpack(data)
00043         hello.SendBufferSize = uabin.Primitives.UInt32.unpack(data)
00044         hello.MaxMessageSize = uabin.Primitives.UInt32.unpack(data)
00045         hello.MaxChunkCount = uabin.Primitives.UInt32.unpack(data)
00046         hello.EndpointUrl = uabin.Primitives.String.unpack(data)
00047         return hello
00048 
00049 
00050 class MessageType(object):
00051     Invalid = b"INV"  # FIXME: check value
00052     Hello = b"HEL"
00053     Acknowledge = b"ACK"
00054     Error = b"ERR"
00055     SecureOpen = b"OPN"
00056     SecureClose = b"CLO"
00057     SecureMessage = b"MSG"
00058 
00059 
00060 class ChunkType(object):
00061     Invalid = b"0"  # FIXME check
00062     Single = b"F"
00063     Intermediate = b"C"
00064     Abort = b"A"    # when an error occurred and the Message is aborted (body is ErrorMessage)
00065 
00066 
00067 class Header(uatypes.FrozenClass):
00068 
00069     def __init__(self, msgType=None, chunkType=None, channelid=0):
00070         self.MessageType = msgType
00071         self.ChunkType = chunkType
00072         self.ChannelId = channelid
00073         self.body_size = 0
00074         self.packet_size = 0
00075         self._freeze = True
00076 
00077     def add_size(self, size):
00078         self.body_size += size
00079 
00080     def to_binary(self):
00081         b = []
00082         b.append(struct.pack("<3ss", self.MessageType, self.ChunkType))
00083         size = self.body_size + 8
00084         if self.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
00085             size += 4
00086         b.append(uabin.Primitives.UInt32.pack(size))
00087         if self.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
00088             b.append(uabin.Primitives.UInt32.pack(self.ChannelId))
00089         return b"".join(b)
00090 
00091     @staticmethod
00092     def from_string(data):
00093         hdr = Header()
00094         hdr.MessageType, hdr.ChunkType, hdr.packet_size = struct.unpack("<3scI", data.read(8))
00095         hdr.body_size = hdr.packet_size - 8
00096         if hdr.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
00097             hdr.body_size -= 4
00098             hdr.ChannelId = uabin.Primitives.UInt32.unpack(data)
00099         return hdr
00100 
00101     @staticmethod
00102     def max_size():
00103         return struct.calcsize("<3scII")
00104 
00105     def __str__(self):
00106         return "Header(type:{0}, chunk_type:{1}, body_size:{2}, channel:{3})".format(
00107             self.MessageType, self.ChunkType, self.body_size, self.ChannelId)
00108     __repr__ = __str__
00109 
00110 
00111 class ErrorMessage(uatypes.FrozenClass):
00112 
00113     def __init__(self):
00114         self.Error = uatypes.StatusCode()
00115         self.Reason = ""
00116         self._freeze = True
00117 
00118     def to_binary(self):
00119         b = []
00120         b.append(self.Error.to_binary())
00121         b.append(uabin.Primitives.String.pack(self.Reason))
00122         return b"".join(b)
00123 
00124     @staticmethod
00125     def from_binary(data):
00126         ack = ErrorMessage()
00127         ack.Error = uatypes.StatusCode.from_binary(data)
00128         ack.Reason = uabin.Primitives.String.unpack(data)
00129         return ack
00130 
00131     def __str__(self):
00132         return "MessageAbort(error:{0}, reason:{1})".format(self.Error, self.Reason)
00133     __repr__ = __str__
00134 
00135 
00136 class Acknowledge(uatypes.FrozenClass):
00137 
00138     def __init__(self):
00139         self.ProtocolVersion = 0
00140         self.ReceiveBufferSize = 65536
00141         self.SendBufferSize = 65536
00142         self.MaxMessageSize = 0  # No limits
00143         self.MaxChunkCount = 0  # No limits
00144         self._freeze = True
00145 
00146     def to_binary(self):
00147         return struct.pack(
00148             "<5I",
00149             self.ProtocolVersion,
00150             self.ReceiveBufferSize,
00151             self.SendBufferSize,
00152             self.MaxMessageSize,
00153             self.MaxChunkCount)
00154 
00155     @staticmethod
00156     def from_binary(data):
00157         ack = Acknowledge()
00158         ack.ProtocolVersion, ack.ReceiveBufferSize, ack.SendBufferSize, ack.MaxMessageSize, ack.MaxChunkCount \
00159             = struct.unpack("<5I", data.read(20))
00160         return ack
00161 
00162 
00163 class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
00164 
00165     def __init__(self):
00166         self.SecurityPolicyURI = "http://opcfoundation.org/UA/SecurityPolicy#None"
00167         self.SenderCertificate = None
00168         self.ReceiverCertificateThumbPrint = None
00169         self._freeze = True
00170 
00171     def to_binary(self):
00172         b = []
00173         b.append(uabin.Primitives.String.pack(self.SecurityPolicyURI))
00174         b.append(uabin.Primitives.String.pack(self.SenderCertificate))
00175         b.append(uabin.Primitives.String.pack(self.ReceiverCertificateThumbPrint))
00176         return b"".join(b)
00177 
00178     @staticmethod
00179     def from_binary(data):
00180         hdr = AsymmetricAlgorithmHeader()
00181         hdr.SecurityPolicyURI = uabin.Primitives.String.unpack(data)
00182         hdr.SenderCertificate = uabin.Primitives.Bytes.unpack(data)
00183         hdr.ReceiverCertificateThumbPrint = uabin.Primitives.Bytes.unpack(data)
00184         return hdr
00185 
00186     def __str__(self):
00187         return "{0}(SecurityPolicy:{1}, certificatesize:{2}, receiverCertificatesize:{3} )".format(
00188             self.__class__.__name__, self.SecurityPolicyURI, len(self.SenderCertificate),
00189             len(self.ReceiverCertificateThumbPrint))
00190     __repr__ = __str__
00191 
00192 
00193 class SymmetricAlgorithmHeader(uatypes.FrozenClass):
00194 
00195     def __init__(self):
00196         self.TokenId = 0
00197         self._freeze = True
00198 
00199     @staticmethod
00200     def from_binary(data):
00201         obj = SymmetricAlgorithmHeader()
00202         obj.TokenId = uabin.Primitives.UInt32.unpack(data)
00203         return obj
00204 
00205     def to_binary(self):
00206         return uabin.Primitives.UInt32.pack(self.TokenId)
00207 
00208     @staticmethod
00209     def max_size():
00210         return struct.calcsize("<I")
00211 
00212     def __str__(self):
00213         return "{0}(TokenId:{1} )".format(self.__class__.__name__, self.TokenId)
00214     __repr__ = __str__
00215 
00216 
00217 class SequenceHeader(uatypes.FrozenClass):
00218 
00219     def __init__(self):
00220         self.SequenceNumber = None
00221         self.RequestId = None
00222         self._freeze = True
00223 
00224     @staticmethod
00225     def from_binary(data):
00226         obj = SequenceHeader()
00227         obj.SequenceNumber = uabin.Primitives.UInt32.unpack(data)
00228         obj.RequestId = uabin.Primitives.UInt32.unpack(data)
00229         return obj
00230 
00231     def to_binary(self):
00232         b = []
00233         b.append(uabin.Primitives.UInt32.pack(self.SequenceNumber))
00234         b.append(uabin.Primitives.UInt32.pack(self.RequestId))
00235         return b"".join(b)
00236 
00237     @staticmethod
00238     def max_size():
00239         return struct.calcsize("<II")
00240 
00241     def __str__(self):
00242         return "{0}(SequenceNumber:{1}, RequestId:{2} )".format(
00243             self.__class__.__name__, self.SequenceNumber, self.RequestId)
00244     __repr__ = __str__
00245 
00246 
00247 class CryptographyNone:
00248     """
00249     Base class for symmetric/asymmetric cryprography
00250     """
00251 
00252     def __init__(self):
00253         pass
00254 
00255     def plain_block_size(self):
00256         """
00257         Size of plain text block for block cipher.
00258         """
00259         return 1
00260 
00261     def encrypted_block_size(self):
00262         """
00263         Size of encrypted text block for block cipher.
00264         """
00265         return 1
00266 
00267     def padding(self, size):
00268         """
00269         Create padding for a block of given size.
00270         plain_size = size + len(padding) + signature_size()
00271         plain_size = N * plain_block_size()
00272         """
00273         return b''
00274 
00275     def min_padding_size(self):
00276         return 0
00277 
00278     def signature_size(self):
00279         return 0
00280 
00281     def signature(self, data):
00282         return b''
00283 
00284     def encrypt(self, data):
00285         return data
00286 
00287     def decrypt(self, data):
00288         return data
00289 
00290     def vsignature_size(self):
00291         return 0
00292 
00293     def verify(self, data, signature):
00294         """
00295         Verify signature and raise exception if signature is invalid
00296         """
00297         pass
00298 
00299     def remove_padding(self, data):
00300         return data
00301 
00302 
00303 class SecurityPolicy(object):
00304     """
00305     Base class for security policy
00306     """
00307     URI = "http://opcfoundation.org/UA/SecurityPolicy#None"
00308     signature_key_size = 0
00309     symmetric_key_size = 0
00310 
00311     def __init__(self):
00312         self.asymmetric_cryptography = CryptographyNone()
00313         self.symmetric_cryptography = CryptographyNone()
00314         self.Mode = auto.MessageSecurityMode.None_
00315         self.server_certificate = None
00316         self.client_certificate = None
00317 
00318     def make_symmetric_key(self, a, b):
00319         pass
00320 
00321 
00322 class SecurityPolicyFactory(object):
00323     """
00324     Helper class for creating server-side SecurityPolicy.
00325     Server has one certificate and private key, but needs a separate
00326     SecurityPolicy for every client and client's certificate
00327     """
00328 
00329     def __init__(self, cls=SecurityPolicy, mode=auto.MessageSecurityMode.None_,
00330                  certificate=None, private_key=None):
00331         self.cls = cls
00332         self.mode = mode
00333         self.certificate = certificate
00334         self.private_key = private_key
00335 
00336     def matches(self, uri, mode=None):
00337         return self.cls.URI == uri and (mode is None or self.mode == mode)
00338 
00339     def create(self, peer_certificate):
00340         if self.cls is SecurityPolicy:
00341             return self.cls()
00342         else:
00343             return self.cls(peer_certificate,
00344                             self.certificate, self.private_key,
00345                             self.mode)
00346 
00347 
00348 class MessageChunk(uatypes.FrozenClass):
00349     """
00350     Message Chunk, as described in OPC UA specs Part 6, 6.7.2.
00351     """
00352 
00353     def __init__(self, security_policy, body=b'', msg_type=MessageType.SecureMessage, chunk_type=ChunkType.Single):
00354         self.MessageHeader = Header(msg_type, chunk_type)
00355         if msg_type in (MessageType.SecureMessage, MessageType.SecureClose):
00356             self.SecurityHeader = SymmetricAlgorithmHeader()
00357         elif msg_type == MessageType.SecureOpen:
00358             self.SecurityHeader = AsymmetricAlgorithmHeader()
00359         else:
00360             raise UaError("Unsupported message type: {0}".format(msg_type))
00361         self.SequenceHeader = SequenceHeader()
00362         self.Body = body
00363         self._security_policy = security_policy
00364 
00365     @staticmethod
00366     def from_binary(security_policy, data):
00367         h = Header.from_string(data)
00368         return MessageChunk.from_header_and_body(security_policy, h, data)
00369 
00370     @staticmethod
00371     def from_header_and_body(security_policy, header, buf):
00372         assert len(buf) >= header.body_size, 'Full body expected here'
00373         data = buf.copy(header.body_size)
00374         buf.skip(header.body_size)
00375         if header.MessageType in (MessageType.SecureMessage, MessageType.SecureClose):
00376             security_header = SymmetricAlgorithmHeader.from_binary(data)
00377             crypto = security_policy.symmetric_cryptography
00378         elif header.MessageType == MessageType.SecureOpen:
00379             security_header = AsymmetricAlgorithmHeader.from_binary(data)
00380             crypto = security_policy.asymmetric_cryptography
00381         else:
00382             raise UaError("Unsupported message type: {0}".format(header.MessageType))
00383         obj = MessageChunk(crypto)
00384         obj.MessageHeader = header
00385         obj.SecurityHeader = security_header
00386         decrypted = crypto.decrypt(data.read(len(data)))
00387         signature_size = crypto.vsignature_size()
00388         if signature_size > 0:
00389             signature = decrypted[-signature_size:]
00390             decrypted = decrypted[:-signature_size]
00391             crypto.verify(obj.MessageHeader.to_binary() + obj.SecurityHeader.to_binary() + decrypted, signature)
00392         data = utils.Buffer(crypto.remove_padding(decrypted))
00393         obj.SequenceHeader = SequenceHeader.from_binary(data)
00394         obj.Body = data.read(len(data))
00395         return obj
00396 
00397     def encrypted_size(self, plain_size):
00398         size = plain_size + self._security_policy.signature_size()
00399         pbs = self._security_policy.plain_block_size()
00400         assert(size % pbs == 0)
00401         return size // pbs * self._security_policy.encrypted_block_size()
00402 
00403     def to_binary(self):
00404         security = self.SecurityHeader.to_binary()
00405         encrypted_part = self.SequenceHeader.to_binary() + self.Body
00406         encrypted_part += self._security_policy.padding(len(encrypted_part))
00407         self.MessageHeader.body_size = len(security) + self.encrypted_size(len(encrypted_part))
00408         header = self.MessageHeader.to_binary()
00409         encrypted_part += self._security_policy.signature(header + security + encrypted_part)
00410         return header + security + self._security_policy.encrypt(encrypted_part)
00411 
00412     @staticmethod
00413     def max_body_size(crypto, max_chunk_size):
00414         max_encrypted_size = max_chunk_size - Header.max_size() - SymmetricAlgorithmHeader.max_size()
00415         max_plain_size = (max_encrypted_size // crypto.encrypted_block_size()) * crypto.plain_block_size()
00416         return max_plain_size - SequenceHeader.max_size() - crypto.signature_size() - crypto.min_padding_size()
00417 
00418     @staticmethod
00419     def message_to_chunks(security_policy, body, max_chunk_size,
00420                           message_type=MessageType.SecureMessage, channel_id=1, request_id=1, token_id=1):
00421         """
00422         Pack message body (as binary string) into one or more chunks.
00423         Size of each chunk will not exceed max_chunk_size.
00424         Returns a list of MessageChunks. SequenceNumber is not initialized here,
00425         it must be set by Secure Channel driver.
00426         """
00427         if message_type == MessageType.SecureOpen:
00428             # SecureOpen message must be in a single chunk (specs, Part 6, 6.7.2)
00429             chunk = MessageChunk(security_policy.asymmetric_cryptography, body, message_type, ChunkType.Single)
00430             chunk.SecurityHeader.SecurityPolicyURI = security_policy.URI
00431             if security_policy.client_certificate:
00432                 chunk.SecurityHeader.SenderCertificate = security_policy.client_certificate
00433             if security_policy.server_certificate:
00434                 chunk.SecurityHeader.ReceiverCertificateThumbPrint =\
00435                     hashlib.sha1(security_policy.server_certificate).digest()
00436             chunk.MessageHeader.ChannelId = channel_id
00437             chunk.SequenceHeader.RequestId = request_id
00438             return [chunk]
00439 
00440         crypto = security_policy.symmetric_cryptography
00441         max_size = MessageChunk.max_body_size(crypto, max_chunk_size)
00442 
00443         chunks = []
00444         for i in range(0, len(body), max_size):
00445             part = body[i:i + max_size]
00446             if i + max_size >= len(body):
00447                 chunk_type = ChunkType.Single
00448             else:
00449                 chunk_type = ChunkType.Intermediate
00450             chunk = MessageChunk(crypto, part, message_type, chunk_type)
00451             chunk.SecurityHeader.TokenId = token_id
00452             chunk.MessageHeader.ChannelId = channel_id
00453             chunk.SequenceHeader.RequestId = request_id
00454             chunks.append(chunk)
00455         return chunks
00456 
00457     def __str__(self):
00458         return "{0}({1}, {2}, {3}, {4} bytes)".format(self.__class__.__name__,
00459                                                  self.MessageHeader, self.SequenceHeader,
00460                                                  self.SecurityHeader, len(self.Body))
00461     __repr__ = __str__
00462 
00463 
00464 class Message(object):
00465 
00466     def __init__(self, chunks):
00467         self._chunks = chunks
00468 
00469     def request_id(self):
00470         return self._chunks[0].SequenceHeader.RequestId
00471 
00472     def SequenceHeader(self):
00473         return self._chunks[0].SequenceHeader
00474 
00475     def SecurityHeader(self):
00476         return self._chunks[0].SecurityHeader
00477 
00478     def body(self):
00479         body = b"".join([c.Body for c in self._chunks])
00480         return utils.Buffer(body)
00481 
00482 
00483 class SecureConnection(object):
00484     """
00485     Common logic for client and server
00486     """
00487 
00488     def __init__(self, security_policy):
00489         self._sequence_number = 0
00490         self._peer_sequence_number = None
00491         self._incoming_parts = []
00492         self._security_policy = security_policy
00493         self._policies = []
00494         self.channel = auto.OpenSecureChannelResult()
00495         self._old_tokens = []
00496         self._open = False
00497         self._max_chunk_size = 65536
00498 
00499     def set_channel(self, channel):
00500         """
00501         Called on client side when getting secure channel data from server
00502         """
00503         self.channel = channel
00504 
00505     def open(self, params, server):
00506         """
00507         called on server side to open secure channel
00508         """
00509         if not self._open or params.RequestType == auto.SecurityTokenRequestType.Issue:
00510             self._open = True
00511             self.channel = auto.OpenSecureChannelResult()
00512             self.channel.SecurityToken.TokenId = 13  # random value
00513             self.channel.SecurityToken.ChannelId = server.get_new_channel_id()
00514             self.channel.SecurityToken.RevisedLifetime = params.RequestedLifetime
00515         else:
00516             self._old_tokens.append(self.channel.SecurityToken.TokenId)
00517         self.channel.SecurityToken.TokenId += 1
00518         self.channel.SecurityToken.CreatedAt = datetime.utcnow()
00519         self.channel.SecurityToken.RevisedLifetime = params.RequestedLifetime
00520         self.channel.ServerNonce = utils.create_nonce(self._security_policy.symmetric_key_size)
00521         self._security_policy.make_symmetric_key(self.channel.ServerNonce, params.ClientNonce)
00522         return self.channel
00523 
00524     def close(self):
00525         self._open = False
00526 
00527     def is_open(self):
00528         return self._open
00529 
00530     def set_policy_factories(self, policies):
00531         """
00532         Set a list of available security policies.
00533         Use this in servers with multiple endpoints with different security
00534         """
00535         self._policies = policies
00536 
00537     @staticmethod
00538     def _policy_matches(policy, uri, mode=None):
00539         return policy.URI == uri and (mode is None or policy.Mode == mode)
00540 
00541     def select_policy(self, uri, peer_certificate, mode=None):
00542         for policy in self._policies:
00543             if policy.matches(uri, mode):
00544                 self._security_policy = policy.create(peer_certificate)
00545                 return
00546         if self._security_policy.URI != uri or (mode is not None and
00547                                                 self._security_policy.Mode != mode):
00548             raise UaError("No matching policy: {0}, {1}".format(uri, mode))
00549 
00550     def tcp_to_binary(self, message_type, message):
00551         """
00552         Convert OPC UA TCP message (see OPC UA specs Part 6, 7.1) to binary.
00553         The only supported types are Hello, Acknowledge and ErrorMessage
00554         """
00555         header = Header(message_type, ChunkType.Single)
00556         binmsg = message.to_binary()
00557         header.body_size = len(binmsg)
00558         return header.to_binary() + binmsg
00559 
00560     def message_to_binary(self, message, message_type=MessageType.SecureMessage, request_id=0, algohdr=None):
00561         """
00562         Convert OPC UA secure message to binary.
00563         The only supported types are SecureOpen, SecureMessage, SecureClose
00564         if message_type is SecureMessage, the AlgoritmHeader should be passed as arg
00565         """
00566         if algohdr is None:
00567             token_id = self.channel.SecurityToken.TokenId
00568         else:
00569             token_id = algohdr.TokenId
00570         chunks = MessageChunk.message_to_chunks(
00571             self._security_policy, message, self._max_chunk_size,
00572             message_type=message_type,
00573             channel_id=self.channel.SecurityToken.ChannelId,
00574             request_id=request_id,
00575             token_id=token_id)
00576         for chunk in chunks:
00577             self._sequence_number += 1
00578             if self._sequence_number >= (1 << 32):
00579                 logger.debug("Wrapping sequence number: %d -> 1", self._sequence_number)
00580                 self._sequence_number = 1
00581             chunk.SequenceHeader.SequenceNumber = self._sequence_number
00582         return b"".join([chunk.to_binary() for chunk in chunks])
00583 
00584     def _check_incoming_chunk(self, chunk):
00585         assert isinstance(chunk, MessageChunk), "Expected chunk, got: {0}".format(chunk)
00586         if chunk.MessageHeader.MessageType != MessageType.SecureOpen:
00587             if chunk.MessageHeader.ChannelId != self.channel.SecurityToken.ChannelId:
00588                 raise UaError("Wrong channel id {0}, expected {1}".format(
00589                     chunk.MessageHeader.ChannelId,
00590                     self.channel.SecurityToken.ChannelId))
00591             if chunk.SecurityHeader.TokenId != self.channel.SecurityToken.TokenId:
00592                 if chunk.SecurityHeader.TokenId not in self._old_tokens:
00593                     logger.warning("Received a chunk with wrong token id %s, expected %s", chunk.SecurityHeader.TokenId, self.channel.SecurityToken.TokenId)
00594 
00595                     #raise UaError("Wrong token id {}, expected {}, old tokens are {}".format(
00596                         #chunk.SecurityHeader.TokenId,
00597                         #self.channel.SecurityToken.TokenId,
00598                         #self._old_tokens))
00599 
00600                 else:
00601                     # Do some cleanup, spec says we can remove old tokens when new one are used
00602                     idx = self._old_tokens.index(chunk.SecurityHeader.TokenId)
00603                     if idx != 0:
00604                         self._old_tokens = self._old_tokens[idx:]
00605         if self._incoming_parts:
00606             if self._incoming_parts[0].SequenceHeader.RequestId != chunk.SequenceHeader.RequestId:
00607                 raise UaError("Wrong request id {0}, expected {1}".format(
00608                     chunk.SequenceHeader.RequestId,
00609                     self._incoming_parts[0].SequenceHeader.RequestId))
00610 
00611         # sequence number must be incremented or wrapped
00612         num = chunk.SequenceHeader.SequenceNumber
00613         if self._peer_sequence_number is not None:
00614             if num != self._peer_sequence_number + 1:
00615                 wrap = (1 << 32) - 1024
00616                 if num < 1024 and self._peer_sequence_number >= wrap:
00617                     # specs Part 6, 6.7.2
00618                     logger.debug("Sequence number wrapped: %d -> %d",
00619                                  self._peer_sequence_number, num)
00620                 else:
00621                     raise UaError(
00622                         "Wrong sequence {0} -> {1} (server bug or replay attack)"
00623                         .format(self._peer_sequence_number, num))
00624         self._peer_sequence_number = num
00625 
00626     def receive_from_header_and_body(self, header, body):
00627         """
00628         Convert MessageHeader and binary body to OPC UA TCP message (see OPC UA
00629         specs Part 6, 7.1: Hello, Acknowledge or ErrorMessage), or a Message
00630         object, or None (if intermediate chunk is received)
00631         """
00632         if header.MessageType == MessageType.SecureOpen:
00633             data = body.copy(header.body_size)
00634             security_header = AsymmetricAlgorithmHeader.from_binary(data)
00635             self.select_policy(security_header.SecurityPolicyURI, security_header.SenderCertificate)
00636 
00637         if header.MessageType in (MessageType.SecureMessage,
00638                                   MessageType.SecureOpen,
00639                                   MessageType.SecureClose):
00640             chunk = MessageChunk.from_header_and_body(self._security_policy,
00641                                                       header, body)
00642             return self._receive(chunk)
00643         elif header.MessageType == MessageType.Hello:
00644             msg = Hello.from_binary(body)
00645             self._max_chunk_size = msg.ReceiveBufferSize
00646             return msg
00647         elif header.MessageType == MessageType.Acknowledge:
00648             msg = Acknowledge.from_binary(body)
00649             self._max_chunk_size = msg.SendBufferSize
00650             return msg
00651         elif header.MessageType == MessageType.Error:
00652             msg = ErrorMessage.from_binary(body)
00653             logger.warning("Received an error: %s", msg)
00654             return msg
00655         else:
00656             raise UaError("Unsupported message type {0}".format(header.MessageType))
00657 
00658     def receive_from_socket(self, socket):
00659         """
00660         Convert binary stream to OPC UA TCP message (see OPC UA
00661         specs Part 6, 7.1: Hello, Acknowledge or ErrorMessage), or a Message
00662         object, or None (if intermediate chunk is received)
00663         """
00664         logger.debug("Waiting for header")
00665         header = Header.from_string(socket)
00666         logger.info("received header: %s", header)
00667         body = socket.read(header.body_size)
00668         if len(body) != header.body_size:
00669             raise UaError("{0} bytes expected, {1} available".format(header.body_size, len(body)))
00670         return self.receive_from_header_and_body(header, utils.Buffer(body))
00671 
00672     def _receive(self, msg):
00673         self._check_incoming_chunk(msg)
00674         self._incoming_parts.append(msg)
00675         if msg.MessageHeader.ChunkType == ChunkType.Intermediate:
00676             return None
00677         if msg.MessageHeader.ChunkType == ChunkType.Abort:
00678             err = ErrorMessage.from_binary(utils.Buffer(msg.Body))
00679             logger.warning("Message %s aborted: %s", msg, err)
00680             # specs Part 6, 6.7.3 say that aborted message shall be ignored
00681             # and SecureChannel should not be closed
00682             self._incoming_parts = []
00683             return None
00684         elif msg.MessageHeader.ChunkType == ChunkType.Single:
00685             message = Message(self._incoming_parts)
00686             self._incoming_parts = []
00687             return message
00688         else:
00689             raise UaError("Unsupported chunk type: {0}".format(msg))
00690 
00691 
00692 # FIXES for missing switchfield in NodeAttributes classes
00693 ana = auto.NodeAttributesMask
00694 
00695 
00696 class ObjectAttributes(auto.ObjectAttributes):
00697 
00698     def __init__(self):
00699         auto.ObjectAttributes.__init__(self)
00700         self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.EventNotifier
00701 
00702 
00703 class ObjectTypeAttributes(auto.ObjectTypeAttributes):
00704 
00705     def __init__(self):
00706         auto.ObjectTypeAttributes.__init__(self)
00707         self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract
00708 
00709 
00710 class VariableAttributes(auto.VariableAttributes):
00711 
00712     def __init__(self):
00713         auto.VariableAttributes.__init__(self)
00714         self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Value | ana.DataType | ana.ValueRank | ana.ArrayDimensions | ana.AccessLevel | ana.UserAccessLevel | ana.MinimumSamplingInterval | ana.Historizing
00715         self.Historizing = False
00716 
00717 
00718 class VariableTypeAttributes(auto.VariableTypeAttributes):
00719 
00720     def __init__(self):
00721         auto.VariableTypeAttributes.__init__(self)
00722         self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Value | ana.DataType | ana.ValueRank | ana.ArrayDimensions | ana.IsAbstract
00723 
00724 
00725 class MethodAttributes(auto.MethodAttributes):
00726 
00727     def __init__(self):
00728         auto.MethodAttributes.__init__(self)
00729         self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Executable | ana.UserExecutable
00730 
00731 
00732 class ReferenceTypeAttributes(auto.ReferenceTypeAttributes):
00733 
00734     def __init__(self):
00735         auto.ReferenceTypeAttributes.__init__(self)
00736         self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract | ana.Symmetric | ana.InverseName
00737 
00738 
00739 class DataTypeAttributes(auto.DataTypeAttributes):
00740 
00741     def __init__(self):
00742         auto.DataTypeAttributes.__init__(self)
00743         self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract
00744 
00745 
00746 class ViewAttributes(auto.ViewAttributes):
00747 
00748     def __init__(self):
00749         auto.ViewAttributes.__init__(self)
00750         self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.ContainsNoLoops | ana.EventNotifier
00751 
00752 
00753 class Argument(auto.Argument):
00754 
00755     def __init__(self):
00756         auto.Argument.__init__(self)
00757         self.ValueRank = -2
00758 
00759 #AttributeIdsInv = {v: k for k, v in AttributeIds.__dict__.items()}


ros_opcua_impl_python_opcua
Author(s): Denis Štogl , Daniel Draper
autogenerated on Sat Jun 8 2019 18:26:24