quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ares_buf.c (36767B)


      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 #include "ares_private.h"
     27 #include "ares_buf.h"
     28 #include <limits.h>
     29 #ifdef HAVE_STDINT_H
     30 #  include <stdint.h>
     31 #endif
     32 
     33 struct ares_buf {
     34   const unsigned char *data;          /*!< pointer to start of data buffer */
     35   size_t               data_len;      /*!< total size of data in buffer */
     36 
     37   unsigned char       *alloc_buf;     /*!< Pointer to allocated data buffer,
     38                                        *   not used for const buffers */
     39   size_t               alloc_buf_len; /*!< Size of allocated data buffer */
     40 
     41   size_t               offset;        /*!< Current working offset in buffer */
     42   size_t               tag_offset;    /*!< Tagged offset in buffer. Uses
     43                                        *   SIZE_MAX if not set. */
     44 };
     45 
     46 ares_buf_t *ares_buf_create(void)
     47 {
     48   ares_buf_t *buf = ares_malloc_zero(sizeof(*buf));
     49   if (buf == NULL) {
     50     return NULL;
     51   }
     52 
     53   buf->tag_offset = SIZE_MAX;
     54   return buf;
     55 }
     56 
     57 ares_buf_t *ares_buf_create_const(const unsigned char *data, size_t data_len)
     58 {
     59   ares_buf_t *buf;
     60 
     61   if (data == NULL || data_len == 0) {
     62     return NULL;
     63   }
     64 
     65   buf = ares_buf_create();
     66   if (buf == NULL) {
     67     return NULL;
     68   }
     69 
     70   buf->data     = data;
     71   buf->data_len = data_len;
     72 
     73   return buf;
     74 }
     75 
     76 void ares_buf_destroy(ares_buf_t *buf)
     77 {
     78   if (buf == NULL) {
     79     return;
     80   }
     81   ares_free(buf->alloc_buf);
     82   ares_free(buf);
     83 }
     84 
     85 static ares_bool_t ares_buf_is_const(const ares_buf_t *buf)
     86 {
     87   if (buf == NULL) {
     88     return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
     89   }
     90 
     91   if (buf->data != NULL && buf->alloc_buf == NULL) {
     92     return ARES_TRUE;
     93   }
     94 
     95   return ARES_FALSE;
     96 }
     97 
     98 void ares_buf_reclaim(ares_buf_t *buf)
     99 {
    100   size_t prefix_size;
    101   size_t data_size;
    102 
    103   if (buf == NULL) {
    104     return;
    105   }
    106 
    107   if (ares_buf_is_const(buf)) {
    108     return; /* LCOV_EXCL_LINE: DefensiveCoding */
    109   }
    110 
    111   /* Silence coverity.  All lengths are zero so would bail out later but
    112    * coverity doesn't know this */
    113   if (buf->alloc_buf == NULL) {
    114     return;
    115   }
    116 
    117   if (buf->tag_offset != SIZE_MAX && buf->tag_offset < buf->offset) {
    118     prefix_size = buf->tag_offset;
    119   } else {
    120     prefix_size = buf->offset;
    121   }
    122 
    123   if (prefix_size == 0) {
    124     return;
    125   }
    126 
    127   data_size = buf->data_len - prefix_size;
    128 
    129   memmove(buf->alloc_buf, buf->alloc_buf + prefix_size, data_size);
    130   buf->data      = buf->alloc_buf;
    131   buf->data_len  = data_size;
    132   buf->offset   -= prefix_size;
    133   if (buf->tag_offset != SIZE_MAX) {
    134     buf->tag_offset -= prefix_size;
    135   }
    136 }
    137 
    138 static ares_status_t ares_buf_ensure_space(ares_buf_t *buf, size_t needed_size)
    139 {
    140   size_t         remaining_size;
    141   size_t         alloc_size;
    142   unsigned char *ptr;
    143 
    144   if (buf == NULL) {
    145     return ARES_EFORMERR;
    146   }
    147 
    148   if (ares_buf_is_const(buf)) {
    149     return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
    150   }
    151 
    152   /* When calling ares_buf_finish_str() we end up adding a null terminator,
    153    * so we want to ensure the size is always sufficient for this as we don't
    154    * want an ARES_ENOMEM at that point */
    155   needed_size++;
    156 
    157   /* No need to do an expensive move operation, we have enough to just append */
    158   remaining_size = buf->alloc_buf_len - buf->data_len;
    159   if (remaining_size >= needed_size) {
    160     return ARES_SUCCESS;
    161   }
    162 
    163   /* See if just moving consumed data frees up enough space */
    164   ares_buf_reclaim(buf);
    165 
    166   remaining_size = buf->alloc_buf_len - buf->data_len;
    167   if (remaining_size >= needed_size) {
    168     return ARES_SUCCESS;
    169   }
    170 
    171   alloc_size = buf->alloc_buf_len;
    172 
    173   /* Not yet started */
    174   if (alloc_size == 0) {
    175     alloc_size = 16; /* Always shifts 1, so ends up being 32 minimum */
    176   }
    177 
    178   /* Increase allocation by powers of 2 */
    179   do {
    180     alloc_size     <<= 1;
    181     remaining_size   = alloc_size - buf->data_len;
    182   } while (remaining_size < needed_size);
    183 
    184   ptr = ares_realloc(buf->alloc_buf, alloc_size);
    185   if (ptr == NULL) {
    186     return ARES_ENOMEM;
    187   }
    188 
    189   buf->alloc_buf     = ptr;
    190   buf->alloc_buf_len = alloc_size;
    191   buf->data          = ptr;
    192 
    193   return ARES_SUCCESS;
    194 }
    195 
    196 ares_status_t ares_buf_set_length(ares_buf_t *buf, size_t len)
    197 {
    198   if (buf == NULL || ares_buf_is_const(buf)) {
    199     return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
    200   }
    201 
    202   if (len >= buf->alloc_buf_len - buf->offset) {
    203     return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
    204   }
    205 
    206   buf->data_len = len + buf->offset;
    207   return ARES_SUCCESS;
    208 }
    209 
    210 ares_status_t ares_buf_append(ares_buf_t *buf, const unsigned char *data,
    211                               size_t data_len)
    212 {
    213   ares_status_t status;
    214 
    215   if (data == NULL && data_len != 0) {
    216     return ARES_EFORMERR;
    217   }
    218 
    219   if (data_len == 0) {
    220     return ARES_SUCCESS;
    221   }
    222 
    223   status = ares_buf_ensure_space(buf, data_len);
    224   if (status != ARES_SUCCESS) {
    225     return status;
    226   }
    227 
    228   memcpy(buf->alloc_buf + buf->data_len, data, data_len);
    229   buf->data_len += data_len;
    230   return ARES_SUCCESS;
    231 }
    232 
    233 ares_status_t ares_buf_append_byte(ares_buf_t *buf, unsigned char b)
    234 {
    235   return ares_buf_append(buf, &b, 1);
    236 }
    237 
    238 ares_status_t ares_buf_append_be16(ares_buf_t *buf, unsigned short u16)
    239 {
    240   ares_status_t status;
    241 
    242   status = ares_buf_append_byte(buf, (unsigned char)((u16 >> 8) & 0xff));
    243   if (status != ARES_SUCCESS) {
    244     return status; /* LCOV_EXCL_LINE: OutOfMemory */
    245   }
    246 
    247   status = ares_buf_append_byte(buf, (unsigned char)(u16 & 0xff));
    248   if (status != ARES_SUCCESS) {
    249     return status; /* LCOV_EXCL_LINE: OutOfMemory */
    250   }
    251 
    252   return ARES_SUCCESS;
    253 }
    254 
    255 ares_status_t ares_buf_append_be32(ares_buf_t *buf, unsigned int u32)
    256 {
    257   ares_status_t status;
    258 
    259   status = ares_buf_append_byte(buf, ((unsigned char)(u32 >> 24) & 0xff));
    260   if (status != ARES_SUCCESS) {
    261     return status; /* LCOV_EXCL_LINE: OutOfMemory */
    262   }
    263 
    264   status = ares_buf_append_byte(buf, ((unsigned char)(u32 >> 16) & 0xff));
    265   if (status != ARES_SUCCESS) {
    266     return status; /* LCOV_EXCL_LINE: OutOfMemory */
    267   }
    268 
    269   status = ares_buf_append_byte(buf, ((unsigned char)(u32 >> 8) & 0xff));
    270   if (status != ARES_SUCCESS) {
    271     return status; /* LCOV_EXCL_LINE: OutOfMemory */
    272   }
    273 
    274   status = ares_buf_append_byte(buf, ((unsigned char)u32 & 0xff));
    275   if (status != ARES_SUCCESS) {
    276     return status; /* LCOV_EXCL_LINE: OutOfMemory */
    277   }
    278 
    279   return ARES_SUCCESS;
    280 }
    281 
    282 unsigned char *ares_buf_append_start(ares_buf_t *buf, size_t *len)
    283 {
    284   ares_status_t status;
    285 
    286   if (len == NULL || *len == 0) {
    287     return NULL;
    288   }
    289 
    290   status = ares_buf_ensure_space(buf, *len);
    291   if (status != ARES_SUCCESS) {
    292     return NULL;
    293   }
    294 
    295   /* -1 for possible null terminator for ares_buf_finish_str() */
    296   *len = buf->alloc_buf_len - buf->data_len - 1;
    297   return buf->alloc_buf + buf->data_len;
    298 }
    299 
    300 void ares_buf_append_finish(ares_buf_t *buf, size_t len)
    301 {
    302   if (buf == NULL) {
    303     return;
    304   }
    305 
    306   buf->data_len += len;
    307 }
    308 
    309 unsigned char *ares_buf_finish_bin(ares_buf_t *buf, size_t *len)
    310 {
    311   unsigned char *ptr = NULL;
    312   if (buf == NULL || len == NULL || ares_buf_is_const(buf)) {
    313     return NULL;
    314   }
    315 
    316   ares_buf_reclaim(buf);
    317 
    318   /* We don't want to return NULL except on failure, may be zero-length */
    319   if (buf->alloc_buf == NULL && ares_buf_ensure_space(buf, 1) != ARES_SUCCESS) {
    320     return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
    321   }
    322   ptr  = buf->alloc_buf;
    323   *len = buf->data_len;
    324   ares_free(buf);
    325   return ptr;
    326 }
    327 
    328 char *ares_buf_finish_str(ares_buf_t *buf, size_t *len)
    329 {
    330   char  *ptr;
    331   size_t mylen;
    332 
    333   ptr = (char *)ares_buf_finish_bin(buf, &mylen);
    334   if (ptr == NULL) {
    335     return NULL;
    336   }
    337 
    338   if (len != NULL) {
    339     *len = mylen;
    340   }
    341 
    342   /* NOTE: ensured via ares_buf_ensure_space() that there is always at least
    343    *       1 extra byte available for this specific use-case */
    344   ptr[mylen] = 0;
    345 
    346   return ptr;
    347 }
    348 
    349 void ares_buf_tag(ares_buf_t *buf)
    350 {
    351   if (buf == NULL) {
    352     return;
    353   }
    354 
    355   buf->tag_offset = buf->offset;
    356 }
    357 
    358 ares_status_t ares_buf_tag_rollback(ares_buf_t *buf)
    359 {
    360   if (buf == NULL || buf->tag_offset == SIZE_MAX) {
    361     return ARES_EFORMERR;
    362   }
    363 
    364   buf->offset     = buf->tag_offset;
    365   buf->tag_offset = SIZE_MAX;
    366   return ARES_SUCCESS;
    367 }
    368 
    369 ares_status_t ares_buf_tag_clear(ares_buf_t *buf)
    370 {
    371   if (buf == NULL || buf->tag_offset == SIZE_MAX) {
    372     return ARES_EFORMERR;
    373   }
    374 
    375   buf->tag_offset = SIZE_MAX;
    376   return ARES_SUCCESS;
    377 }
    378 
    379 const unsigned char *ares_buf_tag_fetch(const ares_buf_t *buf, size_t *len)
    380 {
    381   if (buf == NULL || buf->tag_offset == SIZE_MAX || len == NULL) {
    382     return NULL;
    383   }
    384 
    385   *len = buf->offset - buf->tag_offset;
    386   return buf->data + buf->tag_offset;
    387 }
    388 
    389 size_t ares_buf_tag_length(const ares_buf_t *buf)
    390 {
    391   if (buf == NULL || buf->tag_offset == SIZE_MAX) {
    392     return 0;
    393   }
    394   return buf->offset - buf->tag_offset;
    395 }
    396 
    397 ares_status_t ares_buf_tag_fetch_bytes(const ares_buf_t *buf,
    398                                        unsigned char *bytes, size_t *len)
    399 {
    400   size_t               ptr_len = 0;
    401   const unsigned char *ptr     = ares_buf_tag_fetch(buf, &ptr_len);
    402 
    403   if (ptr == NULL || bytes == NULL || len == NULL) {
    404     return ARES_EFORMERR;
    405   }
    406 
    407   if (*len < ptr_len) {
    408     return ARES_EFORMERR;
    409   }
    410 
    411   *len = ptr_len;
    412 
    413   if (ptr_len > 0) {
    414     memcpy(bytes, ptr, ptr_len);
    415   }
    416   return ARES_SUCCESS;
    417 }
    418 
    419 ares_status_t ares_buf_tag_fetch_constbuf(const ares_buf_t *buf,
    420                                           ares_buf_t      **newbuf)
    421 {
    422   size_t               ptr_len = 0;
    423   const unsigned char *ptr     = ares_buf_tag_fetch(buf, &ptr_len);
    424 
    425   if (ptr == NULL || newbuf == NULL) {
    426     return ARES_EFORMERR;
    427   }
    428 
    429   *newbuf = ares_buf_create_const(ptr, ptr_len);
    430   if (*newbuf == NULL) {
    431     return ARES_ENOMEM;
    432   }
    433   return ARES_SUCCESS;
    434 }
    435 
    436 ares_status_t ares_buf_tag_fetch_string(const ares_buf_t *buf, char *str,
    437                                         size_t len)
    438 {
    439   size_t        out_len;
    440   ares_status_t status;
    441   size_t        i;
    442 
    443   if (str == NULL || len == 0) {
    444     return ARES_EFORMERR;
    445   }
    446 
    447   /* Space for NULL terminator */
    448   out_len = len - 1;
    449 
    450   status = ares_buf_tag_fetch_bytes(buf, (unsigned char *)str, &out_len);
    451   if (status != ARES_SUCCESS) {
    452     return status;
    453   }
    454 
    455   /* NULL terminate */
    456   str[out_len] = 0;
    457 
    458   /* Validate string is printable */
    459   for (i = 0; i < out_len; i++) {
    460     if (!ares_isprint(str[i])) {
    461       return ARES_EBADSTR;
    462     }
    463   }
    464 
    465   return ARES_SUCCESS;
    466 }
    467 
    468 ares_status_t ares_buf_tag_fetch_strdup(const ares_buf_t *buf, char **str)
    469 {
    470   size_t               ptr_len = 0;
    471   const unsigned char *ptr     = ares_buf_tag_fetch(buf, &ptr_len);
    472 
    473   if (ptr == NULL || str == NULL) {
    474     return ARES_EFORMERR;
    475   }
    476 
    477   if (!ares_str_isprint((const char *)ptr, ptr_len)) {
    478     return ARES_EBADSTR;
    479   }
    480 
    481   *str = ares_malloc(ptr_len + 1);
    482   if (*str == NULL) {
    483     return ARES_ENOMEM;
    484   }
    485 
    486   if (ptr_len > 0) {
    487     memcpy(*str, ptr, ptr_len);
    488   }
    489   (*str)[ptr_len] = 0;
    490   return ARES_SUCCESS;
    491 }
    492 
    493 static const unsigned char *ares_buf_fetch(const ares_buf_t *buf, size_t *len)
    494 {
    495   if (len != NULL) {
    496     *len = 0;
    497   }
    498 
    499   if (buf == NULL || len == NULL || buf->data == NULL) {
    500     return NULL;
    501   }
    502 
    503   *len = buf->data_len - buf->offset;
    504   if (*len == 0) {
    505     return NULL;
    506   }
    507 
    508   return buf->data + buf->offset;
    509 }
    510 
    511 ares_status_t ares_buf_consume(ares_buf_t *buf, size_t len)
    512 {
    513   size_t remaining_len = ares_buf_len(buf);
    514 
    515   if (remaining_len < len) {
    516     return ARES_EBADRESP;
    517   }
    518 
    519   buf->offset += len;
    520   return ARES_SUCCESS;
    521 }
    522 
    523 ares_status_t ares_buf_fetch_be16(ares_buf_t *buf, unsigned short *u16)
    524 {
    525   size_t               remaining_len;
    526   const unsigned char *ptr = ares_buf_fetch(buf, &remaining_len);
    527   unsigned int         u32;
    528 
    529   if (buf == NULL || u16 == NULL || remaining_len < sizeof(*u16)) {
    530     return ARES_EBADRESP;
    531   }
    532 
    533   /* Do math in an unsigned int in order to prevent warnings due to automatic
    534    * conversion by the compiler from short to int during shifts */
    535   u32  = ((unsigned int)(ptr[0]) << 8 | (unsigned int)ptr[1]);
    536   *u16 = (unsigned short)(u32 & 0xFFFF);
    537 
    538   return ares_buf_consume(buf, sizeof(*u16));
    539 }
    540 
    541 ares_status_t ares_buf_fetch_be32(ares_buf_t *buf, unsigned int *u32)
    542 {
    543   size_t               remaining_len;
    544   const unsigned char *ptr = ares_buf_fetch(buf, &remaining_len);
    545 
    546   if (buf == NULL || u32 == NULL || remaining_len < sizeof(*u32)) {
    547     return ARES_EBADRESP;
    548   }
    549 
    550   *u32 = ((unsigned int)(ptr[0]) << 24 | (unsigned int)(ptr[1]) << 16 |
    551           (unsigned int)(ptr[2]) << 8 | (unsigned int)(ptr[3]));
    552 
    553   return ares_buf_consume(buf, sizeof(*u32));
    554 }
    555 
    556 ares_status_t ares_buf_fetch_bytes(ares_buf_t *buf, unsigned char *bytes,
    557                                    size_t len)
    558 {
    559   size_t               remaining_len;
    560   const unsigned char *ptr = ares_buf_fetch(buf, &remaining_len);
    561 
    562   if (buf == NULL || bytes == NULL || len == 0 || remaining_len < len) {
    563     return ARES_EBADRESP;
    564   }
    565 
    566   memcpy(bytes, ptr, len);
    567   return ares_buf_consume(buf, len);
    568 }
    569 
    570 ares_status_t ares_buf_fetch_bytes_dup(ares_buf_t *buf, size_t len,
    571                                        ares_bool_t     null_term,
    572                                        unsigned char **bytes)
    573 {
    574   size_t               remaining_len;
    575   const unsigned char *ptr = ares_buf_fetch(buf, &remaining_len);
    576 
    577   if (buf == NULL || bytes == NULL || len == 0 || remaining_len < len) {
    578     return ARES_EBADRESP;
    579   }
    580 
    581   *bytes = ares_malloc(null_term ? len + 1 : len);
    582   if (*bytes == NULL) {
    583     return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    584   }
    585 
    586   memcpy(*bytes, ptr, len);
    587   if (null_term) {
    588     (*bytes)[len] = 0;
    589   }
    590   return ares_buf_consume(buf, len);
    591 }
    592 
    593 ares_status_t ares_buf_fetch_str_dup(ares_buf_t *buf, size_t len, char **str)
    594 {
    595   size_t               remaining_len;
    596   size_t               i;
    597   const unsigned char *ptr = ares_buf_fetch(buf, &remaining_len);
    598 
    599   if (buf == NULL || str == NULL || len == 0 || remaining_len < len) {
    600     return ARES_EBADRESP;
    601   }
    602 
    603   /* Validate string is printable */
    604   for (i = 0; i < len; i++) {
    605     if (!ares_isprint(ptr[i])) {
    606       return ARES_EBADSTR;
    607     }
    608   }
    609 
    610   *str = ares_malloc(len + 1);
    611   if (*str == NULL) {
    612     return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    613   }
    614 
    615   memcpy(*str, ptr, len);
    616   (*str)[len] = 0;
    617 
    618   return ares_buf_consume(buf, len);
    619 }
    620 
    621 ares_status_t ares_buf_fetch_bytes_into_buf(ares_buf_t *buf, ares_buf_t *dest,
    622                                             size_t len)
    623 {
    624   size_t               remaining_len;
    625   const unsigned char *ptr = ares_buf_fetch(buf, &remaining_len);
    626   ares_status_t        status;
    627 
    628   if (buf == NULL || dest == NULL || len == 0 || remaining_len < len) {
    629     return ARES_EBADRESP;
    630   }
    631 
    632   status = ares_buf_append(dest, ptr, len);
    633   if (status != ARES_SUCCESS) {
    634     return status;
    635   }
    636 
    637   return ares_buf_consume(buf, len);
    638 }
    639 
    640 static ares_bool_t ares_is_whitespace(unsigned char c,
    641                                       ares_bool_t   include_linefeed)
    642 {
    643   switch (c) {
    644     case '\r':
    645     case '\t':
    646     case ' ':
    647     case '\v':
    648     case '\f':
    649       return ARES_TRUE;
    650     case '\n':
    651       return include_linefeed;
    652     default:
    653       break;
    654   }
    655   return ARES_FALSE;
    656 }
    657 
    658 size_t ares_buf_consume_whitespace(ares_buf_t *buf,
    659                                    ares_bool_t include_linefeed)
    660 {
    661   size_t               remaining_len = 0;
    662   const unsigned char *ptr           = ares_buf_fetch(buf, &remaining_len);
    663   size_t               i;
    664 
    665   if (ptr == NULL) {
    666     return 0;
    667   }
    668 
    669   for (i = 0; i < remaining_len; i++) {
    670     if (!ares_is_whitespace(ptr[i], include_linefeed)) {
    671       break;
    672     }
    673   }
    674 
    675   if (i > 0) {
    676     ares_buf_consume(buf, i);
    677   }
    678   return i;
    679 }
    680 
    681 size_t ares_buf_consume_nonwhitespace(ares_buf_t *buf)
    682 {
    683   size_t               remaining_len = 0;
    684   const unsigned char *ptr           = ares_buf_fetch(buf, &remaining_len);
    685   size_t               i;
    686 
    687   if (ptr == NULL) {
    688     return 0;
    689   }
    690 
    691   for (i = 0; i < remaining_len; i++) {
    692     if (ares_is_whitespace(ptr[i], ARES_TRUE)) {
    693       break;
    694     }
    695   }
    696 
    697   if (i > 0) {
    698     ares_buf_consume(buf, i);
    699   }
    700   return i;
    701 }
    702 
    703 size_t ares_buf_consume_line(ares_buf_t *buf, ares_bool_t include_linefeed)
    704 {
    705   size_t               remaining_len = 0;
    706   const unsigned char *ptr           = ares_buf_fetch(buf, &remaining_len);
    707   size_t               i;
    708 
    709   if (ptr == NULL) {
    710     return 0;
    711   }
    712 
    713   for (i = 0; i < remaining_len; i++) {
    714     if (ptr[i] == '\n') {
    715       goto done;
    716     }
    717   }
    718 
    719 done:
    720   if (include_linefeed && i < remaining_len && ptr[i] == '\n') {
    721     i++;
    722   }
    723 
    724   if (i > 0) {
    725     ares_buf_consume(buf, i);
    726   }
    727   return i;
    728 }
    729 
    730 size_t ares_buf_consume_until_charset(ares_buf_t          *buf,
    731                                       const unsigned char *charset, size_t len,
    732                                       ares_bool_t require_charset)
    733 {
    734   size_t               remaining_len = 0;
    735   const unsigned char *ptr           = ares_buf_fetch(buf, &remaining_len);
    736   size_t               pos;
    737   ares_bool_t          found = ARES_FALSE;
    738 
    739   if (ptr == NULL || charset == NULL || len == 0) {
    740     return 0;
    741   }
    742 
    743   /* Optimize for single character searches */
    744   if (len == 1) {
    745     const unsigned char *p = memchr(ptr, charset[0], remaining_len);
    746     if (p != NULL) {
    747       found = ARES_TRUE;
    748       pos   = (size_t)(p - ptr);
    749     } else {
    750       pos = remaining_len;
    751     }
    752     goto done;
    753   }
    754 
    755   for (pos = 0; pos < remaining_len; pos++) {
    756     size_t j;
    757     for (j = 0; j < len; j++) {
    758       if (ptr[pos] == charset[j]) {
    759         found = ARES_TRUE;
    760         goto done;
    761       }
    762     }
    763   }
    764 
    765 done:
    766   if (require_charset && !found) {
    767     return SIZE_MAX;
    768   }
    769 
    770   if (pos > 0) {
    771     ares_buf_consume(buf, pos);
    772   }
    773   return pos;
    774 }
    775 
    776 size_t ares_buf_consume_until_seq(ares_buf_t *buf, const unsigned char *seq,
    777                                   size_t len, ares_bool_t require_seq)
    778 {
    779   size_t               remaining_len = 0;
    780   const unsigned char *ptr           = ares_buf_fetch(buf, &remaining_len);
    781   const unsigned char *p;
    782   size_t               consume_len = 0;
    783 
    784   if (ptr == NULL || seq == NULL || len == 0) {
    785     return 0;
    786   }
    787 
    788   p = ares_memmem(ptr, remaining_len, seq, len);
    789   if (require_seq && p == NULL) {
    790     return SIZE_MAX;
    791   }
    792 
    793   if (p != NULL) {
    794     consume_len = (size_t)(p - ptr);
    795   } else {
    796     consume_len = remaining_len;
    797   }
    798 
    799   if (consume_len > 0) {
    800     ares_buf_consume(buf, consume_len);
    801   }
    802 
    803   return consume_len;
    804 }
    805 
    806 size_t ares_buf_consume_charset(ares_buf_t *buf, const unsigned char *charset,
    807                                 size_t len)
    808 {
    809   size_t               remaining_len = 0;
    810   const unsigned char *ptr           = ares_buf_fetch(buf, &remaining_len);
    811   size_t               i;
    812 
    813   if (ptr == NULL || charset == NULL || len == 0) {
    814     return 0;
    815   }
    816 
    817   for (i = 0; i < remaining_len; i++) {
    818     size_t j;
    819     for (j = 0; j < len; j++) {
    820       if (ptr[i] == charset[j]) {
    821         break;
    822       }
    823     }
    824     /* Not found */
    825     if (j == len) {
    826       break;
    827     }
    828   }
    829 
    830   if (i > 0) {
    831     ares_buf_consume(buf, i);
    832   }
    833   return i;
    834 }
    835 
    836 static void ares_buf_destroy_cb(void *arg)
    837 {
    838   ares_buf_t **buf = arg;
    839   ares_buf_destroy(*buf);
    840 }
    841 
    842 static ares_bool_t ares_buf_split_isduplicate(ares_array_t        *arr,
    843                                               const unsigned char *val,
    844                                               size_t               len,
    845                                               ares_buf_split_t     flags)
    846 {
    847   size_t i;
    848   size_t num = ares_array_len(arr);
    849 
    850   for (i = 0; i < num; i++) {
    851     ares_buf_t         **bufptr = ares_array_at(arr, i);
    852     const ares_buf_t    *buf    = *bufptr;
    853     size_t               plen   = 0;
    854     const unsigned char *ptr    = ares_buf_peek(buf, &plen);
    855 
    856     /* Can't be duplicate if lengths mismatch */
    857     if (plen != len) {
    858       continue;
    859     }
    860 
    861     if (flags & ARES_BUF_SPLIT_CASE_INSENSITIVE) {
    862       if (ares_memeq_ci(ptr, val, len)) {
    863         return ARES_TRUE;
    864       }
    865     } else {
    866       if (ares_memeq(ptr, val, len)) {
    867         return ARES_TRUE;
    868       }
    869     }
    870   }
    871 
    872   return ARES_FALSE;
    873 }
    874 
    875 ares_status_t ares_buf_split(ares_buf_t *buf, const unsigned char *delims,
    876                              size_t delims_len, ares_buf_split_t flags,
    877                              size_t max_sections, ares_array_t **arr)
    878 {
    879   ares_status_t status = ARES_SUCCESS;
    880   ares_bool_t   first  = ARES_TRUE;
    881 
    882   if (buf == NULL || delims == NULL || delims_len == 0 || arr == NULL) {
    883     return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
    884   }
    885 
    886   *arr = ares_array_create(sizeof(ares_buf_t *), ares_buf_destroy_cb);
    887   if (*arr == NULL) {
    888     status = ARES_ENOMEM;
    889     goto done;
    890   }
    891 
    892   while (ares_buf_len(buf)) {
    893     size_t               len = 0;
    894     const unsigned char *ptr;
    895 
    896     if (first) {
    897       /* No delimiter yet, just tag the start */
    898       ares_buf_tag(buf);
    899     } else {
    900       if (flags & ARES_BUF_SPLIT_KEEP_DELIMS) {
    901         /* tag then eat delimiter so its first byte in buffer */
    902         ares_buf_tag(buf);
    903         ares_buf_consume(buf, 1);
    904       } else {
    905         /* throw away delimiter */
    906         ares_buf_consume(buf, 1);
    907         ares_buf_tag(buf);
    908       }
    909     }
    910 
    911     if (max_sections && ares_array_len(*arr) >= max_sections - 1) {
    912       ares_buf_consume(buf, ares_buf_len(buf));
    913     } else {
    914       ares_buf_consume_until_charset(buf, delims, delims_len, ARES_FALSE);
    915     }
    916 
    917     ptr = ares_buf_tag_fetch(buf, &len);
    918 
    919     /* Shouldn't be possible */
    920     if (ptr == NULL) {
    921       status = ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
    922       goto done;
    923     }
    924 
    925     if (flags & ARES_BUF_SPLIT_LTRIM) {
    926       size_t i;
    927       for (i = 0; i < len; i++) {
    928         if (!ares_is_whitespace(ptr[i], ARES_TRUE)) {
    929           break;
    930         }
    931       }
    932       ptr += i;
    933       len -= i;
    934     }
    935 
    936     if (flags & ARES_BUF_SPLIT_RTRIM) {
    937       while (len > 0 && ares_is_whitespace(ptr[len - 1], ARES_TRUE)) {
    938         len--;
    939       }
    940     }
    941 
    942     if (len != 0 || flags & ARES_BUF_SPLIT_ALLOW_BLANK) {
    943       ares_buf_t *data;
    944 
    945       if (!(flags & ARES_BUF_SPLIT_NO_DUPLICATES) ||
    946           !ares_buf_split_isduplicate(*arr, ptr, len, flags)) {
    947         /* Since we don't allow const buffers of 0 length, and user wants
    948          * 0-length buffers, swap what we do here */
    949         if (len) {
    950           data = ares_buf_create_const(ptr, len);
    951         } else {
    952           data = ares_buf_create();
    953         }
    954 
    955         if (data == NULL) {
    956           status = ARES_ENOMEM;
    957           goto done;
    958         }
    959 
    960         status = ares_array_insertdata_last(*arr, &data);
    961         if (status != ARES_SUCCESS) {
    962           ares_buf_destroy(data);
    963           goto done;
    964         }
    965       }
    966     }
    967 
    968     first = ARES_FALSE;
    969   }
    970 
    971 done:
    972   if (status != ARES_SUCCESS) {
    973     ares_array_destroy(*arr);
    974     *arr = NULL;
    975   }
    976 
    977   return status;
    978 }
    979 
    980 static void ares_free_split_array(void *arg)
    981 {
    982   void **ptr = arg;
    983   ares_free(*ptr);
    984 }
    985 
    986 ares_status_t ares_buf_split_str_array(ares_buf_t          *buf,
    987                                        const unsigned char *delims,
    988                                        size_t               delims_len,
    989                                        ares_buf_split_t     flags,
    990                                        size_t max_sections, ares_array_t **arr)
    991 {
    992   ares_status_t status;
    993   ares_array_t *split = NULL;
    994   size_t        i;
    995   size_t        len;
    996 
    997   if (arr == NULL) {
    998     return ARES_EFORMERR;
    999   }
   1000 
   1001   *arr = NULL;
   1002 
   1003   status = ares_buf_split(buf, delims, delims_len, flags, max_sections, &split);
   1004   if (status != ARES_SUCCESS) {
   1005     goto done;
   1006   }
   1007 
   1008   *arr = ares_array_create(sizeof(char *), ares_free_split_array);
   1009   if (*arr == NULL) {
   1010     status = ARES_ENOMEM;
   1011     goto done;
   1012   }
   1013 
   1014   len = ares_array_len(split);
   1015   for (i = 0; i < len; i++) {
   1016     ares_buf_t **bufptr = ares_array_at(split, i);
   1017     ares_buf_t  *lbuf   = *bufptr;
   1018     char        *str    = NULL;
   1019 
   1020     status = ares_buf_fetch_str_dup(lbuf, ares_buf_len(lbuf), &str);
   1021     if (status != ARES_SUCCESS) {
   1022       goto done;
   1023     }
   1024 
   1025     status = ares_array_insertdata_last(*arr, &str);
   1026     if (status != ARES_SUCCESS) {
   1027       ares_free(str);
   1028       goto done;
   1029     }
   1030   }
   1031 
   1032 done:
   1033   ares_array_destroy(split);
   1034   if (status != ARES_SUCCESS) {
   1035     ares_array_destroy(*arr);
   1036     *arr = NULL;
   1037   }
   1038   return status;
   1039 }
   1040 
   1041 ares_status_t ares_buf_split_str(ares_buf_t *buf, const unsigned char *delims,
   1042                                  size_t delims_len, ares_buf_split_t flags,
   1043                                  size_t max_sections, char ***strs,
   1044                                  size_t *nstrs)
   1045 {
   1046   ares_status_t status;
   1047   ares_array_t *arr = NULL;
   1048 
   1049   if (strs == NULL || nstrs == NULL) {
   1050     return ARES_EFORMERR;
   1051   }
   1052 
   1053   *strs  = NULL;
   1054   *nstrs = 0;
   1055 
   1056   status = ares_buf_split_str_array(buf, delims, delims_len, flags,
   1057                                     max_sections, &arr);
   1058 
   1059   if (status != ARES_SUCCESS) {
   1060     goto done;
   1061   }
   1062 
   1063 done:
   1064   if (status == ARES_SUCCESS) {
   1065     *strs = ares_array_finish(arr, nstrs);
   1066   } else {
   1067     ares_array_destroy(arr);
   1068   }
   1069   return status;
   1070 }
   1071 
   1072 ares_bool_t ares_buf_begins_with(const ares_buf_t    *buf,
   1073                                  const unsigned char *data, size_t data_len)
   1074 {
   1075   size_t               remaining_len = 0;
   1076   const unsigned char *ptr           = ares_buf_fetch(buf, &remaining_len);
   1077 
   1078   if (ptr == NULL || data == NULL || data_len == 0) {
   1079     return ARES_FALSE;
   1080   }
   1081 
   1082   if (data_len > remaining_len) {
   1083     return ARES_FALSE;
   1084   }
   1085 
   1086   if (memcmp(ptr, data, data_len) != 0) {
   1087     return ARES_FALSE;
   1088   }
   1089 
   1090   return ARES_TRUE;
   1091 }
   1092 
   1093 size_t ares_buf_len(const ares_buf_t *buf)
   1094 {
   1095   if (buf == NULL) {
   1096     return 0;
   1097   }
   1098 
   1099   return buf->data_len - buf->offset;
   1100 }
   1101 
   1102 const unsigned char *ares_buf_peek(const ares_buf_t *buf, size_t *len)
   1103 {
   1104   return ares_buf_fetch(buf, len);
   1105 }
   1106 
   1107 ares_status_t ares_buf_replace(ares_buf_t *buf, const unsigned char *srch,
   1108                                size_t srch_size, const unsigned char *rplc,
   1109                                size_t rplc_size)
   1110 {
   1111   size_t        processed_len = 0;
   1112   ares_status_t status;
   1113 
   1114   if (buf->alloc_buf == NULL || srch == NULL || srch_size == 0 ||
   1115       (rplc == NULL && rplc_size != 0)) {
   1116     return ARES_EFORMERR;
   1117   }
   1118 
   1119   while (1) {
   1120     unsigned char *ptr           = buf->alloc_buf + buf->offset + processed_len;
   1121     size_t         remaining_len = buf->data_len - buf->offset - processed_len;
   1122     size_t         found_offset  = 0;
   1123     size_t         move_data_len;
   1124 
   1125     /* Find pattern */
   1126     ptr = ares_memmem(ptr, remaining_len, srch, srch_size);
   1127     if (ptr == NULL) {
   1128       break;
   1129     }
   1130 
   1131     /* Store the offset this was found because our actual pointer might be
   1132      * switched out from under us by the call to ensure_space() if the
   1133      * replacement pattern is larger than the search pattern */
   1134     found_offset   = (size_t)(ptr - (size_t)(buf->alloc_buf + buf->offset));
   1135     if (rplc_size > srch_size) {
   1136       status = ares_buf_ensure_space(buf, rplc_size - srch_size);
   1137       if (status != ARES_SUCCESS) {
   1138         return status;
   1139       }
   1140     }
   1141 
   1142     /* Impossible, but silence clang */
   1143     if (buf->alloc_buf == NULL) {
   1144       return ARES_ENOMEM;
   1145     }
   1146 
   1147     /* Recalculate actual pointer */
   1148     ptr = buf->alloc_buf + buf->offset + found_offset;
   1149 
   1150     /* Move the data */
   1151     move_data_len = buf->data_len - buf->offset - found_offset - srch_size;
   1152     memmove(ptr + rplc_size,
   1153             ptr + srch_size,
   1154             move_data_len);
   1155 
   1156     /* Copy in the replacement data */
   1157     if (rplc != NULL && rplc_size > 0) {
   1158       memcpy(ptr, rplc, rplc_size);
   1159     }
   1160 
   1161     if (rplc_size > srch_size) {
   1162       buf->data_len += rplc_size - srch_size;
   1163     } else {
   1164       buf->data_len -= srch_size - rplc_size;
   1165     }
   1166 
   1167     processed_len = found_offset + rplc_size;
   1168   }
   1169 
   1170   return ARES_SUCCESS;
   1171 }
   1172 
   1173 ares_status_t ares_buf_peek_byte(const ares_buf_t *buf, unsigned char *b)
   1174 {
   1175   size_t               remaining_len = 0;
   1176   const unsigned char *ptr           = ares_buf_fetch(buf, &remaining_len);
   1177 
   1178   if (buf == NULL || b == NULL) {
   1179     return ARES_EFORMERR;
   1180   }
   1181 
   1182   if (remaining_len == 0) {
   1183     return ARES_EBADRESP;
   1184   }
   1185   *b = ptr[0];
   1186   return ARES_SUCCESS;
   1187 }
   1188 
   1189 size_t ares_buf_get_position(const ares_buf_t *buf)
   1190 {
   1191   if (buf == NULL) {
   1192     return 0;
   1193   }
   1194   return buf->offset;
   1195 }
   1196 
   1197 ares_status_t ares_buf_set_position(ares_buf_t *buf, size_t idx)
   1198 {
   1199   if (buf == NULL) {
   1200     return ARES_EFORMERR;
   1201   }
   1202 
   1203   if (idx > buf->data_len) {
   1204     return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
   1205   }
   1206 
   1207   buf->offset = idx;
   1208   return ARES_SUCCESS;
   1209 }
   1210 
   1211 static ares_status_t
   1212   ares_buf_parse_dns_binstr_int(ares_buf_t *buf, size_t remaining_len,
   1213                                 unsigned char **bin, size_t *bin_len,
   1214                                 ares_bool_t validate_printable)
   1215 {
   1216   unsigned char len;
   1217   ares_status_t status = ARES_EBADRESP;
   1218   ares_buf_t   *binbuf = NULL;
   1219 
   1220   if (buf == NULL) {
   1221     return ARES_EFORMERR;
   1222   }
   1223 
   1224   if (remaining_len == 0) {
   1225     return ARES_EBADRESP;
   1226   }
   1227 
   1228   binbuf = ares_buf_create();
   1229   if (binbuf == NULL) {
   1230     return ARES_ENOMEM;
   1231   }
   1232 
   1233   status = ares_buf_fetch_bytes(buf, &len, 1);
   1234   if (status != ARES_SUCCESS) {
   1235     goto done; /* LCOV_EXCL_LINE: DefensiveCoding */
   1236   }
   1237 
   1238   remaining_len--;
   1239 
   1240   if (len > remaining_len) {
   1241     status = ARES_EBADRESP;
   1242     goto done;
   1243   }
   1244 
   1245   if (len) {
   1246     /* When used by the _str() parser, it really needs to be validated to
   1247      * be a valid printable ascii string.  Do that here */
   1248     if (validate_printable && ares_buf_len(buf) >= len) {
   1249       size_t      mylen;
   1250       const char *data = (const char *)ares_buf_peek(buf, &mylen);
   1251       if (!ares_str_isprint(data, len)) {
   1252         status = ARES_EBADSTR;
   1253         goto done;
   1254       }
   1255     }
   1256 
   1257     if (bin != NULL) {
   1258       status = ares_buf_fetch_bytes_into_buf(buf, binbuf, len);
   1259     } else {
   1260       status = ares_buf_consume(buf, len);
   1261     }
   1262   }
   1263 
   1264 done:
   1265   if (status != ARES_SUCCESS) {
   1266     ares_buf_destroy(binbuf);
   1267   } else {
   1268     if (bin != NULL) {
   1269       size_t mylen = 0;
   1270       /* NOTE: we use ares_buf_finish_str() here as we guarantee NULL
   1271        *       Termination even though we are technically returning binary data.
   1272        */
   1273       *bin     = (unsigned char *)ares_buf_finish_str(binbuf, &mylen);
   1274       *bin_len = mylen;
   1275     }
   1276   }
   1277 
   1278   return status;
   1279 }
   1280 
   1281 ares_status_t ares_buf_parse_dns_binstr(ares_buf_t *buf, size_t remaining_len,
   1282                                         unsigned char **bin, size_t *bin_len)
   1283 {
   1284   return ares_buf_parse_dns_binstr_int(buf, remaining_len, bin, bin_len,
   1285                                        ARES_FALSE);
   1286 }
   1287 
   1288 ares_status_t ares_buf_parse_dns_str(ares_buf_t *buf, size_t remaining_len,
   1289                                      char **str)
   1290 {
   1291   size_t len;
   1292 
   1293   return ares_buf_parse_dns_binstr_int(buf, remaining_len,
   1294                                        (unsigned char **)str, &len, ARES_TRUE);
   1295 }
   1296 
   1297 ares_status_t ares_buf_append_num_dec(ares_buf_t *buf, size_t num, size_t len)
   1298 {
   1299   size_t i;
   1300   size_t mod;
   1301 
   1302   if (len == 0) {
   1303     len = ares_count_digits(num);
   1304   }
   1305 
   1306   mod = ares_pow(10, len);
   1307 
   1308   for (i = len; i > 0; i--) {
   1309     size_t        digit = (num % mod);
   1310     ares_status_t status;
   1311 
   1312     mod /= 10;
   1313 
   1314     /* Silence coverity.  Shouldn't be possible since we calculate it above */
   1315     if (mod == 0) {
   1316       return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
   1317     }
   1318 
   1319     digit  /= mod;
   1320     status  = ares_buf_append_byte(buf, '0' + (unsigned char)(digit & 0xFF));
   1321     if (status != ARES_SUCCESS) {
   1322       return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1323     }
   1324   }
   1325   return ARES_SUCCESS;
   1326 }
   1327 
   1328 ares_status_t ares_buf_append_num_hex(ares_buf_t *buf, size_t num, size_t len)
   1329 {
   1330   size_t                     i;
   1331   static const unsigned char hexbytes[] = "0123456789ABCDEF";
   1332 
   1333   if (len == 0) {
   1334     len = ares_count_hexdigits(num);
   1335   }
   1336 
   1337   for (i = len; i > 0; i--) {
   1338     ares_status_t status;
   1339     status = ares_buf_append_byte(buf, hexbytes[(num >> ((i - 1) * 4)) & 0xF]);
   1340     if (status != ARES_SUCCESS) {
   1341       return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1342     }
   1343   }
   1344   return ARES_SUCCESS;
   1345 }
   1346 
   1347 ares_status_t ares_buf_append_str(ares_buf_t *buf, const char *str)
   1348 {
   1349   return ares_buf_append(buf, (const unsigned char *)str, ares_strlen(str));
   1350 }
   1351 
   1352 static ares_status_t ares_buf_hexdump_line(ares_buf_t *buf, size_t idx,
   1353                                            const unsigned char *data,
   1354                                            size_t               len)
   1355 {
   1356   size_t        i;
   1357   ares_status_t status;
   1358 
   1359   /* Address */
   1360   status = ares_buf_append_num_hex(buf, idx, 6);
   1361   if (status != ARES_SUCCESS) {
   1362     return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1363   }
   1364 
   1365   /* | */
   1366   status = ares_buf_append_str(buf, " | ");
   1367   if (status != ARES_SUCCESS) {
   1368     return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1369   }
   1370 
   1371   for (i = 0; i < 16; i++) {
   1372     if (i >= len) {
   1373       status = ares_buf_append_str(buf, "  ");
   1374     } else {
   1375       status = ares_buf_append_num_hex(buf, data[i], 2);
   1376     }
   1377     if (status != ARES_SUCCESS) {
   1378       return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1379     }
   1380 
   1381     status = ares_buf_append_byte(buf, ' ');
   1382     if (status != ARES_SUCCESS) {
   1383       return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1384     }
   1385   }
   1386 
   1387   /* | */
   1388   status = ares_buf_append_str(buf, " | ");
   1389   if (status != ARES_SUCCESS) {
   1390     return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1391   }
   1392 
   1393   for (i = 0; i < 16; i++) {
   1394     if (i >= len) {
   1395       break;
   1396     }
   1397     status = ares_buf_append_byte(buf, ares_isprint(data[i]) ? data[i] : '.');
   1398     if (status != ARES_SUCCESS) {
   1399       return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1400     }
   1401   }
   1402 
   1403   return ares_buf_append_byte(buf, '\n');
   1404 }
   1405 
   1406 ares_status_t ares_buf_hexdump(ares_buf_t *buf, const unsigned char *data,
   1407                                size_t len)
   1408 {
   1409   size_t i;
   1410 
   1411   /* Each line is 16 bytes */
   1412   for (i = 0; i < len; i += 16) {
   1413     ares_status_t status;
   1414     status = ares_buf_hexdump_line(buf, i, data + i, len - i);
   1415     if (status != ARES_SUCCESS) {
   1416       return status; /* LCOV_EXCL_LINE: OutOfMemory */
   1417     }
   1418   }
   1419 
   1420   return ARES_SUCCESS;
   1421 }
   1422 
   1423 ares_status_t ares_buf_load_file(const char *filename, ares_buf_t *buf)
   1424 {
   1425   FILE          *fp        = NULL;
   1426   unsigned char *ptr       = NULL;
   1427   size_t         len       = 0;
   1428   size_t         ptr_len   = 0;
   1429   long           ftell_len = 0;
   1430   ares_status_t  status;
   1431 
   1432   if (filename == NULL || buf == NULL) {
   1433     return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
   1434   }
   1435 
   1436   fp = fopen(filename, "rb");
   1437   if (fp == NULL) {
   1438     int error = errno;
   1439     switch (error) {
   1440       case ENOENT:
   1441       case ESRCH:
   1442         status = ARES_ENOTFOUND;
   1443         goto done;
   1444       default:
   1445         DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
   1446                        strerror(error)));
   1447         DEBUGF(fprintf(stderr, "Error opening file: %s\n", filename));
   1448         status = ARES_EFILE;
   1449         goto done;
   1450     }
   1451   }
   1452 
   1453   /* Get length portably, fstat() is POSIX, not C */
   1454   if (fseek(fp, 0, SEEK_END) != 0) {
   1455     status = ARES_EFILE; /* LCOV_EXCL_LINE: DefensiveCoding */
   1456     goto done;           /* LCOV_EXCL_LINE: DefensiveCoding */
   1457   }
   1458 
   1459   ftell_len = ftell(fp);
   1460   if (ftell_len < 0) {
   1461     status = ARES_EFILE; /* LCOV_EXCL_LINE: DefensiveCoding */
   1462     goto done;           /* LCOV_EXCL_LINE: DefensiveCoding */
   1463   }
   1464   len = (size_t)ftell_len;
   1465 
   1466   if (fseek(fp, 0, SEEK_SET) != 0) {
   1467     status = ARES_EFILE; /* LCOV_EXCL_LINE: DefensiveCoding */
   1468     goto done;           /* LCOV_EXCL_LINE: DefensiveCoding */
   1469   }
   1470 
   1471   if (len == 0) {
   1472     status = ARES_SUCCESS; /* LCOV_EXCL_LINE: DefensiveCoding */
   1473     goto done;             /* LCOV_EXCL_LINE: DefensiveCoding */
   1474   }
   1475 
   1476   /* Read entire data into buffer */
   1477   ptr_len = len;
   1478   ptr     = ares_buf_append_start(buf, &ptr_len);
   1479   if (ptr == NULL) {
   1480     status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
   1481     goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
   1482   }
   1483 
   1484   ptr_len = fread(ptr, 1, len, fp);
   1485   if (ptr_len != len) {
   1486     status = ARES_EFILE; /* LCOV_EXCL_LINE: DefensiveCoding */
   1487     goto done;           /* LCOV_EXCL_LINE: DefensiveCoding */
   1488   }
   1489 
   1490   ares_buf_append_finish(buf, len);
   1491   status = ARES_SUCCESS;
   1492 
   1493 done:
   1494   if (fp != NULL) {
   1495     fclose(fp);
   1496   }
   1497   return status;
   1498 }