D-Bus server: fix support for WBXML

Binary data cannot be sent via D-Bus as string, because D-Bus will
balk on non-UTF8 sequences of bytes. GIO D-Bus failed particularly
spectacularly, with internal asserts followed by a segfault.

This affected sending the initial message as part of SyncParams and
thus all testing with syncevo-http-server. D-Bus testing itself
didn't have a Connection test involving WBXML.

Fixed by using SharedBuffer inside SyncParams with D-Bus traits based
on DBusArray. DBusArray itself is less suitable because it cannot be
copied easily inside the app. Also added a test, with a b64 encoded
binary message inside the Python script.
This commit is contained in:
Patrick Ohly 2012-03-30 10:33:37 +02:00
parent 01882e39f9
commit 6d8d2b4db2
5 changed files with 70 additions and 6 deletions

View File

@ -39,7 +39,7 @@ DBusSync::DBusSync(const SessionCommon::SyncParams &params,
setServerAlerted(params.m_serverAlerted);
if (params.m_serverMode) {
initServer(params.m_sessionID,
SharedBuffer(params.m_initialMessage.c_str(), params.m_initialMessage.size()),
params.m_initialMessage,
params.m_initialMessageType);
}
@ -116,7 +116,7 @@ boost::shared_ptr<TransportAgent> DBusSync::createTransportAgent()
} else if (m_params.m_serverMode) {
// Let transport return initial message to engine.
agent->storeMessage(GDBusCXX::DBusArray<uint8_t>(m_params.m_initialMessage.size(),
reinterpret_cast<const uint8_t *>(m_params.m_initialMessage.c_str())),
reinterpret_cast<const uint8_t *>(m_params.m_initialMessage.get())),
m_params.m_initialMessageType);
}

View File

@ -25,6 +25,9 @@
#include <syncevo/util.h>
#include <syncevo/DBusTraits.h>
#include <syncevo/FilterConfigNode.h>
#include <syncevo/SynthesisEngine.h>
#include <gdbus-cxx-bridge.h>
SE_BEGIN_CXX
@ -119,7 +122,7 @@ namespace SessionCommon
bool m_serverAlerted;
bool m_remoteInitiated;
std::string m_sessionID;
std::string m_initialMessage;
SharedBuffer m_initialMessage;
std::string m_initialMessageType;
SyncEvo::FilterConfigNode::ConfigFilter m_syncFilter;
SyncEvo::FilterConfigNode::ConfigFilter m_sourceFilter;
@ -141,13 +144,47 @@ namespace GDBusCXX {
dbus_member<SyncParams, bool, &SyncParams::m_serverAlerted,
dbus_member<SyncParams, bool, &SyncParams::m_remoteInitiated,
dbus_member<SyncParams, std::string, &SyncParams::m_sessionID,
dbus_member<SyncParams, std::string, &SyncParams::m_initialMessage,
dbus_member<SyncParams, SharedBuffer, &SyncParams::m_initialMessage,
dbus_member<SyncParams, std::string, &SyncParams::m_initialMessageType,
dbus_member<SyncParams, FilterConfigNode::ConfigFilter, &SyncParams::m_syncFilter,
dbus_member<SyncParams, FilterConfigNode::ConfigFilter, &SyncParams::m_sourceFilter,
dbus_member_single<SyncParams, SourceFilters_t, &SyncParams::m_sourceFilters
> > > > > > > > > > > > >
{};
/**
* Similar to DBusArray<uint8_t>, but with different native
* types. Uses encoding/decoding from the base class, copies
* to/from SharedBuffer as needed.
*
* DBusArray<uint8_t> is more efficient because it avoids
* copying the bytes from the D-Bus message when decoding,
* but it is harder to use natively (cannot be copied).
* SharedBuffer does ref counting for the memory chunk,
* so once initialized, copying it is cheap.
*/
template <> struct dbus_traits<SharedBuffer> :
public dbus_traits< DBusArray<uint8_t> >
{
typedef dbus_traits< DBusArray<uint8_t> > base;
typedef SharedBuffer host_type;
typedef const SharedBuffer &arg_type;
static void get(GDBusCXX::connection_type *conn, GDBusCXX::message_type *msg,
GDBusCXX::reader_type &iter, host_type &buffer)
{
base::host_type array;
base::get(conn, msg, iter, array);
buffer = SharedBuffer(reinterpret_cast<const char *>(array.second), array.first);
}
static void append(GDBusCXX::builder_type &builder, arg_type buffer)
{
base::host_type array(buffer.size(), reinterpret_cast<const uint8_t *>(buffer.get()));
base::append(builder, array);
}
};
}
#endif // SESSION_COMMON_H

View File

@ -360,7 +360,7 @@ void Session::sync2(const std::string &mode, const SessionCommon::SourceModes_t
params.m_serverAlerted = m_serverAlerted;
params.m_remoteInitiated = m_remoteInitiated;
params.m_sessionID = m_sessionID;
params.m_initialMessage.assign(m_initialMessage.get(), m_initialMessage.size());
params.m_initialMessage = m_initialMessage;
params.m_initialMessageType = m_initialMessageType;
params.m_syncFilter = m_syncFilter;
params.m_sourceFilter = m_sourceFilter;

View File

@ -70,7 +70,7 @@ class SharedBuffer : public boost::shared_array<char>
m_size(size)
{ memcpy(get(), p, size); }
size_t size() { return m_size; }
size_t size() const { return m_size; }
};
/**

View File

@ -38,6 +38,7 @@ import sys
import traceback
import re
import atexit
import base64
# introduced in python-gobject 2.16, not available
# on all Linux distros => make it optional
@ -2736,6 +2737,9 @@ class TestConnection(unittest.TestCase, DBusUtil):
"""a real message sent to our own server, DevInf stripped, username/password foo/bar"""
message1 = '''<?xml version="1.0" encoding="UTF-8"?><SyncML xmlns='SYNCML:SYNCML1.2'><SyncHdr><VerDTD>1.2</VerDTD><VerProto>SyncML/1.2</VerProto><SessionID>255</SessionID><MsgID>1</MsgID><Target><LocURI>http://127.0.0.1:9000/syncevolution</LocURI></Target><Source><LocURI>sc-api-nat</LocURI><LocName>test</LocName></Source><Cred><Meta><Format xmlns='syncml:metinf'>b64</Format><Type xmlns='syncml:metinf'>syncml:auth-md5</Type></Meta><Data>kHzMn3RWFGWSKeBpXicppQ==</Data></Cred><Meta><MaxMsgSize xmlns='syncml:metinf'>20000</MaxMsgSize><MaxObjSize xmlns='syncml:metinf'>4000000</MaxObjSize></Meta></SyncHdr><SyncBody><Alert><CmdID>1</CmdID><Data>200</Data><Item><Target><LocURI>addressbook</LocURI></Target><Source><LocURI>./addressbook</LocURI></Source><Meta><Anchor xmlns='syncml:metinf'><Last>20091105T092757Z</Last><Next>20091105T092831Z</Next></Anchor><MaxObjSize xmlns='syncml:metinf'>4000000</MaxObjSize></Meta></Item></Alert><Final/></SyncBody></SyncML>'''
"""a real WBXML message, expected to trigger an authentication failure"""
message1WBXML = base64.b64decode('AqQBagBtbHEDMS4yAAFyA1N5bmNNTC8xLjIAAWUDMjExAAFbAzEAAW5XA2h0dHA6Ly9teS5mdW5hbWJvbC5jb20vc3luYwABAWdXA3NjLWFwaS1uYXQAAVYDcGF0cmljay5vaGx5AAEBTloAAUcDYjY0AAFTA3N5bmNtbDphdXRoLW1kNQABAQAATwNyT0dFbGR1Y2FjNE5mc3dZSm5lR2lnPT0AAQFaAAFMAzE1MDAwMAABVQM0MDAwMDAwAAEBAQAAa19LAzEAAVoAAVMDYXBwbGljYXRpb24vdm5kLnN5bmNtbC1kZXZpbmYrd2J4bWwAAQEAAFRnVwMuL2RldmluZjEyAAEBT8OMewKkA2oASmUDMS4yAAFRA1BhdHJpY2sgT2hseQABVQNTeW5jRXZvbHV0aW9uAAFWA1N5bnRoZXNpcyBBRwABTwMxLjIuOTkrMjAxMjAzMjcrU0UrMjI1MGVhMCt1bmNsZWFuAAFeAzMuNC4wLjM1AAFQA3Vua25vd24AAUkDc2MtYXBpLW5hdAABSwN3b3Jrc3RhdGlvbgABKCkqR10DLi9lZHNfZXZlbnQAAUwDZWRzX2V2ZW50AAFSAzY0AAFaRgN0ZXh0L2NhbGVuZGFyAAFkAzIuMAABAWJGA3RleHQvY2FsZW5kYXIAAWQDMi4wAAEBRUYDdGV4dC9jYWxlbmRhcgABZAMyLjAAAWtYA0JFR0lOAAFjA1ZDQUxFTkRBUgABYwNWVElNRVpPTkUAAWMDU1RBTkRBUkQAAWMDREFZTElHSFQAAWMDVlRPRE8AAWMDVkFMQVJNAAFjA1ZFVkVOVAABYwNWQUxBUk0AAQFrWANFTkQAAWMDVkNBTEVOREFSAAFjA1ZUSU1FWk9ORQABYwNTVEFOREFSRAABYwNEQVlMSUdIVAABYwNWVE9ETwABYwNWQUxBUk0AAWMDVkVWRU5UAAFjA1ZBTEFSTQABAWtYA1ZFUlNJT04AAWMDMi4wAAFtAzEAAQFrWANQUk9ESUQAAW0DMQABAWtYA1RaSUQAAQFrWANEVFNUQVJUAAEBa1gDUlJVTEUAAQFrWANUWk9GRlNFVEZST00AAQFrWANUWk9GRlNFVFRPAAEBa1gDVFpOQU1FAAEBa1gDTEFTVC1NT0RJRklFRAABbQMxAAEBa1gDRFRTVEFNUAABbQMxAAEBa1gDQ1JFQVRFRAABbQMxAAEBa1gDVUlEAAFtAzEAAQFrWANTRVFVRU5DRQABbQMxAAEBa1gDR0VPAAFtAzEAAQFrWANDQVRFR09SSUVTAAEBa1gDQ0xBU1MAAW0DMQABAWtYA1NVTU1BUlkAAW0DMQABAWtYA0RFU0NSSVBUSU9OAAFtAzEAAQFrWANMT0NBVElPTgABbQMxAAEBa1gDVVJMAAFtAzEAAQFrWANDT01QTEVURUQAAW0DMQABbFcDVFpJRAABAWxXA1ZBTFVFAAEBAWtYA0RVRQABbQMxAAFsVwNUWklEAAEBbFcDVkFMVUUAAQEBa1gDUFJJT1JJVFkAAW0DMQABAWtYA1NUQVRVUwABbQMxAAEBa1gDUEVSQ0VOVC1DT01QTEVURQABbQMxAAEBa1gDUkVMQVRFRC1UTwABbQMxAAFsVwNSRUxUWVBFAAFjA1BBUkVOVAABAQFrWANUUklHR0VSAAFtAzEAAWxXA1ZBTFVFAAEBbFcDUkVMQVRFRAABYwNTVEFSVAABYwNFTkQAAQEBa1gDQUNUSU9OAAFtAzEAAQFrWANSRVBFQVQAAW0DMQABAWtYA1gtRVZPTFVUSU9OLUFMQVJNLVVJRAABbQMxAAEBa1gDVFoAAW0DMQABAWtYA1RSQU5TUAABbQMxAAEBa1gDUkVDVVJSRU5DRS1JRAABbQMxAAFsVwNUWklEAAEBbFcDVkFMVUUAAQEBa1gDRVhEQVRFAAEBa1gDWC1TWU5DRVZPTFVUSU9OLUVYREFURS1ERVRBQ0hFRAABbFcDVFpJRAABAQFrWANEVEVORAABbQMxAAFsVwNUWklEAAEBbFcDVkFMVUUAAQEBa1gDRFVSQVRJT04AAW0DMQABAWtYA0FUVEVOREVFAAFsVwNDTgABAWxXA1BBUlRTVEFUAAFjA05FRURTLUFDVElPTgABYwNBQ0NFUFRFRAABYwNERUNMSU5FRAABYwNURU5UQVRJVkUAAWMDREVMRUdBVEVEAAEBbFcDUk9MRQABYwNDSEFJUgABYwNSRVEtUEFSVElDSVBBTlQAAWMDT1BULVBBUlRJQ0lQQU5UAAFjA05PTi1QQVJUSUNJUEFOVAABAWxXA1JTVlAAAWMDVFJVRQABYwNGQUxTRQABAWxXA0xBTkdVQUdFAAEBbFcDQ1VUWVBFAAFjA0lORElWSURVQUwAAWMDR1JPVVAAAWMDUkVTT1VSQ0UAAWMDUk9PTQABYwNVTktOT1dOAAEBAWtYA09SR0FOSVpFUgABbQMxAAFsVwNDTgABAQEBX2ADMQABYAMyAAFgAzMAAWADNAABYAM1AAFgAzYAAWADNwABYAM1NDQwMDEAAQEBAQEBAVNLAzIAAVoAAVMDYXBwbGljYXRpb24vdm5kLnN5bmNtbC1kZXZpbmYrd2J4bWwAAQEAAFRuVwMuL2RldmluZjEyAAEBAQFGSwMzAAFPAzIwMAABVG5XA2V2ZW50AAEBZ1cDLi9lZHNfZXZlbnQAAQFaAAFFSgMyMDEyMDMyOVQxMTAxMjZaAAFPAzIwMTIwMzI5VDExMDE1MloAAQFVAzQwMDAwMDAAAQEBAQAAEgEB')
def setUp(self):
self.setUpServer()
self.setUpListeners(None)
@ -2889,6 +2893,29 @@ class TestConnection(unittest.TestCase, DBusUtil):
self.assertEqual(DBusUtil.quit_events, ["connection " + conpath + " aborted",
"session done"])
@timeout(60)
def testCredentialsWrongWBXML(self):
"""TestConnection.testCredentialsWrongWBXML - send invalid credentials using WBXML"""
self.setupConfig()
conpath, connection = self.getConnection(must_authenticate=True)
connection.Process(TestConnection.message1WBXML, 'application/vnd.syncml+wbxml')
loop.run()
self.assertEqual(DBusUtil.quit_events, ["connection " + conpath + " got reply"])
DBusUtil.quit_events = []
# TODO: check events
self.assertNotEqual(DBusUtil.reply, None)
self.assertEqual(DBusUtil.reply[1], 'application/vnd.syncml+wbxml')
# Credentials should have been rejected because of wrong Nonce.
# Impossible to check with WBXML...
# self.assertTrue('<Chal>' in DBusUtil.reply[0])
self.assertEqual(DBusUtil.reply[3], False)
self.assertNotEqual(DBusUtil.reply[4], '')
loop.run()
loop.run()
DBusUtil.quit_events.sort()
self.assertEqual(DBusUtil.quit_events, ["connection " + conpath + " aborted",
"session done"])
@timeout(60)
def testCredentialsRight(self):
"""TestConnection.testCredentialsRight - send correct credentials"""