mirror of https://github.com/c-ares/c-ares.git
Fix for TCP back to back queries (#552)
As per #266, TCP queries are basically broken. If we get a partial reply, things just don't work, but unlike UDP, TCP may get fragmented and we need to properly handle that. I've started creating a basic parser/buffer framework for c-ares for memory safety reasons, but it also helps for things like this where we shouldn't be manually tracking positions and fetching only a couple of bytes at a time from a socket. This parser/buffer will be expanded and used more in the future. This also resolves #206 by allowing NULL to be specified for some socket callbacks so they will auto-route to the built-in c-ares functions. Fixes: #206, #266 Fix By: Brad House (@bradh352)pull/557/head
parent
17ab747945
commit
fab4039b9b
18 changed files with 715 additions and 486 deletions
@ -0,0 +1,320 @@ |
|||||||
|
/* Copyright (C) 2023 by Brad House
|
||||||
|
* |
||||||
|
* Permission to use, copy, modify, and distribute this |
||||||
|
* software and its documentation for any purpose and without |
||||||
|
* fee is hereby granted, provided that the above copyright |
||||||
|
* notice appear in all copies and that both that copyright |
||||||
|
* notice and this permission notice appear in supporting |
||||||
|
* documentation, and that the name of M.I.T. not be used in |
||||||
|
* advertising or publicity pertaining to distribution of the |
||||||
|
* software without specific, written prior permission. |
||||||
|
* M.I.T. makes no representations about the suitability of |
||||||
|
* this software for any purpose. It is provided "as is" |
||||||
|
* without express or implied warranty. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
#include "ares_setup.h" |
||||||
|
#include "ares.h" |
||||||
|
#include "ares_private.h" |
||||||
|
#include "ares__buf.h" |
||||||
|
#include <limits.h> |
||||||
|
#ifdef HAVE_STDINT_H |
||||||
|
# include <stdint.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
struct ares__buf { |
||||||
|
const unsigned char *data; /*!< pointer to start of data buffer */ |
||||||
|
size_t data_len; /*!< total size of data in buffer */ |
||||||
|
|
||||||
|
unsigned char *alloc_buf; /*!< Pointer to allocated data buffer,
|
||||||
|
* not used for const buffers */ |
||||||
|
size_t alloc_buf_len; /*!< Size of allocated data buffer */ |
||||||
|
|
||||||
|
size_t offset; /*!< Current working offset in buffer */ |
||||||
|
size_t tag_offset; /*!< Tagged offset in buffer. Uses
|
||||||
|
* SIZE_MAX if not set. */ |
||||||
|
}; |
||||||
|
|
||||||
|
ares__buf_t *ares__buf_create(void) |
||||||
|
{ |
||||||
|
ares__buf_t *buf = ares_malloc(sizeof(*buf)); |
||||||
|
if (buf == NULL) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
memset(buf, 0, sizeof(*buf)); |
||||||
|
buf->tag_offset = SIZE_MAX; |
||||||
|
return buf; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
ares__buf_t *ares__buf_create_const(const unsigned char *data, size_t data_len) |
||||||
|
{ |
||||||
|
ares__buf_t *buf; |
||||||
|
|
||||||
|
if (data == NULL || data_len == 0) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
buf = ares__buf_create(); |
||||||
|
if (buf == NULL) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
buf->data = data; |
||||||
|
buf->data_len = data_len; |
||||||
|
|
||||||
|
return buf; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ares__buf_destroy(ares__buf_t *buf) |
||||||
|
{ |
||||||
|
if (buf == NULL) |
||||||
|
return; |
||||||
|
ares_free(buf->alloc_buf); |
||||||
|
ares_free(buf); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static int ares__buf_is_const(const ares__buf_t *buf) |
||||||
|
{ |
||||||
|
if (buf == NULL) |
||||||
|
return 0; |
||||||
|
|
||||||
|
if (buf->data != NULL && buf->alloc_buf == NULL) |
||||||
|
return 1; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static void ares__buf_reclaim(ares__buf_t *buf, size_t needed_size) |
||||||
|
{ |
||||||
|
size_t prefix_size; |
||||||
|
size_t remaining_size; |
||||||
|
size_t data_size; |
||||||
|
|
||||||
|
if (buf == NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
if (ares__buf_is_const(buf)) |
||||||
|
return; |
||||||
|
|
||||||
|
remaining_size = buf->alloc_buf_len - buf->data_len; |
||||||
|
|
||||||
|
/* No need to do an expensive move operation, we have enough to just append */ |
||||||
|
if (remaining_size >= needed_size) |
||||||
|
return; |
||||||
|
|
||||||
|
if (buf->tag_offset != SIZE_MAX) { |
||||||
|
prefix_size = buf->tag_offset; |
||||||
|
} else { |
||||||
|
prefix_size = buf->offset; |
||||||
|
} |
||||||
|
|
||||||
|
if (prefix_size == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
data_size = buf->data_len - prefix_size; |
||||||
|
|
||||||
|
memmove(buf->alloc_buf, buf->alloc_buf + prefix_size, data_size); |
||||||
|
buf->data = buf->alloc_buf; |
||||||
|
buf->data_len = data_size; |
||||||
|
buf->offset -= prefix_size; |
||||||
|
if (buf->tag_offset != SIZE_MAX) |
||||||
|
buf->tag_offset -= prefix_size; |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static int ares__buf_ensure_space(ares__buf_t *buf, size_t needed_size) |
||||||
|
{ |
||||||
|
size_t remaining_size; |
||||||
|
size_t alloc_size; |
||||||
|
unsigned char *ptr; |
||||||
|
|
||||||
|
if (buf == NULL) |
||||||
|
return 0; |
||||||
|
|
||||||
|
if (ares__buf_is_const(buf)) |
||||||
|
return 0; |
||||||
|
|
||||||
|
/* See if just moving consumed data frees up enough space */ |
||||||
|
ares__buf_reclaim(buf, needed_size); |
||||||
|
|
||||||
|
remaining_size = buf->alloc_buf_len - buf->data_len; |
||||||
|
if (remaining_size >= needed_size) |
||||||
|
return 1; |
||||||
|
|
||||||
|
alloc_size = buf->alloc_buf_len; |
||||||
|
|
||||||
|
/* Not yet started */ |
||||||
|
if (alloc_size == 0) |
||||||
|
alloc_size = 512; |
||||||
|
|
||||||
|
/* Increase allocation by powers of 2 */ |
||||||
|
do { |
||||||
|
alloc_size <<= 1; |
||||||
|
remaining_size = alloc_size - buf->data_len; |
||||||
|
} while (remaining_size < needed_size); |
||||||
|
|
||||||
|
ptr = ares_realloc(buf->alloc_buf, alloc_size); |
||||||
|
if (ptr == NULL) |
||||||
|
return 0; |
||||||
|
|
||||||
|
buf->alloc_buf = ptr; |
||||||
|
buf->alloc_buf_len = alloc_size; |
||||||
|
buf->data = ptr; |
||||||
|
|
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int ares__buf_append(ares__buf_t *buf, const unsigned char *data, |
||||||
|
size_t data_len) |
||||||
|
{ |
||||||
|
if (data == NULL || data_len == 0) |
||||||
|
return 0; |
||||||
|
|
||||||
|
if (!ares__buf_ensure_space(buf, data_len)) |
||||||
|
return 0; |
||||||
|
|
||||||
|
memcpy(buf->alloc_buf + buf->data_len, data, data_len); |
||||||
|
buf->data_len += data_len; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
unsigned char *ares__buf_append_start(ares__buf_t *buf, size_t *len) |
||||||
|
{ |
||||||
|
if (len == NULL || *len == 0) |
||||||
|
return 0; |
||||||
|
|
||||||
|
if (!ares__buf_ensure_space(buf, *len)) |
||||||
|
return 0; |
||||||
|
|
||||||
|
*len = buf->alloc_buf_len - buf->data_len; |
||||||
|
return buf->alloc_buf + buf->data_len; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ares__buf_append_finish(ares__buf_t *buf, size_t len) |
||||||
|
{ |
||||||
|
if (buf == NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
buf->data_len += len; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ares__buf_tag(ares__buf_t *buf) |
||||||
|
{ |
||||||
|
if (buf == NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
buf->tag_offset = buf->offset; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int ares__buf_tag_rollback(ares__buf_t *buf) |
||||||
|
{ |
||||||
|
if (buf == NULL || buf->tag_offset == SIZE_MAX) |
||||||
|
return 0; |
||||||
|
|
||||||
|
buf->offset = buf->tag_offset; |
||||||
|
buf->tag_offset = SIZE_MAX; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int ares__buf_tag_clear(ares__buf_t *buf) |
||||||
|
{ |
||||||
|
if (buf == NULL || buf->tag_offset == SIZE_MAX) |
||||||
|
return 0; |
||||||
|
|
||||||
|
buf->tag_offset = SIZE_MAX; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const unsigned char *ares__buf_tag_fetch(const ares__buf_t *buf, size_t *len) |
||||||
|
{ |
||||||
|
if (buf == NULL || buf->tag_offset == SIZE_MAX || len == NULL) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
*len = buf->offset - buf->tag_offset; |
||||||
|
return buf->data + buf->tag_offset; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static const unsigned char *ares__buf_fetch(const ares__buf_t *buf, size_t *len) |
||||||
|
{ |
||||||
|
if (len != NULL) |
||||||
|
*len = 0; |
||||||
|
|
||||||
|
if (buf == NULL || len == NULL || buf->data == NULL) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
*len = buf->data_len - buf->offset; |
||||||
|
return buf->data + buf->offset; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int ares__buf_consume(ares__buf_t *buf, size_t len) |
||||||
|
{ |
||||||
|
size_t remaining_len; |
||||||
|
|
||||||
|
ares__buf_fetch(buf, &remaining_len); |
||||||
|
|
||||||
|
if (remaining_len < len) |
||||||
|
return 0; |
||||||
|
|
||||||
|
buf->offset += len; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int ares__buf_fetch_be16(ares__buf_t *buf, unsigned short *u16) |
||||||
|
{ |
||||||
|
size_t remaining_len; |
||||||
|
const unsigned char *ptr = ares__buf_fetch(buf, &remaining_len); |
||||||
|
|
||||||
|
if (buf == NULL || u16 == NULL || remaining_len < sizeof(*u16)) |
||||||
|
return 0; |
||||||
|
|
||||||
|
*u16 = (unsigned short)((unsigned short)(ptr[0]) << 8 | (unsigned short)ptr[1]); |
||||||
|
|
||||||
|
return ares__buf_consume(buf, sizeof(*u16)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int ares__buf_fetch_bytes(ares__buf_t *buf, unsigned char *bytes, |
||||||
|
size_t len) |
||||||
|
{ |
||||||
|
size_t remaining_len; |
||||||
|
const unsigned char *ptr = ares__buf_fetch(buf, &remaining_len); |
||||||
|
|
||||||
|
if (buf == NULL || bytes == NULL || len == 0 || remaining_len < len) |
||||||
|
return 0; |
||||||
|
|
||||||
|
memcpy(bytes, ptr, len); |
||||||
|
return ares__buf_consume(buf, len); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
size_t ares__buf_len(const ares__buf_t *buf) |
||||||
|
{ |
||||||
|
size_t len = 0; |
||||||
|
ares__buf_fetch(buf, &len); |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const unsigned char *ares__buf_peek(const ares__buf_t *buf, size_t *len) |
||||||
|
{ |
||||||
|
return ares__buf_fetch(buf, len); |
||||||
|
} |
||||||
|
|
||||||
|
#if 0 |
||||||
|
int ares__buf_fetch_dnsheader(ares__buf_t *buf, ares_dns_header_t *header); |
||||||
|
#endif |
@ -0,0 +1,180 @@ |
|||||||
|
/* Copyright (C) 2023 by Brad House
|
||||||
|
* |
||||||
|
* Permission to use, copy, modify, and distribute this |
||||||
|
* software and its documentation for any purpose and without |
||||||
|
* fee is hereby granted, provided that the above copyright |
||||||
|
* notice appear in all copies and that both that copyright |
||||||
|
* notice and this permission notice appear in supporting |
||||||
|
* documentation, and that the name of M.I.T. not be used in |
||||||
|
* advertising or publicity pertaining to distribution of the |
||||||
|
* software without specific, written prior permission. |
||||||
|
* M.I.T. makes no representations about the suitability of |
||||||
|
* this software for any purpose. It is provided "as is" |
||||||
|
* without express or implied warranty. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
#ifndef __ARES__BUF_H |
||||||
|
#define __ARES__BUF_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. |
||||||
|
* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
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. |
||||||
|
*/ |
||||||
|
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 |
||||||
|
*/ |
||||||
|
void ares__buf_destroy(ares__buf_t *buf); |
||||||
|
|
||||||
|
/*! Append 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 1 on success, 0 on failure (out of memory, const buffer, usage, etc) |
||||||
|
*/ |
||||||
|
int ares__buf_append(ares__buf_t *buf, const unsigned char *data, |
||||||
|
size_t data_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) |
||||||
|
*/ |
||||||
|
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(). |
||||||
|
*/ |
||||||
|
void ares__buf_append_finish(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 |
||||||
|
*/ |
||||||
|
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 1 on success, 0 if no tag |
||||||
|
*/ |
||||||
|
int 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 1 on success, 0 if no tag |
||||||
|
*/ |
||||||
|
int 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 |
||||||
|
*/ |
||||||
|
const unsigned char *ares__buf_tag_fetch(const ares__buf_t *buf, size_t *len); |
||||||
|
|
||||||
|
/*! Consume the given number of bytes without reading them.
|
||||||
|
* |
||||||
|
* \param[in] buf Initialized buffer object |
||||||
|
* \param[in] len Length to consume |
||||||
|
* \return 1 on success, 0 if insufficient buffer remaining |
||||||
|
*/ |
||||||
|
int 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 1 on success, 0 if insufficient buffer remaining |
||||||
|
*/ |
||||||
|
int ares__buf_fetch_be16(ares__buf_t *buf, unsigned short *u16); |
||||||
|
|
||||||
|
/*! 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 1 on success, 0 if insufficient buffer remaining (or misuse) |
||||||
|
*/ |
||||||
|
int ares__buf_fetch_bytes(ares__buf_t *buf, unsigned char *bytes, |
||||||
|
size_t len); |
||||||
|
|
||||||
|
/*! Size of unprocessed remaining data length
|
||||||
|
* |
||||||
|
* \param[in] buf Initialized buffer object |
||||||
|
* \return length remaining |
||||||
|
*/ |
||||||
|
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 |
||||||
|
*/ |
||||||
|
const unsigned char *ares__buf_peek(const ares__buf_t *buf, |
||||||
|
size_t *len); |
||||||
|
|
||||||
|
#if 0 |
||||||
|
int ares__buf_fetch_dnsheader(ares__buf_t *buf, ares_dns_header_t *header); |
||||||
|
#endif |
||||||
|
|
||||||
|
/*! @} */ |
||||||
|
|
||||||
|
#endif /* __ARES__buffer_H */ |
@ -1,79 +0,0 @@ |
|||||||
/* Copyright 1998 by the Massachusetts Institute of Technology.
|
|
||||||
* |
|
||||||
* Permission to use, copy, modify, and distribute this |
|
||||||
* software and its documentation for any purpose and without |
|
||||||
* fee is hereby granted, provided that the above copyright |
|
||||||
* notice appear in all copies and that both that copyright |
|
||||||
* notice and this permission notice appear in supporting |
|
||||||
* documentation, and that the name of M.I.T. not be used in |
|
||||||
* advertising or publicity pertaining to distribution of the |
|
||||||
* software without specific, written prior permission. |
|
||||||
* M.I.T. makes no representations about the suitability of |
|
||||||
* this software for any purpose. It is provided "as is" |
|
||||||
* without express or implied warranty. |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "ares_setup.h" |
|
||||||
|
|
||||||
#ifdef HAVE_LIMITS_H |
|
||||||
# include <limits.h> |
|
||||||
#endif |
|
||||||
|
|
||||||
#include "ares.h" |
|
||||||
#include "ares_private.h" |
|
||||||
|
|
||||||
#ifndef HAVE_WRITEV |
|
||||||
ares_ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt) |
|
||||||
{ |
|
||||||
char *buffer, *bp; |
|
||||||
int i; |
|
||||||
size_t bytes = 0; |
|
||||||
ares_ssize_t result; |
|
||||||
|
|
||||||
/* Validate iovcnt */ |
|
||||||
if (iovcnt <= 0) |
|
||||||
{ |
|
||||||
SET_ERRNO(EINVAL); |
|
||||||
return (-1); |
|
||||||
} |
|
||||||
|
|
||||||
/* Validate and find the sum of the iov_len values in the iov array */ |
|
||||||
for (i = 0; i < iovcnt; i++) |
|
||||||
{ |
|
||||||
if (iov[i].iov_len > INT_MAX - bytes) |
|
||||||
{ |
|
||||||
SET_ERRNO(EINVAL); |
|
||||||
return (-1); |
|
||||||
} |
|
||||||
bytes += iov[i].iov_len; |
|
||||||
} |
|
||||||
|
|
||||||
if (bytes == 0) |
|
||||||
return (0); |
|
||||||
|
|
||||||
/* Allocate a temporary buffer to hold the data */ |
|
||||||
buffer = ares_malloc(bytes); |
|
||||||
if (!buffer) |
|
||||||
{ |
|
||||||
SET_ERRNO(ENOMEM); |
|
||||||
return (-1); |
|
||||||
} |
|
||||||
|
|
||||||
/* Copy the data into buffer */ |
|
||||||
for (bp = buffer, i = 0; i < iovcnt; ++i) |
|
||||||
{ |
|
||||||
memcpy (bp, iov[i].iov_base, iov[i].iov_len); |
|
||||||
bp += iov[i].iov_len; |
|
||||||
} |
|
||||||
|
|
||||||
/* Send buffer contents */ |
|
||||||
result = swrite(s, buffer, bytes); |
|
||||||
|
|
||||||
ares_free(buffer); |
|
||||||
|
|
||||||
return (result); |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
@ -1,38 +0,0 @@ |
|||||||
#ifndef HEADER_CARES_WRITEV_H |
|
||||||
#define HEADER_CARES_WRITEV_H |
|
||||||
|
|
||||||
|
|
||||||
/* Copyright 1998 by the Massachusetts Institute of Technology.
|
|
||||||
* |
|
||||||
* Permission to use, copy, modify, and distribute this |
|
||||||
* software and its documentation for any purpose and without |
|
||||||
* fee is hereby granted, provided that the above copyright |
|
||||||
* notice appear in all copies and that both that copyright |
|
||||||
* notice and this permission notice appear in supporting |
|
||||||
* documentation, and that the name of M.I.T. not be used in |
|
||||||
* advertising or publicity pertaining to distribution of the |
|
||||||
* software without specific, written prior permission. |
|
||||||
* M.I.T. makes no representations about the suitability of |
|
||||||
* this software for any purpose. It is provided "as is" |
|
||||||
* without express or implied warranty. |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: MIT |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "ares_setup.h" |
|
||||||
#include "ares.h" |
|
||||||
|
|
||||||
#ifndef HAVE_WRITEV |
|
||||||
|
|
||||||
/* Structure for scatter/gather I/O. */ |
|
||||||
struct iovec |
|
||||||
{ |
|
||||||
void *iov_base; /* Pointer to data. */ |
|
||||||
size_t iov_len; /* Length of data. */ |
|
||||||
}; |
|
||||||
|
|
||||||
extern ares_ssize_t ares_writev(ares_socket_t s, const struct iovec *iov, int iovcnt); |
|
||||||
|
|
||||||
#endif |
|
||||||
|
|
||||||
#endif /* HEADER_CARES_WRITEV_H */ |
|
Loading…
Reference in new issue