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"
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"
00062 Single = b"F"
00063 Intermediate = b"C"
00064 Abort = b"A"
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
00143 self.MaxChunkCount = 0
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
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
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
00596
00597
00598
00599
00600 else:
00601
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
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
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
00681
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
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