events: fix parsing events with empty parameters

Empty parameter value is encoded as b'parameter\0\0', so we can't simply
read the data until b'\0\0', because it isn't necessary event end.
Instead, read event parts separately, according to specification.
This commit is contained in:
Marek Marczykowski-Górecki 2017-05-30 01:31:13 +02:00
parent 96b27fdf14
commit 065eb036df
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
2 changed files with 24 additions and 15 deletions

View File

@ -147,25 +147,32 @@ class EventsDispatcher(object):
some_event_received = False some_event_received = False
while not reader.at_eof(): while not reader.at_eof():
try: try:
event_data = yield from reader.readuntil(b'\0\0') event_header = yield from reader.readuntil(b'\0')
if event_data == b'1\0\0': if event_header != b'1\0':
# event with non-VM subject contains \0\0 inside of raise qubesadmin.exc.QubesDaemonCommunicationError(
# event, need to receive rest of the data 'Non-event received on events connection: '
event_data += yield from reader.readuntil(b'\0\0') + repr(event_header))
subject = (yield from reader.readuntil(b'\0'))[:-1].decode(
'utf-8')
event = (yield from reader.readuntil(b'\0'))[:-1].decode(
'utf-8')
kwargs = {}
while True:
key = (yield from reader.readuntil(b'\0'))[:-1].decode(
'utf-8')
if not key:
break
value = (yield from reader.readuntil(b'\0'))[:-1].\
decode('utf-8')
kwargs[key] = value
except asyncio.IncompleteReadError as err: except asyncio.IncompleteReadError as err:
if err.partial == b'': if err.partial == b'':
break break
else: else:
raise raise
if not event_data.startswith(b'1\0'): if not subject:
raise qubesadmin.exc.QubesDaemonCommunicationError( subject = None
'Non-event received on events connection: '
+ repr(event_data))
event_data = event_data.decode('utf-8')
_, subject, event, *kwargs = event_data.split('\0')
# convert list to dict, remove last empty entry
kwargs = dict(zip(kwargs[:-2:2], kwargs[1:-2:2]))
self.handle(subject, event, **kwargs) self.handle(subject, event, **kwargs)
some_event_received = True some_event_received = True

View File

@ -107,7 +107,7 @@ class TC_00_Events(qubesadmin.tests.QubesTestCase):
events = [ events = [
b'1\0\0some-event\0arg1\0value1\0\0', b'1\0\0some-event\0arg1\0value1\0\0',
b'1\0some-vm\0some-event\0arg1\0value1\0\0', b'1\0some-vm\0some-event\0arg1\0value1\0\0',
b'1\0some-vm\0some-event\0\0', b'1\0some-vm\0some-event\0arg_without_value\0\0arg2\0value\0\0',
b'1\0some-vm\0other-event\0\0', b'1\0some-vm\0other-event\0\0',
] ]
asyncio.ensure_future(self.send_events(stream, events)) asyncio.ensure_future(self.send_events(stream, events))
@ -117,7 +117,9 @@ class TC_00_Events(qubesadmin.tests.QubesTestCase):
unittest.mock.call(None, 'some-event', arg1='value1'), unittest.mock.call(None, 'some-event', arg1='value1'),
unittest.mock.call( unittest.mock.call(
self.app.domains['some-vm'], 'some-event', arg1='value1'), self.app.domains['some-vm'], 'some-event', arg1='value1'),
unittest.mock.call(self.app.domains['some-vm'], 'some-event'), unittest.mock.call(
self.app.domains['some-vm'], 'some-event',
arg_without_value='', arg2='value'),
]) ])
cleanup_func.assert_called_once_with() cleanup_func.assert_called_once_with()
loop.close() loop.close()