aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/c-ares/src/lib/include/ares_buf.h
blob: 7836a313e066d1a0bf0915ab57ab583c1a98e7bf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
/* MIT License
 *
 * Copyright (c) 2023 Brad House
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * SPDX-License-Identifier: MIT
 */
#ifndef __ARES__BUF_H
#define __ARES__BUF_H

#include "ares.h"
#include "ares_array.h"

/*! \addtogroup ares_buf Safe Data Builder and buffer
 *
 * This is a buffer building and parsing framework with a focus on security over
 * performance. All data to be read from the buffer will perform explicit length
 * validation and return a success/fail result.  There are also various helpers
 * for writing data to the buffer which dynamically grows.
 *
 * All operations that fetch or consume data from the buffer will move forward
 * the internal pointer, thus marking the data as processed which may no longer
 * be accessible after certain operations (such as append).
 *
 * The helpers for this object are meant to be added as needed.  If you can't
 * find it, write it!
 *
 * @{
 */
struct ares_buf;

/*! Opaque data type for generic hash table implementation */
typedef struct ares_buf     ares_buf_t;

/*! Create a new buffer object that dynamically allocates buffers for data.
 *
 *  \return initialized buffer object or NULL if out of memory.
 */
CARES_EXTERN ares_buf_t    *ares_buf_create(void);

/*! Create a new buffer object that uses a user-provided data pointer.  The
 *  data provided will not be manipulated, and cannot be appended to.  This
 *  is strictly used for parsing.
 *
 *  \param[in] data     Data to provide to buffer, must not be NULL.
 *  \param[in] data_len Size of buffer provided, must be > 0
 *
 *  \return initialized buffer object or NULL if out of memory or misuse.
 */
CARES_EXTERN ares_buf_t    *ares_buf_create_const(const unsigned char *data,
                                                  size_t               data_len);


/*! Destroy an initialized buffer object.
 *
 *  \param[in] buf  Initialized buf object
 */
CARES_EXTERN void           ares_buf_destroy(ares_buf_t *buf);


/*! Append multiple bytes to a dynamic buffer object
 *
 *  \param[in] buf      Initialized buffer object
 *  \param[in] data     Data to copy to buffer object
 *  \param[in] data_len Length of data to copy to buffer object.
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t  ares_buf_append(ares_buf_t          *buf,
                                            const unsigned char *data,
                                            size_t               data_len);

/*! Append a single byte to the dynamic buffer object
 *
 *  \param[in] buf      Initialized buffer object
 *  \param[in] b        Single byte to append to buffer object.
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t  ares_buf_append_byte(ares_buf_t   *buf,
                                                 unsigned char b);

/*! Append a null-terminated string to the dynamic buffer object
 *
 *  \param[in] buf      Initialized buffer object
 *  \param[in] str      String to append to buffer object.
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t  ares_buf_append_str(ares_buf_t *buf,
                                                const char *str);

/*! Append a 16bit Big Endian number to the buffer.
 *
 *  \param[in]  buf     Initialized buffer object
 *  \param[out] u16     16bit integer
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t  ares_buf_append_be16(ares_buf_t    *buf,
                                                 unsigned short u16);

/*! Append a 32bit Big Endian number to the buffer.
 *
 *  \param[in]  buf     Initialized buffer object
 *  \param[out] u32     32bit integer
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t  ares_buf_append_be32(ares_buf_t  *buf,
                                                 unsigned int u32);

/*! Append a number in ASCII decimal form.
 *
 *  \param[in] buf  Initialized buffer object
 *  \param[in] num  Number to print
 *  \param[in] len  Length to output, use 0 for no padding
 *  \return ARES_SUCCESS on success
 */
CARES_EXTERN ares_status_t  ares_buf_append_num_dec(ares_buf_t *buf, size_t num,
                                                    size_t len);

/*! Append a number in ASCII hexadecimal form.
 *
 *  \param[in] buf  Initialized buffer object
 *  \param[in] num  Number to print
 *  \param[in] len  Length to output, use 0 for no padding
 *  \return ARES_SUCCESS on success
 */
CARES_EXTERN ares_status_t  ares_buf_append_num_hex(ares_buf_t *buf, size_t num,
                                                    size_t len);

/*! Sets the current buffer length.  This *may* be used if there is a need to
 *  override a prior position in the buffer, such as if there is a length
 *  prefix that isn't easily predictable, and you must go back and overwrite
 *  that position.
 *
 *  Only valid on non-const buffers.  Length provided must not exceed current
 *  allocated buffer size, but otherwise there are very few protections on
 *  this function.  Use cautiously.
 *
 *  \param[in]  buf  Initialized buffer object
 *  \param[in]  len  Length to set
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t  ares_buf_set_length(ares_buf_t *buf, size_t len);


/*! Start a dynamic append operation that returns a buffer suitable for
 *  writing.  A desired minimum length is passed in, and the actual allocated
 *  buffer size is returned which may be greater than the requested size.
 *  No operation other than ares_buf_append_finish() is allowed on the
 *  buffer after this request.
 *
 *  \param[in]     buf     Initialized buffer object
 *  \param[in,out] len     Desired non-zero length passed in, actual buffer size
 *                         returned.
 *  \return Pointer to writable buffer or NULL on failure (usage, out of mem)
 */
CARES_EXTERN unsigned char *ares_buf_append_start(ares_buf_t *buf, size_t *len);

/*! Finish a dynamic append operation.  Called after
 *  ares_buf_append_start() once desired data is written.
 *
 *  \param[in] buf    Initialized buffer object.
 *  \param[in] len    Length of data written.  May be zero to terminate
 *                    operation. Must not be greater than returned from
 *                    ares_buf_append_start().
 */
CARES_EXTERN void           ares_buf_append_finish(ares_buf_t *buf, size_t len);

/*! Write the data provided to the buffer in a hexdump format.
 *
 *  \param[in] buf      Initialized buffer object.
 *  \param[in] data     Data to hex dump
 *  \param[in] len      Length of data to hexdump
 *  \return ARES_SUCCESS on success.
 */
CARES_EXTERN ares_status_t  ares_buf_hexdump(ares_buf_t          *buf,
                                             const unsigned char *data,
                                             size_t               len);

/*! Clean up ares_buf_t and return allocated pointer to unprocessed data.  It
 *  is the responsibility of the  caller to ares_free() the returned buffer.
 *  The passed in buf parameter is invalidated by this call.
 *
 * \param[in]  buf    Initialized buffer object. Can not be a "const" buffer.
 * \param[out] len    Length of data returned
 * \return pointer to unprocessed data (may be zero length) or NULL on error.
 */
CARES_EXTERN unsigned char *ares_buf_finish_bin(ares_buf_t *buf, size_t *len);

/*! Clean up ares_buf_t and return allocated pointer to unprocessed data and
 *  return it as a string (null terminated).  It is the responsibility of the
 *  caller to ares_free() the returned buffer. The passed in buf parameter is
 *  invalidated by this call.
 *
 *  This function in no way validates the data in this buffer is actually
 *  a string, that characters are printable, or that there aren't multiple
 *  NULL terminators.  It is assumed that the caller will either validate that
 *  themselves or has built this buffer with only a valid character set.
 *
 * \param[in]  buf    Initialized buffer object. Can not be a "const" buffer.
 * \param[out] len    Optional. Length of data returned, or NULL if not needed.
 * \return pointer to unprocessed data or NULL on error.
 */
CARES_EXTERN char          *ares_buf_finish_str(ares_buf_t *buf, size_t *len);

/*! Tag a position to save in the buffer in case parsing needs to rollback,
 *  such as if insufficient data is available, but more data may be added in
 *  the future.  Only a single tag can be set per buffer object.  Setting a
 *  tag will override any pre-existing tag.
 *
 *  \param[in] buf Initialized buffer object
 */
CARES_EXTERN void           ares_buf_tag(ares_buf_t *buf);

/*! Rollback to a tagged position.  Will automatically clear the tag.
 *
 *  \param[in] buf Initialized buffer object
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t  ares_buf_tag_rollback(ares_buf_t *buf);

/*! Clear the tagged position without rolling back.  You should do this any
 *  time a tag is no longer needed as future append operations can reclaim
 *  buffer space.
 *
 *  \param[in] buf Initialized buffer object
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t  ares_buf_tag_clear(ares_buf_t *buf);

/*! Fetch the buffer and length of data starting from the tagged position up
 *  to the _current_ position.  It will not unset the tagged position.  The
 *  data may be invalidated by any future ares_buf_*() calls.
 *
 *  \param[in]  buf    Initialized buffer object
 *  \param[out] len    Length between tag and current offset in buffer
 *  \return NULL on failure (such as no tag), otherwise pointer to start of
 *          buffer
 */
CARES_EXTERN const unsigned char *ares_buf_tag_fetch(const ares_buf_t *buf,
                                                     size_t           *len);

/*! Get the length of the current tag offset to the current position.
 *
 *  \param[in]  buf    Initialized buffer object
 *  \return length
 */
CARES_EXTERN size_t               ares_buf_tag_length(const ares_buf_t *buf);

/*! Fetch the bytes starting from the tagged position up to the _current_
 *  position using the provided buffer.  It will not unset the tagged position.
 *
 *  \param[in]     buf    Initialized buffer object
 *  \param[in,out] bytes  Buffer to hold data
 *  \param[in,out] len    On input, buffer size, on output, bytes place in
 *                        buffer.
 *  \return ARES_SUCCESS if fetched, ARES_EFORMERR if insufficient buffer size
 */
CARES_EXTERN ares_status_t ares_buf_tag_fetch_bytes(const ares_buf_t *buf,
                                                    unsigned char    *bytes,
                                                    size_t           *len);

/*! Fetch the bytes starting from the tagged position up to the _current_
 *  position as a NULL-terminated string using the provided buffer.  The data
 *  is validated to be ASCII-printable data.  It will not unset the tagged
 *  position.
 *
 *  \param[in]     buf    Initialized buffer object
 *  \param[in,out] str    Buffer to hold data
 *  \param[in]     len    buffer size
 *  \return ARES_SUCCESS if fetched, ARES_EFORMERR if insufficient buffer size,
 *          ARES_EBADSTR if not printable ASCII
 */
CARES_EXTERN ares_status_t ares_buf_tag_fetch_string(const ares_buf_t *buf,
                                                     char *str, size_t len);

/*! Fetch the bytes starting from the tagged position up to the _current_
 *  position as a NULL-terminated string and placed into a newly allocated
 *  buffer.  The data is validated to be ASCII-printable data.  It will not
 *  unset the tagged position.
 *
 *  \param[in]  buf    Initialized buffer object
 *  \param[out] str    New buffer to hold output, free with ares_free()
 *
 *  \return ARES_SUCCESS if fetched, ARES_EFORMERR if insufficient buffer size,
 *          ARES_EBADSTR if not printable ASCII
 */
CARES_EXTERN ares_status_t ares_buf_tag_fetch_strdup(const ares_buf_t *buf,
                                                     char            **str);

/*! Fetch the bytes starting from the tagged position up to the _current_
 *  position as const buffer.  Care must be taken to not append or destroy the
 *  passed in buffer until the newly fetched buffer is no longer needed since
 *  it points to memory inside the passed in buffer which could be invalidated.
 *
 *  \param[in]     buf    Initialized buffer object
 *  \param[out]    newbuf New const buffer object, must be destroyed when done.

 *  \return ARES_SUCCESS if fetched
 */
CARES_EXTERN ares_status_t ares_buf_tag_fetch_constbuf(const ares_buf_t *buf,
                                                       ares_buf_t **newbuf);

/*! Consume the given number of bytes without reading them.
 *
 *  \param[in] buf    Initialized buffer object
 *  \param[in] len    Length to consume
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t ares_buf_consume(ares_buf_t *buf, size_t len);

/*! Fetch a 16bit Big Endian number from the buffer.
 *
 *  \param[in]  buf     Initialized buffer object
 *  \param[out] u16     Buffer to hold 16bit integer
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t ares_buf_fetch_be16(ares_buf_t     *buf,
                                               unsigned short *u16);

/*! Fetch a 32bit Big Endian number from the buffer.
 *
 *  \param[in]  buf     Initialized buffer object
 *  \param[out] u32     Buffer to hold 32bit integer
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t ares_buf_fetch_be32(ares_buf_t   *buf,
                                               unsigned int *u32);


/*! Fetch the requested number of bytes into the provided buffer
 *
 *  \param[in]  buf     Initialized buffer object
 *  \param[out] bytes   Buffer to hold data
 *  \param[in]  len     Requested number of bytes (must be > 0)
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t ares_buf_fetch_bytes(ares_buf_t    *buf,
                                                unsigned char *bytes,
                                                size_t         len);


/*! Fetch the requested number of bytes and return a new buffer that must be
 *  ares_free()'d by the caller.
 *
 *  \param[in]  buf       Initialized buffer object
 *  \param[in]  len       Requested number of bytes (must be > 0)
 *  \param[in]  null_term Even though this is considered binary data, the user
 *                        knows it may be a vald string, so add a null
 *                        terminator.
 *  \param[out] bytes     Pointer passed by reference. Will be allocated.
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t ares_buf_fetch_bytes_dup(ares_buf_t *buf, size_t len,
                                                    ares_bool_t     null_term,
                                                    unsigned char **bytes);

/*! Fetch the requested number of bytes and place them into the provided
 *  dest buffer object.
 *
 *  \param[in]  buf     Initialized buffer object
 *  \param[out] dest    Buffer object to append bytes.
 *  \param[in]  len     Requested number of bytes (must be > 0)
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t ares_buf_fetch_bytes_into_buf(ares_buf_t *buf,
                                                         ares_buf_t *dest,
                                                         size_t      len);

/*! 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.  The data is validated to be ASCII-printable.
 *
 *  \param[in]  buf     Initialized buffer object
 *  \param[in]  len     Requested number of bytes (must be > 0)
 *  \param[out] str     Pointer passed by reference. Will be allocated.
 *  \return ARES_SUCCESS or one of the c-ares error codes
 */
CARES_EXTERN ares_status_t ares_buf_fetch_str_dup(ares_buf_t *buf, size_t len,
                                                  char **str);

/*! Consume whitespace characters (0x09, 0x0B, 0x0C, 0x0D, 0x20, and optionally
 *  0x0A).
 *
 *  \param[in]  buf               Initialized buffer object
 *  \param[in]  include_linefeed  ARES_TRUE to include consuming 0x0A,
 *                                ARES_FALSE otherwise.
 *  \return number of whitespace characters consumed
 */
CARES_EXTERN size_t        ares_buf_consume_whitespace(ares_buf_t *buf,
                                                       ares_bool_t include_linefeed);


/*! Consume any non-whitespace character (anything other than 0x09, 0x0B, 0x0C,
 *  0x0D, 0x20, and 0x0A).
 *
 *  \param[in]  buf               Initialized buffer object
 *  \return number of characters consumed
 */
CARES_EXTERN size_t        ares_buf_consume_nonwhitespace(ares_buf_t *buf);


/*! Consume until a character in the character set provided is reached.  Does
 *  not include the character from the charset at the end.
 *
 *  \param[in] buf                Initialized buffer object
 *  \param[in] charset            character set
 *  \param[in] len                length of character set
 *  \param[in] require_charset    require we find a character from the charset.
 *                                if ARES_FALSE it will simply consume the
 *                                rest of the buffer.  If ARES_TRUE will return
 *                                SIZE_MAX if not found.
 *  \return number of characters consumed
 */
CARES_EXTERN size_t        ares_buf_consume_until_charset(ares_buf_t          *buf,
                                                          const unsigned char *charset,
                                                          size_t               len,
                                                          ares_bool_t require_charset);


/*! Consume until a sequence of bytes is encountered.  Does not include the
 *  sequence of characters itself.
 *
 *  \param[in] buf                Initialized buffer object
 *  \param[in] seq                sequence of bytes
 *  \param[in] len                length of sequence
 *  \param[in] require_charset    require we find the sequence.
 *                                if ARES_FALSE it will simply consume the
 *                                rest of the buffer.  If ARES_TRUE will return
 *                                SIZE_MAX if not found.
 *  \return number of characters consumed
 */
CARES_EXTERN size_t        ares_buf_consume_until_seq(ares_buf_t          *buf,
                                                      const unsigned char *seq,
                                                      size_t               len,
                                                      ares_bool_t require_seq);

/*! Consume while the characters match the characters in the provided set.
 *
 *  \param[in] buf                Initialized buffer object
 *  \param[in] charset            character set
 *  \param[in] len                length of character set
 *  \return number of characters consumed
 */
CARES_EXTERN size_t        ares_buf_consume_charset(ares_buf_t          *buf,
                                                    const unsigned char *charset,
                                                    size_t               len);


/*! Consume from the current position until the end of the line, and optionally
 *  the end of line character (0x0A) itself.
 *
 *  \param[in]  buf               Initialized buffer object
 *  \param[in]  include_linefeed  ARES_TRUE to include consuming 0x0A,
 *                                ARES_FALSE otherwise.
 *  \return number of characters consumed
 */
CARES_EXTERN size_t        ares_buf_consume_line(ares_buf_t *buf,
                                                 ares_bool_t include_linefeed);

typedef enum {
  /*! No flags */
  ARES_BUF_SPLIT_NONE = 0,
  /*! The delimiter will be the first character in the buffer, except the
   *  first buffer since the start doesn't have a delimiter.  This option is
   *  incompatible with ARES_BUF_SPLIT_LTRIM since the delimiter is always
   *  the first character.
   */
  ARES_BUF_SPLIT_KEEP_DELIMS = 1 << 0,
  /*! Allow blank sections, by default blank sections are not emitted.  If using
   *  ARES_BUF_SPLIT_KEEP_DELIMS, the delimiter is not counted as part
   *  of the section */
  ARES_BUF_SPLIT_ALLOW_BLANK = 1 << 1,
  /*! Remove duplicate entries */
  ARES_BUF_SPLIT_NO_DUPLICATES = 1 << 2,
  /*! Perform case-insensitive matching when comparing values */
  ARES_BUF_SPLIT_CASE_INSENSITIVE = 1 << 3,
  /*! Trim leading whitespace from buffer */
  ARES_BUF_SPLIT_LTRIM = 1 << 4,
  /*! Trim trailing whitespace from buffer */
  ARES_BUF_SPLIT_RTRIM = 1 << 5,
  /*! Trim leading and trailing whitespace from buffer */
  ARES_BUF_SPLIT_TRIM = (ARES_BUF_SPLIT_LTRIM | ARES_BUF_SPLIT_RTRIM)
} ares_buf_split_t;

/*! Split the provided buffer into multiple sub-buffers stored in the variable
 *  pointed to by the linked list.  The sub buffers are const buffers pointing
 *  into the buf provided.
 *
 *  \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] arr               Result. Depending on flags, this may be a
 *                                valid array with no elements.  Use
 *                                ares_array_destroy() to free the memory which
 *                                will also free the contained ares_buf_t *
 *                                objects. Each buf object returned by
 *                                ares_array_at() or similar is a pointer to
 *                                an ares_buf_t * object, meaning you need to
 *                                accept it as "ares_buf_t **" then dereference.
 *  \return ARES_SUCCESS on success, or error like ARES_ENOMEM.
 */
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_array_t **arr);

/*! Split the provided buffer into an ares_array_t 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] arr               Array of strings. Free using
 *                                ares_array_destroy().
 *  \return ARES_SUCCESS on success, or error like ARES_ENOMEM.
 */
CARES_EXTERN ares_status_t ares_buf_split_str_array(
  ares_buf_t *buf, const unsigned char *delims, size_t delims_len,
  ares_buf_split_t flags, size_t max_sections, ares_array_t **arr);

/*! 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.
 *
 *  \param[in] buf          Initialized buffer object
 *  \param[in] data         Bytes of data to compare.
 *  \param[in] data_len     Length of data to compare.
 *  \return ARES_TRUE on match, ARES_FALSE otherwise.
 */
CARES_EXTERN ares_bool_t          ares_buf_begins_with(const ares_buf_t    *buf,
                                                       const unsigned char *data,
                                                       size_t               data_len);


/*! Size of unprocessed remaining data length
 *
 *  \param[in] buf Initialized buffer object
 *  \return length remaining
 */
CARES_EXTERN size_t               ares_buf_len(const ares_buf_t *buf);

/*! Retrieve a pointer to the currently unprocessed data.  Generally this isn't
 *  recommended to be used in practice.  The returned pointer may be invalidated
 *  by any future ares_buf_*() calls.
 *
 *  \param[in]  buf    Initialized buffer object
 *  \param[out] len    Length of available data
 *  \return Pointer to buffer of unprocessed data
 */
CARES_EXTERN const unsigned char *ares_buf_peek(const ares_buf_t *buf,
                                                size_t           *len);

/*! Retrieve the next byte in the buffer without moving forward.
 *
 *  \param[in]  buf  Initialized buffer object
 *  \param[out] b    Single byte
 *  \return \return ARES_SUCCESS on success, or error
 */
CARES_EXTERN ares_status_t        ares_buf_peek_byte(const ares_buf_t *buf,
                                                     unsigned char    *b);

/*! Wipe any processed data from the beginning of the buffer.  This will
 *  move any remaining data to the front of the internally allocated buffer.
 *
 *  Can not be used on const buffer objects.
 *
 *  Typically not needed to call, as any new append operation will automatically
 *  call this function if there is insufficient space to append the data in
 *  order to try to avoid another memory allocation.
 *
 *  It may be useful to call in order to ensure the current message being
 *  processed is in the beginning of the buffer if there is an intent to use
 *  ares_buf_set_position() and ares_buf_get_position() as may be necessary
 *  when processing DNS compressed names.
 *
 *  If there is an active tag, it will NOT clear the tag, it will use the tag
 *  as the start of the unprocessed data rather than the current offset.  If
 *  a prior tag is no longer needed, may be wise to call ares_buf_tag_clear().
 *
 *  \param[in]  buf    Initialized buffer object
 */
CARES_EXTERN void                 ares_buf_reclaim(ares_buf_t *buf);

/*! Set the current offset within the internal buffer.
 *
 *  Typically this should not be used, if possible, use the ares_buf_tag*()
 *  operations instead.
 *
 *  One exception is DNS name compression which may backwards reference to
 *  an index in the message.  It may be necessary in such a case to call
 *  ares_buf_reclaim() if using a dynamic (non-const) buffer before processing
 *  such a message.
 *
 *  \param[in] buf  Initialized buffer object
 *  \param[in] idx  Index to set position
 *  \return ARES_SUCCESS if valid index
 */
CARES_EXTERN ares_status_t ares_buf_set_position(ares_buf_t *buf, size_t idx);

/*! Get the current offset within the internal buffer.
 *
 *  Typically this should not be used, if possible, use the ares_buf_tag*()
 *  operations instead.
 *
 *  This can be used to get the current position, useful for saving if a
 *  jump via ares_buf_set_position() is performed and need to restore the
 *  current position for future operations.
 *
 *  \param[in] buf Initialized buffer object
 *  \return index of current position
 */
CARES_EXTERN size_t        ares_buf_get_position(const ares_buf_t *buf);

/*! Parse a character-string as defined in RFC1035, as a null-terminated
 *  string.
 *
 *  \param[in]  buf            initialized buffer object
 *  \param[in]  remaining_len  maximum length that should be used for parsing
 *                             the string, this is often less than the remaining
 *                             buffer and is based on the RR record length.
 *  \param[out] name           Pointer passed by reference to be filled in with
 *                             allocated string of the parsed that must be
 *                             ares_free()'d by the caller.
 *  \return ARES_SUCCESS on success
 */
CARES_EXTERN ares_status_t ares_buf_parse_dns_str(ares_buf_t *buf,
                                                  size_t      remaining_len,
                                                  char      **name);

/*! Parse a character-string as defined in RFC1035, as binary, however for
 *  convenience this does guarantee a NULL terminator (that is not included
 *  in the returned length).
 *
 *  \param[in]  buf            initialized buffer object
 *  \param[in]  remaining_len  maximum length that should be used for parsing
 *                             the string, this is often less than the remaining
 *                             buffer and is based on the RR record length.
 *  \param[out] bin            Pointer passed by reference to be filled in with
 *                             allocated string of the parsed that must be
 *                             ares_free()'d by the caller.
 *  \param[out] bin_len        Length of returned string.
 *  \return ARES_SUCCESS on success
 */
CARES_EXTERN ares_status_t ares_buf_parse_dns_binstr(ares_buf_t *buf,
                                                     size_t      remaining_len,
                                                     unsigned char **bin,
                                                     size_t         *bin_len);

/*! Load data from specified file path into provided buffer.  The entire file
 *  is loaded into memory.
 *
 *  \param[in]     filename complete path to file
 *  \param[in,out] buf      Initialized (non-const) buffer object to load data
 *                          into
 *  \return ARES_ENOTFOUND if file not found, ARES_EFILE if issues reading
 *          file, ARES_ENOMEM if out of memory, ARES_SUCCESS on success.
 */
CARES_EXTERN ares_status_t ares_buf_load_file(const char *filename,
                                              ares_buf_t *buf);

/*! @} */

#endif /* __ARES__BUF_H */