Skip to content

Commit

Permalink
server handle malformed packets
Browse files Browse the repository at this point in the history
fix test after rebase

server handle malformed packets

close connection on error

reorder imports

change test
  • Loading branch information
schroeder- authored and oroulet committed Sep 16, 2022
1 parent acdb6a6 commit f6603da
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 10 deletions.
23 changes: 15 additions & 8 deletions asyncua/server/binary_server_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,22 @@ def data_received(self, data):
try:
header = header_from_binary(buf)
except NotEnoughData:
logger.debug('Not enough data while parsing header from client, waiting for more')
# a packet should at least contain a header otherwise it is malformed (8 or 12 bytes)
logger.debug('Not enough data while parsing header from client, empty the buffer')
self.transport.close()
return
if len(buf) < header.body_size:
logger.debug('We did not receive enough data from client. Need %s got %s', header.body_size,
len(buf))
return
# we have a complete message
self.messages.put_nowait((header, buf))
self._buffer = self._buffer[(header.header_size + header.body_size):]
if header.header_size + header.body_size <= header.header_size:
# malformed header prevent invalid access of your buffer
logger.error(f'Got malformed header {header}')
self.transport.close()
else:
if len(buf) < header.body_size:
logger.debug('We did not receive enough data from client. Need %s got %s', header.body_size,
len(buf))
return
# we have a complete message
self.messages.put_nowait((header, buf))
self._buffer = self._buffer[(header.header_size + header.body_size):]
except Exception:
logger.exception('Exception raised while parsing message from client')
return
Expand Down
20 changes: 18 additions & 2 deletions tests/test_connections.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# coding: utf-8
import asyncio
import pytest
import asyncio
import struct

from asyncua import Client, Server
from asyncua import Client, Server, ua
from asyncua.ua.uaerrors import BadMaxConnectionsReached

from .conftest import port_num, find_free_port

pytestmark = pytest.mark.asyncio
Expand All @@ -26,6 +27,21 @@ async def test_max_connections_1(opc):
opc.server.iserver.isession.__class__.max_connections = 1000


async def test_dos_server(opc):
# See issue 1013 a crafted packet triggered dos
port = opc.server.endpoint.port
async with Client(f'opc.tcp://127.0.0.1:{port}') as c:
# craft invalid packet that trigger dos
message_type, chunk_type, packet_size = [ua.MessageType.SecureOpen, b'E', 0]
c.uaclient.protocol.transport.write(struct.pack("<3scI", message_type, chunk_type, packet_size))
# sleep to give the server time to handle the message because we bypass the asyncio
await asyncio.sleep(1.0)
with pytest.raises(ConnectionError):
# now try to read a value to see if server is still alive
server_time_node = c.get_node(ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime))
await server_time_node.read_value()


async def test_safe_disconnect():
c = Client(url="opc.tcp://example:4840")
await c.disconnect()
Expand Down

0 comments on commit f6603da

Please sign in to comment.