1 /* Simple sanity-check for D-Bus message serialization.
2 *
3 * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
4 * Copyright © 2010-2011 Nokia Corporation
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction,
9 * including without limitation the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27 #include <config.h>
28
29 #include <glib.h>
30
31 #include <dbus/dbus.h>
32 #include <dbus/dbus-glib-lowlevel.h>
33
34 typedef struct {
35 DBusError e;
36 } Fixture;
37
38 static void
assert_no_error(const DBusError * e)39 assert_no_error (const DBusError *e)
40 {
41 if (G_UNLIKELY (dbus_error_is_set (e)))
42 g_error ("expected success but got error: %s: %s", e->name, e->message);
43 }
44
45 static void
setup(Fixture * f,gconstpointer arg G_GNUC_UNUSED)46 setup (Fixture *f,
47 gconstpointer arg G_GNUC_UNUSED)
48 {
49 dbus_error_init (&f->e);
50 }
51
52 /* this is meant to be obviously correct, not efficient! */
53 static guint32
get_uint32(const gchar * blob,gsize offset,char endian)54 get_uint32 (const gchar *blob,
55 gsize offset,
56 char endian)
57 {
58 if (endian == 'l')
59 {
60 return
61 blob[offset] |
62 (blob[offset + 1] << 8) |
63 (blob[offset + 2] << 16) |
64 (blob[offset + 3] << 24);
65 }
66 else if (endian == 'B')
67 {
68 return
69 (blob[offset] << 24) |
70 (blob[offset + 1] << 16) |
71 (blob[offset + 2] << 8) |
72 blob[offset + 3];
73 }
74 else
75 {
76 g_assert_not_reached ();
77 }
78 }
79
80 #define BLOB_LENGTH (sizeof (le_blob) - 1)
81 #define OFFSET_BODY_LENGTH (4)
82 #define OFFSET_SERIAL (8)
83
84 const gchar le_blob[] =
85 /* byte 0 */
86 /* yyyyuu fixed headers */
87 "l" /* little-endian */
88 "\2" /* reply (which is the simplest message) */
89 "\2" /* no auto-starting */
90 "\1" /* D-Bus version = 1 */
91 /* byte 4 */
92 "\4\0\0\0" /* bytes in body = 4 */
93 /* byte 8 */
94 "\x78\x56\x34\x12" /* serial number = 0x12345678 */
95 /* byte 12 */
96 /* a(uv) variable headers start here */
97 "\x0f\0\0\0" /* bytes in array of variable headers = 15 */
98 /* pad to 8-byte boundary = nothing */
99 /* byte 16 */
100 "\5" /* in reply to: */
101 "\1u\0" /* variant signature = u */
102 /* pad to 4-byte boundary = nothing */
103 "\x12\xef\xcd\xab" /* 0xabcdef12 */
104 /* pad to 8-byte boundary = nothing */
105 /* byte 24 */
106 "\x08" /* signature: */
107 "\1g\0" /* variant signature = g */
108 "\1u\0" /* 1 byte, u, NUL (no alignment needed) */
109 "\0" /* pad to 8-byte boundary for body */
110 /* body; byte 32 */
111 "\xef\xbe\xad\xde" /* 0xdeadbeef */
112 ;
113
114 const gchar be_blob[] =
115 /* byte 0 */
116 /* yyyyuu fixed headers */
117 "B" /* big-endian */
118 "\2" /* reply (which is the simplest message) */
119 "\2" /* no auto-starting */
120 "\1" /* D-Bus version = 1 */
121 /* byte 4 */
122 "\0\0\0\4" /* bytes in body = 4 */
123 /* byte 8 */
124 "\x12\x34\x56\x78" /* serial number = 0x12345678 */
125 /* byte 12 */
126 /* a(uv) variable headers start here */
127 "\0\0\0\x0f" /* bytes in array of variable headers = 15 */
128 /* pad to 8-byte boundary = nothing */
129 /* byte 16 */
130 "\5" /* in reply to: */
131 "\1u\0" /* variant signature = u */
132 /* pad to 4-byte boundary = nothing */
133 "\xab\xcd\xef\x12" /* 0xabcdef12 */
134 /* pad to 8-byte boundary = nothing */
135 /* byte 24 */
136 "\x08" /* signature: */
137 "\1g\0" /* variant signature = g */
138 "\1u\0" /* 1 byte, u, NUL (no alignment needed) */
139 "\0" /* pad to 8-byte boundary for body */
140 /* body; byte 32 */
141 "\xde\xad\xbe\xef" /* 0xdeadbeef */
142 ;
143
144 static void
test_endian(Fixture * f,gconstpointer arg)145 test_endian (Fixture *f,
146 gconstpointer arg)
147 {
148 const gchar *blob = arg;
149 char *output;
150 DBusMessage *m;
151 int len;
152 dbus_uint32_t u;
153 dbus_bool_t ok;
154
155 g_assert_cmpuint ((guint) sizeof (le_blob), ==, (guint) sizeof (be_blob));
156
157 g_assert_cmpuint (get_uint32 (blob, OFFSET_BODY_LENGTH, blob[0]), ==, 4);
158 g_assert_cmpuint (get_uint32 (blob, OFFSET_SERIAL, blob[0]), ==,
159 0x12345678u);
160
161 len = dbus_message_demarshal_bytes_needed (blob, sizeof (le_blob));
162 /* everything in the string except the implicit "\0" at the end is part of
163 * the message */
164 g_assert_cmpint (len, ==, BLOB_LENGTH);
165
166 m = dbus_message_demarshal (blob, sizeof (le_blob), &f->e);
167 assert_no_error (&f->e);
168 g_assert (m != NULL);
169
170 g_assert_cmpuint (dbus_message_get_serial (m), ==, 0x12345678u);
171 g_assert_cmpuint (dbus_message_get_reply_serial (m), ==, 0xabcdef12u);
172 g_assert_cmpstr (dbus_message_get_signature (m), ==, "u");
173
174 /* Implementation detail: appending to the message results in it being
175 * byteswapped into compiler byte order, which exposed a bug in libdbus,
176 * fd.o #38120. (If that changes, this test might not exercise that
177 * particular bug but will still be valid.) */
178 u = 0xdecafbadu;
179 ok = dbus_message_append_args (m,
180 DBUS_TYPE_UINT32, &u,
181 DBUS_TYPE_INVALID);
182 g_assert (ok);
183
184 dbus_message_marshal (m, &output, &len);
185
186 g_assert (output[0] == 'l' || output[0] == 'B');
187 /* the single-byte fields are unaffected, even if the endianness was
188 * swapped */
189 g_assert_cmpint (output[1], ==, blob[1]);
190 g_assert_cmpint (output[2], ==, blob[2]);
191 g_assert_cmpint (output[3], ==, blob[3]);
192 /* the length and serial are in the new endianness, the length has expanded
193 * to 8, and the serial is correct */
194 g_assert_cmpuint (get_uint32 (output, OFFSET_BODY_LENGTH, output[0]), ==, 8);
195 g_assert_cmpuint (get_uint32 (output, OFFSET_SERIAL, output[0]), ==,
196 0x12345678u);
197 /* the second "u" in the signature replaced a padding byte, so only
198 * the length of the body changed */
199 g_assert_cmpint (len, ==, BLOB_LENGTH + 4);
200 }
201
202 static void
test_needed(Fixture * f,gconstpointer arg)203 test_needed (Fixture *f,
204 gconstpointer arg)
205 {
206 const gchar *blob = arg;
207
208 /* We need at least 16 bytes to know how long the message is - that's just
209 * a fact of the D-Bus protocol. */
210 g_assert_cmpint (
211 dbus_message_demarshal_bytes_needed (blob, 0), ==, 0);
212 g_assert_cmpint (
213 dbus_message_demarshal_bytes_needed (blob, 15), ==, 0);
214 /* This is enough that we should be able to tell how much we need. */
215 g_assert_cmpint (
216 dbus_message_demarshal_bytes_needed (blob, 16), ==, BLOB_LENGTH);
217 /* The header is 32 bytes long (here), so that's another interesting
218 * boundary. */
219 g_assert_cmpint (
220 dbus_message_demarshal_bytes_needed (blob, 31), ==, BLOB_LENGTH);
221 g_assert_cmpint (
222 dbus_message_demarshal_bytes_needed (blob, 32), ==, BLOB_LENGTH);
223 g_assert_cmpint (
224 dbus_message_demarshal_bytes_needed (blob, 33), ==, BLOB_LENGTH);
225 g_assert_cmpint (
226 dbus_message_demarshal_bytes_needed (blob, BLOB_LENGTH - 1), ==,
227 BLOB_LENGTH);
228 g_assert_cmpint (
229 dbus_message_demarshal_bytes_needed (blob, BLOB_LENGTH), ==,
230 BLOB_LENGTH);
231 g_assert_cmpint (
232 dbus_message_demarshal_bytes_needed (blob, sizeof (be_blob)), ==,
233 BLOB_LENGTH);
234 }
235
236 static void
teardown(Fixture * f,gconstpointer arg G_GNUC_UNUSED)237 teardown (Fixture *f,
238 gconstpointer arg G_GNUC_UNUSED)
239 {
240 dbus_error_free (&f->e);
241 }
242
243 int
main(int argc,char ** argv)244 main (int argc,
245 char **argv)
246 {
247 g_test_init (&argc, &argv, NULL);
248
249 g_test_add ("/demarshal/le", Fixture, le_blob, setup, test_endian, teardown);
250 g_test_add ("/demarshal/be", Fixture, be_blob, setup, test_endian, teardown);
251 g_test_add ("/demarshal/needed/le", Fixture, le_blob, setup, test_needed,
252 teardown);
253 g_test_add ("/demarshal/needed/be", Fixture, be_blob, setup, test_needed,
254 teardown);
255
256 return g_test_run ();
257 }
258