diff --git a/src/lib/dsa/ares__array.c b/src/lib/dsa/ares__array.c index 0c724248..40c2598a 100644 --- a/src/lib/dsa/ares__array.c +++ b/src/lib/dsa/ares__array.c @@ -265,6 +265,46 @@ ares_status_t ares__array_insert_first(void **elem_ptr, ares__array_t *arr) return ares__array_insert_at(elem_ptr, arr, 0); } +ares_status_t ares__array_insertdata_at(ares__array_t *arr, size_t idx, + void *data_ptr) +{ + ares_status_t status; + void *ptr = NULL; + + status = ares__array_insert_at(&ptr, arr, idx); + if (status != ARES_SUCCESS) { + return status; + } + memcpy(ptr, data_ptr, arr->member_size); + return ARES_SUCCESS; +} + +ares_status_t ares__array_insertdata_last(ares__array_t *arr, void *data_ptr) +{ + ares_status_t status; + void *ptr = NULL; + + status = ares__array_insert_last(&ptr, arr); + if (status != ARES_SUCCESS) { + return status; + } + memcpy(ptr, data_ptr, arr->member_size); + return ARES_SUCCESS; +} + +ares_status_t ares__array_insertdata_first(ares__array_t *arr, void *data_ptr) +{ + ares_status_t status; + void *ptr = NULL; + + status = ares__array_insert_last(&ptr, arr); + if (status != ARES_SUCCESS) { + return status; + } + memcpy(ptr, data_ptr, arr->member_size); + return ARES_SUCCESS; +} + void *ares__array_first(ares__array_t *arr) { return ares__array_at(arr, 0); diff --git a/src/lib/include/ares__array.h b/src/lib/include/ares__array.h index 7b0fca4f..b568cf8b 100644 --- a/src/lib/include/ares__array.h +++ b/src/lib/include/ares__array.h @@ -153,6 +153,52 @@ CARES_EXTERN ares_status_t ares__array_insert_last(void **elem_ptr, CARES_EXTERN ares_status_t ares__array_insert_first(void **elem_ptr, ares__array_t *arr); + +/*! Insert a new array member at the given index and copy the data pointed + * to by the data pointer into the array. This will copy member_size bytes + * from the provided pointer, this may not be safe for some data types + * that may have a smaller size than the provided member_size which includes + * padding as discussed in ares__array_create(). + * + * \param[in] arr Initialized array object. + * \param[in] idx Index in array to place new element, will shift any + * elements down that exist after this point. + * \param[in] data_ptr Pointer to data to copy into array. + * \return ARES_SUCCESS on success, ARES_EFORMERR on bad index or null data ptr, + * ARES_ENOMEM on out of memory. + */ +CARES_EXTERN ares_status_t ares__array_insertdata_at(ares__array_t *arr, + size_t idx, + void *data_ptr); + +/*! Insert a new array member at the end of the array and copy the data pointed + * to by the data pointer into the array. This will copy member_size bytes + * from the provided pointer, this may not be safe for some data types + * that may have a smaller size than the provided member_size which includes + * padding as discussed in ares__array_create(). + * + * \param[in] arr Initialized array object. + * \param[in] data_ptr Pointer to data to copy into array. + * \return ARES_SUCCESS on success, ARES_EFORMERR on bad index or null data ptr, + * ARES_ENOMEM on out of memory. + */ +CARES_EXTERN ares_status_t ares__array_insertdata_last(ares__array_t *arr, + void *data_ptr); + +/*! Insert a new array member at the beginning of the array and copy the data pointed + * to by the data pointer into the array. This will copy member_size bytes + * from the provided pointer, this may not be safe for some data types + * that may have a smaller size than the provided member_size which includes + * padding as discussed in ares__array_create(). + * + * \param[in] arr Initialized array object. + * \param[in] data_ptr Pointer to data to copy into array. + * \return ARES_SUCCESS on success, ARES_EFORMERR on bad index or null data ptr, + * ARES_ENOMEM on out of memory. + */ +CARES_EXTERN ares_status_t ares__array_insertdata_first(ares__array_t *arr, + void *data_ptr); + /*! Fetch a pointer to the given element in the array * \param[in] array Initialized array object * \param[in] idx Index to fetch @@ -227,6 +273,7 @@ CARES_EXTERN ares_status_t ares__array_remove_first(ares__array_t *arr); */ CARES_EXTERN ares_status_t ares__array_remove_last(ares__array_t *arr); + /*! @} */ #endif /* __ARES__ARRAY_H */ diff --git a/src/lib/include/ares__buf.h b/src/lib/include/ares__buf.h index e1f10adf..4c1a1361 100644 --- a/src/lib/include/ares__buf.h +++ b/src/lib/include/ares__buf.h @@ -361,7 +361,7 @@ CARES_EXTERN ares_status_t ares__buf_fetch_bytes_into_buf(ares__buf_t *buf, /*! Fetch the requested number of bytes and return a new buffer that must be * ares_free()'d by the caller. The returned buffer is a null terminated - * string. + * string. The data is validated to be ASCII-printable. * * \param[in] buf Initialized buffer object * \param[in] len Requested number of bytes (must be > 0) @@ -482,6 +482,26 @@ CARES_EXTERN ares_status_t ares__buf_split( ares__buf_t *buf, const unsigned char *delims, size_t delims_len, ares__buf_split_t flags, size_t max_sections, ares__llist_t **list); +/*! Split the provided buffer into a C array of C strings. + * + * \param[in] buf Initialized buffer object + * \param[in] delims Possible delimiters + * \param[in] delims_len Length of possible delimiters + * \param[in] flags One more more flags + * \param[in] max_sections Maximum number of sections. Use 0 for + * unlimited. Useful for splitting key/value + * pairs where the delimiter may be a valid + * character in the value. A value of 1 would + * have little usefulness and would effectively + * ignore the delimiter itself. + * \param[out] strs Array of strings. Free using + * ares_free_array(strs, nstrs, ares_free) + * \param[out] nstrs Number of elements in the array. + * \return ARES_SUCCESS on success, or error like ARES_ENOMEM. + */ +CARES_EXTERN ares_status_t ares__buf_split_str( + ares__buf_t *buf, const unsigned char *delims, size_t delims_len, + ares__buf_split_t flags, size_t max_sections, char ***strs, size_t *nstrs); /*! Check the unprocessed buffer to see if it begins with the sequence of * characters provided. diff --git a/src/lib/include/ares_str.h b/src/lib/include/ares_str.h index 473cdb35..cae7bbfd 100644 --- a/src/lib/include/ares_str.h +++ b/src/lib/include/ares_str.h @@ -214,4 +214,16 @@ CARES_EXTERN ares_bool_t ares_strcaseeq(const char *a, const char *b); CARES_EXTERN ares_bool_t ares_strcaseeq_max(const char *a, const char *b, size_t n); +/*! Free a C array, each element in the array will be freed by the provided + * free function. Both NULL-terminated arrays and known length arrays are + * supported. + * + * \param[in] arr Array to be freed. + * \param[in] nmembers Number of members in the array, or SIZE_MAX for + * NULL-terminated arrays + * \param[in] freefunc Function to call on each array member (e.g. ares_free) + */ +CARES_EXTERN void ares_free_array(void *arr, size_t nmembers, + void (*freefunc)(void *)); + #endif /* __ARES_STR_H */ diff --git a/src/lib/str/ares__buf.c b/src/lib/str/ares__buf.c index 36334a06..54b00d7d 100644 --- a/src/lib/str/ares__buf.c +++ b/src/lib/str/ares__buf.c @@ -553,12 +553,20 @@ ares_status_t ares__buf_fetch_bytes_dup(ares__buf_t *buf, size_t len, ares_status_t ares__buf_fetch_str_dup(ares__buf_t *buf, size_t len, char **str) { size_t remaining_len; + size_t i; const unsigned char *ptr = ares__buf_fetch(buf, &remaining_len); if (buf == NULL || str == NULL || len == 0 || remaining_len < len) { return ARES_EBADRESP; } + /* Validate string is printable */ + for (i = 0; i < len; i++) { + if (!ares__isprint(ptr[i])) { + return ARES_EBADSTR; + } + } + *str = ares_malloc(len + 1); if (*str == NULL) { return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ @@ -884,6 +892,67 @@ done: return status; } +static void ares__free_split_array(void *arg) +{ + void **ptr = arg; + ares_free(*ptr); +} + +ares_status_t ares__buf_split_str(ares__buf_t *buf, const unsigned char *delims, + size_t delims_len, ares__buf_split_t flags, + size_t max_sections, char ***strs, + size_t *nstrs) +{ + ares_status_t status; + ares__llist_t *list = NULL; + ares__llist_node_t *node; + ares__array_t *arr = NULL; + + if (strs == NULL || nstrs == NULL) { + return ARES_EFORMERR; + } + + *strs = NULL; + *nstrs = 0; + + status = ares__buf_split(buf, delims, delims_len, flags, max_sections, &list); + if (status != ARES_SUCCESS) { + goto done; + } + + arr = ares__array_create(sizeof(char *), ares__free_split_array); + if (arr == NULL) { + status = ARES_ENOMEM; + goto done; + } + + for (node = ares__llist_node_first(list); node != NULL; + node = ares__llist_node_next(node)) { + ares__buf_t *lbuf = ares__llist_node_val(node); + char *str = NULL; + + status = ares__buf_fetch_str_dup(lbuf, ares__buf_len(lbuf), &str); + if (status != ARES_SUCCESS) { + goto done; + } + + status = ares__array_insertdata_last(arr, &str); + if (status != ARES_SUCCESS) { + ares_free(str); + goto done; + } + } + +done: + ares__llist_destroy(list); + if (status == ARES_SUCCESS) { + *strs = ares__array_finish(arr, nstrs); + } else { + ares__array_destroy(arr); + } + return status; +} + ares_bool_t ares__buf_begins_with(const ares__buf_t *buf, const unsigned char *data, size_t data_len) { diff --git a/src/lib/str/ares_str.c b/src/lib/str/ares_str.c index 5800e228..5b84d45e 100644 --- a/src/lib/str/ares_str.c +++ b/src/lib/str/ares_str.c @@ -394,3 +394,25 @@ ares_bool_t ares_streq_max(const char *a, const char *b, size_t n) return ares_strncmp(a,b,n) == 0?ARES_TRUE:ARES_FALSE; } +void ares_free_array(void *arrp, size_t nmembers, void (*freefunc)(void *)) +{ + size_t i; + void **arr = arrp; + + if (arr == NULL) + return; + + if (freefunc != NULL) { + if (nmembers == SIZE_MAX) { + for (i = 0; arr[i] != NULL; i++) { + freefunc(arr[i]); + } + } else { + for (i = 0; i