int decompress_blob_xz(const void *src, uint64_t src_size, void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { #ifdef HAVE_XZ _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; lzma_ret ret; size_t space; assert(src); assert(src_size > 0); assert(dst); assert(dst_alloc_size); assert(dst_size); assert(*dst_alloc_size == 0 || *dst); ret = lzma_stream_decoder(&s, UINT64_MAX, 0); if (ret != LZMA_OK) return -ENOMEM; space = MIN(src_size * 2, dst_max ?: (size_t) -1); if (!greedy_realloc(dst, dst_alloc_size, space, 1)) return -ENOMEM; s.next_in = src; s.avail_in = src_size; s.next_out = *dst; s.avail_out = space; for (;;) { size_t used; ret = lzma_code(&s, LZMA_FINISH); if (ret == LZMA_STREAM_END) break; else if (ret != LZMA_OK) return -ENOMEM; if (dst_max > 0 && (space - s.avail_out) >= dst_max) break; else if (dst_max > 0 && space == dst_max) return -ENOBUFS; used = space - s.avail_out; space = MIN(2 * space, dst_max ?: (size_t) -1); if (!greedy_realloc(dst, dst_alloc_size, space, 1)) return -ENOMEM; s.avail_out = space - used; s.next_out = *dst + used; } *dst_size = space - s.avail_out; return 0; #else return -EPROTONOSUPPORT; #endif }
int decompress_startswith_xz(const void *src, uint64_t src_size, void **buffer, size_t *buffer_size, const void *prefix, size_t prefix_len, uint8_t extra) { #ifdef HAVE_XZ _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; lzma_ret ret; /* Checks whether the decompressed blob starts with the * mentioned prefix. The byte extra needs to follow the * prefix */ assert(src); assert(src_size > 0); assert(buffer); assert(buffer_size); assert(prefix); assert(*buffer_size == 0 || *buffer); ret = lzma_stream_decoder(&s, UINT64_MAX, 0); if (ret != LZMA_OK) return -EBADMSG; if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) return -ENOMEM; s.next_in = src; s.avail_in = src_size; s.next_out = *buffer; s.avail_out = *buffer_size; for (;;) { ret = lzma_code(&s, LZMA_FINISH); if (ret != LZMA_STREAM_END && ret != LZMA_OK) return -EBADMSG; if (*buffer_size - s.avail_out >= prefix_len + 1) return memcmp(*buffer, prefix, prefix_len) == 0 && ((const uint8_t*) *buffer)[prefix_len] == extra; if (ret == LZMA_STREAM_END) return 0; s.avail_out += *buffer_size; if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1))) return -ENOMEM; s.next_out = *buffer + *buffer_size - s.avail_out; } #else return -EPROTONOSUPPORT; #endif }
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) { size_t prev; uint8_t *q; assert(p); assert(allocated); prev = *allocated; q = greedy_realloc(p, allocated, need, size); if (!q) return NULL; if (*allocated > prev) memzero(q + prev * size, (*allocated - prev) * size); return q; }
int decompress_startswith_lz4(const void *src, uint64_t src_size, void **buffer, size_t *buffer_size, const void *prefix, size_t prefix_len, uint8_t extra) { #ifdef HAVE_LZ4 /* Checks whether the decompressed blob starts with the * mentioned prefix. The byte extra needs to follow the * prefix */ int r; assert(src); assert(src_size > 0); assert(buffer); assert(buffer_size); assert(prefix); assert(*buffer_size == 0 || *buffer); if (src_size <= 8) return -EBADMSG; if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) return -ENOMEM; r = LZ4_decompress_safe_partial(src + 8, *buffer, src_size - 8, prefix_len + 1, *buffer_size); if (r < 0) return -EBADMSG; if ((unsigned) r >= prefix_len + 1) return memcmp(*buffer, prefix, prefix_len) == 0 && ((const uint8_t*) *buffer)[prefix_len] == extra; else return 0; #else return -EPROTONOSUPPORT; #endif }
static int parse_env_file_internal( const char *fname, const char *newline, int (*push) (const char *key, char *value, void *userdata), void *userdata) { _cleanup_free_ char *contents = NULL, *key = NULL; size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; char *p, *value = NULL; int r; enum { PRE_KEY, KEY, PRE_VALUE, VALUE, VALUE_ESCAPE, SINGLE_QUOTE_VALUE, SINGLE_QUOTE_VALUE_ESCAPE, DOUBLE_QUOTE_VALUE, DOUBLE_QUOTE_VALUE_ESCAPE, COMMENT, COMMENT_ESCAPE } state = PRE_KEY; assert(fname); assert(newline); r = read_full_file(fname, &contents, NULL); if (r < 0) return r; for (p = contents; *p; p++) { char c = *p; switch (state) { case PRE_KEY: if (strchr(COMMENTS, c)) state = COMMENT; else if (!strchr(WHITESPACE, c)) { state = KEY; last_key_whitespace = (size_t) -1; if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) { r = -ENOMEM; goto fail; } key[n_key++] = c; } break; case KEY: if (strchr(newline, c)) { state = PRE_KEY; n_key = 0; } else if (c == '=') { state = PRE_VALUE; last_value_whitespace = (size_t) -1; } else { if (!strchr(WHITESPACE, c)) last_key_whitespace = (size_t) -1; else if (last_key_whitespace == (size_t) -1) last_key_whitespace = n_key; if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) { r = -ENOMEM; goto fail; } key[n_key++] = c; } break; case PRE_VALUE: if (strchr(newline, c)) { state = PRE_KEY; key[n_key] = 0; if (value) value[n_value] = 0; /* strip trailing whitespace from key */ if (last_key_whitespace != (size_t) -1) key[last_key_whitespace] = 0; r = push(key, value, userdata); if (r < 0) goto fail; n_key = 0; value = NULL; value_alloc = n_value = 0; } else if (c == '\'') state = SINGLE_QUOTE_VALUE; else if (c == '\"') state = DOUBLE_QUOTE_VALUE; else if (c == '\\') state = VALUE_ESCAPE; else if (!strchr(WHITESPACE, c)) { state = VALUE; if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case VALUE: if (strchr(newline, c)) { state = PRE_KEY; key[n_key] = 0; if (value) value[n_value] = 0; /* Chomp off trailing whitespace from value */ if (last_value_whitespace != (size_t) -1) value[last_value_whitespace] = 0; /* strip trailing whitespace from key */ if (last_key_whitespace != (size_t) -1) key[last_key_whitespace] = 0; r = push(key, value, userdata); if (r < 0) goto fail; n_key = 0; value = NULL; value_alloc = n_value = 0; } else if (c == '\\') { state = VALUE_ESCAPE; last_value_whitespace = (size_t) -1; } else { if (!strchr(WHITESPACE, c)) last_value_whitespace = (size_t) -1; else if (last_value_whitespace == (size_t) -1) last_value_whitespace = n_value; if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case VALUE_ESCAPE: state = VALUE; if (!strchr(newline, c)) { /* Escaped newlines we eat up entirely */ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case SINGLE_QUOTE_VALUE: if (c == '\'') state = PRE_VALUE; else if (c == '\\') state = SINGLE_QUOTE_VALUE_ESCAPE; else { if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case SINGLE_QUOTE_VALUE_ESCAPE: state = SINGLE_QUOTE_VALUE; if (!strchr(newline, c)) { if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case DOUBLE_QUOTE_VALUE: if (c == '\"') state = PRE_VALUE; else if (c == '\\') state = DOUBLE_QUOTE_VALUE_ESCAPE; else { if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case DOUBLE_QUOTE_VALUE_ESCAPE: state = DOUBLE_QUOTE_VALUE; if (!strchr(newline, c)) { if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { r = -ENOMEM; goto fail; } value[n_value++] = c; } break; case COMMENT: if (c == '\\') state = COMMENT_ESCAPE; else if (strchr(newline, c)) state = PRE_KEY; break; case COMMENT_ESCAPE: state = COMMENT; break; } } if (state == PRE_VALUE || state == VALUE || state == VALUE_ESCAPE || state == SINGLE_QUOTE_VALUE || state == SINGLE_QUOTE_VALUE_ESCAPE || state == DOUBLE_QUOTE_VALUE || state == DOUBLE_QUOTE_VALUE_ESCAPE) { key[n_key] = 0; if (value) value[n_value] = 0; if (state == VALUE) if (last_value_whitespace != (size_t) -1) value[last_value_whitespace] = 0; /* strip trailing whitespace from key */ if (last_key_whitespace != (size_t) -1) key[last_key_whitespace] = 0; r = push(key, value, userdata); if (r < 0) goto fail; } return 0; fail: free(value); return r; }
/* returns the number of bytes sent, or a negative error code */ int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) { union { struct sockaddr sa; struct sockaddr_nl nl; } addr = { .nl.nl_family = AF_NETLINK, }; ssize_t k; assert(nl); assert(m); assert(m->hdr); k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len, 0, &addr.sa, sizeof(addr)); if (k < 0) return (errno == EAGAIN) ? 0 : -errno; return k; } static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) { uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(struct nl_pktinfo))]; struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1, .msg_control = cred_buffer, .msg_controllen = sizeof(cred_buffer), }; struct cmsghdr *cmsg; uint32_t group = 0; bool auth = false; int r; assert(fd >= 0); assert(iov); r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); if (r < 0) { /* no data */ if (errno == ENOBUFS) log_debug("rtnl: kernel receive buffer overrun"); return (errno == EAGAIN) ? 0 : -errno; } else if (r == 0) /* connection was closed by the kernel */ return -ECONNRESET; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { struct ucred *ucred = (void *)CMSG_DATA(cmsg); /* from the kernel */ if (ucred->uid == 0 && ucred->pid == 0) auth = true; } else if (cmsg->cmsg_level == SOL_NETLINK && cmsg->cmsg_type == NETLINK_PKTINFO && cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) { struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg); /* multi-cast group */ group = pktinfo->group; } } if (!auth) /* not from the kernel, ignore */ return 0; if (group) *_group = group; return r; } /* On success, the number of bytes received is returned and *ret points to the received message * which has a valid header and the correct size. * If nothing useful was received 0 is returned. * On failure, a negative error code is returned. */ int socket_read_message(sd_rtnl *rtnl) { _cleanup_rtnl_message_unref_ sd_rtnl_message *first = NULL; struct iovec iov = {}; uint32_t group = 0; bool multi_part = false, done = false; struct nlmsghdr *new_msg; size_t len; int r; unsigned i = 0; assert(rtnl); assert(rtnl->rbuffer); assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); /* read nothing, just get the pending message size */ r = socket_recv_message(rtnl->fd, &iov, &group, true); if (r <= 0) return r; else len = (size_t)r; /* make room for the pending message */ if (!greedy_realloc((void **)&rtnl->rbuffer, &rtnl->rbuffer_allocated, len, sizeof(uint8_t))) return -ENOMEM; iov.iov_base = rtnl->rbuffer; iov.iov_len = rtnl->rbuffer_allocated; /* read the pending message */ r = socket_recv_message(rtnl->fd, &iov, &group, false); if (r <= 0) return r; else len = (size_t)r; if (len > rtnl->rbuffer_allocated) /* message did not fit in read buffer */ return -EIO; if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) { multi_part = true; for (i = 0; i < rtnl->rqueue_partial_size; i++) { if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) == rtnl->rbuffer->nlmsg_seq) { first = rtnl->rqueue_partial[i]; break; } } } for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len); new_msg = NLMSG_NEXT(new_msg, len)) { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; const NLType *nl_type; if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid) /* not broadcast and not for us */ continue; if (new_msg->nlmsg_type == NLMSG_NOOP) /* silently drop noop messages */ continue; if (new_msg->nlmsg_type == NLMSG_DONE) { /* finished reading multi-part message */ done = true; break; } /* check that we support this message type */ r = type_system_get_type(NULL, &nl_type, new_msg->nlmsg_type); if (r < 0) { if (r == -ENOTSUP) log_debug("sd-rtnl: ignored message with unknown type: %u", new_msg->nlmsg_type); continue; } /* check that the size matches the message type */ if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size)) continue; r = message_new_empty(rtnl, &m); if (r < 0) return r; m->hdr = memdup(new_msg, new_msg->nlmsg_len); if (!m->hdr) return -ENOMEM; /* seal and parse the top-level message */ r = sd_rtnl_message_rewind(m); if (r < 0) return r; /* push the message onto the multi-part message stack */ if (first) m->next = first; first = m; m = NULL; } if (len) log_debug("sd-rtnl: discarding %zu bytes of incoming message", len); if (!first) return 0; if (!multi_part || done) { /* we got a complete message, push it on the read queue */ r = rtnl_rqueue_make_room(rtnl); if (r < 0) return r; rtnl->rqueue[rtnl->rqueue_size ++] = first; first = NULL; if (multi_part && (i < rtnl->rqueue_partial_size)) { /* remove the message form the partial read queue */ memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1, sizeof(sd_rtnl_message*) * (rtnl->rqueue_partial_size - i - 1)); rtnl->rqueue_partial_size --; } return 1; } else { /* we only got a partial multi-part message, push it on the partial read queue */ if (i < rtnl->rqueue_partial_size) { rtnl->rqueue_partial[i] = first; } else { r = rtnl_rqueue_partial_make_room(rtnl); if (r < 0) return r; rtnl->rqueue_partial[rtnl->rqueue_partial_size ++] = first; } first = NULL; return 0; } }
size_t buxton_serialize_message(uint8_t **dest, BuxtonControlMessage message, uint32_t msgid, BuxtonArray *list) { uint16_t i = 0; uint8_t *data = NULL; size_t ret = 0; size_t offset = 0; size_t size = 0; size_t curSize = 0; uint16_t control, msg; assert(dest); assert(list); buxton_debug("Serializing message...\n"); if (list->len > BUXTON_MESSAGE_MAX_PARAMS) { errno = EINVAL; return ret; } if (message >= BUXTON_CONTROL_MAX || message < BUXTON_CONTROL_SET) { errno = EINVAL; return ret; } /* * initial size = * control code + control message (uint16_t * 2) + * message size (uint32_t) + * message id (uint32_t) + * param count (uint32_t) */ data = malloc0(sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t)); if (!data) { errno = ENOMEM; goto end; } control = BUXTON_CONTROL_CODE; memcpy(data, &control, sizeof(uint16_t)); offset += sizeof(uint16_t); msg = (uint16_t)message; memcpy(data+offset, &msg, sizeof(uint16_t)); offset += sizeof(uint16_t); /* Save room for final size */ offset += sizeof(uint32_t); memcpy(data+offset, &msgid, sizeof(uint32_t)); offset += sizeof(uint32_t); /* Now write the parameter count */ memcpy(data+offset, &(list->len), sizeof(uint32_t)); offset += sizeof(uint32_t); size = offset; /* Deal with parameters */ BuxtonData *param; size_t p_length = 0; for (i=0; i < list->len; i++) { param = buxton_array_get(list, i); if (!param) { errno = EINVAL; goto fail; } switch (param->type) { case BUXTON_TYPE_STRING: p_length = param->store.d_string.length; break; case BUXTON_TYPE_INT32: p_length = sizeof(int32_t); break; case BUXTON_TYPE_UINT32: p_length = sizeof(uint32_t); break; case BUXTON_TYPE_INT64: p_length = sizeof(int64_t); break; case BUXTON_TYPE_UINT64: p_length = sizeof(uint64_t); break; case BUXTON_TYPE_FLOAT: p_length = sizeof(float); break; case BUXTON_TYPE_DOUBLE: p_length = sizeof(double); break; case BUXTON_TYPE_BOOLEAN: p_length = sizeof(bool); break; default: errno = EINVAL; buxton_log("Invalid parameter type %lu\n", param->type); goto fail; }; buxton_debug("offset: %lu\n", offset); buxton_debug("value length: %lu\n", p_length); /* Need to allocate enough room to hold this data */ size += sizeof(uint16_t) + sizeof(uint32_t) + p_length; if (curSize < size) { if (!(data = greedy_realloc((void**)&data, &curSize, size))) { errno = ENOMEM; goto fail; } memzero(data+offset, size - offset); } /* Copy data type */ memcpy(data+offset, &(param->type), sizeof(uint16_t)); offset += sizeof(uint16_t); /* Write out the length of value */ memcpy(data+offset, &p_length, sizeof(uint32_t)); offset += sizeof(uint32_t); switch (param->type) { case BUXTON_TYPE_STRING: memcpy(data+offset, param->store.d_string.value, p_length); break; case BUXTON_TYPE_INT32: memcpy(data+offset, &(param->store.d_int32), sizeof(int32_t)); break; case BUXTON_TYPE_UINT32: memcpy(data+offset, &(param->store.d_uint32), sizeof(uint32_t)); break; case BUXTON_TYPE_INT64: memcpy(data+offset, &(param->store.d_int64), sizeof(int64_t)); break; case BUXTON_TYPE_UINT64: memcpy(data+offset, &(param->store.d_uint64), sizeof(uint64_t)); break; case BUXTON_TYPE_FLOAT: memcpy(data+offset, &(param->store.d_float), sizeof(float)); break; case BUXTON_TYPE_DOUBLE: memcpy(data+offset, &(param->store.d_double), sizeof(double)); break; case BUXTON_TYPE_BOOLEAN: memcpy(data+offset, &(param->store.d_boolean), sizeof(bool)); break; default: /* already tested this above, can't get here * normally */ assert(0); }; offset += p_length; p_length = 0; } memcpy(data+BUXTON_LENGTH_OFFSET, &offset, sizeof(uint32_t)); ret = offset; *dest = data; fail: /* Clean up */ if (ret == 0) { free(data); } end: buxton_debug("Serializing returned:%lu\n", ret); return ret; }