Completed
Push — master ( 651344...8c3df2 )
by Olivier
02:34
created

opcua.ua.ViewAttributes   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 5
Duplicated Lines 0 %

Test Coverage

Coverage 50%
Metric Value
dl 0
loc 5
ccs 2
cts 4
cp 0.5
rs 10
wmc 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A __init__() 0 3 1
1 1
import struct
2 1
import logging
3 1
import hashlib
4 1
from enum import IntEnum
5
6 1
from opcua.ua import uaprotocol_auto as auto
7 1
from opcua.ua import uatypes
8 1
from opcua.ua import UaError
9 1
from opcua.ua.uatypes import uatype_UInt32
10 1
from opcua.common import utils
11
12 1
logger = logging.getLogger('opcua.uaprotocol')
13
14 1
OPC_TCP_SCHEME = 'opc.tcp'
15
16
17 1
class ValueRank(IntEnum):
18
    """
19
    Defines dimensions of a variable.
20
    This enum does not support all cases since ValueRank support any n>0
21
    but since it is an IntEnum it can be replace by a normal int
22
    """
23 1
    ScalarOrOneDimension = -3
24 1
    Any = -2
25 1
    Scalar = -1
26 1
    OneOrMoreDimensions = 0
27 1
    OneDimension = 1
28
    # the next names are not in spec but so common we express them here
29 1
    TwoDimensions = 2
30 1
    ThreeDimensions = 3
31 1
    FourDimensions = 3
32
33
34 1
class AccessLevelMask(object):
35
    """
36
    Used by AccessLevel and UserAccessLevel
37
    """
38 1
    CurrentRead = 0
39 1
    CurrentWrite = 1
40 1
    HistoryRead = 2
41 1
    HistoryWrite = 3
42 1
    SemanticChange = 4
43
44
45 1
class Hello(uatypes.FrozenClass):
46
47 1
    def __init__(self):
48 1
        self.ProtocolVersion = 0
49 1
        self.ReceiveBufferSize = 65536
50 1
        self.SendBufferSize = 65536
51 1
        self.MaxMessageSize = 0
52 1
        self.MaxChunkCount = 0
53 1
        self.EndpointUrl = ""
54 1
        self._freeze = True
55
56 1
    def to_binary(self):
57 1
        b = []
58 1
        b.append(uatype_UInt32.pack(self.ProtocolVersion))
59 1
        b.append(uatype_UInt32.pack(self.ReceiveBufferSize))
60 1
        b.append(uatype_UInt32.pack(self.SendBufferSize))
61 1
        b.append(uatype_UInt32.pack(self.MaxMessageSize))
62 1
        b.append(uatype_UInt32.pack(self.MaxChunkCount))
63 1
        b.append(uatypes.pack_string(self.EndpointUrl))
64 1
        return b"".join(b)
65
66 1
    @staticmethod
67
    def from_binary(data):
68 1
        hello = Hello()
69 1
        hello.ProtocolVersion = uatype_UInt32.unpack(data.read(4))[0]
70 1
        hello.ReceiveBufferSize = uatype_UInt32.unpack(data.read(4))[0]
71 1
        hello.SendBufferSize = uatype_UInt32.unpack(data.read(4))[0]
72 1
        hello.MaxMessageSize = uatype_UInt32.unpack(data.read(4))[0]
73 1
        hello.MaxChunkCount = uatype_UInt32.unpack(data.read(4))[0]
74 1
        hello.EndpointUrl = uatypes.unpack_string(data)
75 1
        return hello
76
77
78 1
class MessageType(object):
79 1
    Invalid = b"INV"  # FIXME: check value
80 1
    Hello = b"HEL"
81 1
    Acknowledge = b"ACK"
82 1
    Error = b"ERR"
83 1
    SecureOpen = b"OPN"
84 1
    SecureClose = b"CLO"
85 1
    SecureMessage = b"MSG"
86
87
88 1
class ChunkType(object):
89 1
    Invalid = b"0"  # FIXME check
90 1
    Single = b"F"
91 1
    Intermediate = b"C"
92 1
    Abort = b"A"    # when an error occurred and the Message is aborted (body is ErrorMessage)
93
94
95 1
class Header(uatypes.FrozenClass):
96
97 1
    def __init__(self, msgType=None, chunkType=None, channelid=0):
98 1
        self.MessageType = msgType
99 1
        self.ChunkType = chunkType
100 1
        self.ChannelId = channelid
101 1
        self.body_size = 0
102 1
        self.packet_size = 0
103 1
        self._freeze = True
104
105 1
    def add_size(self, size):
106
        self.body_size += size
107
108 1
    def to_binary(self):
109 1
        b = []
110 1
        b.append(struct.pack("<3ss", self.MessageType, self.ChunkType))
111 1
        size = self.body_size + 8
112 1
        if self.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
113 1
            size += 4
114 1
        b.append(uatype_UInt32.pack(size))
115 1
        if self.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
116 1
            b.append(uatype_UInt32.pack(self.ChannelId))
117 1
        return b"".join(b)
118
119 1
    @staticmethod
120
    def from_string(data):
121 1
        hdr = Header()
122 1
        hdr.MessageType, hdr.ChunkType, hdr.packet_size = struct.unpack("<3scI", data.read(8))
123 1
        hdr.body_size = hdr.packet_size - 8
124 1
        if hdr.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
125 1
            hdr.body_size -= 4
126 1
            hdr.ChannelId = uatype_UInt32.unpack(data.read(4))[0]
127 1
        return hdr
128
129 1
    @staticmethod
130
    def max_size():
131 1
        return struct.calcsize("<3scII")
132
133 1
    def __str__(self):
134
        return "Header(type:{}, chunk_type:{}, body_size:{}, channel:{})".format(self.MessageType, self.ChunkType, self.body_size, self.ChannelId)
135 1
    __repr__ = __str__
136
137
138 1
class ErrorMessage(uatypes.FrozenClass):
139
140 1
    def __init__(self):
141
        self.Error = uatypes.StatusCode()
142
        self.Reason = ""
143
        self._freeze = True
144
145 1
    def to_binary(self):
146
        b = []
147
        b.append(self.Error.to_binary())
148
        b.append(uatypes.pack_string(self.Reason))
149
        return b"".join(b)
150
151 1
    @staticmethod
152
    def from_binary(data):
153
        ack = ErrorMessage()
154
        ack.Error = uatypes.StatusCode.from_binary(data)
155
        ack.Reason = uatypes.unpack_string(data)
156
        return ack
157
158 1
    def __str__(self):
159
        return "MessageAbort(error:{}, reason:{})".format(self.Error, self.Reason)
160 1
    __repr__ = __str__
161
162
163 1
class Acknowledge(uatypes.FrozenClass):
164
165 1
    def __init__(self):
166 1
        self.ProtocolVersion = 0
167 1
        self.ReceiveBufferSize = 65536
168 1
        self.SendBufferSize = 65536
169 1
        self.MaxMessageSize = 0  # No limits
170 1
        self.MaxChunkCount = 0  # No limits
171 1
        self._freeze = True
172
173 1
    def to_binary(self):
174 1
        return struct.pack(
175
            "<5I",
176
            self.ProtocolVersion,
177
            self.ReceiveBufferSize,
178
            self.SendBufferSize,
179
            self.MaxMessageSize,
180
            self.MaxChunkCount)
181
182 1
    @staticmethod
183
    def from_binary(data):
184 1
        ack = Acknowledge()
185 1
        ack.ProtocolVersion, ack.ReceiveBufferSize, ack.SendBufferSize, ack.MaxMessageSize, ack.MaxChunkCount \
186
            = struct.unpack("<5I", data.read(20))
187 1
        return ack
188
189
190 1
class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
191
192 1
    def __init__(self):
193 1
        self.SecurityPolicyURI = "http://opcfoundation.org/UA/SecurityPolicy#None"
194 1
        self.SenderCertificate = b""
195 1
        self.ReceiverCertificateThumbPrint = b""
196 1
        self._freeze = True
197
198 1
    def to_binary(self):
199 1
        b = []
200 1
        b.append(uatypes.pack_string(self.SecurityPolicyURI))
201 1
        b.append(uatypes.pack_string(self.SenderCertificate))
202 1
        b.append(uatypes.pack_string(self.ReceiverCertificateThumbPrint))
203 1
        return b"".join(b)
204
205 1
    @staticmethod
206
    def from_binary(data):
207 1
        hdr = AsymmetricAlgorithmHeader()
208 1
        hdr.SecurityPolicyURI = uatypes.unpack_string(data)
209 1
        hdr.SenderCertificate = uatypes.unpack_bytes(data)
210 1
        hdr.ReceiverCertificateThumbPrint = uatypes.unpack_bytes(data)
211 1
        return hdr
212
213 1
    def __str__(self):
214
        return "{}(SecurityPolicy:{}, certificatesize:{}, receiverCertificatesize:{} )".format(self.__class__.__name__, self.SecurityPolicyURI, len(self.SenderCertificate), len(self.ReceiverCertificateThumbPrint))
215 1
    __repr__ = __str__
216
217
218 1
class SymmetricAlgorithmHeader(uatypes.FrozenClass):
219
220 1
    def __init__(self):
221 1
        self.TokenId = 0
222 1
        self._freeze = True
223
224 1
    @staticmethod
225
    def from_binary(data):
226 1
        obj = SymmetricAlgorithmHeader()
227 1
        obj.TokenId = uatype_UInt32.unpack(data.read(4))[0]
228 1
        return obj
229
230 1
    def to_binary(self):
231 1
        return uatype_UInt32.pack(self.TokenId)
232
233 1
    @staticmethod
234
    def max_size():
235 1
        return struct.calcsize("<I")
236
237 1
    def __str__(self):
238
        return "{}(TokenId:{} )".format(self.__class__.__name__, self.TokenId)
239 1
    __repr__ = __str__
240
241
242 1
class SequenceHeader(uatypes.FrozenClass):
243
244 1
    def __init__(self):
245 1
        self.SequenceNumber = None
246 1
        self.RequestId = None
247 1
        self._freeze = True
248
249 1
    @staticmethod
250
    def from_binary(data):
251 1
        obj = SequenceHeader()
252 1
        obj.SequenceNumber = uatype_UInt32.unpack(data.read(4))[0]
253 1
        obj.RequestId = uatype_UInt32.unpack(data.read(4))[0]
254 1
        return obj
255
256 1
    def to_binary(self):
257 1
        b = []
258 1
        b.append(uatype_UInt32.pack(self.SequenceNumber))
259 1
        b.append(uatype_UInt32.pack(self.RequestId))
260 1
        return b"".join(b)
261
262 1
    @staticmethod
263
    def max_size():
264 1
        return struct.calcsize("<II")
265
266 1
    def __str__(self):
267
        return "{}(SequenceNumber:{}, RequestId:{} )".format(self.__class__.__name__, self.SequenceNumber, self.RequestId)
268 1
    __repr__ = __str__
269
270
271 1
class CryptographyNone:
272
    """
273
    Base class for symmetric/asymmetric cryprography
274
    """
275
276 1
    def __init__(self):
277 1
        pass
278
279 1
    def plain_block_size(self):
280
        """
281
        Size of plain text block for block cipher.
282
        """
283 1
        return 1
284
285 1
    def encrypted_block_size(self):
286
        """
287
        Size of encrypted text block for block cipher.
288
        """
289 1
        return 1
290
291 1
    def padding(self, size):
292
        """
293
        Create padding for a block of given size.
294
        plain_size = size + len(padding) + signature_size()
295
        plain_size = N * plain_block_size()
296
        """
297 1
        return b''
298
299 1
    def min_padding_size(self):
300 1
        return 0
301
302 1
    def signature_size(self):
303 1
        return 0
304
305 1
    def signature(self, data):
306 1
        return b''
307
308 1
    def encrypt(self, data):
309 1
        return data
310
311 1
    def decrypt(self, data):
312 1
        return data
313
314 1
    def vsignature_size(self):
315 1
        return 0
316
317 1
    def verify(self, data, signature):
318
        """
319
        Verify signature and raise exception if signature is invalid
320
        """
321 1
        pass
322
323 1
    def remove_padding(self, data):
324 1
        return data
325
326
327 1
class SecurityPolicy(object):
328
    """
329
    Base class for security policy
330
    """
331 1
    URI = "http://opcfoundation.org/UA/SecurityPolicy#None"
332 1
    signature_key_size = 0
333 1
    symmetric_key_size = 0
334
335 1
    def __init__(self):
336 1
        self.asymmetric_cryptography = CryptographyNone()
337 1
        self.symmetric_cryptography = CryptographyNone()
338 1
        self.Mode = auto.MessageSecurityMode.None_
339 1
        self.server_certificate = b""
340 1
        self.client_certificate = b""
341
342 1
    def make_symmetric_key(self, a, b):
343 1
        pass
344
345
346 1
class SecurityPolicyFactory(object):
347
    """
348
    Helper class for creating server-side SecurityPolicy.
349
    Server has one certificate and private key, but needs a separate
350
    SecurityPolicy for every client and client's certificate
351
    """
352
353 1
    def __init__(self, cls=SecurityPolicy, mode=auto.MessageSecurityMode.None_,
354
                 certificate=None, private_key=None):
355 1
        self.cls = cls
356 1
        self.mode = mode
357 1
        self.certificate = certificate
358 1
        self.private_key = private_key
359
360 1
    def matches(self, uri, mode=None):
361 1
        return self.cls.URI == uri and (mode is None or self.mode == mode)
362
363 1
    def create(self, peer_certificate):
364 1
        if self.cls is SecurityPolicy:
365 1
            return self.cls()
366
        else:
367
            return self.cls(peer_certificate,
368
                            self.certificate, self.private_key,
369
                            self.mode)
370
371
372 1
class MessageChunk(uatypes.FrozenClass):
373
    """
374
    Message Chunk, as described in OPC UA specs Part 6, 6.7.2.
375
    """
376
377 1
    def __init__(self, security_policy, body=b'', msg_type=MessageType.SecureMessage, chunk_type=ChunkType.Single):
378 1
        self.MessageHeader = Header(msg_type, chunk_type)
379 1
        if msg_type in (MessageType.SecureMessage, MessageType.SecureClose):
380 1
            self.SecurityHeader = SymmetricAlgorithmHeader()
381 1
        elif msg_type == MessageType.SecureOpen:
382 1
            self.SecurityHeader = AsymmetricAlgorithmHeader()
383
        else:
384
            raise UaError("Unsupported message type: {}".format(msg_type))
385 1
        self.SequenceHeader = SequenceHeader()
386 1
        self.Body = body
387 1
        self._security_policy = security_policy
388
389 1
    @staticmethod
390
    def from_binary(security_policy, data):
391 1
        h = Header.from_string(data)
392 1
        return MessageChunk.from_header_and_body(security_policy, h, data)
393
394 1
    @staticmethod
395
    def from_header_and_body(security_policy, header, buf):
396 1
        assert len(buf) >= header.body_size, 'Full body expected here'
397 1
        data = buf.copy(header.body_size)
398 1
        buf.skip(header.body_size)
399 1
        if header.MessageType in (MessageType.SecureMessage, MessageType.SecureClose):
400 1
            security_header = SymmetricAlgorithmHeader.from_binary(data)
401 1
            crypto = security_policy.symmetric_cryptography
402 1
        elif header.MessageType == MessageType.SecureOpen:
403 1
            security_header = AsymmetricAlgorithmHeader.from_binary(data)
404 1
            crypto = security_policy.asymmetric_cryptography
405
        else:
406
            raise UaError("Unsupported message type: {}".format(header.MessageType))
407 1
        obj = MessageChunk(crypto)
408 1
        obj.MessageHeader = header
409 1
        obj.SecurityHeader = security_header
410 1
        decrypted = crypto.decrypt(data.read(len(data)))
411 1
        signature_size = crypto.vsignature_size()
412 1
        if signature_size > 0:
413
            signature = decrypted[-signature_size:]
414
            decrypted = decrypted[:-signature_size]
415
            crypto.verify(obj.MessageHeader.to_binary() + obj.SecurityHeader.to_binary() + decrypted, signature)
416 1
        data = utils.Buffer(crypto.remove_padding(decrypted))
417 1
        obj.SequenceHeader = SequenceHeader.from_binary(data)
418 1
        obj.Body = data.read(len(data))
419 1
        return obj
420
421 1
    def encrypted_size(self, plain_size):
422 1
        size = plain_size + self._security_policy.signature_size()
423 1
        pbs = self._security_policy.plain_block_size()
424 1
        assert(size % pbs == 0)
425 1
        return size // pbs * self._security_policy.encrypted_block_size()
426
427 1
    def to_binary(self):
428 1
        security = self.SecurityHeader.to_binary()
429 1
        encrypted_part = self.SequenceHeader.to_binary() + self.Body
430 1
        encrypted_part += self._security_policy.padding(len(encrypted_part))
431 1
        self.MessageHeader.body_size = len(security) + self.encrypted_size(len(encrypted_part))
432 1
        header = self.MessageHeader.to_binary()
433 1
        encrypted_part += self._security_policy.signature(header + security + encrypted_part)
434 1
        return header + security + self._security_policy.encrypt(encrypted_part)
435
436 1
    @staticmethod
437
    def max_body_size(crypto, max_chunk_size):
438 1
        max_encrypted_size = max_chunk_size - Header.max_size() - SymmetricAlgorithmHeader.max_size()
439 1
        max_plain_size = (max_encrypted_size // crypto.encrypted_block_size()) * crypto.plain_block_size()
440 1
        return max_plain_size - SequenceHeader.max_size() - crypto.signature_size() - crypto.min_padding_size()
441
442 1
    @staticmethod
443 1
    def message_to_chunks(security_policy, body, max_chunk_size, message_type=MessageType.SecureMessage, channel_id=1, request_id=1, token_id=1):
444
        """
445
        Pack message body (as binary string) into one or more chunks.
446
        Size of each chunk will not exceed max_chunk_size.
447
        Returns a list of MessageChunks. SequenceNumber is not initialized here,
448
        it must be set by Secure Channel driver.
449
        """
450 1
        if message_type == MessageType.SecureOpen:
451
            # SecureOpen message must be in a single chunk (specs, Part 6, 6.7.2)
452 1
            chunk = MessageChunk(security_policy.asymmetric_cryptography, body, message_type, ChunkType.Single)
453 1
            chunk.SecurityHeader.SecurityPolicyURI = security_policy.URI
454 1
            if security_policy.client_certificate:
455
                chunk.SecurityHeader.SenderCertificate = security_policy.client_certificate
456 1
            if security_policy.server_certificate:
457
                chunk.SecurityHeader.ReceiverCertificateThumbPrint = hashlib.sha1(security_policy.server_certificate).digest()
458 1
            chunk.MessageHeader.ChannelId = channel_id
459 1
            chunk.SequenceHeader.RequestId = request_id
460 1
            return [chunk]
461
462 1
        crypto = security_policy.symmetric_cryptography
463 1
        max_size = MessageChunk.max_body_size(crypto, max_chunk_size)
464
465 1
        chunks = []
466 1
        for i in range(0, len(body), max_size):
467 1
            part = body[i:i + max_size]
468 1
            if i + max_size >= len(body):
469 1
                chunk_type = ChunkType.Single
470
            else:
471 1
                chunk_type = ChunkType.Intermediate
472 1
            chunk = MessageChunk(crypto, part, message_type, chunk_type)
473 1
            chunk.SecurityHeader.TokenId = token_id
474 1
            chunk.MessageHeader.ChannelId = channel_id
475 1
            chunk.SequenceHeader.RequestId = request_id
476 1
            chunks.append(chunk)
477 1
        return chunks
478
479 1
    def __str__(self):
480
        return "{}({}, {}, {}, {} bytes)".format(self.__class__.__name__,
481
                                                 self.MessageHeader, self.SequenceHeader, self.SecurityHeader, len(self.Body))
482 1
    __repr__ = __str__
483
484
485 1
class Message(object):
486
487 1
    def __init__(self, chunks):
488 1
        self._chunks = chunks
489
490 1
    def request_id(self):
491 1
        return self._chunks[0].SequenceHeader.RequestId
492
493 1
    def SequenceHeader(self):
494 1
        return self._chunks[0].SequenceHeader
495
496 1
    def SecurityHeader(self):
497 1
        return self._chunks[0].SecurityHeader
498
499 1
    def body(self):
500 1
        body = b"".join([c.Body for c in self._chunks])
501 1
        return utils.Buffer(body)
502
503
504 1
class SecureConnection(object):
505
    """
506
    Common logic for client and server
507
    """
508
509 1
    def __init__(self, security_policy):
510 1
        self._sequence_number = 0
511 1
        self._peer_sequence_number = None
512 1
        self._incoming_parts = []
513 1
        self._security_policy = security_policy
514 1
        self._policies = []
515 1
        self._security_token = auto.ChannelSecurityToken()
516 1
        self._max_chunk_size = 65536
517
518 1
    def set_policy_factories(self, policies):
519
        """
520
        Set a list of available security policies.
521
        Use this in servers with multiple endpoints with different security
522
        """
523 1
        self._policies = policies
524
525 1
    @staticmethod
526 1
    def _policy_matches(policy, uri, mode=None):
527
        return policy.URI == uri and (mode is None or policy.Mode == mode)
528
529 1
    def select_policy(self, uri, peer_certificate, mode=None):
530 1
        for policy in self._policies:
531 1
            if policy.matches(uri, mode):
532 1
                self._security_policy = policy.create(peer_certificate)
533 1
                return
534 1
        if self._security_policy.URI != uri or (mode is not None and
535
                                                self._security_policy.Mode != mode):
536
            raise UaError("No matching policy: {}, {}".format(uri, mode))
537
538 1
    def set_security_token(self, tok):
539 1
        self._security_token = tok
540
541 1
    def tcp_to_binary(self, message_type, message):
542
        """
543
        Convert OPC UA TCP message (see OPC UA specs Part 6, 7.1) to binary.
544
        The only supported types are Hello, Acknowledge and ErrorMessage
545
        """
546 1
        header = Header(message_type, ChunkType.Single)
547 1
        binmsg = message.to_binary()
548 1
        header.body_size = len(binmsg)
549 1
        return header.to_binary() + binmsg
550
551 1
    def message_to_binary(self, message,
552
                          message_type=MessageType.SecureMessage, request_id=0):
553
        """
554
        Convert OPC UA secure message to binary.
555
        The only supported types are SecureOpen, SecureMessage, SecureClose
556
        """
557 1
        chunks = MessageChunk.message_to_chunks(
558
            self._security_policy, message, self._max_chunk_size,
559
            message_type=message_type,
560
            channel_id=self._security_token.ChannelId,
561
            request_id=request_id,
562
            token_id=self._security_token.TokenId)
563 1
        for chunk in chunks:
564 1
            self._sequence_number += 1
565 1
            if self._sequence_number >= (1 << 32):
566
                logger.debug("Wrapping sequence number: %d -> 1",
567
                             self._sequence_number)
568
                self._sequence_number = 1
569 1
            chunk.SequenceHeader.SequenceNumber = self._sequence_number
570 1
        return b"".join([chunk.to_binary() for chunk in chunks])
571
572 1
    def _check_incoming_chunk(self, chunk):
573 1
        assert isinstance(chunk, MessageChunk), "Expected chunk, got: {}".format(chunk)
574 1
        if chunk.MessageHeader.MessageType != MessageType.SecureOpen:
575 1
            if chunk.MessageHeader.ChannelId != self._security_token.ChannelId:
576
                raise UaError("Wrong channel id {}, expected {}".format(
577
                    chunk.MessageHeader.ChannelId,
578
                    self._security_token.ChannelId))
579 1
            if chunk.SecurityHeader.TokenId != self._security_token.TokenId:
580
                raise UaError("Wrong token id {}, expected {}".format(
581
                    chunk.SecurityHeader.TokenId,
582
                    self._security_token.TokenId))
583 1
        if self._incoming_parts:
584
            if self._incoming_parts[0].SequenceHeader.RequestId != chunk.SequenceHeader.RequestId:
585
                raise UaError("Wrong request id {}, expected {}".format(
586
                    chunk.SequenceHeader.RequestId,
587
                    self._incoming_parts[0].SequenceHeader.RequestId))
588
589
        # sequence number must be incremented or wrapped
590 1
        num = chunk.SequenceHeader.SequenceNumber
591 1
        if self._peer_sequence_number is not None:
592 1
            if num != self._peer_sequence_number + 1:
593
                wrap = (1 << 32) - 1024
594
                if num < 1024 and self._peer_sequence_number >= wrap:
595
                    # specs Part 6, 6.7.2
596
                    logger.debug("Sequence number wrapped: %d -> %d",
597
                                 self._peer_sequence_number, num)
598
                else:
599
                    raise UaError(
600
                        "Wrong sequence {} -> {} (server bug or replay attack)"
601
                        .format(self._peer_sequence_number, num))
602 1
        self._peer_sequence_number = num
603
604 1
    def receive_from_header_and_body(self, header, body):
605
        """
606
        Convert MessageHeader and binary body to OPC UA TCP message (see OPC UA
607
        specs Part 6, 7.1: Hello, Acknowledge or ErrorMessage), or a Message
608
        object, or None (if intermediate chunk is received)
609
        """
610 1
        if header.MessageType == MessageType.SecureOpen:
611 1
            data = body.copy(header.body_size)
612 1
            security_header = AsymmetricAlgorithmHeader.from_binary(data)
613 1
            self.select_policy(security_header.SecurityPolicyURI, security_header.SenderCertificate)
614
615 1
        if header.MessageType in (MessageType.SecureMessage,
616
                                  MessageType.SecureOpen,
617
                                  MessageType.SecureClose):
618 1
            chunk = MessageChunk.from_header_and_body(self._security_policy,
619
                                                      header, body)
620 1
            return self._receive(chunk)
621 1
        elif header.MessageType == MessageType.Hello:
622 1
            msg = Hello.from_binary(body)
623 1
            self._max_chunk_size = msg.ReceiveBufferSize
624 1
            return msg
625 1
        elif header.MessageType == MessageType.Acknowledge:
626 1
            msg = Acknowledge.from_binary(body)
627 1
            self._max_chunk_size = msg.SendBufferSize
628 1
            return msg
629
        elif header.MessageType == MessageType.Error:
630
            msg = ErrorMessage.from_binary(body)
631
            logger.warning("Received an error: %s", msg)
632
            return msg
633
        else:
634
            raise UaError("Unsupported message type {}".format(header.MessageType))
635
636 1
    def receive_from_socket(self, socket):
637
        """
638
        Convert binary stream to OPC UA TCP message (see OPC UA
639
        specs Part 6, 7.1: Hello, Acknowledge or ErrorMessage), or a Message
640
        object, or None (if intermediate chunk is received)
641
        """
642 1
        logger.debug("Waiting for header")
643 1
        header = Header.from_string(socket)
644 1
        logger.info("received header: %s", header)
645 1
        body = socket.read(header.body_size)
646 1
        if len(body) != header.body_size:
647
            raise UaError("{} bytes expected, {} available".format(header.body_size, len(body)))
648 1
        return self.receive_from_header_and_body(header, utils.Buffer(body))
649
650 1
    def _receive(self, msg):
651 1
        self._check_incoming_chunk(msg)
652 1
        self._incoming_parts.append(msg)
653 1
        if msg.MessageHeader.ChunkType == ChunkType.Intermediate:
654
            return None
655 1
        if msg.MessageHeader.ChunkType == ChunkType.Abort:
656
            err = ErrorMessage.from_binary(utils.Buffer(msg.Body))
657
            logger.warning("Message %s aborted: %s", msg, err)
658
            # specs Part 6, 6.7.3 say that aborted message shall be ignored
659
            # and SecureChannel should not be closed
660
            self._incoming_parts = []
661
            return None
662 1
        elif msg.MessageHeader.ChunkType == ChunkType.Single:
663 1
            message = Message(self._incoming_parts)
664 1
            self._incoming_parts = []
665 1
            return message
666
        else:
667
            raise UaError("Unsupported chunk type: {}".format(msg))
668
669
670
# FIXES for missing switchfield in NodeAttributes classes
671 1
ana = auto.NodeAttributesMask
672
673
674 1
class ObjectAttributes(auto.ObjectAttributes):
675
676 1
    def __init__(self):
677 1
        auto.ObjectAttributes.__init__(self)
678 1
        self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.EventNotifier
679
680
681 1
class ObjectTypeAttributes(auto.ObjectTypeAttributes):
682
683 1
    def __init__(self):
684 1
        auto.ObjectTypeAttributes.__init__(self)
685 1
        self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract
686
687
688 1
class VariableAttributes(auto.VariableAttributes):
689
690 1
    def __init__(self):
691 1
        auto.VariableAttributes.__init__(self)
692 1
        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
693 1
        self.Historizing = False
694
695
696 1
class VariableTypeAttributes(auto.VariableTypeAttributes):
697
698 1
    def __init__(self):
699 1
        auto.VariableTypeAttributes.__init__(self)
700 1
        self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Value | ana.DataType | ana.ValueRank | ana.ArrayDimensions | ana.IsAbstract
701
702
703 1
class MethodAttributes(auto.MethodAttributes):
704
705 1
    def __init__(self):
706 1
        auto.MethodAttributes.__init__(self)
707 1
        self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Executable | ana.UserExecutable
708
709
710 1
class ReferenceTypeAttributes(auto.ReferenceTypeAttributes):
711
712 1
    def __init__(self):
713 1
        auto.ReferenceTypeAttributes.__init__(self)
714 1
        self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract | ana.Symmetric | ana.InverseName
715
716
717 1
class DataTypeAttributes(auto.DataTypeAttributes):
718
719 1
    def __init__(self):
720 1
        auto.DataTypeAttributes.__init__(self)
721 1
        self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract
722
723
724 1
class ViewAttributes(auto.ViewAttributes):
725
726 1
    def __init__(self):
727
        auto.ViewAttributes.__init__(self)
728
        self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.ContainsNoLoops | ana.EventNotifier
729
730
731 1
class Argument(auto.Argument):
732
733 1
    def __init__(self):
734 1
        auto.Argument.__init__(self)
735 1
        self.ValueRank = -2
736
737
738
#AttributeIdsInv = {v: k for k, v in AttributeIds.__dict__.items()}
739