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
34typedef struct {
35    DBusError e;
36} Fixture;
37
38static void
39assert_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
45static void
46setup (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! */
53static guint32
54get_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
84const 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
114const 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
144static void
145test_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
202static void
203test_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
236static void
237teardown (Fixture *f,
238    gconstpointer arg G_GNUC_UNUSED)
239{
240  dbus_error_free (&f->e);
241}
242
243int
244main (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