bus-message.c revision 443a55981388f519fb6528a8ee042f9e69079b68
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <fcntl.h>
#include "util.h"
#include "utf8.h"
#include "strv.h"
#include "time-util.h"
#include "memfd-util.h"
#include "sd-bus.h"
#include "bus-message.h"
#include "bus-internal.h"
#include "bus-type.h"
#include "bus-signature.h"
#include "bus-gvariant.h"
#include "bus-util.h"
if (p == NULL)
return NULL;
return (void*) p;
return (void*) p;
return (void*) p;
}
assert(m);
/* If we can reuse the memfd, try that. For that it
* can't be sealed yet. */
} else {
}
} else if (part->munmap_this)
}
static void message_reset_parts(sd_bus_message *m) {
struct bus_body_part *part;
assert(m);
while (m->n_body_parts > 0) {
message_free_part(m, part);
m->n_body_parts--;
}
m->cached_rindex_part = NULL;
m->cached_rindex_part_begin = 0;
}
static void message_reset_containers(sd_bus_message *m) {
unsigned i;
assert(m);
for (i = 0; i < m->n_containers; i++) {
}
free(m->containers);
m->containers = NULL;
m->n_containers = m->containers_allocated = 0;
m->root_container.index = 0;
}
static void message_free(sd_bus_message *m) {
assert(m);
if (m->free_header)
if (m->release_kdbus)
if (m->free_kdbus)
sd_bus_unref(m->bus);
if (m->free_fds) {
}
if (m->iovec != m->iovec_fixed)
if (m->destination_ptr) {
free(m->destination_ptr);
m->destination_ptr = NULL;
}
bus_creds_done(&m->creds);
free(m);
}
assert(m);
if (m->poisoned)
return NULL;
goto poison;
if (m->free_header) {
if (!np)
goto poison;
} else {
/* Initially, the header is allocated as part of of
* the sd_bus_message itself, let's replace it by
* dynamic data */
if (!np)
goto poison;
}
/* Zero out padding */
/* Adjust quick access pointers */
m->free_header = true;
if (add_offset) {
goto poison;
}
m->poisoned = true;
return NULL;
}
static int message_append_field_string(
sd_bus_message *m,
uint64_t h,
char type,
const char *s,
const char **ret) {
size_t l;
uint8_t *p;
assert(m);
/* dbus1 only allows 8bit header field ids */
if (h > 0xFF)
return -EINVAL;
/* dbus1 doesn't allow strings over 32bit, let's enforce this
* globally, to not risk convertability */
l = strlen(s);
return -EINVAL;
/* Signature "(yv)" where the variant contains "s" */
if (BUS_MESSAGE_IS_GVARIANT(m)) {
/* (field id 64bit, ((string + NUL) + NUL + signature string 's') */
if (!p)
return -ENOMEM;
*((uint64_t*) p) = h;
memcpy(p+8, s, l);
p[8+l] = 0;
p[8+l+1] = 0;
if (ret)
*ret = (char*) p + 8;
} else {
/* (field id byte + (signature length + signature 's' + NUL) + (string length + string + NUL)) */
if (!p)
return -ENOMEM;
p[0] = (uint8_t) h;
p[1] = 1;
p[2] = type;
p[3] = 0;
((uint32_t*) p)[1] = l;
if (ret)
*ret = (char*) p + 8;
}
return 0;
}
static int message_append_field_signature(
sd_bus_message *m,
uint64_t h,
const char *s,
const char **ret) {
size_t l;
uint8_t *p;
assert(m);
/* dbus1 only allows 8bit header field ids */
if (h > 0xFF)
return -EINVAL;
/* dbus1 doesn't allow signatures over 8bit, let's enforce
* this globally, to not risk convertability */
l = strlen(s);
if (l > 255)
return -EINVAL;
/* Signature "(yv)" where the variant contains "g" */
if (BUS_MESSAGE_IS_GVARIANT(m))
/* For gvariant the serialization is the same as for normal strings */
else {
/* (field id byte + (signature length + signature 'g' + NUL) + (string length + string + NUL)) */
if (!p)
return -ENOMEM;
p[0] = (uint8_t) h;
p[1] = 1;
p[2] = SD_BUS_TYPE_SIGNATURE;
p[3] = 0;
p[4] = l;
if (ret)
*ret = (const char*) p + 5;
}
return 0;
}
uint8_t *p;
assert(m);
/* dbus1 only allows 8bit header field ids */
if (h > 0xFF)
return -EINVAL;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
/* (field id 64bit + ((value + NUL + signature string 'u') */
if (!p)
return -ENOMEM;
*((uint64_t*) p) = h;
*((uint32_t*) (p + 8)) = x;
p[12] = 0;
p[13] = 'u';
} else {
/* (field id byte + (signature length + signature 'u' + NUL) + value) */
if (!p)
return -ENOMEM;
p[0] = (uint8_t) h;
p[1] = 1;
p[2] = 'u';
p[3] = 0;
((uint32_t*) p)[1] = x;
}
return 0;
}
uint8_t *p;
assert(m);
/* dbus1 only allows 8bit header field ids */
if (h > 0xFF)
return -EINVAL;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
/* (field id 64bit + ((value + NUL + signature string 't') */
if (!p)
return -ENOMEM;
*((uint64_t*) p) = h;
*((uint64_t*) (p + 8)) = x;
p[16] = 0;
p[17] = 't';
} else {
/* (field id byte + (signature length + signature 't' + NUL) + 4 byte padding + value) */
if (!p)
return -ENOMEM;
p[0] = (uint8_t) h;
p[1] = 1;
p[2] = 't';
p[3] = 0;
p[4] = 0;
p[5] = 0;
p[6] = 0;
p[7] = 0;
((uint64_t*) p)[1] = x;
}
return 0;
}
assert(m);
if (BUS_MESSAGE_IS_GVARIANT(m))
else {
/* 64bit cookies are not supported on dbus1 */
if (cookie > 0xffffffffUL)
return -EOPNOTSUPP;
}
}
void *header,
void *footer,
int *fds,
unsigned n_fds,
const char *label,
sd_bus_message **ret) {
struct bus_header *h;
if (header_accessible < sizeof(struct bus_header))
return -EBADMSG;
if (header_accessible > message_size)
return -EBADMSG;
if (footer_accessible > message_size)
return -EBADMSG;
h = header;
return -EBADMSG;
if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID)
return -EBADMSG;
return -EBADMSG;
/* Note that we are happy with unknown flags in the flags header! */
if (label) {
a += label_sz + 1;
}
m = malloc0(a);
if (!m)
return -ENOMEM;
m->n_ref = 1;
m->sealed = true;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
return -EBADMSG;
/* dbus2 derives the sizes from the message size and
the offset table at the end, since it is formatted as
gvariant "yyyyuta{tv}v". Since the message itself is a
structure with precisely to variable sized entries,
there's only one offset in the table, which marks the
end of the fields array. */
if (footer_accessible < ws)
return -EBADMSG;
return -EBADMSG;
if (m->fields_size < sizeof(struct bus_header))
return -EBADMSG;
m->fields_size -= sizeof(struct bus_header);
} else {
return -EBADMSG;
/* dbus1 has the sizes in the header */
return -EBADMSG;
}
if (label) {
}
*ret = m;
m = NULL;
return 0;
}
void *buffer,
int *fds,
unsigned n_fds,
const char *label,
sd_bus_message **ret) {
sd_bus_message *m;
int r;
bus,
0, &m);
if (r < 0)
return r;
if (sz > 0) {
m->n_body_parts = 1;
}
m->n_iovec = 1;
m->iovec = m->iovec_fixed;
r = bus_message_parse_fields(m);
if (r < 0)
goto fail;
/* We take possession of the memory and fds now */
m->free_header = true;
m->free_fds = true;
*ret = m;
return 0;
fail:
message_free(m);
return r;
}
sd_bus_message *m;
if (!m)
return NULL;
m->n_ref = 1;
return m;
}
sd_bus_message **m,
const char *path,
const char *interface,
const char *member) {
sd_bus_message *t;
int r;
assert_return(m, -EINVAL);
if (!t)
return -ENOMEM;
r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
if (r < 0)
goto fail;
r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
if (r < 0)
goto fail;
r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
if (r < 0)
goto fail;
*m = t;
return 0;
fail:
return r;
}
sd_bus_message **m,
const char *destination,
const char *path,
const char *interface,
const char *member) {
sd_bus_message *t;
int r;
assert_return(m, -EINVAL);
if (!t)
return -ENOMEM;
r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
if (r < 0)
goto fail;
r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
if (r < 0)
goto fail;
if (interface) {
r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
if (r < 0)
goto fail;
}
if (destination) {
r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &t->destination);
if (r < 0)
goto fail;
}
*m = t;
return 0;
fail:
message_free(t);
return r;
}
static int message_new_reply(
sd_bus_message **m) {
sd_bus_message *t;
int r;
assert_return(m, -EINVAL);
if (!t)
return -ENOMEM;
if (t->reply_cookie == 0)
return -EOPNOTSUPP;
r = message_append_reply_cookie(t, t->reply_cookie);
if (r < 0)
goto fail;
r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->destination);
if (r < 0)
goto fail;
}
*m = t;
return 0;
fail:
message_free(t);
return r;
}
sd_bus_message **m) {
}
sd_bus_message **m,
const sd_bus_error *e) {
sd_bus_message *t;
int r;
assert_return(m, -EINVAL);
if (r < 0)
return r;
r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name);
if (r < 0)
goto fail;
if (e->message) {
if (r < 0)
goto fail;
}
*m = t;
return 0;
fail:
message_free(t);
return r;
}
sd_bus_message **m,
const char *name,
const char *format,
...) {
assert_return(m, -EINVAL);
}
sd_bus_message **m,
int error,
const sd_bus_error *p) {
if (sd_bus_error_is_set(p))
return sd_bus_message_new_method_error(call, m, p);
}
sd_bus_message **m,
int error,
const char *format,
...) {
}
assert(m);
m->creds.well_known_names_local = true;
}
assert(m);
m->creds.well_known_names_driver = true;
}
const sd_bus_error *e,
sd_bus_message **m) {
sd_bus_message *t;
int r;
assert(m);
if (!t)
return -ENOMEM;
t->reply_cookie = cookie;
r = message_append_reply_cookie(t, t->reply_cookie);
if (r < 0)
goto fail;
r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, bus->unique_name, &t->destination);
if (r < 0)
goto fail;
}
r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name);
if (r < 0)
goto fail;
if (e->message) {
if (r < 0)
goto fail;
}
*m = t;
return 0;
fail:
message_free(t);
return r;
}
assert_return(m, NULL);
m->n_ref++;
return m;
}
if (!m)
return NULL;
m->n_ref--;
if (m->n_ref > 0)
return NULL;
message_free(m);
return NULL;
}
assert_return(m, -EINVAL);
return 0;
}
uint64_t c;
assert_return(m, -EINVAL);
c = BUS_MESSAGE_COOKIE(m);
if (c == 0)
return -ENODATA;
*cookie = BUS_MESSAGE_COOKIE(m);
return 0;
}
assert_return(m, -EINVAL);
if (m->reply_cookie == 0)
return -ENODATA;
*cookie = m->reply_cookie;
return 0;
}
assert_return(m, -EINVAL);
}
assert_return(m, -EINVAL);
}
assert_return(m, -EINVAL);
}
assert_return(m, NULL);
return m->path;
}
assert_return(m, NULL);
return m->interface;
}
assert_return(m, NULL);
return m->member;
}
assert_return(m, NULL);
return m->destination;
}
assert_return(m, NULL);
return m->sender;
}
assert_return(m, NULL);
return &m->error;
}
assert_return(m, -EINVAL);
if (m->monotonic <= 0)
return -ENODATA;
return 0;
}
assert_return(m, -EINVAL);
if (m->realtime <= 0)
return -ENODATA;
return 0;
}
assert_return(m, -EINVAL);
if (m->seqnum <= 0)
return -ENODATA;
return 0;
}
assert_return(m, NULL);
return NULL;
return &m->creds;
}
sd_bus_message *m,
const char *interface,
const char *member) {
assert_return(m, -EINVAL);
return 0;
return 0;
return 0;
return 1;
}
sd_bus_message *m,
const char *interface,
const char *member) {
assert_return(m, -EINVAL);
return 0;
return 0;
return 0;
return 1;
}
assert_return(m, -EINVAL);
return 0;
return 0;
return 1;
}
assert_return(m, -EINVAL);
if (b)
else
return 0;
}
assert_return(m, -EINVAL);
if (b)
else
return 0;
}
assert_return(m, -EINVAL);
if (b)
else
return 0;
}
assert(m);
if (m->n_containers == 0)
return &m->root_container;
assert(m->containers);
}
struct bus_body_part *part;
assert(m);
if (m->poisoned)
return NULL;
if (m->n_body_parts <= 0) {
} else {
if (!part) {
m->poisoned = true;
return NULL;
}
}
m->n_body_parts ++;
return part;
}
/* All other fields can be left in their defaults */
}
static int part_make_space(
struct sd_bus_message *m,
struct bus_body_part *part,
void **q) {
void *n;
int r;
assert(m);
if (m->poisoned)
return -ENOMEM;
}
if (r < 0) {
m->poisoned = true;
return r;
}
}
else
if (n == MAP_FAILED) {
m->poisoned = true;
return -errno;
}
part->memfd_offset = 0;
}
part->munmap_this = true;
} else {
if (!n) {
m->poisoned = true;
return -ENOMEM;
}
}
}
if (q)
return 0;
}
struct bus_container *c;
assert(m);
/* Add offset to current container, unless this is the first
* item in it, which will have the 0 offset, which we can
* ignore. */
c = message_get_container(m);
if (!c->need_offsets)
return 0;
return -ENOMEM;
return 0;
}
struct bus_container *c;
assert(m);
if (expand <= 0)
return;
/* Update counters */
if (c->array_size)
*c->array_size += expand;
}
}
static void *message_extend_body(
sd_bus_message *m,
bool add_offset,
bool force_inline) {
void *p;
int r;
assert(m);
if (m->poisoned)
return NULL;
/* Check for 32bit overflows */
end_body < start_body) {
m->poisoned = true;
return NULL;
}
if (added > 0) {
bool add_new_part;
m->n_body_parts <= 0 ||
(force_inline && m->body_end->size > MEMFD_MIN_SIZE); /* if this must be an inlined extension, let's create a new part if the previous part is large enough to be inlined */
if (add_new_part) {
if (padding > 0) {
part = message_append_part(m);
if (!part)
return NULL;
}
part = message_append_part(m);
if (!part)
return NULL;
if (r < 0)
return NULL;
} else {
struct bus_container *c;
void *op;
if (r < 0)
return NULL;
if (padding > 0) {
}
/* Readjust pointers */
}
} else
/* Return something that is not NULL and is aligned */
if (add_offset) {
r = message_add_offset(m, end_body);
if (r < 0) {
m->poisoned = true;
return NULL;
}
}
return p;
}
int *f, copy;
assert(m);
if (fd < 0)
return -EINVAL;
if (!m->allow_fds)
return -EOPNOTSUPP;
if (copy < 0)
return -errno;
if (!f) {
m->poisoned = true;
return -ENOMEM;
}
m->fds = f;
m->free_fds = true;
return copy;
}
struct bus_container *c;
void *a;
assert_return(m, -EINVAL);
c = message_get_container(m);
/* Container signature is already set */
return -ENXIO;
} else {
char *e;
/* Maybe we can append to the signature? But only if this is the top-level container */
if (c->enclosing != 0)
return -ENXIO;
if (!e) {
m->poisoned = true;
return -ENOMEM;
}
}
if (BUS_MESSAGE_IS_GVARIANT(m)) {
switch (type) {
case SD_BUS_TYPE_SIGNATURE:
case SD_BUS_TYPE_STRING:
p = strempty(p);
/* Fall through... */
case SD_BUS_TYPE_OBJECT_PATH:
if (!p)
return -EINVAL;
align = 1;
break;
case SD_BUS_TYPE_BOOLEAN:
u8 = p && *(int*) p;
p = &u8;
break;
case SD_BUS_TYPE_UNIX_FD:
if (!p)
return -EINVAL;
fd = message_push_fd(m, *(int*) p);
if (fd < 0)
return fd;
p = &u32;
break;
default:
break;
}
if (!a)
return -ENOMEM;
if (stored)
} else {
switch (type) {
case SD_BUS_TYPE_STRING:
/* To make things easy we'll serialize a NULL string
* into the empty string */
p = strempty(p);
/* Fall through... */
case SD_BUS_TYPE_OBJECT_PATH:
if (!p)
return -EINVAL;
align = 4;
break;
case SD_BUS_TYPE_SIGNATURE:
p = strempty(p);
align = 1;
break;
case SD_BUS_TYPE_BOOLEAN:
u32 = p && *(int*) p;
p = &u32;
break;
case SD_BUS_TYPE_UNIX_FD:
if (!p)
return -EINVAL;
fd = message_push_fd(m, *(int*) p);
if (fd < 0)
return fd;
p = &u32;
break;
default:
break;
}
if (!a)
return -ENOMEM;
if (stored)
} else if (type == SD_BUS_TYPE_SIGNATURE) {
if (stored)
} else {
if (stored)
*stored = a;
}
}
if (type == SD_BUS_TYPE_UNIX_FD)
m->n_fds ++;
if (c->enclosing != SD_BUS_TYPE_ARRAY)
c->index++;
fd = -1;
return 0;
}
}
sd_bus_message *m,
char **s) {
struct bus_container *c;
void *a;
assert_return(m, -EINVAL);
assert_return(s, -EINVAL);
c = message_get_container(m);
/* Container signature is already set */
return -ENXIO;
} else {
char *e;
/* Maybe we can append to the signature? But only if this is the top-level container */
if (c->enclosing != 0)
return -ENXIO;
if (!e) {
m->poisoned = true;
return -ENOMEM;
}
}
if (BUS_MESSAGE_IS_GVARIANT(m)) {
if (!a)
return -ENOMEM;
*s = a;
} else {
if (!a)
return -ENOMEM;
*s = (char*) a + 4;
}
(*s)[size] = 0;
if (c->enclosing != SD_BUS_TYPE_ARRAY)
c->index++;
return 0;
}
sd_bus_message *m,
unsigned n) {
unsigned i;
char *p;
int r;
assert_return(m, -EINVAL);
r = sd_bus_message_append_string_space(m, size, &p);
if (r < 0)
return r;
for (i = 0; i < n; i++) {
else
}
return 0;
}
static int bus_message_open_array(
sd_bus_message *m,
struct bus_container *c,
const char *contents,
bool *need_offsets) {
unsigned nindex;
int alignment, r;
assert(m);
assert(c);
if (!signature_is_single(contents, true))
return -EINVAL;
/* Verify the existing signature */
return -ENXIO;
return -ENXIO;
} else {
char *e;
if (c->enclosing != 0)
return -ENXIO;
/* Extend the existing signature */
if (!e) {
m->poisoned = true;
return -ENOMEM;
}
}
if (BUS_MESSAGE_IS_GVARIANT(m)) {
if (alignment < 0)
return alignment;
/* Add alignment padding and add to offset list */
if (!message_extend_body(m, alignment, 0, false, false))
return -ENOMEM;
if (r < 0)
return r;
*need_offsets = r == 0;
} else {
void *a, *op;
struct bus_body_part *o;
if (alignment < 0)
return alignment;
if (!a)
return -ENOMEM;
o = m->body_end;
/* Add alignment between size and first element */
if (!message_extend_body(m, alignment, 0, false, false))
return -ENOMEM;
/* location of array size might have changed so let's readjust a */
if (o == m->body_end)
*(uint32_t*) a = 0;
*array_size = a;
}
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return 0;
}
static int bus_message_open_variant(
sd_bus_message *m,
struct bus_container *c,
const char *contents) {
assert(m);
assert(c);
if (!signature_is_single(contents, false))
return -EINVAL;
if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
return -EINVAL;
return -ENXIO;
} else {
char *e;
if (c->enclosing != 0)
return -ENXIO;
if (!e) {
m->poisoned = true;
return -ENOMEM;
}
}
if (BUS_MESSAGE_IS_GVARIANT(m)) {
/* Variants are always aligned to 8 */
if (!message_extend_body(m, 8, 0, false, false))
return -ENOMEM;
} else {
size_t l;
void *a;
if (!a)
return -ENOMEM;
*(uint8_t*) a = l;
}
if (c->enclosing != SD_BUS_TYPE_ARRAY)
c->index++;
return 0;
}
static int bus_message_open_struct(
sd_bus_message *m,
struct bus_container *c,
const char *contents,
bool *need_offsets) {
int r;
assert(m);
assert(c);
if (!signature_is_valid(contents, false))
return -EINVAL;
size_t l;
return -ENXIO;
} else {
char *e;
if (c->enclosing != 0)
return -ENXIO;
e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_BEGIN), contents, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_END), NULL);
if (!e) {
m->poisoned = true;
return -ENOMEM;
}
}
if (BUS_MESSAGE_IS_GVARIANT(m)) {
int alignment;
if (alignment < 0)
return alignment;
if (!message_extend_body(m, alignment, 0, false, false))
return -ENOMEM;
if (r < 0)
return r;
*need_offsets = r == 0;
} else {
/* Align contents to 8 byte boundary */
if (!message_extend_body(m, 8, 0, false, false))
return -ENOMEM;
}
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return 0;
}
static int bus_message_open_dict_entry(
sd_bus_message *m,
struct bus_container *c,
const char *contents,
bool *need_offsets) {
int r;
assert(m);
assert(c);
if (!signature_is_pair(contents))
return -EINVAL;
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return -ENXIO;
size_t l;
return -ENXIO;
} else
return -ENXIO;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
int alignment;
if (alignment < 0)
return alignment;
if (!message_extend_body(m, alignment, 0, false, false))
return -ENOMEM;
if (r < 0)
return r;
*need_offsets = r == 0;
} else {
/* Align contents to 8 byte boundary */
if (!message_extend_body(m, 8, 0, false, false))
return -ENOMEM;
}
return 0;
}
sd_bus_message *m,
char type,
const char *contents) {
struct bus_container *c, *w;
char *signature;
bool need_offsets = false;
int r;
assert_return(m, -EINVAL);
/* Make sure we have space for one more container */
m->poisoned = true;
return -ENOMEM;
}
c = message_get_container(m);
if (!signature) {
m->poisoned = true;
return -ENOMEM;
}
/* Save old index in the parent container, in case we have to
* abort this container */
c->saved_index = c->index;
if (type == SD_BUS_TYPE_ARRAY)
else if (type == SD_BUS_TYPE_VARIANT)
r = bus_message_open_variant(m, c, contents);
else if (type == SD_BUS_TYPE_STRUCT)
else if (type == SD_BUS_TYPE_DICT_ENTRY)
else
r = -EINVAL;
if (r < 0) {
return r;
}
/* OK, let's fill it in */
w = m->containers + m->n_containers++;
w->index = 0;
w->array_size = array_size;
w->n_offsets = w->offsets_allocated = 0;
w->need_offsets = need_offsets;
return 0;
}
assert(m);
assert(c);
if (!BUS_MESSAGE_IS_GVARIANT(m))
return 0;
if (c->need_offsets) {
uint8_t *a;
/* Variable-width arrays */
if (!a)
return -ENOMEM;
for (i = 0; i < c->n_offsets; i++)
} else {
void *a;
/* Fixed-width or empty arrays */
if (!a)
return -ENOMEM;
}
return 0;
}
uint8_t *a;
size_t l;
assert(m);
assert(c);
if (!BUS_MESSAGE_IS_GVARIANT(m))
return 0;
if (!a)
return -ENOMEM;
a[0] = 0;
return 0;
}
bool fixed_size = true;
size_t n_variable = 0;
unsigned i = 0;
const char *p;
uint8_t *a;
int r;
assert(m);
assert(c);
if (!BUS_MESSAGE_IS_GVARIANT(m))
return 0;
while (*p != 0) {
size_t n;
r = signature_element_length(p, &n);
if (r < 0)
return r;
else {
char t[n+1];
memcpy(t, p, n);
t[n] = 0;
r = bus_gvariant_is_fixed_size(t);
if (r < 0)
return r;
}
/* We need to add an offset for each item that has a
* variable size and that is not the last one in the
* list */
if (r == 0)
fixed_size = false;
if (r == 0 && p[n] != 0)
n_variable++;
i++;
p += n;
}
if (n_variable <= 0) {
int alignment = 1;
/* Structures with fixed-size members only have to be
* fixed-size themselves. But gvariant requires all fixed-size
* elements to be sized a multiple of their alignment. Hence,
* we must *always* add final padding after the last member so
* the overall size of the structure is properly aligned. */
if (fixed_size)
if (!a)
return -ENOMEM;
} else {
unsigned j;
if (!a)
return -ENOMEM;
for (i = 0, j = 0; i < c->n_offsets; i++) {
unsigned k;
size_t n;
r = signature_element_length(p, &n);
if (r < 0)
return r;
else {
char t[n+1];
memcpy(t, p, n);
t[n] = 0;
p += n;
r = bus_gvariant_is_fixed_size(t);
if (r < 0)
return r;
if (r > 0 || p[0] == 0)
continue;
}
k = n_variable - 1 - j;
j++;
}
}
return 0;
}
struct bus_container *c;
int r;
assert_return(m, -EINVAL);
c = message_get_container(m);
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return -EINVAL;
m->n_containers--;
if (c->enclosing == SD_BUS_TYPE_ARRAY)
r = bus_message_close_array(m, c);
else if (c->enclosing == SD_BUS_TYPE_VARIANT)
r = bus_message_close_variant(m, c);
r = bus_message_close_struct(m, c, true);
else
assert_not_reached("Unknown container type");
return r;
}
typedef struct {
const char *types;
unsigned n_struct;
unsigned n_array;
} TypeStack;
static int type_stack_push(TypeStack *stack, unsigned max, unsigned *i, const char *types, unsigned n_struct, unsigned n_array) {
if (*i >= max)
return -EINVAL;
(*i)++;
return 0;
}
static int type_stack_pop(TypeStack *stack, unsigned max, unsigned *i, const char **types, unsigned *n_struct, unsigned *n_array) {
if (*i <= 0)
return 0;
(*i)--;
return 1;
}
sd_bus_message *m,
const char *types,
unsigned stack_ptr = 0;
int r;
assert(m);
if (!types)
return 0;
n_array = (unsigned) -1;
for (;;) {
const char *t;
if (r < 0)
return r;
if (r == 0)
break;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
continue;
}
t = types;
if (n_array != (unsigned) -1)
n_array --;
else {
types ++;
n_struct--;
}
switch (*t) {
case SD_BUS_TYPE_BYTE: {
uint8_t x;
r = sd_bus_message_append_basic(m, *t, &x);
break;
}
case SD_BUS_TYPE_BOOLEAN:
case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32:
case SD_BUS_TYPE_UNIX_FD: {
uint32_t x;
/* We assume a boolean is the same as int32_t */
r = sd_bus_message_append_basic(m, *t, &x);
break;
}
case SD_BUS_TYPE_INT16:
case SD_BUS_TYPE_UINT16: {
uint16_t x;
r = sd_bus_message_append_basic(m, *t, &x);
break;
}
case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64: {
uint64_t x;
r = sd_bus_message_append_basic(m, *t, &x);
break;
}
case SD_BUS_TYPE_DOUBLE: {
double x;
r = sd_bus_message_append_basic(m, *t, &x);
break;
}
case SD_BUS_TYPE_STRING:
case SD_BUS_TYPE_OBJECT_PATH:
case SD_BUS_TYPE_SIGNATURE: {
const char *x;
r = sd_bus_message_append_basic(m, *t, x);
break;
}
case SD_BUS_TYPE_ARRAY: {
size_t k;
r = signature_element_length(t + 1, &k);
if (r < 0)
return r;
{
char s[k + 1];
memcpy(s, t + 1, k);
s[k] = 0;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
if (r < 0)
return r;
}
if (n_array == (unsigned) -1) {
types += k;
n_struct -= k;
}
if (r < 0)
return r;
types = t + 1;
n_struct = k;
break;
}
case SD_BUS_TYPE_VARIANT: {
const char *s;
if (!s)
return -EINVAL;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, s);
if (r < 0)
return r;
if (r < 0)
return r;
types = s;
n_array = (unsigned) -1;
break;
}
case SD_BUS_TYPE_STRUCT_BEGIN:
case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
size_t k;
r = signature_element_length(t, &k);
if (r < 0)
return r;
{
char s[k - 1];
s[k - 2] = 0;
r = sd_bus_message_open_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
if (r < 0)
return r;
}
if (n_array == (unsigned) -1) {
types += k - 1;
n_struct -= k - 1;
}
if (r < 0)
return r;
types = t + 1;
n_struct = k - 2;
n_array = (unsigned) -1;
break;
}
default:
r = -EINVAL;
}
if (r < 0)
return r;
}
return 1;
}
int r;
assert_return(m, -EINVAL);
return r;
}
sd_bus_message *m,
char type,
void **ptr) {
void *a;
int r;
assert_return(m, -EINVAL);
/* alignment and size of the trivial types (except bool) is
* identical for gvariant and dbus1 marshalling */
return -EINVAL;
if (r < 0)
return r;
if (!a)
return -ENOMEM;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
*ptr = a;
return 0;
}
sd_bus_message *m,
char type,
const void *ptr,
int r;
void *p;
assert_return(m, -EINVAL);
if (r < 0)
return r;
if (size > 0)
return 0;
}
sd_bus_message *m,
char type,
unsigned n) {
unsigned i;
void *p;
int r;
assert_return(m, -EINVAL);
if (r < 0)
return r;
for (i = 0; i < n; i++) {
else
}
return 0;
}
sd_bus_message *m,
char type,
int memfd,
struct bus_body_part *part;
void *a;
int r;
assert_return(m, -EINVAL);
r = memfd_set_sealed(memfd);
if (r < 0)
return r;
if (copy_fd < 0)
return copy_fd;
if (r < 0)
return r;
return -EMSGSIZE;
return -EINVAL;
return -EINVAL;
return -EINVAL;
if (r < 0)
return r;
a = message_extend_body(m, align, 0, false, false);
if (!a)
return -ENOMEM;
part = message_append_part(m);
if (!part)
return -ENOMEM;
copy_fd = -1;
return sd_bus_message_close_container(m);
}
sd_bus_message *m,
int memfd,
struct bus_body_part *part;
struct bus_container *c;
void *a;
int r;
assert_return(m, -EINVAL);
r = memfd_set_sealed(memfd);
if (r < 0)
return r;
if (copy_fd < 0)
return copy_fd;
if (r < 0)
return r;
return -EMSGSIZE;
/* We require this to be NUL terminated */
if (size == 0)
return -EINVAL;
return -EINVAL;
c = message_get_container(m);
/* Container signature is already set */
return -ENXIO;
} else {
char *e;
/* Maybe we can append to the signature? But only if this is the top-level container */
if (c->enclosing != 0)
return -ENXIO;
if (!e) {
m->poisoned = true;
return -ENOMEM;
}
}
if (!BUS_MESSAGE_IS_GVARIANT(m)) {
if (!a)
return -ENOMEM;
}
part = message_append_part(m);
if (!part)
return -ENOMEM;
copy_fd = -1;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
r = message_add_offset(m, m->body_size);
if (r < 0) {
m->poisoned = true;
return -ENOMEM;
}
}
if (c->enclosing != SD_BUS_TYPE_ARRAY)
c->index++;
return 0;
}
char **i;
int r;
assert_return(m, -EINVAL);
if (r < 0)
return r;
STRV_FOREACH(i, l) {
r = sd_bus_message_append_basic(m, 's', *i);
if (r < 0)
return r;
}
return sd_bus_message_close_container(m);
}
static int bus_message_close_header(sd_bus_message *m) {
assert(m);
/* The actual user data is finished now, we just complete the
variant and struct now (at least on gvariant). Remember
this position, so that during parsing we know where to to
put the outer container end. */
m->user_body_size = m->body_size;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
const char *signature;
void *d;
/* Add offset table to end of fields array */
if (m->n_header_offsets >= 1) {
uint8_t *a;
unsigned i;
if (!a)
return -ENOMEM;
for (i = 0; i < m->n_header_offsets; i++)
}
/* Add gvariant NUL byte plus signature to the end of
* the body, followed by the final offset pointing to
* the end of the fields array */
sz = bus_gvariant_determine_word_size(sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size + 1 + l, 1);
if (!d)
return -ENOMEM;
*(uint8_t*) d = 0;
m->footer = d;
} else {
}
return 0;
}
struct bus_body_part *part;
size_t a;
unsigned i;
int r;
assert(m);
if (m->sealed)
return -EPERM;
if (m->n_containers > 0)
return -EBADMSG;
if (m->poisoned)
return -ESTALE;
if (cookie > 0xffffffffULL &&
return -EOPNOTSUPP;
/* In vtables the return signature of method calls is listed,
* let's check if they match if this is a response */
m->enforced_reply_signature &&
return -ENOMSG;
/* If gvariant marshalling is used we need to close the body structure */
r = bus_message_close_struct(m, &m->root_container, false);
if (r < 0)
return r;
/* If there's a non-trivial signature set, then add it in
* here, but only on dbus1 */
r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL);
if (r < 0)
return r;
}
if (m->n_fds > 0) {
if (r < 0)
return r;
}
r = bus_message_close_header(m);
if (r < 0)
return r;
if (BUS_MESSAGE_IS_GVARIANT(m))
else
/* Add padding at the end of the fields part, since we know
* the body needs to start at an 8 byte alignment. We made
* sure we allocated enough space for this, so all we need to
* do here is to zero it out. */
if (a > 0)
/* If this is something we can send as memfd, then let's seal
the memfd now. Note that we can send memfds as payload only
for directed messages, and not for broadcasts. */
MESSAGE_FOREACH_PART(part, i, m)
/* Try to seal it if that makes
* sense. First, unmap our own map to
* make sure we don't keep it busy. */
/* Then, sync up real memfd size */
if (r < 0)
return r;
/* Finally, try to seal */
}
}
m->root_container.index = 0;
m->root_container.offset_index = 0;
m->sealed = true;
return 0;
}
void *p;
return 0;
return 0;
/* For smaller zero parts (as used for padding) we don't need to map anything... */
return 0;
}
else
return -EINVAL;
if (p == MAP_FAILED)
return -errno;
part->mmap_begin = p;
part->munmap_this = true;
return 0;
}
return;
if (!part->mmap_begin)
return;
if (!part->munmap_this)
return;
part->munmap_this = false;
return;
}
static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) {
return -EBADMSG;
/* Verify that padding is 0 */
if (((const uint8_t*) p)[k] != 0)
return -EBADMSG;
if (r)
return 1;
}
static bool message_end_of_signature(sd_bus_message *m) {
struct bus_container *c;
assert(m);
c = message_get_container(m);
}
struct bus_container *c;
assert(m);
c = message_get_container(m);
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return false;
if (BUS_MESSAGE_IS_GVARIANT(m))
else {
assert(c->array_size);
}
}
assert_return(m, -EINVAL);
if (complete && m->n_containers > 0)
return false;
if (message_end_of_signature(m))
return true;
if (message_end_of_array(m, m->rindex))
return true;
return false;
}
struct bus_body_part *part;
int r;
assert(m);
part = m->cached_rindex_part;
} else {
begin = 0;
}
while (part) {
return NULL;
r = bus_body_part_map(part);
if (r < 0)
return NULL;
if (p)
m->cached_rindex_part = part;
return part;
}
}
return NULL;
}
int r;
assert(m);
assert(c);
if (!BUS_MESSAGE_IS_GVARIANT(m))
return 0;
if (c->enclosing == SD_BUS_TYPE_ARRAY) {
int sz;
if (sz < 0) {
int alignment;
goto end;
/* Variable-size array */
} else {
goto end;
/* Fixed-size array */
}
c->offset_index++;
} else if (c->enclosing == 0 ||
c->enclosing == SD_BUS_TYPE_STRUCT ||
c->enclosing == SD_BUS_TYPE_DICT_ENTRY) {
int alignment;
size_t n, j;
goto end;
if (r < 0)
return r;
if (r < 0)
return r;
else {
char t[j+1];
t[j] = 0;
}
c->offset_index++;
} else if (c->enclosing == SD_BUS_TYPE_VARIANT)
goto end;
else
assert_not_reached("Unknown container type");
return 0;
end:
/* Reached the end */
c->item_size = 0;
return 0;
}
static int message_peek_body(
sd_bus_message *m,
void **ret) {
struct bus_body_part *part;
uint8_t *q;
assert(m);
if (end > m->user_body_size)
return -EBADMSG;
if (!part)
return -EBADMSG;
if (q) {
/* Verify padding */
for (k = 0; k < padding; k++)
if (q[k] != 0)
return -EBADMSG;
}
return -EBADMSG;
if (ret)
*ret = q;
return 0;
}
static bool validate_nul(const char *s, size_t l) {
/* Check for NUL chars in the string */
if (memchr(s, 0, l))
return false;
/* Check for NUL termination */
if (s[l] != 0)
return false;
return true;
}
static bool validate_string(const char *s, size_t l) {
if (!validate_nul(s, l))
return false;
/* Check if valid UTF8 */
if (!utf8_is_valid(s))
return false;
return true;
}
static bool validate_signature(const char *s, size_t l) {
if (!validate_nul(s, l))
return false;
/* Check if valid signature */
if (!signature_is_valid(s, true))
return false;
return true;
}
static bool validate_object_path(const char *s, size_t l) {
if (!validate_nul(s, l))
return false;
if (!object_path_is_valid(s))
return false;
return true;
}
struct bus_container *c;
void *q;
int r;
assert_return(m, -EINVAL);
if (message_end_of_signature(m))
return -ENXIO;
if (message_end_of_array(m, m->rindex))
return 0;
c = message_get_container(m);
return -ENXIO;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
bool ok;
if (r < 0)
return r;
if (type == SD_BUS_TYPE_STRING)
else if (type == SD_BUS_TYPE_OBJECT_PATH)
else
if (!ok)
return -EBADMSG;
if (p)
*(const char**) p = q;
} else {
return -EBADMSG;
if (r < 0)
return r;
switch (type) {
case SD_BUS_TYPE_BYTE:
if (p)
break;
case SD_BUS_TYPE_BOOLEAN:
if (p)
*(int*) p = !!*(uint8_t*) q;
break;
case SD_BUS_TYPE_INT16:
case SD_BUS_TYPE_UINT16:
if (p)
break;
case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32:
if (p)
break;
case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64:
case SD_BUS_TYPE_DOUBLE:
if (p)
break;
case SD_BUS_TYPE_UNIX_FD: {
uint32_t j;
j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
if (j >= m->n_fds)
return -EBADMSG;
if (p)
*(int*) p = m->fds[j];
break;
}
default:
assert_not_reached("unexpected type");
}
}
r = container_next_item(m, c, &rindex);
if (r < 0)
return r;
} else {
uint32_t l;
bool ok;
if (r < 0)
return r;
l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
if (r < 0)
return r;
if (type == SD_BUS_TYPE_OBJECT_PATH)
ok = validate_object_path(q, l);
else
ok = validate_string(q, l);
if (!ok)
return -EBADMSG;
if (p)
*(const char**) p = q;
} else if (type == SD_BUS_TYPE_SIGNATURE) {
uint8_t l;
if (r < 0)
return r;
l = *(uint8_t*) q;
if (r < 0)
return r;
if (!validate_signature(q, l))
return -EBADMSG;
if (p)
*(const char**) p = q;
} else {
if (r < 0)
return r;
switch (type) {
case SD_BUS_TYPE_BYTE:
if (p)
break;
case SD_BUS_TYPE_BOOLEAN:
if (p)
*(int*) p = !!*(uint32_t*) q;
break;
case SD_BUS_TYPE_INT16:
case SD_BUS_TYPE_UINT16:
if (p)
break;
case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32:
if (p)
break;
case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64:
case SD_BUS_TYPE_DOUBLE:
if (p)
break;
case SD_BUS_TYPE_UNIX_FD: {
uint32_t j;
j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
if (j >= m->n_fds)
return -EBADMSG;
if (p)
*(int*) p = m->fds[j];
break;
}
default:
assert_not_reached("Unknown basic type...");
}
}
}
if (c->enclosing != SD_BUS_TYPE_ARRAY)
c->index++;
return 1;
}
static int bus_message_enter_array(
sd_bus_message *m,
struct bus_container *c,
const char *contents,
void *q;
int r, alignment;
assert(m);
assert(c);
if (!signature_is_single(contents, true))
return -EINVAL;
return -ENXIO;
return -ENXIO;
return -ENXIO;
if (!BUS_MESSAGE_IS_GVARIANT(m)) {
/* dbus1 */
if (r < 0)
return r;
return -EBADMSG;
if (alignment < 0)
return alignment;
if (r < 0)
return r;
*array_size = (uint32_t*) q;
} else if (c->item_size <= 0) {
/* gvariant: empty array */
*item_size = 0;
*n_offsets = 0;
} else if (bus_gvariant_is_fixed_size(contents)) {
/* gvariant: fixed length array */
*n_offsets = 0;
} else {
unsigned i;
/* gvariant: variable length array */
if (r < 0)
return r;
return -EBADMSG;
return -EBADMSG;
if (r < 0)
return r;
if (!*offsets)
return -ENOMEM;
for (i = 0; i < *n_offsets; i++) {
size_t x;
return -EBADMSG;
if (x < p)
return -EBADMSG;
p = x;
}
}
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return 1;
}
static int bus_message_enter_variant(
sd_bus_message *m,
struct bus_container *c,
const char *contents,
uint8_t l;
void *q;
int r;
assert(m);
assert(c);
if (!signature_is_single(contents, false))
return -EINVAL;
if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
return -EINVAL;
return -ENXIO;
return -ENXIO;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
if (1+k > c->item_size)
return -EBADMSG;
if (r < 0)
return r;
if (*(char*) q != 0)
return -EBADMSG;
return -ENXIO;
} else {
if (r < 0)
return r;
l = *(uint8_t*) q;
if (r < 0)
return r;
if (!validate_signature(q, l))
return -EBADMSG;
return -ENXIO;
}
if (c->enclosing != SD_BUS_TYPE_ARRAY)
c->index++;
return 1;
}
static int build_struct_offsets(
sd_bus_message *m,
const char *signature,
unsigned n_variable = 0, n_total = 0, v;
const char *p;
void *q;
int r;
assert(m);
*item_size = 0;
*n_offsets = 0;
return 0;
}
if (sz <= 0)
return -EBADMSG;
/* First, loop over signature and count variable elements and
* elements in general. We use this to know how large the
* offset array is at the end of the structure. Note that
* GVariant only stores offsets for all variable size elements
* that are not the last item. */
p = signature;
while (*p != 0) {
size_t n;
r = signature_element_length(p, &n);
if (r < 0)
return r;
else {
char t[n+1];
memcpy(t, p, n);
t[n] = 0;
r = bus_gvariant_is_fixed_size(t);
}
if (r < 0)
return r;
if (r == 0 && p[n] != 0) /* except the last item */
n_variable ++;
n_total++;
p += n;
}
return -EBADMSG;
if (r < 0)
return r;
v = n_variable;
if (!*offsets)
return -ENOMEM;
*n_offsets = 0;
/* Second, loop again and build an offset table */
p = signature;
while (*p != 0) {
int k;
r = signature_element_length(p, &n);
if (r < 0)
return r;
else {
char t[n+1];
memcpy(t, p, n);
t[n] = 0;
k = bus_gvariant_get_size(t);
if (k < 0) {
size_t x;
/* variable size */
if (v > 0) {
v--;
if (x >= size)
return -EBADMSG;
return -EBADMSG;
} else
/* The last item's end
* is determined from
* the start of the
* offset array */
} else {
/* fixed size */
}
}
p += n;
}
assert(v == 0);
return 0;
}
static int enter_struct_or_dict_entry(
sd_bus_message *m,
struct bus_container *c,
const char *contents,
int r;
assert(m);
assert(c);
if (!BUS_MESSAGE_IS_GVARIANT(m)) {
/* dbus1 */
if (r < 0)
return r;
} else if (c->item_size <= 0) {
/* gvariant empty struct */
*item_size = 0;
*n_offsets = 0;
} else
/* gvariant with contents */
return 0;
}
static int bus_message_enter_struct(
sd_bus_message *m,
struct bus_container *c,
const char *contents,
size_t l;
int r;
assert(m);
assert(c);
if (!signature_is_valid(contents, false))
return -EINVAL;
return -ENXIO;
return -ENXIO;
if (r < 0)
return r;
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return 1;
}
static int bus_message_enter_dict_entry(
sd_bus_message *m,
struct bus_container *c,
const char *contents,
size_t l;
int r;
assert(m);
assert(c);
if (!signature_is_pair(contents))
return -EINVAL;
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return -ENXIO;
return 0;
return -ENXIO;
if (r < 0)
return r;
if (c->enclosing != SD_BUS_TYPE_ARRAY)
return 1;
}
char type,
const char *contents) {
struct bus_container *c, *w;
char *signature;
int r;
assert_return(m, -EINVAL);
const char *cc;
char tt;
/* Allow entering into anonymous containers */
if (r < 0)
return r;
return -ENXIO;
return -ENXIO;
}
/*
* We enforce a global limit on container depth, that is much
* higher than the 32 structs and 32 arrays the specification
* mandates. This is simpler to implement for us, and we need
* this only to ensure our container array doesn't grow
* without bounds. We are happy to return any data from a
* message as long as the data itself is valid, even if the
* overall message might be not.
*
* Note that the message signature is validated when
* parsing the headers, and that validation does check the
* 32/32 limit.
*
* Note that the specification defines no limits on the depth
* of stacked variants, but we do.
*/
if (m->n_containers >= BUS_CONTAINER_DEPTH)
return -EBADMSG;
return -ENOMEM;
if (message_end_of_signature(m))
return -ENXIO;
if (message_end_of_array(m, m->rindex))
return 0;
c = message_get_container(m);
if (!signature)
return -ENOMEM;
c->saved_index = c->index;
if (type == SD_BUS_TYPE_ARRAY)
else if (type == SD_BUS_TYPE_VARIANT)
else if (type == SD_BUS_TYPE_STRUCT)
else if (type == SD_BUS_TYPE_DICT_ENTRY)
else
r = -EINVAL;
if (r <= 0) {
return r;
}
/* OK, let's fill it in */
w = m->containers + m->n_containers++;
w->peeked_signature = NULL;
w->index = 0;
w->array_size = array_size;
w->offset_index = 0;
return 1;
}
struct bus_container *c;
unsigned saved;
int r;
assert_return(m, -EINVAL);
c = message_get_container(m);
if (c->enclosing != SD_BUS_TYPE_ARRAY) {
return -EBUSY;
}
if (BUS_MESSAGE_IS_GVARIANT(m)) {
return -EBUSY;
} else if (c->enclosing == SD_BUS_TYPE_ARRAY) {
uint32_t l;
l = BUS_MESSAGE_BSWAP32(m, *c->array_size);
return -EBUSY;
}
free(c->peeked_signature);
m->n_containers--;
c = message_get_container(m);
c->index = c->saved_index;
r = container_next_item(m, c, &m->rindex);
if (r < 0)
return r;
return 1;
}
static void message_quit_container(sd_bus_message *m) {
struct bus_container *c;
assert(m);
assert(m->n_containers > 0);
c = message_get_container(m);
/* Undo seeks */
/* Free container */
m->n_containers--;
/* Correct index of new top-level container */
c = message_get_container(m);
c->index = c->saved_index;
}
struct bus_container *c;
int r;
assert_return(m, -EINVAL);
if (message_end_of_signature(m))
goto eof;
if (message_end_of_array(m, m->rindex))
goto eof;
c = message_get_container(m);
if (contents)
if (type)
return 1;
}
if (contents) {
size_t l;
char *sig;
if (r < 0)
return r;
assert(l >= 1);
if (!sig)
return -ENOMEM;
free(c->peeked_signature);
}
if (type)
return 1;
}
if (contents) {
size_t l;
char *sig;
if (r < 0)
return r;
assert(l >= 2);
if (!sig)
return -ENOMEM;
free(c->peeked_signature);
}
if (type)
*type = c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY;
return 1;
}
if (contents) {
void *q;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
size_t k;
if (c->item_size < 2)
return -EBADMSG;
/* Look for the NUL delimiter that
separates the payload from the
signature. Since the body might be
in a different part that then the
signature we map byte by byte. */
for (k = 2; k <= c->item_size; k++) {
if (r < 0)
return r;
if (*(char*) q == 0)
break;
}
if (k > c->item_size)
return -EBADMSG;
free(c->peeked_signature);
if (!c->peeked_signature)
return -ENOMEM;
if (!signature_is_valid(c->peeked_signature, true))
return -EBADMSG;
*contents = c->peeked_signature;
} else {
if (r < 0)
return r;
l = *(uint8_t*) q;
if (r < 0)
return r;
if (!validate_signature(q, l))
return -EBADMSG;
*contents = q;
}
}
if (type)
return 1;
}
return -EINVAL;
eof:
if (type)
*type = 0;
if (contents)
return 0;
}
struct bus_container *c;
assert_return(m, -EINVAL);
if (complete) {
m->rindex = 0;
c = message_get_container(m);
} else {
c = message_get_container(m);
c->offset_index = 0;
c->index = 0;
}
c->offset_index = 0;
}
static int message_read_ap(
sd_bus_message *m,
const char *types,
unsigned stack_ptr = 0;
unsigned n_loop = 0;
int r;
assert(m);
return 0;
/* Ideally, we'd just call ourselves recursively on every
* complex type. However, the state of a va_list that is
* passed to a function is undefined after that function
* returns. This means we need to docode the va_list linearly
* in a single stackframe. We hence implement our own
* home-grown stack in an array. */
for (;;) {
const char *t;
n_loop++;
if (r < 0)
return r;
if (r == 0)
break;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
continue;
}
t = types;
if (n_array != (unsigned) -1)
n_array --;
else {
types ++;
n_struct--;
}
switch (*t) {
case SD_BUS_TYPE_BYTE:
case SD_BUS_TYPE_BOOLEAN:
case SD_BUS_TYPE_INT16:
case SD_BUS_TYPE_UINT16:
case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32:
case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64:
case SD_BUS_TYPE_DOUBLE:
case SD_BUS_TYPE_STRING:
case SD_BUS_TYPE_OBJECT_PATH:
case SD_BUS_TYPE_SIGNATURE:
case SD_BUS_TYPE_UNIX_FD: {
void *p;
r = sd_bus_message_read_basic(m, *t, p);
if (r < 0)
return r;
if (r == 0) {
if (n_loop <= 1)
return 0;
return -ENXIO;
}
break;
}
case SD_BUS_TYPE_ARRAY: {
size_t k;
r = signature_element_length(t + 1, &k);
if (r < 0)
return r;
{
char s[k + 1];
memcpy(s, t + 1, k);
s[k] = 0;
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s);
if (r < 0)
return r;
if (r == 0) {
if (n_loop <= 1)
return 0;
return -ENXIO;
}
}
if (n_array == (unsigned) -1) {
types += k;
n_struct -= k;
}
if (r < 0)
return r;
types = t + 1;
n_struct = k;
break;
}
case SD_BUS_TYPE_VARIANT: {
const char *s;
if (!s)
return -EINVAL;
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, s);
if (r < 0)
return r;
if (r == 0) {
if (n_loop <= 1)
return 0;
return -ENXIO;
}
if (r < 0)
return r;
types = s;
n_array = (unsigned) -1;
break;
}
case SD_BUS_TYPE_STRUCT_BEGIN:
case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
size_t k;
r = signature_element_length(t, &k);
if (r < 0)
return r;
{
char s[k - 1];
s[k - 2] = 0;
r = sd_bus_message_enter_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
if (r < 0)
return r;
if (r == 0) {
if (n_loop <= 1)
return 0;
return -ENXIO;
}
}
if (n_array == (unsigned) -1) {
types += k - 1;
n_struct -= k - 1;
}
if (r < 0)
return r;
types = t + 1;
n_struct = k - 2;
n_array = (unsigned) -1;
break;
}
default:
return -EINVAL;
}
}
return 1;
}
int r;
assert_return(m, -EINVAL);
return r;
}
int r;
assert_return(m, -EINVAL);
/* If types is NULL, read exactly one element */
if (!types) {
struct bus_container *c;
size_t l;
if (message_end_of_signature(m))
return -ENXIO;
if (message_end_of_array(m, m->rindex))
return 0;
c = message_get_container(m);
if (r < 0)
return r;
}
switch (*types) {
case 0: /* Nothing to drop */
return 0;
case SD_BUS_TYPE_BYTE:
case SD_BUS_TYPE_BOOLEAN:
case SD_BUS_TYPE_INT16:
case SD_BUS_TYPE_UINT16:
case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32:
case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64:
case SD_BUS_TYPE_DOUBLE:
case SD_BUS_TYPE_STRING:
case SD_BUS_TYPE_OBJECT_PATH:
case SD_BUS_TYPE_SIGNATURE:
case SD_BUS_TYPE_UNIX_FD:
if (r <= 0)
return r;
if (r < 0)
return r;
return 1;
case SD_BUS_TYPE_ARRAY: {
size_t k;
if (r < 0)
return r;
{
char s[k+1];
s[k] = 0;
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s);
if (r <= 0)
return r;
for (;;) {
r = sd_bus_message_skip(m, s);
if (r < 0)
return r;
if (r == 0)
break;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
}
if (r < 0)
return r;
return 1;
}
case SD_BUS_TYPE_VARIANT: {
const char *contents;
char x;
r = sd_bus_message_peek_type(m, &x, &contents);
if (r <= 0)
return r;
if (x != SD_BUS_TYPE_VARIANT)
return -ENXIO;
if (r <= 0)
return r;
r = sd_bus_message_skip(m, contents);
if (r < 0)
return r;
assert(r != 0);
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
if (r < 0)
return r;
return 1;
}
case SD_BUS_TYPE_STRUCT_BEGIN:
case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
size_t k;
r = signature_element_length(types, &k);
if (r < 0)
return r;
{
char s[k-1];
s[k-2] = 0;
r = sd_bus_message_enter_container(m, *types == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
if (r <= 0)
return r;
r = sd_bus_message_skip(m, s);
if (r < 0)
return r;
assert(r != 0);
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
}
r = sd_bus_message_skip(m, types + k);
if (r < 0)
return r;
return 1;
}
default:
return -EINVAL;
}
}
sd_bus_message *m,
char type,
const void **ptr,
struct bus_container *c;
void *p;
int r;
assert_return(m, -EINVAL);
if (r <= 0)
return r;
c = message_get_container(m);
if (BUS_MESSAGE_IS_GVARIANT(m)) {
if (align < 0)
return align;
} else {
if (align < 0)
return align;
}
if (sz == 0)
/* Zero length array, let's return some aligned
* pointer that is not NULL */
else {
if (r < 0)
goto fail;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
goto fail;
*ptr = (const void*) p;
return 1;
fail:
return r;
}
static int message_peek_fields(
sd_bus_message *m,
void **ret) {
assert(m);
}
static int message_peek_field_uint32(
sd_bus_message *m,
int r;
void *q;
assert(m);
return -EBADMSG;
/* identical for gvariant and dbus1 */
if (r < 0)
return r;
if (ret)
return 0;
}
static int message_peek_field_uint64(
sd_bus_message *m,
int r;
void *q;
assert(m);
return -EBADMSG;
/* identical for gvariant and dbus1 */
if (r < 0)
return r;
if (ret)
return 0;
}
static int message_peek_field_string(
sd_bus_message *m,
bool (*validate)(const char *p),
const char **ret) {
uint32_t l;
int r;
void *q;
assert(m);
if (BUS_MESSAGE_IS_GVARIANT(m)) {
if (item_size <= 0)
return -EBADMSG;
if (r < 0)
return r;
l = item_size - 1;
} else {
if (r < 0)
return r;
if (r < 0)
return r;
}
if (validate) {
if (!validate_nul(q, l))
return -EBADMSG;
if (!validate(q))
return -EBADMSG;
} else {
if (!validate_string(q, l))
return -EBADMSG;
}
if (ret)
*ret = q;
return 0;
}
static int message_peek_field_signature(
sd_bus_message *m,
const char **ret) {
size_t l;
int r;
void *q;
assert(m);
if (BUS_MESSAGE_IS_GVARIANT(m)) {
if (item_size <= 0)
return -EBADMSG;
if (r < 0)
return r;
l = item_size - 1;
} else {
if (r < 0)
return r;
l = *(uint8_t*) q;
if (r < 0)
return r;
}
if (!validate_signature(q, l))
return -EBADMSG;
if (ret)
*ret = q;
return 0;
}
static int message_skip_fields(
sd_bus_message *m,
const char **signature) {
int r;
assert(m);
original_index = *ri;
for (;;) {
char t;
size_t l;
return 0;
t = **signature;
if (!t)
return 0;
if (t == SD_BUS_TYPE_STRING) {
if (r < 0)
return r;
(*signature)++;
} else if (t == SD_BUS_TYPE_OBJECT_PATH) {
if (r < 0)
return r;
(*signature)++;
} else if (t == SD_BUS_TYPE_SIGNATURE) {
if (r < 0)
return r;
(*signature)++;
} else if (bus_type_is_basic(t)) {
align = bus_type_get_alignment(t);
k = bus_type_get_size(t);
if (r < 0)
return r;
(*signature)++;
} else if (t == SD_BUS_TYPE_ARRAY) {
if (r < 0)
return r;
assert(l >= 1);
{
char sig[l-1], *s;
int alignment;
s = sig;
if (alignment < 0)
return alignment;
if (r < 0)
return r;
if (nas > BUS_ARRAY_MAX_SIZE)
return -EBADMSG;
if (r < 0)
return r;
if (r < 0)
return r;
}
(*signature) += 1 + l;
} else if (t == SD_BUS_TYPE_VARIANT) {
const char *s;
r = message_peek_field_signature(m, ri, 0, &s);
if (r < 0)
return r;
if (r < 0)
return r;
(*signature)++;
} else if (t == SD_BUS_TYPE_STRUCT ||
t == SD_BUS_TYPE_DICT_ENTRY) {
r = signature_element_length(*signature, &l);
if (r < 0)
return r;
assert(l >= 2);
{
char sig[l-1], *s;
s = sig;
if (r < 0)
return r;
}
*signature += l;
} else
return -EINVAL;
}
}
int bus_message_parse_fields(sd_bus_message *m) {
int r;
bool unix_fds_set = false;
unsigned n_offsets = 0;
unsigned i = 0;
assert(m);
if (BUS_MESSAGE_IS_GVARIANT(m)) {
char *p;
/* Read the signature from the end of the body variant first */
return -EBADMSG;
for (;;) {
if (p < (char*) m->footer)
return -EBADMSG;
if (*p == 0) {
char *c;
/* We found the beginning of the signature string, yay! */
if (!c)
return -ENOMEM;
m->root_container.signature = c;
break;
}
p--;
}
/* Calculate the actual user body size, by removing
* the trailing variant signature and struct offset
* table */
/* Pull out the offset table for the fields array */
if (sz > 0) {
void *q;
if (r < 0)
return r;
return -EBADMSG;
return -EBADMSG;
if (r < 0)
return r;
}
} else
m->user_body_size = m->body_size;
ri = 0;
while (ri < m->fields_size) {
const char *signature;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
if (i >= n_offsets)
break;
if (i == 0)
ri = 0;
else
if (r < 0)
return r;
} else {
if (r < 0)
return r;
field_type = *u8;
}
if (BUS_MESSAGE_IS_GVARIANT(m)) {
char *b;
void *q;
return -EBADMSG;
if (r < 0)
return r;
if (!b)
return -EBADMSG;
if (!sig)
return -ENOMEM;
item_size = b - (char*) q;
} else {
if (r < 0)
return r;
}
switch (field_type) {
return -EBADMSG;
case BUS_MESSAGE_HEADER_PATH:
if (m->path)
return -EBADMSG;
return -EBADMSG;
break;
if (m->interface)
return -EBADMSG;
return -EBADMSG;
break;
if (m->member)
return -EBADMSG;
return -EBADMSG;
break;
return -EBADMSG;
return -EBADMSG;
if (r >= 0)
break;
if (m->destination)
return -EBADMSG;
return -EBADMSG;
break;
if (m->sender)
return -EBADMSG;
return -EBADMSG;
}
break;
case BUS_MESSAGE_HEADER_SIGNATURE: {
const char *s;
char *c;
if (BUS_MESSAGE_IS_GVARIANT(m)) /* only applies to dbus1 */
return -EBADMSG;
if (m->root_container.signature)
return -EBADMSG;
return -EBADMSG;
if (r < 0)
return r;
c = strdup(s);
if (!c)
return -ENOMEM;
m->root_container.signature = c;
break;
}
if (m->reply_cookie != 0)
return -EBADMSG;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
/* 64bit on dbus2 */
return -EBADMSG;
if (r < 0)
return r;
} else {
/* 32bit on dbus1 */
return -EBADMSG;
if (r < 0)
return r;
m->reply_cookie = serial;
}
if (m->reply_cookie == 0)
return -EBADMSG;
break;
if (unix_fds_set)
return -EBADMSG;
return -EBADMSG;
if (r < 0)
return -EBADMSG;
unix_fds_set = true;
break;
default:
if (!BUS_MESSAGE_IS_GVARIANT(m))
}
if (r < 0)
return r;
i++;
}
return -EBADMSG;
case SD_BUS_MESSAGE_SIGNAL:
return -EBADMSG;
if (m->reply_cookie != 0)
return -EBADMSG;
break;
return -EBADMSG;
if (m->reply_cookie != 0)
return -EBADMSG;
break;
if (m->reply_cookie == 0)
return -EBADMSG;
break;
return -EBADMSG;
break;
}
/* Refuse non-local messages that claim they are local */
return -EBADMSG;
return -EBADMSG;
return -EBADMSG;
if (BUS_MESSAGE_IS_GVARIANT(m)) {
r = build_struct_offsets(
m,
m->user_body_size,
&m->root_container.item_size,
&m->root_container.offsets,
&m->root_container.n_offsets);
if (r < 0)
return r;
}
/* Try to read the error message, but if we can't it's a non-issue */
return 0;
}
assert_return(m, -EINVAL);
return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination);
}
void *p, *e;
unsigned i;
struct bus_body_part *part;
assert(m);
total = BUS_MESSAGE_SIZE(m);
if (!p)
return -ENOMEM;
MESSAGE_FOREACH_PART(part, i, m)
*buffer = p;
return 0;
}
int bus_message_read_strv_extend(sd_bus_message *m, char ***l) {
const char *s;
int r;
assert(m);
assert(l);
if (r <= 0)
return r;
while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0) {
r = strv_extend(l, s);
if (r < 0)
return r;
}
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return 1;
}
int r;
assert_return(m, -EINVAL);
assert_return(l, -EINVAL);
r = bus_message_read_strv_extend(m, &strv);
if (r <= 0) {
return r;
}
*l = strv;
return 1;
}
const char *contents;
unsigned j;
char type;
int r;
assert(m);
r = sd_bus_message_rewind(m, true);
if (r < 0)
return r;
for (j = 0;; j++) {
if (r < 0)
return r;
if (r == 0)
return -ENXIO;
/* Don't match against arguments after the first one we don't understand */
return -ENXIO;
if (j >= i)
break;
r = sd_bus_message_skip(m, NULL);
if (r < 0)
return r;
}
if (type == SD_BUS_TYPE_ARRAY) {
r = sd_bus_message_read_strv(m, strv);
if (r < 0)
return r;
} else {
if (r < 0)
return r;
}
return 0;
}
assert_return(m, EINVAL);
return 0;
return sd_bus_error_get_errno(&m->error);
}
struct bus_container *c;
assert_return(m, NULL);
}
assert_return(m, -EINVAL);
}
assert_return(m, -EINVAL);
}
bool done_something = false;
int r;
assert_return(m, -EINVAL);
do {
const char *contents;
char type;
union {
double d64;
const char *string;
int i;
} basic;
if (r < 0)
return r;
if (r == 0)
break;
done_something = true;
if (bus_type_is_container(type) > 0) {
if (r < 0)
return r;
if (r < 0)
return r;
r = sd_bus_message_copy(m, source, true);
if (r < 0)
return r;
r = sd_bus_message_close_container(m);
if (r < 0)
return r;
if (r < 0)
return r;
continue;
}
if (r < 0)
return r;
assert(r > 0);
if (type == SD_BUS_TYPE_OBJECT_PATH ||
type == SD_BUS_TYPE_SIGNATURE ||
else
if (r < 0)
return r;
} while (all);
return done_something;
}
const char *c;
char t;
int r;
assert_return(m, -EINVAL);
r = sd_bus_message_peek_type(m, &t, &c);
if (r <= 0)
return r;
return 0;
return 0;
return 1;
}
assert_return(m, NULL);
return m->bus;
}
int r;
assert(m);
assert(*m);
case SD_BUS_MESSAGE_SIGNAL:
if (r < 0)
return r;
break;
r = sd_bus_message_new_method_call(bus, &n, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member);
if (r < 0)
return r;
break;
if (!n)
return -ENOMEM;
n->reply_cookie = (*m)->reply_cookie;
r = message_append_reply_cookie(n, n->reply_cookie);
if (r < 0)
return r;
r = message_append_field_string(n, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, (*m)->error.name, &n->error.message);
if (r < 0)
return r;
}
break;
default:
return -EINVAL;
}
if ((*m)->destination && !n->destination) {
r = message_append_field_string(n, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, (*m)->destination, &n->destination);
if (r < 0)
return r;
}
r = message_append_field_string(n, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, (*m)->sender, &n->sender);
if (r < 0)
return r;
}
n->header->flags |= (*m)->header->flags & (BUS_MESSAGE_NO_REPLY_EXPECTED|BUS_MESSAGE_NO_AUTO_START);
r = sd_bus_message_copy(n, *m, true);
if (r < 0)
return r;
if (r < 0)
return r;
sd_bus_message_unref(*m);
*m = n;
n = NULL;
return 0;
}
assert(m);
return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender);
}
assert_return(m, -EINVAL);
return 0;
}
assert_return(m, -EINVAL);
return 0;
}