• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* MIT License
2  *
3  * Copyright (c) 2023 Brad House
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * SPDX-License-Identifier: MIT
25  */
26 #ifndef __ARES__BUF_H
27 #define __ARES__BUF_H
28 
29 /*! \addtogroup ares__buf Safe Data Builder and buffer
30  *
31  * This is a buffer building and parsing framework with a focus on security over
32  * performance. All data to be read from the buffer will perform explicit length
33  * validation and return a success/fail result.  There are also various helpers
34  * for writing data to the buffer which dynamically grows.
35  *
36  * All operations that fetch or consume data from the buffer will move forward
37  * the internal pointer, thus marking the data as processed which may no longer
38  * be accessible after certain operations (such as append).
39  *
40  * The helpers for this object are meant to be added as needed.  If you can't
41  * find it, write it!
42  *
43  * @{
44  */
45 struct ares__buf;
46 
47 /*! Opaque data type for generic hash table implementation */
48 typedef struct ares__buf ares__buf_t;
49 
50 /*! Create a new buffer object that dynamically allocates buffers for data.
51  *
52  *  \return initialized buffer object or NULL if out of memory.
53  */
54 ares__buf_t             *ares__buf_create(void);
55 
56 /*! Create a new buffer object that uses a user-provided data pointer.  The
57  *  data provided will not be manipulated, and cannot be appended to.  This
58  *  is strictly used for parsing.
59  *
60  *  \param[in] data     Data to provide to buffer, must not be NULL.
61  *  \param[in] data_len Size of buffer provided, must be > 0
62  *
63  *  \return initialized buffer object or NULL if out of memory or misuse.
64  */
65 ares__buf_t *ares__buf_create_const(const unsigned char *data, size_t data_len);
66 
67 
68 /*! Destroy an initialized buffer object.
69  *
70  *  \param[in] buf  Initialized buf object
71  */
72 void         ares__buf_destroy(ares__buf_t *buf);
73 
74 
75 /*! Append multiple bytes to a dynamic buffer object
76  *
77  *  \param[in] buf      Initialized buffer object
78  *  \param[in] data     Data to copy to buffer object
79  *  \param[in] data_len Length of data to copy to buffer object.
80  *  \return ARES_SUCCESS or one of the c-ares error codes
81  */
82 ares_status_t  ares__buf_append(ares__buf_t *buf, const unsigned char *data,
83                                 size_t data_len);
84 
85 /*! Append a single byte to the dynamic buffer object
86  *
87  *  \param[in] buf      Initialized buffer object
88  *  \param[in] byte     Single byte to append to buffer object.
89  *  \return ARES_SUCCESS or one of the c-ares error codes
90  */
91 ares_status_t  ares__buf_append_byte(ares__buf_t *buf, unsigned char byte);
92 
93 /*! Append a null-terminated string to the dynamic buffer object
94  *
95  *  \param[in] buf      Initialized buffer object
96  *  \param[in] str      String to append to buffer object.
97  *  \return ARES_SUCCESS or one of the c-ares error codes
98  */
99 ares_status_t  ares__buf_append_str(ares__buf_t *buf, const char *str);
100 
101 /*! Append a 16bit Big Endian number to the buffer.
102  *
103  *  \param[in]  buf     Initialized buffer object
104  *  \param[out] u16     16bit integer
105  *  \return ARES_SUCCESS or one of the c-ares error codes
106  */
107 ares_status_t  ares__buf_append_be16(ares__buf_t *buf, unsigned short u16);
108 
109 /*! Append a 32bit Big Endian number to the buffer.
110  *
111  *  \param[in]  buf     Initialized buffer object
112  *  \param[out] u32     32bit integer
113  *  \return ARES_SUCCESS or one of the c-ares error codes
114  */
115 ares_status_t  ares__buf_append_be32(ares__buf_t *buf, unsigned int u32);
116 
117 /*! Append a number in ASCII decimal form.
118  *
119  *  \param[in] buf  Initialized buffer object
120  *  \param[in] num  Number to print
121  *  \param[in] len  Length to output, use 0 for no padding
122  *  \return ARES_SUCCESS on success
123  */
124 ares_status_t  ares__buf_append_num_dec(ares__buf_t *buf, size_t num,
125                                         size_t len);
126 
127 /*! Append a number in ASCII hexadecimal form.
128  *
129  *  \param[in] buf  Initialized buffer object
130  *  \param[in] num  Number to print
131  *  \param[in] len  Length to output, use 0 for no padding
132  *  \return ARES_SUCCESS on success
133  */
134 ares_status_t  ares__buf_append_num_hex(ares__buf_t *buf, size_t num,
135                                         size_t len);
136 
137 /*! Sets the current buffer length.  This *may* be used if there is a need to
138  *  override a prior position in the buffer, such as if there is a length
139  *  prefix that isn't easily predictable, and you must go back and overwrite
140  *  that position.
141  *
142  *  Only valid on non-const buffers.  Length provided must not exceed current
143  *  allocated buffer size, but otherwise there are very few protections on
144  *  this function.  Use cautiously.
145  *
146  *  \param[in]  buf  Initialized buffer object
147  *  \param[in]  len  Length to set
148  *  \return ARES_SUCCESS or one of the c-ares error codes
149  */
150 ares_status_t  ares__buf_set_length(ares__buf_t *buf, size_t len);
151 
152 
153 /*! Start a dynamic append operation that returns a buffer suitable for
154  *  writing.  A desired minimum length is passed in, and the actual allocated
155  *  buffer size is returned which may be greater than the requested size.
156  *  No operation other than ares__buf_append_finish() is allowed on the
157  *  buffer after this request.
158  *
159  *  \param[in]     buf     Initialized buffer object
160  *  \param[in,out] len     Desired non-zero length passed in, actual buffer size
161  *                         returned.
162  *  \return Pointer to writable buffer or NULL on failure (usage, out of mem)
163  */
164 unsigned char *ares__buf_append_start(ares__buf_t *buf, size_t *len);
165 
166 /*! Finish a dynamic append operation.  Called after
167  *  ares__buf_append_start() once desired data is written.
168  *
169  *  \param[in] buf    Initialized buffer object.
170  *  \param[in] len    Length of data written.  May be zero to terminate
171  *                    operation. Must not be greater than returned from
172  *                    ares__buf_append_start().
173  */
174 void           ares__buf_append_finish(ares__buf_t *buf, size_t len);
175 
176 /*! Write the data provided to the buffer in a hexdump format.
177  *
178  *  \param[in] buf      Initialized buffer object.
179  *  \param[in] data     Data to hex dump
180  *  \param[in] data_len Length of data to hexdump
181  *  \return ARES_SUCCESS on success.
182  */
183 ares_status_t  ares__buf_hexdump(ares__buf_t *buf, const unsigned char *data,
184                                  size_t len);
185 
186 /*! Clean up ares__buf_t and return allocated pointer to unprocessed data.  It
187  *  is the responsibility of the  caller to ares_free() the returned buffer.
188  *  The passed in buf parameter is invalidated by this call.
189  *
190  * \param[in]  buf    Initialized buffer object. Can not be a "const" buffer.
191  * \param[out] len    Length of data returned
192  * \return pointer to unprocessed data (may be zero length) or NULL on error.
193  */
194 unsigned char *ares__buf_finish_bin(ares__buf_t *buf, size_t *len);
195 
196 /*! Clean up ares__buf_t and return allocated pointer to unprocessed data and
197  *  return it as a string (null terminated).  It is the responsibility of the
198  *  caller to ares_free() the returned buffer. The passed in buf parameter is
199  *  invalidated by this call.
200  *
201  *  This function in no way validates the data in this buffer is actually
202  *  a string, that characters are printable, or that there aren't multiple
203  *  NULL terminators.  It is assumed that the caller will either validate that
204  *  themselves or has built this buffer with only a valid character set.
205  *
206  * \param[in]  buf    Initialized buffer object. Can not be a "const" buffer.
207  * \param[out] len    Optional. Length of data returned, or NULL if not needed.
208  * \return pointer to unprocessed data or NULL on error.
209  */
210 char          *ares__buf_finish_str(ares__buf_t *buf, size_t *len);
211 
212 /*! Tag a position to save in the buffer in case parsing needs to rollback,
213  *  such as if insufficient data is available, but more data may be added in
214  *  the future.  Only a single tag can be set per buffer object.  Setting a
215  *  tag will override any pre-existing tag.
216  *
217  *  \param[in] buf Initialized buffer object
218  */
219 void           ares__buf_tag(ares__buf_t *buf);
220 
221 /*! Rollback to a tagged position.  Will automatically clear the tag.
222  *
223  *  \param[in] buf Initialized buffer object
224  *  \return ARES_SUCCESS or one of the c-ares error codes
225  */
226 ares_status_t  ares__buf_tag_rollback(ares__buf_t *buf);
227 
228 /*! Clear the tagged position without rolling back.  You should do this any
229  *  time a tag is no longer needed as future append operations can reclaim
230  *  buffer space.
231  *
232  *  \param[in] buf Initialized buffer object
233  *  \return ARES_SUCCESS or one of the c-ares error codes
234  */
235 ares_status_t  ares__buf_tag_clear(ares__buf_t *buf);
236 
237 /*! Fetch the buffer and length of data starting from the tagged position up
238  *  to the _current_ position.  It will not unset the tagged position.  The
239  *  data may be invalidated by any future ares__buf_*() calls.
240  *
241  *  \param[in]  buf    Initialized buffer object
242  *  \param[out] len    Length between tag and current offset in buffer
243  *  \return NULL on failure (such as no tag), otherwise pointer to start of
244  *          buffer
245  */
246 const unsigned char *ares__buf_tag_fetch(const ares__buf_t *buf, size_t *len);
247 
248 /*! Get the length of the current tag offset to the current position.
249  *
250  *  \param[in]  buf    Initialized buffer object
251  *  \return length
252  */
253 size_t               ares__buf_tag_length(const ares__buf_t *buf);
254 
255 /*! Fetch the bytes starting from the tagged position up to the _current_
256  *  position using the provided buffer.  It will not unset the tagged position.
257  *
258  *  \param[in]     buf    Initialized buffer object
259  *  \param[in,out] bytes  Buffer to hold data
260  *  \param[in,out] len    On input, buffer size, on output, bytes place in
261  *                        buffer.
262  *  \return ARES_SUCCESS if fetched, ARES_EFORMERR if insufficient buffer size
263  */
264 ares_status_t        ares__buf_tag_fetch_bytes(const ares__buf_t *buf,
265                                                unsigned char *bytes, size_t *len);
266 
267 /*! Fetch the bytes starting from the tagged position up to the _current_
268  *  position as a NULL-terminated string using the provided buffer.  The data
269  *  is validated to be ASCII-printable data.  It will not unset the tagged
270  *  poition.
271  *
272  *  \param[in]     buf    Initialized buffer object
273  *  \param[in,out] str    Buffer to hold data
274  *  \param[in]     len    On input, buffer size, on output, bytes place in
275  *                        buffer.
276  *  \return ARES_SUCCESS if fetched, ARES_EFORMERR if insufficient buffer size,
277  *          ARES_EBADSTR if not printable ASCII
278  */
279 ares_status_t ares__buf_tag_fetch_string(const ares__buf_t *buf, char *str,
280                                          size_t len);
281 
282 /*! Consume the given number of bytes without reading them.
283  *
284  *  \param[in] buf    Initialized buffer object
285  *  \param[in] len    Length to consume
286  *  \return ARES_SUCCESS or one of the c-ares error codes
287  */
288 ares_status_t ares__buf_consume(ares__buf_t *buf, size_t len);
289 
290 /*! Fetch a 16bit Big Endian number from the buffer.
291  *
292  *  \param[in]  buf     Initialized buffer object
293  *  \param[out] u16     Buffer to hold 16bit integer
294  *  \return ARES_SUCCESS or one of the c-ares error codes
295  */
296 ares_status_t ares__buf_fetch_be16(ares__buf_t *buf, unsigned short *u16);
297 
298 /*! Fetch a 32bit Big Endian number from the buffer.
299  *
300  *  \param[in]  buf     Initialized buffer object
301  *  \param[out] u32     Buffer to hold 32bit integer
302  *  \return ARES_SUCCESS or one of the c-ares error codes
303  */
304 ares_status_t ares__buf_fetch_be32(ares__buf_t *buf, unsigned int *u32);
305 
306 
307 /*! Fetch the requested number of bytes into the provided buffer
308  *
309  *  \param[in]  buf     Initialized buffer object
310  *  \param[out] bytes   Buffer to hold data
311  *  \param[in]  len     Requested number of bytes (must be > 0)
312  *  \return ARES_SUCCESS or one of the c-ares error codes
313  */
314 ares_status_t ares__buf_fetch_bytes(ares__buf_t *buf, unsigned char *bytes,
315                                     size_t len);
316 
317 
318 /*! Fetch the requested number of bytes and return a new buffer that must be
319  *  ares_free()'d by the caller.
320  *
321  *  \param[in]  buf       Initialized buffer object
322  *  \param[in]  len       Requested number of bytes (must be > 0)
323  *  \param[in]  null_term Even though this is considered binary data, the user
324  *                        knows it may be a vald string, so add a null
325  *                        terminator.
326  *  \param[out] bytes     Pointer passed by reference. Will be allocated.
327  *  \return ARES_SUCCESS or one of the c-ares error codes
328  */
329 ares_status_t ares__buf_fetch_bytes_dup(ares__buf_t *buf, size_t len,
330                                         ares_bool_t     null_term,
331                                         unsigned char **bytes);
332 
333 /*! Fetch the requested number of bytes and place them into the provided
334  *  dest buffer object.
335  *
336  *  \param[in]  buf     Initialized buffer object
337  *  \param[out] dest    Buffer object to append bytes.
338  *  \param[in]  len     Requested number of bytes (must be > 0)
339  *  \return ARES_SUCCESS or one of the c-ares error codes
340  */
341 ares_status_t ares__buf_fetch_bytes_into_buf(ares__buf_t *buf,
342                                              ares__buf_t *dest, size_t len);
343 
344 /*! Fetch the requested number of bytes and return a new buffer that must be
345  *  ares_free()'d by the caller.  The returned buffer is a null terminated
346  *  string.
347  *
348  *  \param[in]  buf     Initialized buffer object
349  *  \param[in]  len     Requested number of bytes (must be > 0)
350  *  \param[out] str     Pointer passed by reference. Will be allocated.
351  *  \return ARES_SUCCESS or one of the c-ares error codes
352  */
353 ares_status_t ares__buf_fetch_str_dup(ares__buf_t *buf, size_t len, char **str);
354 
355 /*! Consume whitespace characters (0x09, 0x0B, 0x0C, 0x0D, 0x20, and optionally
356  *  0x0A).
357  *
358  *  \param[in]  buf               Initialized buffer object
359  *  \param[in]  include_linefeed  ARES_TRUE to include consuming 0x0A,
360  *                                ARES_FALSE otherwise.
361  *  \return number of whitespace characters consumed
362  */
363 size_t        ares__buf_consume_whitespace(ares__buf_t *buf,
364                                            ares_bool_t  include_linefeed);
365 
366 
367 /*! Consume any non-whitespace character (anything other than 0x09, 0x0B, 0x0C,
368  *  0x0D, 0x20, and 0x0A).
369  *
370  *  \param[in]  buf               Initialized buffer object
371  *  \return number of characters consumed
372  */
373 size_t        ares__buf_consume_nonwhitespace(ares__buf_t *buf);
374 
375 
376 /*! Consume until a character in the character set provided is reached
377  *
378  *  \param[in] buf                Initialized buffer object
379  *  \param[in] charset            character set
380  *  \param[in] len                length of character set
381  *  \param[in] require_charset    require we find a character from the charset.
382  *                                if ARES_FALSE it will simply consume the
383  *                                rest of the buffer.  If ARES_TRUE will return
384  *                                0 if not found.
385  *  \return number of characters consumed
386  */
387 size_t        ares__buf_consume_until_charset(ares__buf_t         *buf,
388                                               const unsigned char *charset, size_t len,
389                                               ares_bool_t require_charset);
390 
391 
392 /*! Consume while the characters match the characters in the provided set.
393  *
394  *  \param[in] buf                Initialized buffer object
395  *  \param[in] charset            character set
396  *  \param[in] len                length of character set
397  *  \return number of characters consumed
398  */
399 size_t ares__buf_consume_charset(ares__buf_t *buf, const unsigned char *charset,
400                                  size_t len);
401 
402 
403 /*! Consume from the current position until the end of the line, and optionally
404  *  the end of line character (0x0A) itself.
405  *
406  *  \param[in]  buf               Initialized buffer object
407  *  \param[in]  include_linefeed  ARES_TRUE to include consuming 0x0A,
408  *                                ARES_FALSE otherwise.
409  *  \return number of characters consumed
410  */
411 size_t ares__buf_consume_line(ares__buf_t *buf, ares_bool_t include_linefeed);
412 
413 typedef enum {
414   /*! No flags */
415   ARES_BUF_SPLIT_NONE = 0,
416   /*! The delimiter will be the first character in the buffer, except the
417    *  first buffer since the start doesn't have a delimiter
418    */
419   ARES_BUF_SPLIT_DONT_CONSUME_DELIMS = 1 << 0,
420   /*! Allow blank sections, by default blank sections are not emitted.  If using
421    *  ARES_BUF_SPLIT_DONT_CONSUME_DELIMS, the delimiter is not counted as part
422    *  of the section */
423   ARES_BUF_SPLIT_ALLOW_BLANK = 1 << 1,
424   /*! Remove duplicate entries */
425   ARES_BUF_SPLIT_NO_DUPLICATES = 1 << 2,
426   /*! Perform case-insensitive matching when comparing values */
427   ARES_BUF_SPLIT_CASE_INSENSITIVE = 1 << 3
428 } ares__buf_split_t;
429 
430 /*! Split the provided buffer into multiple sub-buffers stored in the variable
431  *  pointed to by the linked list.  The sub buffers are const buffers pointing
432  *  into the buf provided.
433  *
434  *  \param[in]  buf               Initialized buffer object
435  *  \param[in]  delims            Possible delimiters
436  *  \param[in]  delims_len        Length of possible delimiters
437  *  \param[in]  flags             One more more flags
438  *  \param[out] list              Result. Depending on flags, this may be a
439  *                                valid list with no elements.  Use
440  *                                ares__llist_destroy() to free the memory which
441  *                                will also free the contained ares__buf_t
442  *                                objects.
443  *  \return ARES_SUCCESS on success, or error like ARES_ENOMEM.
444  */
445 ares_status_t ares__buf_split(ares__buf_t *buf, const unsigned char *delims,
446                               size_t delims_len, ares__buf_split_t flags,
447                               ares__llist_t **list);
448 
449 
450 /*! Check the unprocessed buffer to see if it begins with the sequence of
451  *  characters provided.
452  *
453  *  \param[in] buf          Initialized buffer object
454  *  \param[in] data         Bytes of data to compare.
455  *  \param[in] data_len     Length of data to compare.
456  *  \return ARES_TRUE on match, ARES_FALSE otherwise.
457  */
458 ares_bool_t   ares__buf_begins_with(const ares__buf_t   *buf,
459                                     const unsigned char *data, size_t data_len);
460 
461 
462 /*! Size of unprocessed remaining data length
463  *
464  *  \param[in] buf Initialized buffer object
465  *  \return length remaining
466  */
467 size_t        ares__buf_len(const ares__buf_t *buf);
468 
469 /*! Retrieve a pointer to the currently unprocessed data.  Generally this isn't
470  *  recommended to be used in practice.  The returned pointer may be invalidated
471  *  by any future ares__buf_*() calls.
472  *
473  *  \param[in]  buf    Initialized buffer object
474  *  \param[out] len    Length of available data
475  *  \return Pointer to buffer of unprocessed data
476  */
477 const unsigned char *ares__buf_peek(const ares__buf_t *buf, size_t *len);
478 
479 
480 /*! Wipe any processed data from the beginning of the buffer.  This will
481  *  move any remaining data to the front of the internally allocated buffer.
482  *
483  *  Can not be used on const buffer objects.
484  *
485  *  Typically not needed to call, as any new append operation will automatically
486  *  call this function if there is insufficient space to append the data in
487  *  order to try to avoid another memory allocation.
488  *
489  *  It may be useful to call in order to ensure the current message being
490  *  processed is in the beginning of the buffer if there is an intent to use
491  *  ares__buf_set_position() and ares__buf_get_position() as may be necessary
492  *  when processing DNS compressed names.
493  *
494  *  If there is an active tag, it will NOT clear the tag, it will use the tag
495  *  as the start of the unprocessed data rather than the current offset.  If
496  *  a prior tag is no longer needed, may be wise to call ares__buf_tag_clear().
497  *
498  *  \param[in]  buf    Initialized buffer object
499  */
500 void                 ares__buf_reclaim(ares__buf_t *buf);
501 
502 /*! Set the current offset within the internal buffer.
503  *
504  *  Typically this should not be used, if possible, use the ares__buf_tag*()
505  *  operations instead.
506  *
507  *  One exception is DNS name compression which may backwards reference to
508  *  an index in the message.  It may be necessary in such a case to call
509  *  ares__buf_reclaim() if using a dynamic (non-const) buffer before processing
510  *  such a message.
511  *
512  *  \param[in] buf  Initialized buffer object
513  *  \param[in] idx  Index to set position
514  *  \return ARES_SUCCESS if valid index
515  */
516 ares_status_t        ares__buf_set_position(ares__buf_t *buf, size_t idx);
517 
518 /*! Get the current offset within the internal buffer.
519  *
520  *  Typically this should not be used, if possible, use the ares__buf_tag*()
521  *  operations instead.
522  *
523  *  This can be used to get the current position, useful for saving if a
524  *  jump via ares__buf_set_position() is performed and need to restore the
525  *  current position for future operations.
526  *
527  *  \param[in] buf Initialized buffer object
528  *  \return index of current position
529  */
530 size_t               ares__buf_get_position(const ares__buf_t *buf);
531 
532 /*! Parse a character-string as defined in RFC1035, as a null-terminated
533  *  string.
534  *
535  *  \param[in]  buf            initialized buffer object
536  *  \param[in]  remaining_len  maximum length that should be used for parsing
537  *                             the string, this is often less than the remaining
538  *                             buffer and is based on the RR record length.
539  *  \param[out] str            Pointer passed by reference to be filled in with
540  *                             allocated string of the parsed that must be
541  *                             ares_free()'d by the caller.
542  *  \param[in]  allow_multiple ARES_TRUE if it should attempt to parse multiple
543  *                             strings back to back, and will concatenate in
544  *                             the returned str.
545  *  \return ARES_SUCCESS on success
546  */
547 ares_status_t ares__buf_parse_dns_str(ares__buf_t *buf, size_t remaining_len,
548                                       char **name, ares_bool_t allow_multiple);
549 
550 /*! Parse a character-string as defined in RFC1035, as binary, however for
551  *  convenience this does guarantee a NULL terminator (that is not included
552  *  in the returned length).
553  *
554  *  \param[in]  buf            initialized buffer object
555  *  \param[in]  remaining_len  maximum length that should be used for parsing
556  *                             the string, this is often less than the remaining
557  *                             buffer and is based on the RR record length.
558  *  \param[out] bin            Pointer passed by reference to be filled in with
559  *                             allocated string of the parsed that must be
560  *                             ares_free()'d by the caller.
561  *  \param[out] bin_len        Length of returned string.
562  *  \param[in]  allow_multiple ARES_TRUE if it should attempt to parse multiple
563  *                             strings back to back, and will concatenate in
564  *                             the returned str.
565  *  \return ARES_SUCCESS on success
566  */
567 ares_status_t ares__buf_parse_dns_binstr(ares__buf_t *buf, size_t remaining_len,
568                                          unsigned char **bin, size_t *bin_len,
569                                          ares_bool_t allow_multiple);
570 /*! @} */
571 
572 #endif /* __ARES__BUF_H */
573