/src/dbus-broker/src/dbus/message.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * D-Bus Messages |
3 | | * |
4 | | * This encapsulates incoming and outgoing D-Bus messages. This is used to hold the |
5 | | * message data, the attached FDs and optional the cached metadata. |
6 | | */ |
7 | | |
8 | | #include <c-dvar.h> |
9 | | #include <c-dvar-type.h> |
10 | | #include <c-stdaux.h> |
11 | | #include <endian.h> |
12 | | #include <stdlib.h> |
13 | | #include "dbus/message.h" |
14 | | #include "dbus/protocol.h" |
15 | | #include "util/error.h" |
16 | | #include "util/fdlist.h" |
17 | | #include "util/log.h" |
18 | | |
19 | | static_assert(_DBUS_MESSAGE_FIELD_N <= 8 * sizeof(unsigned int), "Header fields exceed bitmap"); |
20 | | |
21 | 2.98k | static int message_new(Message **messagep, bool big_endian, size_t n_extra) { |
22 | 2.98k | _c_cleanup_(message_unrefp) Message *message = NULL; |
23 | | |
24 | 2.98k | static_assert(alignof(message->extra) >= 8, |
25 | 2.98k | "Message payload has insufficient alignment"); |
26 | | |
27 | 2.98k | message = malloc(sizeof(*message) + c_align_to(n_extra, 8)); |
28 | 2.98k | if (!message) |
29 | 0 | return error_origin(-ENOMEM); |
30 | | |
31 | 2.98k | *message = (Message)MESSAGE_INIT(big_endian); |
32 | | |
33 | 2.98k | *messagep = message; |
34 | 2.98k | message = NULL; |
35 | 2.98k | return 0; |
36 | 2.98k | } |
37 | | |
38 | | /** |
39 | | * message_new_incoming() - create new incoming message object |
40 | | * @messagep: output pointer to new message object |
41 | | * @header: header of new message |
42 | | * |
43 | | * This creates a new message object in @messagep, to hold an incoming message with |
44 | | * header @header. Only the header is initialized, the backing memory for the message |
45 | | * payload is allocated, but not yet initialized. |
46 | | * |
47 | | * Return: 0 on success, MESSAGE_E_CORRUPT_HEADER if unknown endianness, |
48 | | * MESSAGE_E_TOO_LARGE if the declared message size violates the spec, |
49 | | * or a negative error code on failure. |
50 | | */ |
51 | 3.01k | int message_new_incoming(Message **messagep, MessageHeader header) { |
52 | 3.01k | _c_cleanup_(message_unrefp) Message *message = NULL; |
53 | 3.01k | uint64_t n_header, n_body, n_data; |
54 | 3.01k | int r; |
55 | | |
56 | 3.01k | if (_c_likely_(header.endian == 'l')) { |
57 | 2.05k | n_header = sizeof(header) + (uint64_t)le32toh(header.n_fields); |
58 | 2.05k | n_body = (uint64_t)le32toh(header.n_body); |
59 | 2.05k | } else if (header.endian == 'B') { |
60 | 937 | n_header = sizeof(header) + (uint64_t)be32toh(header.n_fields); |
61 | 937 | n_body = (uint64_t)be32toh(header.n_body); |
62 | 937 | } else { |
63 | 21 | return MESSAGE_E_CORRUPT_HEADER; |
64 | 21 | } |
65 | | |
66 | 2.99k | n_data = c_align_to(n_header, 8) + n_body; |
67 | 2.99k | if (n_data > MESSAGE_SIZE_MAX) |
68 | 14 | return MESSAGE_E_TOO_LARGE; |
69 | | |
70 | 2.98k | r = message_new(&message, (header.endian == 'B'), n_data); |
71 | 2.98k | if (r) |
72 | 0 | return error_trace(r); |
73 | | |
74 | 2.98k | message->n_data = n_data; |
75 | 2.98k | message->n_header = n_header; |
76 | 2.98k | message->n_body = n_body; |
77 | 2.98k | message->data = message->extra; |
78 | 2.98k | message->header = (void *)message->data; |
79 | 2.98k | message->body = message->data + c_align_to(n_header, 8); |
80 | 2.98k | message->vecs[0] = (struct iovec){ message->header, c_align_to(n_header, 8) }; |
81 | 2.98k | message->vecs[1] = (struct iovec){ NULL, 0 }; |
82 | 2.98k | message->vecs[2] = (struct iovec){ NULL, 0 }; |
83 | 2.98k | message->vecs[3] = (struct iovec){ message->body, n_body }; |
84 | | |
85 | 2.98k | message->n_copied += sizeof(header); |
86 | 2.98k | c_memcpy(message->data, &header, sizeof(header)); |
87 | | |
88 | 2.98k | *messagep = message; |
89 | 2.98k | message = NULL; |
90 | 2.98k | return 0; |
91 | 2.98k | } |
92 | | |
93 | | /** |
94 | | * message_new_outgoing() - create a new outgoing message object |
95 | | * @messagep: return pointer to new message object |
96 | | * @data: the message contents |
97 | | * @n_data: the size of the message contents |
98 | | * |
99 | | * The consumes the provided @data, which must be a valid D-Bus message and |
100 | | * creates an outgoing message representing it. |
101 | | * |
102 | | * Return: 0 on success, or a negative error code on failure. |
103 | | */ |
104 | 0 | int message_new_outgoing(Message **messagep, void *data, size_t n_data) { |
105 | 0 | _c_cleanup_(message_unrefp) Message *message = NULL; |
106 | 0 | MessageHeader *header = data; |
107 | 0 | uint64_t n_header, n_body; |
108 | 0 | int r; |
109 | |
|
110 | 0 | c_assert(n_data >= sizeof(MessageHeader)); |
111 | 0 | c_assert(!((unsigned long)data & 0x7)); |
112 | 0 | c_assert((header->endian == 'B') == (__BYTE_ORDER == __BIG_ENDIAN) && |
113 | 0 | (header->endian == 'l') == (__BYTE_ORDER == __LITTLE_ENDIAN)); |
114 | 0 | c_assert(n_data >= sizeof(MessageHeader) + c_align_to(header->n_fields, 8)); |
115 | | |
116 | 0 | n_header = sizeof(MessageHeader) + header->n_fields; |
117 | 0 | n_body = n_data - c_align_to(n_header, 8); |
118 | |
|
119 | 0 | header->n_body = n_data - sizeof(MessageHeader) - c_align_to(header->n_fields, 8); |
120 | |
|
121 | 0 | r = message_new(&message, (header->endian == 'B'), 0); |
122 | 0 | if (r) |
123 | 0 | return error_trace(r); |
124 | | |
125 | 0 | message->allocated_data = true; |
126 | 0 | message->n_data = n_data; |
127 | 0 | message->n_header = n_header; |
128 | 0 | message->n_body = n_body; |
129 | 0 | message->data = data; |
130 | 0 | message->header = (void *)message->data; |
131 | 0 | message->body = message->data + c_align_to(n_header, 8); |
132 | 0 | message->vecs[0] = (struct iovec){ message->header, c_align_to(n_header, 8) }; |
133 | 0 | message->vecs[1] = (struct iovec){ NULL, 0 }; |
134 | 0 | message->vecs[2] = (struct iovec){ NULL, 0 }; |
135 | 0 | message->vecs[3] = (struct iovec){ message->body, n_body }; |
136 | |
|
137 | 0 | *messagep = message; |
138 | 0 | message = NULL; |
139 | 0 | return 0; |
140 | 0 | } |
141 | | |
142 | | /* internal callback for message_unref() */ |
143 | 2.98k | void message_free(_Atomic unsigned long *n_refs, void *userdata) { |
144 | 2.98k | Message *message = c_container_of(n_refs, Message, n_refs); |
145 | | |
146 | 2.98k | if (message->allocated_data) |
147 | 0 | free(message->data); |
148 | 2.98k | fdlist_free(message->fds); |
149 | 2.98k | free(message); |
150 | 2.98k | } |
151 | | |
152 | 2.91k | static int message_parse_header(Message *message, MessageMetadata *metadata) { |
153 | 2.91k | static const CDVarType type[] = { |
154 | 2.91k | C_DVAR_T_INIT( |
155 | 2.91k | C_DVAR_T_TUPLE7( |
156 | 2.91k | C_DVAR_T_y, |
157 | 2.91k | C_DVAR_T_y, |
158 | 2.91k | C_DVAR_T_y, |
159 | 2.91k | C_DVAR_T_y, |
160 | 2.91k | C_DVAR_T_u, |
161 | 2.91k | C_DVAR_T_u, |
162 | 2.91k | C_DVAR_T_ARRAY( |
163 | 2.91k | C_DVAR_T_TUPLE2( |
164 | 2.91k | C_DVAR_T_y, |
165 | 2.91k | C_DVAR_T_v |
166 | 2.91k | ) |
167 | 2.91k | ) |
168 | 2.91k | ) |
169 | 2.91k | ), /* (yyyyuua(yv)) */ |
170 | 2.91k | }; |
171 | 2.91k | _c_cleanup_(c_dvar_deinit) CDVar v = C_DVAR_INIT; |
172 | 2.91k | unsigned int mask; |
173 | 2.91k | uint8_t field; |
174 | 2.91k | int r; |
175 | | |
176 | 2.91k | c_dvar_begin_read(&v, message->big_endian, type, 1, message->header, message->n_header); |
177 | | |
178 | | /* |
179 | | * Validate static header fields. Byte-order and body-length are part |
180 | | * of the stream-validation, and skipped here. The others are: |
181 | | * type: |
182 | | * Anything but "INVALID" is accepted. |
183 | | * flags: |
184 | | * Anything is accepted. |
185 | | * version: |
186 | | * Must be '1'. |
187 | | * serial: |
188 | | * Anything but 0 is accepted. |
189 | | */ |
190 | | |
191 | 2.91k | c_dvar_read(&v, "(yyyyuu[", |
192 | 2.91k | NULL, |
193 | 2.91k | &metadata->header.type, |
194 | 2.91k | &metadata->header.flags, |
195 | 2.91k | &metadata->header.version, |
196 | 2.91k | NULL, |
197 | 2.91k | &metadata->header.serial); |
198 | | |
199 | 2.91k | if (metadata->header.type == DBUS_MESSAGE_TYPE_INVALID) |
200 | 1 | return MESSAGE_E_INVALID_HEADER; |
201 | 2.91k | if (metadata->header.version != 1) |
202 | 9 | return MESSAGE_E_INVALID_HEADER; |
203 | 2.90k | if (!metadata->header.serial) |
204 | 2 | return MESSAGE_E_INVALID_HEADER; |
205 | | |
206 | | /* |
207 | | * Validate header fields one-by-one. We follow exactly what |
208 | | * dbus-daemon(1) does: |
209 | | * - Unknown fields are ignored |
210 | | * - Duplicates are rejected (except if they are unknown) |
211 | | * - Types must match expected types |
212 | | * |
213 | | * Additionally, each header field has some restrictions on its own |
214 | | * validity. |
215 | | */ |
216 | | |
217 | 8.75k | while (c_dvar_more(&v)) { |
218 | 6.80k | c_dvar_read(&v, "(y", &field); |
219 | | |
220 | 6.80k | if (field >= _DBUS_MESSAGE_FIELD_N) { |
221 | 5.01k | c_dvar_skip(&v, "*)"); |
222 | 5.01k | continue; |
223 | 5.01k | } |
224 | | |
225 | 1.79k | if (metadata->fields.available & (1U << field)) |
226 | 2 | return MESSAGE_E_INVALID_HEADER; |
227 | | |
228 | 1.79k | metadata->fields.available |= 1U << field; |
229 | | |
230 | 1.79k | switch (field) { |
231 | 111 | case DBUS_MESSAGE_FIELD_INVALID: |
232 | 111 | return MESSAGE_E_INVALID_HEADER; |
233 | | |
234 | 142 | case DBUS_MESSAGE_FIELD_PATH: |
235 | 142 | c_dvar_read(&v, "<o>)", c_dvar_type_o, &metadata->fields.path); |
236 | | |
237 | 142 | if (!strcmp(metadata->fields.path, "/org/freedesktop/DBus/Local")) |
238 | 1 | return MESSAGE_E_INVALID_HEADER; |
239 | | |
240 | 141 | break; |
241 | | |
242 | 319 | case DBUS_MESSAGE_FIELD_INTERFACE: |
243 | 319 | c_dvar_read(&v, "<s>)", c_dvar_type_s, &metadata->fields.interface); |
244 | | |
245 | 319 | if (!strcmp(metadata->fields.interface, "org.freedesktop.DBus.Local")) |
246 | 1 | return MESSAGE_E_INVALID_HEADER; |
247 | | |
248 | 318 | if (!dbus_validate_interface(metadata->fields.interface, strlen(metadata->fields.interface))) |
249 | 278 | return MESSAGE_E_INVALID_HEADER; |
250 | | |
251 | 40 | break; |
252 | | |
253 | 156 | case DBUS_MESSAGE_FIELD_MEMBER: |
254 | 156 | c_dvar_read(&v, "<s>)", c_dvar_type_s, &metadata->fields.member); |
255 | | |
256 | 156 | if (!dbus_validate_member(metadata->fields.member, strlen(metadata->fields.member))) |
257 | 143 | return MESSAGE_E_INVALID_HEADER; |
258 | | |
259 | 13 | break; |
260 | | |
261 | 150 | case DBUS_MESSAGE_FIELD_ERROR_NAME: |
262 | 150 | c_dvar_read(&v, "<s>)", c_dvar_type_s, &metadata->fields.error_name); |
263 | | |
264 | 150 | if (!dbus_validate_error_name(metadata->fields.error_name, strlen(metadata->fields.error_name))) |
265 | 146 | return MESSAGE_E_INVALID_HEADER; |
266 | | |
267 | 4 | break; |
268 | | |
269 | 53 | case DBUS_MESSAGE_FIELD_REPLY_SERIAL: |
270 | 53 | c_dvar_read(&v, "<u>)", c_dvar_type_u, &metadata->fields.reply_serial); |
271 | | |
272 | 53 | if (!metadata->fields.reply_serial) |
273 | 8 | return MESSAGE_E_INVALID_HEADER; |
274 | | |
275 | 45 | break; |
276 | | |
277 | 152 | case DBUS_MESSAGE_FIELD_DESTINATION: |
278 | 152 | c_dvar_read(&v, "<s>)", c_dvar_type_s, &metadata->fields.destination); |
279 | | |
280 | 152 | if (!dbus_validate_name(metadata->fields.destination, strlen(metadata->fields.destination))) |
281 | 143 | return MESSAGE_E_INVALID_HEADER; |
282 | | |
283 | 9 | break; |
284 | | |
285 | 95 | case DBUS_MESSAGE_FIELD_SENDER: |
286 | 95 | c_dvar_read(&v, "<s>)", c_dvar_type_s, &metadata->fields.sender); |
287 | | |
288 | 95 | if (!dbus_validate_name(metadata->fields.sender, strlen(metadata->fields.sender))) |
289 | 85 | return MESSAGE_E_INVALID_HEADER; |
290 | | |
291 | | /* cache sender in case it needs to be stitched out */ |
292 | 10 | message->original_sender = (void *)metadata->fields.sender; |
293 | 10 | break; |
294 | | |
295 | 574 | case DBUS_MESSAGE_FIELD_SIGNATURE: |
296 | 574 | c_dvar_read(&v, "<g>)", c_dvar_type_g, &metadata->fields.signature); |
297 | 574 | break; |
298 | | |
299 | 38 | case DBUS_MESSAGE_FIELD_UNIX_FDS: |
300 | 38 | c_dvar_read(&v, "<u>)", c_dvar_type_u, &metadata->fields.unix_fds); |
301 | | |
302 | 38 | if (metadata->fields.unix_fds > fdlist_count(message->fds)) |
303 | 32 | return MESSAGE_E_MISSING_FDS; |
304 | | |
305 | 6 | break; |
306 | | |
307 | 6 | default: |
308 | 0 | return error_origin(-ENOTRECOVERABLE); |
309 | 1.79k | } |
310 | 1.79k | } |
311 | | |
312 | | /* |
313 | | * Check mandatory fields. That is, depending on the message types, all |
314 | | * mandatory fields must be present. |
315 | | */ |
316 | | |
317 | 1.95k | switch (metadata->header.type) { |
318 | 61 | case DBUS_MESSAGE_TYPE_METHOD_CALL: |
319 | 61 | mask = (1U << DBUS_MESSAGE_FIELD_PATH) | |
320 | 61 | (1U << DBUS_MESSAGE_FIELD_MEMBER); |
321 | 61 | break; |
322 | 34 | case DBUS_MESSAGE_TYPE_METHOD_RETURN: |
323 | 34 | mask = (1U << DBUS_MESSAGE_FIELD_REPLY_SERIAL); |
324 | 34 | break; |
325 | 17 | case DBUS_MESSAGE_TYPE_ERROR: |
326 | 17 | mask = (1U << DBUS_MESSAGE_FIELD_ERROR_NAME) | |
327 | 17 | (1U << DBUS_MESSAGE_FIELD_REPLY_SERIAL); |
328 | 17 | break; |
329 | 14 | case DBUS_MESSAGE_TYPE_SIGNAL: |
330 | 14 | mask = (1U << DBUS_MESSAGE_FIELD_PATH) | |
331 | 14 | (1U << DBUS_MESSAGE_FIELD_INTERFACE) | |
332 | 14 | (1U << DBUS_MESSAGE_FIELD_MEMBER); |
333 | 14 | break; |
334 | 1.82k | default: |
335 | 1.82k | mask = 0; |
336 | 1.82k | break; |
337 | 1.95k | } |
338 | | |
339 | 1.95k | if ((metadata->fields.available & mask) != mask) |
340 | 126 | return MESSAGE_E_INVALID_HEADER; |
341 | | |
342 | | /* |
343 | | * Fix up the signature. The DBus spec states that missing signatures |
344 | | * should be treated as empty. |
345 | | */ |
346 | | |
347 | 1.82k | metadata->fields.signature = metadata->fields.signature ?: ""; |
348 | | |
349 | | /* |
350 | | * Finish the variant parser. If anything went wobbly in between, we |
351 | | * will be told here. |
352 | | */ |
353 | | |
354 | 1.82k | c_dvar_read(&v, "])"); |
355 | | |
356 | 1.26k | r = c_dvar_end_read(&v); |
357 | 1.26k | if (r > 0) |
358 | 923 | return MESSAGE_E_INVALID_HEADER; |
359 | 339 | else if (r) |
360 | 0 | return error_fold(r); |
361 | | |
362 | 339 | return 0; |
363 | 1.26k | } |
364 | | |
365 | 865 | static int message_parse_body(Message *message, MessageMetadata *metadata) { |
366 | 865 | _c_cleanup_(c_dvar_deinit) CDVar v = C_DVAR_INIT; |
367 | 865 | const char *signature = metadata->fields.signature; |
368 | 865 | size_t i, n_signature, n_types; |
369 | 865 | CDVarType *t, *types; |
370 | 865 | int r; |
371 | | |
372 | | /* |
373 | | * Parse body-signature into CDVarType array. We use a single array |
374 | | * with all the argument-types concatenated. |
375 | | */ |
376 | | |
377 | 865 | n_signature = strlen(signature); |
378 | 865 | c_assert(n_signature < 256); |
379 | 865 | types = alloca(n_signature * sizeof(CDVarType)); |
380 | 865 | n_types = 0; |
381 | | |
382 | 5.81k | for (i = 0; i < n_signature; i += types[i].length) { |
383 | 4.94k | t = types + i; |
384 | 4.94k | r = c_dvar_type_new_from_signature(&t, signature + i, n_signature - i); |
385 | 4.94k | if (r) |
386 | 0 | return r < 0 ? error_origin(r) : MESSAGE_E_INVALID_HEADER; |
387 | | |
388 | 4.94k | ++n_types; |
389 | 4.94k | } |
390 | | |
391 | | /* |
392 | | * Now that we know the argument types, use c_dvar_skip() to verify |
393 | | * them. While at it, cache all the string/path arguments, so the match |
394 | | * rule processing can access them directly. |
395 | | */ |
396 | | |
397 | 865 | c_dvar_begin_read(&v, message->big_endian, types, n_types, message->body, message->n_body); |
398 | | |
399 | 5.81k | for (i = 0, t = types; i < n_types; ++i, t += t->length) { |
400 | 4.94k | switch (t->element) { |
401 | 1.11k | case 's': |
402 | 1.78k | case 'o': |
403 | 1.78k | if (i < C_ARRAY_SIZE(metadata->args)) { |
404 | 1.30k | metadata->args[i].element = t->element; |
405 | 1.30k | c_dvar_read(&v, (char[2]){ t->element, 0 }, &metadata->args[i].value); |
406 | 1.30k | metadata->n_args = i + 1; |
407 | 1.30k | break; |
408 | 1.30k | } |
409 | | |
410 | | /* fallthrough */ |
411 | 3.64k | default: |
412 | 3.64k | c_dvar_skip(&v, "*"); |
413 | 3.64k | break; |
414 | 4.94k | } |
415 | 4.94k | } |
416 | | |
417 | 865 | r = c_dvar_end_read(&v); |
418 | 865 | if (r) |
419 | 497 | return r < 0 ? error_origin(r) : MESSAGE_E_INVALID_BODY; |
420 | | |
421 | 368 | return 0; |
422 | 865 | } |
423 | | |
424 | | /** |
425 | | * message_parse_metadata() - parse message metadata |
426 | | * @message: message to operate on |
427 | | * |
428 | | * This parses the message, verifies its complience to the spec, and caches its metadata. If |
429 | | * the message contains more FDs than expected, the excess ones are dropped, otherwise the |
430 | | * message object is not altered. |
431 | | * |
432 | | * This method is idempotent. |
433 | | * |
434 | | * Return: 0 on success, |
435 | | * MESSAGE_E_MISSING_FDS if the message contains fewer FDs than declared in the metadata, |
436 | | * MESSAGE_E_INVALID_HEADER if the header violates the spec in other ways, |
437 | | * MESSAGE_E_INVALID_BODY if the body could not be parsed. |
438 | | */ |
439 | 2.91k | int message_parse_metadata(Message *message) { |
440 | 2.91k | void *p; |
441 | 2.91k | int r; |
442 | | |
443 | 2.91k | if (message->parsed) |
444 | 0 | return 0; |
445 | | |
446 | | /* |
447 | | * As first step, parse the static header and the dynamic header |
448 | | * fields. Any error there is fatal. |
449 | | */ |
450 | 2.91k | r = message_parse_header(message, &message->metadata); |
451 | 2.91k | if (r) |
452 | 2.01k | return error_trace(r); |
453 | | |
454 | | /* |
455 | | * Validate the padding between the header and body. Those must be 0! |
456 | | * We usually wouldn't care but must be compatible to dbus-daemon(1), |
457 | | * so lets verify them. |
458 | | */ |
459 | 999 | for (p = (void *)message->header + message->n_header; p < message->body; ++p) |
460 | 134 | if (*(const uint8_t *)p) |
461 | 39 | return MESSAGE_E_INVALID_HEADER; |
462 | | |
463 | | /* |
464 | | * Now that the header is validated, we read through the message body. |
465 | | * Again, this is required for compatibility with dbus-daemon(1), but |
466 | | * also to fetch the arguments for match-filters used by broadcasts. |
467 | | */ |
468 | 865 | r = message_parse_body(message, &message->metadata); |
469 | 865 | if (r) |
470 | 497 | return error_trace(r); |
471 | | |
472 | | /* |
473 | | * dbus-daemon(1) only ever fetches the correct number of FDs from its |
474 | | * stream. This violates the D-Bus specification, which requires FDs to |
475 | | * be sent together with the message, and in a single hunk. Therefore, |
476 | | * we try to stick to dbus-daemon(1) behavior as close as possible, by |
477 | | * rejecting if the requested count exceeds the passed count. However, |
478 | | * we always discard any remaining FDs silently. |
479 | | */ |
480 | 368 | if (message->fds) |
481 | 0 | fdlist_truncate(message->fds, message->metadata.fields.unix_fds); |
482 | | |
483 | 368 | message->parsed = true; |
484 | 368 | return 0; |
485 | 865 | } |
486 | | |
487 | | /** |
488 | | * message_stitch_sender() - stitch in new sender field |
489 | | * @message: message to operate on |
490 | | * @sender_id: sender id to stitch in |
491 | | * |
492 | | * When the broker forwards messages, it needs to fill in the sender-field |
493 | | * reliably. Unfortunately, this requires modifying the fields-array of the |
494 | | * D-Bus header. Since we do not want to re-write the entire array, we allow |
495 | | * some stitching magic here to happen. |
496 | | * |
497 | | * This means, we use some nice properties of tuple-arrays in the D-Bus |
498 | | * marshalling (namely, they're 8-byte aligned, thus statically discoverable |
499 | | * when we know the offset), and simply cut out the existing sender field and |
500 | | * append a new one. |
501 | | * |
502 | | * This function must not be called more than once on any message (it will |
503 | | * throw a fatal error). Furthermore, this will cut the message in parts, such |
504 | | * that it is no longer readable linearly. However, none of the fields are |
505 | | * relocated nor overwritten. That is, any cached pointer stays valid, though |
506 | | * maybe no longer part of the actual message. |
507 | | */ |
508 | 368 | void message_stitch_sender(Message *message, uint64_t sender_id) { |
509 | 368 | size_t n, n_stitch, n_field, n_sender; |
510 | 368 | const char *sender; |
511 | 368 | void *end, *field; |
512 | | |
513 | | /* |
514 | | * Must not be called more than once. We reserve the 2 iovecs between |
515 | | * the original header and body to stitch the sender field. The caller |
516 | | * must have parsed the metadata before. |
517 | | */ |
518 | 368 | c_assert(message->parsed); |
519 | 368 | c_assert(!message->vecs[1].iov_base && !message->vecs[1].iov_len); |
520 | 368 | c_assert(!message->vecs[2].iov_base && !message->vecs[2].iov_len); |
521 | | |
522 | | /* |
523 | | * Convert the sender id to a unique name. This should never fail on |
524 | | * a valid sender id. |
525 | | */ |
526 | 368 | sender = address_to_string(&(Address)ADDRESS_INIT_ID(sender_id)); |
527 | 368 | message->metadata.sender_id = sender_id; |
528 | | |
529 | | /* |
530 | | * Calculate string, field, and buffer lengths. We need to possibly cut |
531 | | * out a `(yv)' and insert another one at the end. See the D-Bus |
532 | | * marshalling for details, but shortly this means: |
533 | | * |
534 | | * - Tuples are always 8-byte aligned. Hence, we can reliably |
535 | | * calculate field offsets. |
536 | | * |
537 | | * - A string-field needs `1 + 3 + 4 + n + 1' bytes: |
538 | | * |
539 | | * - length of 'y': 1 |
540 | | * - length of 'v': 3 + 4 + n + 1 |
541 | | * - type 'g' needs: |
542 | | * - size field byte: 1 |
543 | | * - type string 's': 1 |
544 | | * - zero termination: 1 |
545 | | * - sender string needs: |
546 | | * - alignment to 4: 0 |
547 | | * - size field int: 4 |
548 | | * - sender string: n |
549 | | * - zero termination: 1 |
550 | | */ |
551 | 368 | n_sender = strlen(sender); |
552 | 368 | n_field = 1 + 3 + 4 + n_sender + 1; |
553 | 368 | n_stitch = c_align_to(n_field, 8); |
554 | | |
555 | | /* |
556 | | * The patch buffer is pre-allocated. Verify its size is sufficient to |
557 | | * hold the stitched sender. |
558 | | */ |
559 | 368 | { |
560 | 368 | static_assert(1 + 3 + 4 + ADDRESS_ID_STRING_MAX + 1 <= sizeof(message->patch), |
561 | 368 | "Message patch buffer has insufficient size"); |
562 | 368 | static_assert(alignof(message->patch) >= 8, |
563 | 368 | "Message patch buffer has insufficient alignment"); |
564 | 368 | c_assert(n_stitch <= sizeof(message->patch)); |
565 | 368 | c_assert(n_sender <= ADDRESS_ID_STRING_MAX); |
566 | 368 | } |
567 | | |
568 | 368 | if (message->original_sender) { |
569 | | /* |
570 | | * If @message already has a sender field, we need to remove it |
571 | | * first, so we can append the correct sender. The message |
572 | | * parser cached the start of a possible sender field as |
573 | | * @message->original_sender (pointing to the start of the |
574 | | * sender string!). Hence, calculate the offset to its |
575 | | * surrounding field and cut it out. |
576 | | * See above for size-calculations of `(yv)' fields. |
577 | | */ |
578 | 6 | n = strlen(message->original_sender); |
579 | 6 | end = (void *)message->header + c_align_to(message->n_header, 8); |
580 | 6 | field = message->original_sender - (1 + 3 + 4); |
581 | | |
582 | 6 | c_assert(message->original_sender >= (void *)message->header); |
583 | 6 | c_assert(message->original_sender + n + 1 <= end); |
584 | | |
585 | | /* fold remaining fields into following vector */ |
586 | 6 | message->vecs[1].iov_base = field + c_align_to(1 + 3 + 4 + n + 1, 8); |
587 | 6 | message->vecs[1].iov_len = message->vecs[0].iov_len; |
588 | 6 | message->vecs[1].iov_len -= message->vecs[1].iov_base - message->vecs[0].iov_base; |
589 | | |
590 | | /* cut field from previous vector */ |
591 | 6 | message->vecs[0].iov_len = field - message->vecs[0].iov_base; |
592 | | |
593 | | /* |
594 | | * @message->n_header as well as @message->header->n_fields are |
595 | | * screwed here, but fixed up below. |
596 | | * |
597 | | * Note that we cannot fix them here, since we can only |
598 | | * calculate them if we actually append data. Otherwise, we |
599 | | * cannot know the length of the last field, and as such cannot |
600 | | * subtract the trailing padding. |
601 | | */ |
602 | 6 | } |
603 | | |
604 | | /* |
605 | | * Now that any possible sender field was cut out, we can append the |
606 | | * new sender field at the end. The 3rd iovec is reserved for that |
607 | | * purpose. |
608 | | */ |
609 | | |
610 | 368 | message->vecs[2].iov_base = message->patch; |
611 | 368 | message->vecs[2].iov_len = n_stitch; |
612 | | |
613 | | /* fill in `(yv)' with sender and padding */ |
614 | 368 | message->patch[0] = DBUS_MESSAGE_FIELD_SENDER; |
615 | 368 | message->patch[1] = 1; |
616 | 368 | message->patch[2] = 's'; |
617 | 368 | message->patch[3] = 0; |
618 | 368 | if (message->big_endian) |
619 | 97 | c_memcpy(message->patch + 4, (uint32_t[1]){ htobe32(n_sender) }, sizeof(uint32_t)); |
620 | 271 | else |
621 | 271 | c_memcpy(message->patch + 4, (uint32_t[1]){ htole32(n_sender) }, sizeof(uint32_t)); |
622 | 368 | c_memcpy(message->patch + 8, sender, n_sender + 1); |
623 | 368 | c_memset(message->patch + 8 + n_sender + 1, 0, n_stitch - n_field); |
624 | | |
625 | | /* |
626 | | * After we cut the previous sender field and inserted the new, adjust |
627 | | * all the size-counters in the message again. |
628 | | */ |
629 | | |
630 | 368 | message->n_header = message->vecs[0].iov_len + |
631 | 368 | message->vecs[1].iov_len + |
632 | 368 | n_field; |
633 | 368 | message->n_data = c_align_to(message->n_header, 8) + message->n_body; |
634 | | |
635 | 368 | if (message->big_endian) |
636 | 97 | message->header->n_fields = htobe32(message->n_header - sizeof(*message->header)); |
637 | 271 | else |
638 | 271 | message->header->n_fields = htole32(message->n_header - sizeof(*message->header)); |
639 | 368 | } |
640 | | |
641 | | /** |
642 | | * message_log_append() - append message metadata to the log |
643 | | * @message: message to operate on |
644 | | * @log: log to append to |
645 | | * |
646 | | * This appends the metadata of @message to the next log message written |
647 | | * to @log. |
648 | | */ |
649 | 0 | void message_log_append(Message *message, Log *log) { |
650 | 0 | log_appendf(log, |
651 | 0 | "DBUS_BROKER_MESSAGE_DESTINATION=%s\n" |
652 | 0 | "DBUS_BROKER_MESSAGE_SERIAL=%"PRIu32"\n" |
653 | 0 | "DBUS_BROKER_MESSAGE_SIGNATURE=%s\n" |
654 | 0 | "DBUS_BROKER_MESSAGE_UNIX_FDS=%"PRIu32"\n", |
655 | 0 | message->metadata.fields.destination ?: "<broadcast>", |
656 | 0 | message->metadata.header.serial, |
657 | 0 | message->metadata.fields.signature ?: "<missing>", |
658 | 0 | message->metadata.fields.unix_fds); |
659 | |
|
660 | 0 | switch (message->metadata.header.type) { |
661 | 0 | case DBUS_MESSAGE_TYPE_METHOD_CALL: |
662 | 0 | log_appendf(log, |
663 | 0 | "DBUS_BROKER_MESSAGE_TYPE=method_call\n" |
664 | 0 | "DBUS_BROKER_MESSAGE_PATH=%s\n" |
665 | 0 | "DBUS_BROKER_MESSAGE_INTERFACE=%s\n" |
666 | 0 | "DBUS_BROKER_MESSAGE_MEMBER=%s\n", |
667 | 0 | message->metadata.fields.path ?: "<missing>", |
668 | 0 | message->metadata.fields.interface ?: "<missing>", |
669 | 0 | message->metadata.fields.member ?: "<missing>"); |
670 | 0 | break; |
671 | 0 | case DBUS_MESSAGE_TYPE_SIGNAL: |
672 | 0 | log_appendf(log, |
673 | 0 | "DBUS_BROKER_MESSAGE_TYPE=signal\n" |
674 | 0 | "DBUS_BROKER_MESSAGE_PATH=%s\n" |
675 | 0 | "DBUS_BROKER_MESSAGE_INTERFACE=%s\n" |
676 | 0 | "DBUS_BROKER_MESSAGE_MEMBER=%s\n", |
677 | 0 | message->metadata.fields.path ?: "<missing>", |
678 | 0 | message->metadata.fields.interface ?: "<missing>", |
679 | 0 | message->metadata.fields.member ?: "<missing>"); |
680 | 0 | break; |
681 | 0 | case DBUS_MESSAGE_TYPE_METHOD_RETURN: |
682 | 0 | log_appendf(log, |
683 | 0 | "DBUS_BROKER_MESSAGE_TYPE=method_return\n" |
684 | 0 | "MESSAGE_REPLY_SERIAL=%"PRIu32"\n", |
685 | 0 | message->metadata.fields.reply_serial); |
686 | 0 | break; |
687 | 0 | case DBUS_MESSAGE_TYPE_ERROR: |
688 | 0 | log_appendf(log, |
689 | 0 | "DBUS_BROKER_MESSAGE_TYPE=method_return\n" |
690 | 0 | "DBUS_BROKER_MESSAGE_ERROR_NAME=%s\n" |
691 | 0 | "DBUS_BROKER_MESSAGE_REPLY_SERIAL=%"PRIu32"\n", |
692 | 0 | message->metadata.fields.error_name, |
693 | 0 | message->metadata.fields.reply_serial); |
694 | 0 | break; |
695 | 0 | default: |
696 | 0 | log_appendf(log, "DBUS_BROKER_MESSAGE_TYPE=%u\n", message->metadata.header.type); |
697 | 0 | } |
698 | 0 | } |