/** * Fill supplied buffer with the formatted string describing the message. * * @param data start of the G2 message * @param len length of the message * @param buf buffer where formatted string is written * @param buflen length of the destination buffer * * @return the amount of bytes written. */ size_t g2_msg_infostr_to_buf(const void *data, size_t len, char *buf, size_t buflen) { enum g2_msg m; const guid_t *muid = NULL; g_assert(size_is_non_negative(len)); g_assert(size_is_non_negative(buflen)); /* * Check whether we need to decompile the packet to access the GUID, which * is the payload of the root element in the tree. Given the way things * are serialized, that would be the last 16 bytes of the message, so * we don't have to deserialize everything just to access it. */ m = g2_msg_type(data, len); switch (m) { case G2_MSG_Q2: case G2_MSG_QA: case G2_MSG_QH2: if (len > GUID_RAW_SIZE) muid = const_ptr_add_offset(data, len - GUID_RAW_SIZE); /* FALL THROUGH */ default: break; } return str_bprintf(buf, buflen, "/%s (%zu byte%s)%s%s", g2_msg_type_name(m), len, plural(len), NULL == muid ? "" : " #", NULL == muid ? "" : guid_hex_str(muid)); }
/** * Creates an incremental zlib deflater for `len' bytes starting at `data', * with specified compression `level'. * * @param data data to compress; if NULL, will be incrementally given * @param len length of data to compress (if data not NULL) or estimation * @param dest where compressed data should go, or NULL if allocated * @param destlen length of supplied output buffer, if dest != NULL * @param level compression level, between 0 and 9. * * @return new deflater, or NULL if error. */ static zlib_deflater_t * zlib_deflater_alloc( const void *data, size_t len, void *dest, size_t destlen, int level) { zlib_deflater_t *zd; z_streamp outz; int ret; g_assert(size_is_non_negative(len)); g_assert(size_is_non_negative(destlen)); g_assert(level == Z_DEFAULT_COMPRESSION || (level >= 0 && level <= 9)); WALLOC(outz); outz->zalloc = zlib_alloc_func; outz->zfree = zlib_free_func; outz->opaque = NULL; ret = deflateInit(outz, level); if (ret != Z_OK) { WFREE(outz); g_carp("%s(): unable to initialize compressor: %s", G_STRFUNC, zlib_strerror(ret)); return NULL; } WALLOC0(zd); zd->zs.magic = ZLIB_DEFLATER_MAGIC; zd->zs.z = outz; zd->zs.closed = FALSE; zlib_stream_init(&zd->zs, data, len, dest, destlen); return zd; }
/** * Initialize internal state for our incremental zlib stream. * * @param zs the zlib stream structure to initialize * @param data input data to process; if NULL, will be incrementally given * @param len length of data to process (if data not NULL) or estimation * @param dest where processed data should go, or NULL if allocated * @param destlen length of supplied output buffer, if dest != NULL */ static void zlib_stream_init(zlib_stream_t *zs, const void *data, size_t len, void *dest, size_t destlen) { z_streamp z; g_assert(size_is_non_negative(len)); g_assert(size_is_non_negative(destlen)); zs->in = data; zs->inlen = data ? len : 0; zs->inlen_total = zs->inlen; /* * When deflating zlib requires normally 0.1% more + 12 bytes, we use * 0.5% to be safe. * * When inflating, assume we'll double the input at least. */ if (NULL == dest) { /* Processed data go to a dynamically allocated buffer */ if (!zs->allocated) { if (data == NULL && len == 0) len = 512; if (ZLIB_DEFLATER_MAGIC == zs->magic) { zs->outlen = (len * 1.005 + 12.0); g_assert(zs->outlen > len); g_assert(zs->outlen - len >= 12); } else { zs->outlen = UNSIGNED(len) * 2; } zs->out = halloc(zs->outlen); zs->allocated = TRUE; } } else { /* Processed data go to a supplied buffer, not-resizable */ if (zs->allocated) hfree(zs->out); zs->out = dest; zs->outlen = destlen; zs->allocated = FALSE; } /* * Initialize Z stream. */ z = zs->z; g_assert(z != NULL); /* Stream not closed yet */ z->next_out = zs->out; z->avail_out = zs->outlen; z->next_in = deconstify_pointer(zs->in); z->avail_in = 0; /* Will be set by zlib_xxx_step() */ }
/** * Initialize expanded list. * * Assuming items in the list are defined as: * * struct item { * <data fields> * struct chaining { * <some chaining links> * struct chaining *next; // our "next" pointer, linking field * } chain; * }; * * then the offset argument can be given as: * * offsetof(struct item, chain) * * and the link_offset argument can be given as: * * offsetof(struct chaining, next) * * to indicate the place of the field chaining items together. * * @param list the list structure to initialize * @param offset the offset of the expanded link field within items * @param link_offset the offset of the linking field in the chaining struct */ void xslist_init(xslist_t *list, size_t offset, size_t link_offset) { g_assert(list != NULL); g_assert(size_is_non_negative(offset)); g_assert(size_is_non_negative(link_offset)); list->magic = XSLIST_MAGIC; list->head = NULL; list->tail = NULL; list->count = 0; list->offset = offset; list->link_offset = link_offset; }
/** * Appends `n_bytes' to the pmsg_t buffer. If the last pmsg_t is writable * it is filled with as much data as space is still available. Otherwise * or if this space is not sufficient another pmsg_t is created and * appendded to the list. */ void pmsg_slist_append(slist_t *slist, const void *data, size_t n_bytes) { pmsg_t *mb; g_assert(slist); g_assert_log(size_is_non_negative(n_bytes), "%s(): n_bytes=%zd", G_STRFUNC, n_bytes); if (0 == n_bytes) return; g_assert(NULL != data); mb = slist_tail(slist); if (mb && pmsg_is_writable(mb)) { size_t n; n = pmsg_write(mb, data, n_bytes); data = (const char *) data + n; n_bytes -= n; } if (n_bytes > 0) { mb = pmsg_new(PMSG_P_DATA, NULL, MAX(n_bytes, PMSG_SLIST_GROW_MIN)); pmsg_write(mb, data, n_bytes); slist_append(slist, mb); } }
/** * Read data from the pmsg list into supplied buffer. Copied data is * removed from the list. * * @param slist the pmsg list * @param buf start of buffer where data must be copied * @param len length of buffer * * @return amount of copied bytes. */ size_t pmsg_slist_read(slist_t *slist, void *buf, size_t len) { slist_iter_t *iter; size_t remain = len; void *p; g_assert(slist != NULL); g_assert_log(size_is_non_negative(len), "%s(): len=%zd", G_STRFUNC, len); iter = slist_iter_removable_on_head(slist); p = buf; while (remain != 0 && slist_iter_has_item(iter)) { pmsg_t *mb = slist_iter_current(iter); int n; n = pmsg_read(mb, p, remain); remain -= n; p = ptr_add_offset(p, n); if (0 == pmsg_size(mb)) { /* Fully copied message */ pmsg_free(mb); slist_iter_remove(iter); /* Warning: moves to next */ } else { break; /* No need to continue on partial copy */ } } slist_iter_free(&iter); return len - remain; }
/** * Convenience routine: format tree to memory buffer. * * @param root tree to dump * @param buf buffer where formatting is done * @param len buffer length * @param options formatting options * * @return length of generated string, -1 on failure. */ size_t xfmt_tree_to_buffer(const xnode_t *root, void *buf, size_t len, uint32 options) { ostream_t *os; pdata_t *pd; pmsg_t *mb; bool ok; size_t written = (size_t) -1; g_assert(root != NULL); g_assert(buf != NULL); g_assert(size_is_non_negative(len)); pd = pdata_allocb_ext(buf, len, pdata_free_nop, NULL); mb = pmsg_alloc(PMSG_P_DATA, pd, 0, 0); os = ostream_open_pmsg(mb); ok = xfmt_tree(root, os, options); ok = ostream_close(os) && ok; if (ok) written = pmsg_size(mb); pmsg_free(mb); g_assert((size_t) -1 == written || written <= len); return written; }
/** * Initialize and load linked items into a list. * * This routine is meant to allow the creation of an expanded list from * homogeneous items that happen to be linked into another data structure * through a single pointer. * * It is useful to allow reuse of code that can process such lists, such * as xslist_sort(), xslist_shuffle(), etc... It is naturally up to the * caller to then refetch the proper head pointer. * * @param list the list into which we are loading items * @param head first data item part of the linked list (NULL possible) * @param offset the offset of the expanded link field within items * @param link_offset the offset of the linking field in the chaining struct * * @return the amount of loaded items, as a convenience. */ size_t xslist_load(xslist_t *list, void *head, size_t offset, size_t link_offset) { xslink_t *lk, *next; size_t n; g_assert(list != NULL); g_assert(size_is_non_negative(offset)); xslist_init(list, offset, link_offset); if G_UNLIKELY(NULL == head) return 0; lk = ptr_add_offset(head, offset); list->head = lk; for (n = 1; NULL != (next = xslist_next(list, lk)); n++, lk = next) /* empty */; list->tail = lk; list->count = n; safety_assert(xslist_length(list, list->head) == list->count); return n; }
/** * Initialize embedded tree. * * Assuming items in the tree are defined as: * * struct item { * <data fields> * node_t n; * }; * * then the last argument can be given as: * * offsetof(struct item, n) * * to indicate the place of the node field within the item. * * Extended trees are available to make etree_append_child() efficient but if * children can only be prepended, use normal trees (less overhead per node). * * @param tree the tree structure to initialize * @param extended whether extended nodex_t are used instead of node_t * @param offset the offset of the embedded link field within items */ void etree_init(etree_t *tree, bool extended, size_t offset) { g_assert(tree != NULL); g_assert(size_is_non_negative(offset)); tree->magic = extended ? ETREE_EXT_MAGIC : ETREE_MAGIC; tree->root = NULL; tree->offset = offset; tree->count = 0; }
/** * Shuffle array in-place. */ static void entropy_array_shuffle(void *ary, size_t len, size_t elem_size) { g_assert(ary != NULL); g_assert(size_is_non_negative(len)); g_assert(size_is_positive(elem_size)); if (len > RANDOM_SHUFFLE_MAX) s_carp("%s: cannot shuffle %zu items without bias", G_STRFUNC, len); shuffle_with((random_fn_t) entropy_rand31, ary, len, elem_size); }
/** * Collect entropy by randomly executing the callbacks given in the array. */ static void entropy_array_cb_collect(SHA1Context *ctx, entropy_cb_t *ary, size_t len) { size_t i; g_assert(ctx != NULL); g_assert(ary != NULL); g_assert(size_is_non_negative(len)); entropy_array_shuffle(ary, len, sizeof ary[0]); for (i = 0; i < len; i++) { (*ary[i])(ctx); } }
/** * Replace value data in-place. * * @param db the sdbm database * @param bval start of big value in the page * @param data the new value * @param len length of data * * @return 0 if OK, -1 on error with errno set. */ int big_replace(DBM *db, char *bval, const char *data, size_t len) { size_t old_len = big_length(bval); g_assert(size_is_non_negative(len)); g_assert(bigblocks(old_len) == bigblocks(len)); g_assert(len <= MAX_INT_VAL(guint32)); /* * Write data on the same blocks as before, since we know it will fit. */ poke_be32(bval, (guint32) len); /* First 4 bytes: real data length */ return big_store(db, bigval_blocks(bval), data, len); }
/** * Write NUL-terminated string, up to `n' characters or the first seen NUL * in the buffer, whichever comes first. * * The string is written as: <ule64(length)><bytes>, no trailing NUL. */ void pmsg_write_fixed_string(pmsg_t *mb, const char *str, size_t n) { size_t len; g_assert(pmsg_is_writable(mb)); /* Not shared, or would corrupt data */ g_assert(UNSIGNED(pmsg_available(mb)) >= n + 10); /* Need ule64 length */ g_assert_log(size_is_non_negative(n), "%s(): n=%zd", G_STRFUNC, n); len = strlen(str); len = MIN(n, len); pmsg_write_ule64(mb, (uint64) len); if (len != 0) { pmsg_write(mb, str, len); } }
/** * Write NUL-terminated string. * * If (size_t) -1 is given as length, then it is computed via strlen(), in * which case the string buffer must be NUL-terminated. Otherwise, the value * is taken to be the pre-computed string length. * * The string is written as: <ule64(length)><bytes>, no trailing NUL. */ void pmsg_write_string(pmsg_t *mb, const char *str, size_t length) { size_t len; g_assert(pmsg_is_writable(mb)); /* Not shared, or would corrupt data */ g_assert_log(size_is_non_negative(length) || (size_t) -1 == length, "%s(): length=%zd", G_STRFUNC, length); len = (size_t) -1 == length ? strlen(str) : length; g_assert(UNSIGNED(pmsg_available(mb)) >= len + 10); /* Need ule64 length */ pmsg_write_ule64(mb, (uint64) len); if (len != 0) { pmsg_write(mb, str, len); } }
/** * Copies up to ``*size'' bytes from current data block * (bh->b_data + bh->b_offset) to the buffer ``dest''. * * @param bh an initialized browse host context. * @param dest the destination buffer. * @param size must point to a ``size_t'' variable and initialized to the * number of bytes that ``dest'' can hold. It's value is * automagically decreased by the amount of bytes copied. * @return The amount of bytes copied. Use this to advance ``dest''. */ static inline size_t browse_host_read_data(struct browse_host_upload *bh, char *dest, size_t *size) { size_t len; g_assert(NULL != size); g_assert(size_is_non_negative(bh->b_offset)); g_assert(bh->b_offset <= bh->b_size); g_assert(bh->b_data != NULL); g_assert(*size <= INT_MAX); len = bh->b_size - bh->b_offset; len = MIN(*size, len); memcpy(dest, &bh->b_data[bh->b_offset], len); bh->b_offset += len; *size -= len; return len; }
/** * Create a new formatting context for a header line. * * @param `field' is the header field name, without trailing ':'. * * @param `separator' is the optional default separator to emit between * the values added via header_fmd_append_value(). To supersede the * default separator, use header_fmd_append() and specify another separator * explicitly. If set to NULL, there will be no default separator and * values will be simply concatenated together. The value given must * NOT be freed before the header_fmt_end() call (usually it will just * be a static string). Trailing spaces in the separator will be stripped * if it is emitted at the end of a line before a continuation. * * @param `len_hint' is the expected line size, for pre-sizing purposes. * (0 to guess). * * @param `max_size' is the maximum header size, including the final "\r\n" * and the trailing NUL. If the initial field name is larger than the * configured maximum size, the header field will remain completely empty. * * @return pointer to the formatting object. */ header_fmt_t * header_fmt_make(const char *field, const char *separator, size_t len_hint, size_t max_size) { struct header_fmt *hf; g_assert(size_is_non_negative(len_hint)); WALLOC(hf); hf->magic = HEADER_FMT_MAGIC; hf->header = str_new(len_hint ? len_hint : HEADER_FMT_DFLT_LEN); hf->maxlen = HEADER_FMT_LINE_LEN; hf->data_emitted = FALSE; hf->frozen = FALSE; hf->max_size = max_size; hf->sep = atom_str_get(separator ? separator : ""); hf->seplen = strlen(hf->sep); hf->stripped_seplen = stripped_strlen(hf->sep, hf->seplen); str_cat(hf->header, field); STR_CAT(hf->header, ": "); hf->current_len = str_len(hf->header); /* * If right from the start the header would be larger than the configured * size, force it to stay empty. That means, the final string returned * will be "", the empty string. */ if (str_len(hf->header) + sizeof("\r\n") > hf->max_size) { hf->empty = TRUE; str_setlen(hf->header, 0); } else { hf->empty = FALSE; } header_fmt_check(hf); return hf; }
/** * Collect entropy by randomly feeding values from array. */ static void entropy_array_data_collect(SHA1Context *ctx, enum entropy_data data, void *ary, size_t len, size_t elem_size) { size_t i; void *p; g_assert(ctx != NULL); g_assert(ary != NULL); g_assert(size_is_non_negative(len)); g_assert(size_is_positive(elem_size)); entropy_array_shuffle(ary, len, elem_size); for (i = 0, p = ary; i < len; i++, p = ptr_add_offset(p, elem_size)) { switch (data) { case ENTROPY_ULONG: sha1_feed_ulong(ctx, *(unsigned long *) p); break; case ENTROPY_STRING: sha1_feed_string(ctx, *(char **) p); break; case ENTROPY_STAT: sha1_feed_stat(ctx, *(char **) p); break; case ENTROPY_FSTAT: sha1_feed_fstat(ctx, *(int *) p); break; case ENTROPY_DOUBLE: sha1_feed_double(ctx, *(double *) p); break; case ENTROPY_POINTER: sha1_feed_pointer(ctx, *(void **) p); break; } } }
/** * Pending data were all flushed. */ static void deflate_flushed(txdrv_t *tx) { struct attr *attr = tx->opaque; double flush = 0.0; g_assert(size_is_non_negative(attr->unflushed)); attr->total_input += attr->unflushed; attr->total_output += attr->flushed; g_return_unless(attr->total_input != 0); attr->ratio = 1.0 - ((double) attr->total_output / attr->total_input); if (0 != attr->unflushed) { /* * Fast EMA for compression ratio is computed for the last n=3 flushes, * so the smoothing factor sm=2/(n+1) is 1/2. */ flush = 1.0 - ((double) attr->flushed / attr->unflushed); attr->ratio_ema += (flush / 2.0) - (attr->ratio_ema / 2.0); } if (tx_deflate_debugging(4)) { g_debug("TX %s: (%s) deflated %zu bytes into %zu " "(%.2f%%, EMA=%.2f%%, overall %.2f%%)", G_STRFUNC, gnet_host_to_string(&tx->host), attr->unflushed, attr->flushed, 100 * flush, 100 * attr->ratio_ema, 100 * attr->ratio); } attr->unflushed = attr->flushed = 0; attr->flags &= ~DF_FLUSH; }
/** * Append data `str' to the header line, atomically. * * @param `hf' no brief description. * @param `str' no brief description. * @param `separator' is an optional separator string that will be emitted * BEFORE outputting the data, and only when nothing has been emitted * already. * @param `slen' is the separator length, 0 if empty. * @param `sslen' is the stripped separator length, (size_t)-1 if unknown yet. * * @return TRUE if we were able to fit the string, FALSE if it would have * resulted in the header being larger than the configured max size (the * header line is left in the state it was in upon entry, in that case). */ static bool header_fmt_append_full(header_fmt_t *hf, const char *str, const char *separator, size_t slen, size_t sslen) { size_t len, curlen; gsize gslen; bool success; header_fmt_check(hf); g_assert(size_is_non_negative(slen)); g_assert((size_t)-1 == sslen || size_is_non_negative(sslen)); if (hf->empty) return FALSE; gslen = str_len(hf->header); len = strlen(str); curlen = hf->current_len; g_assert(size_is_non_negative(curlen)); g_assert(len <= INT_MAX); /* Legacy bug */ if ( size_saturate_add(curlen, size_saturate_add(len, slen)) > UNSIGNED(hf->maxlen) ) { /* * Emit sperator, if any and data was already emitted. */ if (separator != NULL && hf->data_emitted) { sslen = (size_t)-1 != sslen ? sslen : stripped_strlen(separator, slen); str_cat_len(hf->header, separator, sslen); } STR_CAT(hf->header, "\r\n\t"); /* Includes continuation */ curlen = 1; /* One tab */ } else if (hf->data_emitted) { str_cat(hf->header, separator); curlen += slen; } str_cat(hf->header, str); /* * Check for overflows, undoing string changes if needed. */ if (str_len(hf->header) + sizeof("\r\n") > hf->max_size) { success = FALSE; str_setlen(hf->header, gslen); /* Undo! */ } else { success = TRUE; hf->data_emitted = TRUE; hf->current_len = curlen + len; } g_assert(str_len(hf->header) + sizeof("\r\n") <= hf->max_size); return success; }
/** * Create a security token from host address and port using specified key. * * Optionally, extra contextual data may be given (i.e. the token is not * only based on the address and port) to make the token more unique to * a specific context. * * @param stg the security token generator * @param n key index to use * @param tok where security token is written * @param addr address of the host for which we're generating a token * @param port port of the host for which we're generating a token * @param data optional contextual data * @param len length of contextual data */ static void sectoken_generate_n(sectoken_gen_t *stg, size_t n, sectoken_t *tok, host_addr_t addr, uint16 port, const void *data, size_t len) { char block[8]; char enc[8]; char *p = block; sectoken_gen_check(stg); g_assert(tok != NULL); g_assert(size_is_non_negative(n)); g_assert(n < stg->keycnt); g_assert((NULL != data) == (len != 0)); switch (host_addr_net(addr)) { case NET_TYPE_IPV4: p = poke_be32(p, host_addr_ipv4(addr)); break; case NET_TYPE_IPV6: { uint val; val = binary_hash(host_addr_ipv6(&addr), 16); p = poke_be32(p, val); } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_error("unexpected address for security token generation: %s", host_addr_to_string(addr)); } p = poke_be16(p, port); p = poke_be16(p, 0); /* Filler */ g_assert(p == &block[8]); STATIC_ASSERT(sizeof(tok->v) == sizeof(uint32)); STATIC_ASSERT(sizeof(block) == sizeof(enc)); tea_encrypt(&stg->keys[n], enc, block, sizeof block); /* * If they gave contextual data, encrypt them by block of 8 bytes, * filling the last partial block with zeroes if needed. */ if (data != NULL) { const void *q = data; size_t remain = len; char denc[8]; STATIC_ASSERT(sizeof(denc) == sizeof(enc)); while (remain != 0) { size_t fill = MIN(remain, 8U); unsigned i; if (fill != 8U) ZERO(&block); memcpy(block, q, fill); remain -= fill; q = const_ptr_add_offset(q, fill); /* * Encrypt block of contextual data (possibly filled with trailing * zeroes) and merge back the result into the main encryption * output with XOR. */ tea_encrypt(&stg->keys[n], denc, block, sizeof block); for (i = 0; i < sizeof denc; i++) enc[i] ^= denc[i]; } } poke_be32(tok->v, tea_squeeze(enc, sizeof enc)); }
/** * Needs brief description here. * * Substitutes variables from string: * * - The leading "~" is replaced by the home directory. * - Variables like "$PATH" or "${PATH}" are replaced by their value, as * fetched from the environment, or the empty string if not found. * * If given a NULL input, we return NULL. * * @return string constant, which is not meant to be freed until exit time. */ const char * eval_subst(const char *str) { char buf[MAX_STRING]; char *end = &buf[sizeof(buf)]; char *p; size_t len; char c; if (str == NULL) return NULL; len = g_strlcpy(buf, str, sizeof buf); if (len >= sizeof buf) { g_warning("%s(): string too large for substitution (%zu bytes)", G_STRFUNC, len); return constant_str(str); } if (common_dbg > 3) g_debug("%s: on entry: \"%s\"", G_STRFUNC, buf); for (p = buf, c = *p++; c; c = *p++) { const char *val = NULL; char *start = p - 1; switch (c) { case '~': if (start == buf && ('\0' == buf[1] || '/' == buf[1])) { /* Leading ~ only */ val = gethomedir(); g_assert(val); memmove(start, &start[1], len - (start - buf)); len--; g_assert(size_is_non_negative(len)); } break; case '$': { const char *after; val = get_variable(p, &after); g_assert(val); memmove(start, after, len + 1 - (after - buf)); len -= after - start; /* Also removing leading '$' */ g_assert(size_is_non_negative(len)); } break; } if (val != NULL) { char *next; next = insert_value(val, start, start - buf, len, sizeof buf - 1); len += next - start; p = next; g_assert(len < sizeof buf); g_assert(p < end); } g_assert(p <= &buf[len]); } if (common_dbg > 3) g_debug("%s: on exit: \"%s\"", G_STRFUNC, buf); g_assert(len == strlen(buf)); return constant_str(buf); }