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
Brad House 1 year ago committed by GitHub
parent 17ab747945
commit fab4039b9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/ares_set_socket_functions.3
  2. 4
      src/lib/Makefile.inc
  3. 320
      src/lib/ares__buf.c
  4. 180
      src/lib/ares__buf.h
  5. 23
      src/lib/ares__close_sockets.c
  6. 2
      src/lib/ares__htable.c
  7. 2
      src/lib/ares_destroy.c
  8. 5
      src/lib/ares_fds.c
  9. 4
      src/lib/ares_getsock.c
  10. 20
      src/lib/ares_init.c
  11. 36
      src/lib/ares_private.h
  12. 416
      src/lib/ares_process.c
  13. 79
      src/lib/ares_writev.c
  14. 38
      src/lib/ares_writev.h
  15. 24
      test/ares-test-internal.cc
  16. 3
      test/ares-test-mock.cc
  17. 39
      test/ares-test.cc
  18. 2
      test/ares-test.h

@ -25,7 +25,9 @@ void ares_set_socket_functions(ares_channel \fIchannel\fP,
This function sets a set of callback \fIfunctions\fP in the given ares channel handle.
These callback functions will be invoked to create/destroy socket objects and perform
io, instead of the normal system calls. A client application can override normal network
operation fully through this functionality, and provide its own transport layer.
operation fully through this functionality, and provide its own transport layer. You
can choose to only implement some of the socket functions, and provide NULL to any
others and c-ares will use its built-in system functions in that case.
.PP
All callback functions are expected to operate like their system equivalents, and to
set

@ -10,6 +10,7 @@ CSOURCES = ares__addrinfo2hostent.c \
ares__htable_stvp.c \
ares__llist.c \
ares__parse_into_addrinfo.c \
ares__buf.c \
ares__readaddrinfo.c \
ares__slist.c \
ares__sortaddrinfo.c \
@ -60,7 +61,6 @@ CSOURCES = ares__addrinfo2hostent.c \
ares_strsplit.c \
ares_timeout.c \
ares_version.c \
ares_writev.c \
bitncmp.c \
inet_net_pton.c \
inet_ntop.c \
@ -70,6 +70,7 @@ HHEADERS = ares__htable.h \
ares__htable_asvp.h \
ares__htable_stvp.h \
ares__llist.h \
ares__buf.h \
ares__slist.h \
ares_android.h \
ares_data.h \
@ -83,7 +84,6 @@ HHEADERS = ares__htable.h \
ares_strcasecmp.h \
ares_strdup.h \
ares_strsplit.h \
ares_writev.h \
bitncmp.h \
ares_setup.h \
setup_once.h

@ -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 */

@ -29,24 +29,9 @@ void ares__close_connection(struct server_connection *conn)
ares_channel channel = server->channel;
if (conn->is_tcp) {
struct send_request *sendreq;
/* Free all pending output buffers. */
while (server->qhead) {
/* Advance server->qhead; pull out query as we go. */
sendreq = server->qhead;
server->qhead = sendreq->next;
if (sendreq->data_storage != NULL)
ares_free(sendreq->data_storage);
ares_free(sendreq);
}
server->qtail = NULL;
/* Reset any existing input buffer. */
if (server->tcp_buffer)
ares_free(server->tcp_buffer);
server->tcp_buffer = NULL;
server->tcp_lenbuf_pos = 0;
/* Reset any existing input and output buffer. */
ares__buf_consume(server->tcp_parser, ares__buf_len(server->tcp_parser));
ares__buf_consume(server->tcp_send, ares__buf_len(server->tcp_send));
server->tcp_connection_generation = ++channel->tcp_connection_generation;
server->tcp_conn = NULL;
}
@ -100,7 +85,7 @@ void ares__check_cleanup_conn(ares_channel channel, ares_socket_t fd)
/* If the udp connection hit its max queries, always close it */
if (!conn->is_tcp && channel->udp_max_queries > 0 &&
conn->total_queries >= channel->udp_max_queries) {
conn->total_queries >= (size_t)channel->udp_max_queries) {
do_cleanup = 1;
}

@ -311,7 +311,7 @@ unsigned int ares__htable_hash_FNV1a(const unsigned char *key, size_t key_len,
/* tolower() is locale-specific. Use a lookup table fast conversion that only
* operates on ASCII */
static const char ares__tolower_lookup[] = {
static const unsigned char ares__tolower_lookup[] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,

@ -122,6 +122,8 @@ void ares__destroy_servers_state(ares_channel channel)
server = &channel->servers[i];
ares__close_sockets(server);
ares__llist_destroy(server->connections);
ares__buf_destroy(server->tcp_parser);
ares__buf_destroy(server->tcp_send);
}
ares_free(channel->servers);
channel->servers = NULL;

@ -33,9 +33,8 @@ int ares_fds(ares_channel channel, fd_set *read_fds, fd_set *write_fds)
nfds = 0;
for (i = 0; i < channel->nservers; i++) {
server = &channel->servers[i];
ares__llist_node_t *node;
server = &channel->servers[i];
for (node = ares__llist_node_first(server->connections);
node != NULL;
@ -51,7 +50,7 @@ int ares_fds(ares_channel channel, fd_set *read_fds, fd_set *write_fds)
nfds = conn->fd + 1;
}
if (conn->is_tcp && server->qhead) {
if (conn->is_tcp && ares__buf_len(server->tcp_send)) {
FD_SET(conn->fd, write_fds);
}
}

@ -33,9 +33,9 @@ int ares_getsock(ares_channel channel,
size_t active_queries = ares__llist_len(channel->all_queries);
for (i = 0; i < channel->nservers; i++) {
ares__llist_node_t *node;
server = &channel->servers[i];
ares__llist_node_t *node;
for (node = ares__llist_node_first(server->connections);
node != NULL;
node = ares__llist_node_next(node)) {
@ -57,7 +57,7 @@ int ares_getsock(ares_channel channel,
bitmap |= ARES_GETSOCK_READABLE(setbits, sockindex);
}
if (conn->is_tcp && server->qhead) {
if (conn->is_tcp && ares__buf_len(server->tcp_send)) {
/* then the tcp socket is also writable! */
bitmap |= ARES_GETSOCK_WRITABLE(setbits, sockindex);
}

@ -2395,17 +2395,23 @@ int ares__init_servers_state(ares_channel channel)
/* NOTE: Can't use memset() here because the server addresses have been
* filled in already */
server->tcp_lenbuf_pos = 0;
server->tcp_buffer_pos = 0;
server->tcp_buffer = NULL;
server->tcp_length = 0;
server->qhead = NULL;
server->qtail = NULL;
server->tcp_parser = ares__buf_create();
if (server->tcp_parser == NULL)
return ARES_ENOMEM;
server->tcp_send = ares__buf_create();
if (server->tcp_send == NULL) {
ares__buf_destroy(server->tcp_parser);
return ARES_ENOMEM;
}
server->idx = i;
server->connections = ares__llist_create(NULL);
if (server->connections == NULL)
if (server->connections == NULL) {
ares__buf_destroy(server->tcp_parser);
ares__buf_destroy(server->tcp_send);
return ARES_ENOMEM;
}
server->tcp_connection_generation = ++channel->tcp_connection_generation;
server->channel = channel;

@ -35,8 +35,6 @@
#ifdef WATT32
#include <tcp.h>
#include <sys/ioctl.h>
#define writev(s,v,c) writev_s(s,v,c)
#define HAVE_WRITEV 1
#endif
#define DEFAULT_TIMEOUT 2000 /* milliseconds */
@ -112,6 +110,7 @@ typedef struct ares_rand_state ares_rand_state;
#include "ares__slist.h"
#include "ares__htable_stvp.h"
#include "ares__htable_asvp.h"
#include "ares__buf.h"
#ifndef HAVE_GETENV
# include "ares_getenv.h"
@ -131,11 +130,6 @@ typedef struct ares_rand_state ares_rand_state;
# define strncasecmp(p1,p2,n) ares_strncasecmp(p1,p2,n)
#endif
#ifndef HAVE_WRITEV
# include "ares_writev.h"
# define writev(s,ptr,cnt) ares_writev(s,ptr,cnt)
#endif
/********* EDNS defines section ******/
#define EDNSPACKETSZ 1280 /* Reasonable UDP payload size, as suggested
in RFC2671 */
@ -157,20 +151,6 @@ struct ares_addr {
struct query;
struct send_request {
/* Remaining data to send */
const unsigned char *data;
size_t len;
/* The query for which we're sending this data */
struct query* owner_query;
/* The buffer we're using, if we have our own copy of the packet */
unsigned char *data_storage;
/* Next request in queue */
struct send_request *next;
};
struct server_state;
struct server_connection {
@ -190,18 +170,12 @@ struct server_state {
ares__llist_t *connections;
struct server_connection *tcp_conn;
/* Mini-buffer for reading the length word */
unsigned char tcp_lenbuf[2];
int tcp_lenbuf_pos;
int tcp_length;
/* Buffer for reading actual TCP data */
unsigned char *tcp_buffer;
int tcp_buffer_pos;
/* TCP buffer since multiple responses can come back in one read, or partial
* in a read */
ares__buf_t *tcp_parser;
/* TCP output queue */
struct send_request *qhead;
struct send_request *qtail;
ares__buf_t *tcp_send;
/* Which incarnation of this connection is this? We don't want to
* retransmit requests into the very same socket, but if the server

@ -62,10 +62,8 @@ static void write_tcp_data(ares_channel channel, fd_set *write_fds,
ares_socket_t write_fd, struct timeval *now);
static void read_packets(ares_channel channel, fd_set *read_fds,
ares_socket_t read_fd, struct timeval *now);
static void advance_tcp_send_queue(ares_channel channel, int whichserver,
ares_ssize_t num_bytes);
static void process_timeouts(ares_channel channel, struct timeval *now);
static void process_answer(ares_channel channel, unsigned char *abuf,
static void process_answer(ares_channel channel, const unsigned char *abuf,
int alen, struct server_connection *conn, int tcp,
struct timeval *now);
static void handle_error(struct server_connection *conn, struct timeval *now);
@ -80,7 +78,9 @@ static int same_questions(const unsigned char *qbuf, int qlen,
static int same_address(struct sockaddr *sa, struct ares_addr *aa);
static int has_opt_rr(const unsigned char *abuf, int alen);
static void end_query(ares_channel channel, struct query *query, int status,
unsigned char *abuf, int alen);
const unsigned char *abuf, int alen);
static ares_ssize_t ares__socket_write(ares_channel channel, ares_socket_t s,
const void * data, size_t len);
/* return true if now is exactly check time or later */
int ares__timedout(struct timeval *now,
@ -169,25 +169,6 @@ static int try_again(int errnum)
return 0;
}
static ares_ssize_t socket_writev(ares_channel channel, ares_socket_t s, const struct iovec * vec, int len)
{
if (channel->sock_funcs)
return channel->sock_funcs->asendv(s, vec, len, channel->sock_func_cb_data);
return writev(s, vec, len);
}
static ares_ssize_t socket_write(ares_channel channel, ares_socket_t s, const void * data, size_t len)
{
if (channel->sock_funcs)
{
struct iovec vec;
vec.iov_base = (void*)data;
vec.iov_len = len;
return channel->sock_funcs->asendv(s, &vec, 1, channel->sock_func_cb_data);
}
return swrite(s, data, len);
}
/* If any TCP sockets select true for writing, write out queued data
* we have for them.
@ -198,133 +179,60 @@ static void write_tcp_data(ares_channel channel,
struct timeval *now)
{
struct server_state *server;
struct send_request *sendreq;
struct iovec *vec;
int i;
ares_ssize_t scount;
ares_ssize_t wcount;
size_t n;
/* From writev manpage: An implementation can advertise its limit by defining
IOV_MAX in <limits.h> or at run time via the return value from
sysconf(_SC_IOV_MAX). On modern Linux systems, the limit is 1024. Back in
Linux 2.0 days, this limit was 16. */
#if defined(IOV_MAX)
const size_t maxn = IOV_MAX; /* FreeBSD */
#elif defined(_SC_IOV_MAX)
const size_t maxn = sysconf(_SC_IOV_MAX); /* Linux */
#else
const size_t maxn = 16; /* Safe default */
#endif
if(!write_fds && (write_fd == ARES_SOCKET_BAD))
/* no possible action */
return;
for (i = 0; i < channel->nservers; i++)
{
/* Make sure server has data to send and is selected in write_fds or
write_fd. */
server = &channel->servers[i];
if (!server->qhead || server->tcp_conn == NULL)
continue;
if(write_fds) {
if(!FD_ISSET(server->tcp_conn->fd, write_fds))
continue;
}
else {
if(server->tcp_conn->fd != write_fd)
continue;
}
if(write_fds)
/* If there's an error and we close this socket, then open
* another with the same fd to talk to another server, then we
* don't want to think that it was the new socket that was
* ready. This is not disastrous, but is likely to result in
* extra system calls and confusion. */
FD_CLR(server->tcp_conn->fd, write_fds);
/* Count the number of send queue items. */
n = 0;
for (sendreq = server->qhead; sendreq; sendreq = sendreq->next)
n++;
/* Allocate iovecs so we can send all our data at once. */
vec = ares_malloc(n * sizeof(struct iovec));
if (vec)
{
/* Fill in the iovecs and send. */
n = 0;
for (sendreq = server->qhead; sendreq; sendreq = sendreq->next)
{
vec[n].iov_base = (char *) sendreq->data;
vec[n].iov_len = sendreq->len;
n++;
if(n >= maxn)
break;
}
wcount = socket_writev(channel, server->tcp_conn->fd, vec, (int)n);
ares_free(vec);
if (wcount < 0)
{
if (!try_again(SOCKERRNO))
handle_error(server->tcp_conn, now);
continue;
}
/* Advance the send queue by as many bytes as we sent. */
advance_tcp_send_queue(channel, i, wcount);
}
else
{
/* Can't allocate iovecs; just send the first request. */
sendreq = server->qhead;
for (i = 0; i < channel->nservers; i++) {
const unsigned char *data;
size_t data_len;
ares_ssize_t count;
/* Make sure server has data to send and is selected in write_fds or
write_fd. */
server = &channel->servers[i];
if (ares__buf_len(server->tcp_send) == 0 || server->tcp_conn == NULL)
continue;
scount = socket_write(channel, server->tcp_conn->fd, sendreq->data, sendreq->len);
if (scount < 0)
{
if (!try_again(SOCKERRNO))
handle_error(server->tcp_conn, now);
continue;
}
if (write_fds) {
if (!FD_ISSET(server->tcp_conn->fd, write_fds))
continue;
} else {
if (server->tcp_conn->fd != write_fd)
continue;
}
/* Advance the send queue by as many bytes as we sent. */
advance_tcp_send_queue(channel, i, scount);
}
if (write_fds) {
/* If there's an error and we close this socket, then open
* another with the same fd to talk to another server, then we
* don't want to think that it was the new socket that was
* ready. This is not disastrous, but is likely to result in
* extra system calls and confusion. */
FD_CLR(server->tcp_conn->fd, write_fds);
}
}
/* Consume the given number of bytes from the head of the TCP send queue. */
static void advance_tcp_send_queue(ares_channel channel, int whichserver,
ares_ssize_t num_bytes)
{
struct send_request *sendreq;
struct server_state *server = &channel->servers[whichserver];
while (num_bytes > 0) {
sendreq = server->qhead;
if ((size_t)num_bytes >= sendreq->len) {
num_bytes -= sendreq->len;
server->qhead = sendreq->next;
if (sendreq->data_storage)
ares_free(sendreq->data_storage);
ares_free(sendreq);
if (server->qhead == NULL) {
SOCK_STATE_CALLBACK(channel, server->tcp_conn->fd, 1, 0);
server->qtail = NULL;
/* qhead is NULL so we cannot continue this loop */
break;
data = ares__buf_peek(server->tcp_send, &data_len);
count = ares__socket_write(channel, server->tcp_conn->fd, data, data_len);
if (count <= 0) {
if (!try_again(SOCKERRNO)) {
handle_error(server->tcp_conn, now);
}
continue;
}
else {
sendreq->data += num_bytes;
sendreq->len -= num_bytes;
num_bytes = 0;
/* Strip data written from the buffer */
ares__buf_consume(server->tcp_send, count);
/* Notify state callback all data is written */
if (ares__buf_len(server->tcp_send) == 0) {
SOCK_STATE_CALLBACK(channel, server->tcp_conn->fd, 1, 0);
}
}
}
static ares_ssize_t socket_recvfrom(ares_channel channel,
ares_socket_t s,
void * data,
@ -333,7 +241,7 @@ static ares_ssize_t socket_recvfrom(ares_channel channel,
struct sockaddr *from,
ares_socklen_t *from_len)
{
if (channel->sock_funcs)
if (channel->sock_funcs && channel->sock_funcs->arecvfrom)
return channel->sock_funcs->arecvfrom(s, data, data_len,
flags, from, from_len,
channel->sock_func_cb_data);
@ -350,7 +258,7 @@ static ares_ssize_t socket_recv(ares_channel channel,
void * data,
size_t data_len)
{
if (channel->sock_funcs)
if (channel->sock_funcs && channel->sock_funcs->arecvfrom)
return channel->sock_funcs->arecvfrom(s, data, data_len, 0, 0, 0,
channel->sock_func_cb_data);
@ -366,60 +274,68 @@ static void read_tcp_data(ares_channel channel, struct server_connection *conn,
struct timeval *now)
{
ares_ssize_t count;
struct server_state *server = conn->server;
struct server_state *server = conn->server;
if (server->tcp_lenbuf_pos != 2) {
/* We haven't yet read a length word, so read that (or
* what's left to read of it).
*/
count = socket_recv(channel, conn->fd,
server->tcp_lenbuf + server->tcp_lenbuf_pos,
2 - server->tcp_lenbuf_pos);
/* Fetch buffer to store data we are reading */
size_t ptr_len = 512;
unsigned char *ptr = ares__buf_append_start(server->tcp_parser,
&ptr_len);
if (count <= 0) {
if (!(count == -1 && try_again(SOCKERRNO)))
handle_error(conn, now);
if (ptr == NULL) {
handle_error(conn, now);
return; /* bail out on malloc failure. TODO: make this
function return error codes */
}
/* Read from socket */
count = socket_recv(channel, conn->fd, ptr, ptr_len);
if (count <= 0) {
ares__buf_append_finish(server->tcp_parser, 0);
if (!(count == -1 && try_again(SOCKERRNO)))
handle_error(conn, now);
return;
}
/* Record amount of data read */
ares__buf_append_finish(server->tcp_parser, count);
/* Process all queued answers */
while (1) {
unsigned short dns_len = 0;
const unsigned char *data = NULL;
size_t data_len = 0;
/* Tag so we can roll back */
ares__buf_tag(server->tcp_parser);
/* Read length indicator */
if (!ares__buf_fetch_be16(server->tcp_parser, &dns_len)) {
ares__buf_tag_rollback(server->tcp_parser);
return;
}
server->tcp_lenbuf_pos += (int)count;
if (server->tcp_lenbuf_pos == 2) {
/* We finished reading the length word. Decode the
* length and allocate a buffer for the data.
*/
server->tcp_length = server->tcp_lenbuf[0] << 8 | server->tcp_lenbuf[1];
server->tcp_buffer = ares_malloc(server->tcp_length);
if (!server->tcp_buffer) {
handle_error(conn, now);
return; /* bail out on malloc failure. TODO: make this
function return error codes */
}
server->tcp_buffer_pos = 0;
/* Not enough data for a full response yet */
if (!ares__buf_consume(server->tcp_parser, dns_len)) {
ares__buf_tag_rollback(server->tcp_parser);
return;
}
} else {
/* Read data into the allocated buffer. */
count = socket_recv(channel, conn->fd,
server->tcp_buffer + server->tcp_buffer_pos,
server->tcp_length - server->tcp_buffer_pos);
if (count <= 0) {
if (!(count == -1 && try_again(SOCKERRNO)))
handle_error(conn, now);
/* Can't fail except for misuse */
data = ares__buf_tag_fetch(server->tcp_parser, &data_len);
if (data == NULL) {
ares__buf_tag_clear(server->tcp_parser);
return;
}
server->tcp_buffer_pos += (int)count;
if (server->tcp_buffer_pos == server->tcp_length) {
/* We finished reading this answer; process it and
* prepare to read another length word.
*/
process_answer(channel, server->tcp_buffer, server->tcp_length,
conn, 1, now);
ares_free(server->tcp_buffer);
server->tcp_buffer = NULL;
server->tcp_lenbuf_pos = 0;
server->tcp_buffer_pos = 0;
}
/* Strip off 2 bytes length */
data += 2;
data_len -= 2;
/* We finished reading this answer; process it */
process_answer(channel, data, (int)data_len, conn, 1, now);
/* Since we processed the answer, clear the tag so space can be reclaimed */
ares__buf_tag_clear(server->tcp_parser);
}
}
@ -634,7 +550,7 @@ static void process_timeouts(ares_channel channel, struct timeval *now)
/* Handle an answer from a server. */
static void process_answer(ares_channel channel, unsigned char *abuf,
static void process_answer(ares_channel channel, const unsigned char *abuf,
int alen, struct server_connection *conn, int tcp,
struct timeval *now)
{
@ -846,13 +762,13 @@ static int next_server(ares_channel channel, struct query *query,
int ares__send_query(ares_channel channel, struct query *query,
struct timeval *now)
{
struct send_request *sendreq;
struct server_state *server;
struct server_connection *conn;
int timeplus;
server = &channel->servers[query->server];
if (query->using_tcp) {
size_t prior_len = 0;
/* Make sure the TCP socket for this server is set up and queue
* a send request.
*/
@ -879,30 +795,17 @@ int ares__send_query(ares_channel channel, struct query *query,
conn = server->tcp_conn;
sendreq = ares_malloc(sizeof(struct send_request));
if (!sendreq) {
prior_len = ares__buf_len(server->tcp_send);
if (!ares__buf_append(server->tcp_send, query->tcpbuf, query->tcplen)) {
end_query(channel, query, ARES_ENOMEM, NULL, 0);
return ARES_ENOMEM;
}
memset(sendreq, 0, sizeof(struct send_request));
/* To make the common case fast, we avoid copies by using the query's
* tcpbuf for as long as the query is alive. In the rare case where the
* query ends while it's queued for transmission, then we give the
* sendreq its own copy of the request packet and put it in
* sendreq->data_storage.
*/
sendreq->data_storage = NULL;
sendreq->data = query->tcpbuf;
sendreq->len = query->tcplen;
sendreq->owner_query = query;
sendreq->next = NULL;
if (server->qtail) {
server->qtail->next = sendreq;
} else {
if (prior_len == 0) {
SOCK_STATE_CALLBACK(channel, conn->fd, 1, 1);
server->qhead = sendreq;
}
server->qtail = sendreq;
query->server_info[query->server].tcp_connection_generation =
server->tcp_connection_generation;
} else {
@ -944,7 +847,7 @@ int ares__send_query(ares_channel channel, struct query *query,
}
conn = ares__llist_node_val(node);
if (socket_write(channel, conn->fd, query->qbuf, query->qlen) == -1) {
if (ares__socket_write(channel, conn->fd, query->qbuf, query->qlen) == -1) {
/* FIXME: Handle EAGAIN here since it likely can happen. */
skip_server(channel, query, server);
return next_server(channel, query, now);
@ -1078,7 +981,7 @@ static int configure_socket(ares_socket_t s, int family, ares_channel channel)
} local;
/* do not set options for user-managed sockets */
if (channel->sock_funcs)
if (channel->sock_funcs && channel->sock_funcs->asocket)
return 0;
(void)setsocknonblock(s, TRUE);
@ -1148,7 +1051,7 @@ static int open_socket(ares_channel channel, struct server_state *server,
struct sockaddr_in6 sa6;
} saddr;
struct sockaddr *sa;
unsigned int port;
unsigned short port;
struct server_connection *conn;
ares__llist_node_t *node;
@ -1204,12 +1107,12 @@ static int open_socket(ares_channel channel, struct server_state *server,
* so batching isn't very interesting.
*/
opt = 1;
if (channel->sock_funcs == 0
&&
setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
(void *)&opt, sizeof(opt)) == -1) {
ares__close_socket(channel, s);
return ARES_ECONNREFUSED;
if (!channel->sock_funcs || !channel->sock_funcs->asocket) {
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt))
== -1) {
ares__close_socket(channel, s);
return ARES_ECONNREFUSED;
}
}
}
#endif
@ -1286,7 +1189,6 @@ static int open_socket(ares_channel channel, struct server_state *server,
SOCK_STATE_CALLBACK(channel, s, 1, 0);
if (is_tcp) {
server->tcp_buffer_pos = 0;
server->tcp_connection_generation = ++channel->tcp_connection_generation;
server->tcp_conn = conn;
}
@ -1493,54 +1395,19 @@ static void ares_detach_query(struct query *query)
query->node_all_queries = NULL;
}
static void end_query (ares_channel channel, struct query *query, int status,
unsigned char *abuf, int alen)
static void end_query(ares_channel channel, struct query *query, int status,
const unsigned char *abuf, int alen)
{
int i;
(void)channel;
ares_detach_query(query);
/* First we check to see if this query ended while one of our send
* queues still has pointers to it.
*/
for (i = 0; i < channel->nservers; i++) {
struct server_state *server = &channel->servers[i];
struct send_request *sendreq;
for (sendreq = server->qhead; sendreq; sendreq = sendreq->next) {
if (sendreq->owner_query == query) {
sendreq->owner_query = NULL;
assert(sendreq->data_storage == NULL);
if (status == ARES_SUCCESS) {
/* We got a reply for this query, but this queued sendreq
* points into this soon-to-be-gone query's tcpbuf. Probably
* this means we timed out and queued the query for
* retransmission, then received a response before actually
* retransmitting. This is perfectly fine, so we want to keep
* the connection running smoothly if we can. But in the worst
* case we may have sent only some prefix of the query, with
* some suffix of the query left to send. Also, the buffer may
* be queued on multiple queues. To prevent dangling pointers
* to the query's tcpbuf and handle these cases, we just give
* such sendreqs their own copy of the query packet.
*/
sendreq->data_storage = ares_malloc(sendreq->len);
if (sendreq->data_storage != NULL) {
memcpy(sendreq->data_storage, sendreq->data, sendreq->len);
sendreq->data = sendreq->data_storage;
}
}
if (status != ARES_SUCCESS || sendreq->data_storage == NULL) {
/* Just to be paranoid, zero out this sendreq... */
sendreq->data = NULL;
sendreq->len = 0;
}
}
}
}
/* Invoke the callback */
query->callback(query->arg, status, query->timeouts, abuf, alen);
/* Invoke the callback. */
query->callback(query->arg, status, query->timeouts,
/* due to prior design flaws, abuf isn't meant to be modified,
* but bad prototypes, ugh. Lets cast off constfor compat. */
(unsigned char *)((void *)((size_t)abuf)),
alen);
ares__free_query(query);
}
@ -1559,13 +1426,14 @@ void ares__free_query(struct query *query)
ares_socket_t ares__open_socket(ares_channel channel,
int af, int type, int protocol)
{
if (channel->sock_funcs)
if (channel->sock_funcs && channel->sock_funcs->asocket) {
return channel->sock_funcs->asocket(af,
type,
protocol,
channel->sock_func_cb_data);
else
return socket(af, type, protocol);
}
return socket(af, type, protocol);
}
int ares__connect_socket(ares_channel channel,
@ -1573,19 +1441,41 @@ int ares__connect_socket(ares_channel channel,
const struct sockaddr *addr,
ares_socklen_t addrlen)
{
if (channel->sock_funcs)
if (channel->sock_funcs && channel->sock_funcs->aconnect) {
return channel->sock_funcs->aconnect(sockfd,
addr,
addrlen,
channel->sock_func_cb_data);
else
return connect(sockfd, addr, addrlen);
}
return connect(sockfd, addr, addrlen);
}
void ares__close_socket(ares_channel channel, ares_socket_t s)
{
if (channel->sock_funcs)
if (channel->sock_funcs && channel->sock_funcs->aclose) {
channel->sock_funcs->aclose(s, channel->sock_func_cb_data);
else
} else {
sclose(s);
}
}
#ifndef HAVE_WRITEV
/* Structure for scatter/gather I/O. */
struct iovec
{
void *iov_base; /* Pointer to data. */
size_t iov_len; /* Length of data. */
};
#endif
static ares_ssize_t ares__socket_write(ares_channel channel, ares_socket_t s, const void * data, size_t len)
{
if (channel->sock_funcs && channel->sock_funcs->asendv) {
struct iovec vec;
vec.iov_base = (void*)data;
vec.iov_len = len;
return channel->sock_funcs->asendv(s, &vec, 1, channel->sock_func_cb_data);
}
return swrite(s, data, len);
}

@ -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 */

@ -638,26 +638,10 @@ const struct ares_socket_functions VirtualizeIO::default_functions = {
}
return s;
},
[](ares_socket_t s, void * p) {
return :: sclose(s);
},
[](ares_socket_t s, const struct sockaddr * addr, socklen_t len, void *) {
return ::connect(s, addr, len);
},
[](ares_socket_t s, void * dst, size_t len, int flags, struct sockaddr * addr, socklen_t * alen, void *) -> ares_ssize_t {
#ifdef HAVE_RECVFROM
return ::recvfrom(s, reinterpret_cast<RECV_TYPE_ARG2>(dst), len, flags, addr, alen);
#else
return sread(s, dst, len);
#endif
},
[](ares_socket_t s, const struct iovec * vec, int len, void *) {
#ifndef HAVE_WRITEV
return ares_writev(s, vec, len);
#else
return :: writev(s, vec, len);
#endif
}
NULL,
NULL,
NULL,
NULL
};

@ -271,7 +271,6 @@ TEST_P(MockUDPMaxQueriesTest, GetHostByNameParallelLookups) {
/* There may be an issue with TCP parallel queries, this test fails, as does
* issue #266 indicate */
#if 0
#define TCPPARALLELLOOKUPS 256
TEST_P(MockTCPChannelTest, GetHostByNameParallelLookups) {
DNSPacket rsp;
@ -302,7 +301,7 @@ TEST_P(MockTCPChannelTest, GetHostByNameParallelLookups) {
EXPECT_EQ("{'www.google.com' aliases=[] addrs=[2.3.4.5]}", ss.str());
}
}
#endif
TEST_P(MockTCPChannelTest, MalformedResponse) {
std::vector<byte> one = {0x01};

@ -192,6 +192,8 @@ void DefaultChannelModeTest::Process() {
MockServer::MockServer(int family, int port)
: udpport_(port), tcpport_(port), qid_(-1) {
// Create a TCP socket to receive data on.
tcp_data_ = NULL;
tcp_data_len_ = 0;
tcpfd_ = socket(family, SOCK_STREAM, 0);
EXPECT_NE(-1, tcpfd_);
int optval = 1;
@ -277,6 +279,7 @@ MockServer::~MockServer() {
}
sclose(tcpfd_);
sclose(udpfd_);
free(tcp_data_);
}
void MockServer::ProcessPacket(int fd, struct sockaddr_storage *addr, socklen_t addrlen,
@ -362,36 +365,36 @@ void MockServer::ProcessFD(int fd) {
byte buffer[2048];
int len = recvfrom(fd, BYTE_CAST buffer, sizeof(buffer), 0,
(struct sockaddr *)&addr, &addrlen);
byte* data = buffer;
if (fd != udpfd_) {
if (len == 0) {
connfds_.erase(std::find(connfds_.begin(), connfds_.end(), fd));
sclose(fd);
free(tcp_data_);
tcp_data_ = NULL;
tcp_data_len_ = 0;
return;
}
if (len < 2) {
std::cerr << "Packet too short (" << len << ")" << std::endl;
return;
}
tcp_data_ = (unsigned char *)realloc(tcp_data_, tcp_data_len_ + len);
memcpy(tcp_data_ + tcp_data_len_, buffer, len);
tcp_data_len_ += len;
/* TCP might aggregate the various requests into a single packet, so we
* need to split */
while (len) {
int tcplen = (data[0] << 8) + data[1];
data += 2;
len -= 2;
if (tcplen > len) {
std::cerr << "Warning: TCP length " << tcplen
<< " doesn't match remaining data length " << len << std::endl;
}
int process_len = (tcplen > len)?len:tcplen;
ProcessPacket(fd, &addr, addrlen, data, process_len);
len -= process_len;
data += process_len;
while (tcp_data_len_ > 2) {
int tcplen = (tcp_data_[0] << 8) + tcp_data_[1];
if (tcp_data_len_ - 2 < tcplen)
break;
ProcessPacket(fd, &addr, addrlen, tcp_data_ + 2, tcplen);
/* strip off processed data */
memmove(tcp_data_, tcp_data_ + tcplen + 2, tcp_data_len_ - 2 - tcplen);
tcp_data_len_ -= 2 + tcplen;
}
} else {
/* UDP is always a single packet */
ProcessPacket(fd, &addr, addrlen, data, len);
ProcessPacket(fd, &addr, addrlen, buffer, len);
}
}

@ -188,6 +188,8 @@ class MockServer {
std::set<int> connfds_;
std::vector<byte> reply_;
int qid_;
unsigned char *tcp_data_;
size_t tcp_data_len_;
};
// Test fixture that uses a mock DNS server.

Loading…
Cancel
Save