Skip to content

Commit

Permalink
more implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
bradh352 committed Oct 8, 2024
1 parent e97f02c commit cddab64
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 247 deletions.
69 changes: 68 additions & 1 deletion include/ares.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,11 +575,45 @@ CARES_EXTERN CARES_DEPRECATED_FOR(
*funcs,
void *user_data);

/*! Flags defining behavior of socket functions */
typedef enum {
/*! Strongly recommended to create sockets as non-blocking and set this
* flag */
ARES_SOCKFUNC_FLAG_NONBLOCKING = 1 << 0
} ares_sockfunc_flags_t;

/*! Socket options in request to asetsockopt() in struct
* ares_socket_functions_ex */
typedef enum {
/*! Set the send buffer size. Value is a pointer to an int. (SO_SNDBUF) */
ARES_SOCKET_OPT_SENDBUF_SIZE,
/*! Set the recv buffer size. Value is a pointer to an int. (SO_RCVBUF) */
ARES_SOCKET_OPT_RECVBUF_SIZE,
/*! Set the network interface to use as the source for communication.
* Value is a C string. (SO_BINDTODEVICE) */
ARES_SOCKET_OPT_BIND_DEVICE,
/*! Enable TCP Fast Open. Value is a pointer to an ares_bool_t. On some
* systems this could be a no-op if it is known it is on by default and
* return success. Other systems may be a no-op if known the system does
* not support the feature and returns failure with errno set to ENOTIMP.
*/
ARES_SOCKET_OPT_TCP_FASTOPEN
} ares_socket_opt_t;

/*! Flags for behavior during connect */
typedef enum {
/*! Connect using TCP Fast Open */
ARES_SOCKET_CONN_TCP_FASTOPEN = 1 << 0
} ares_socket_connect_flags_t;

/*! Socket functions to call rather than using OS-native functions */
struct ares_socket_functions_ex {
/*! ABI Version: must be "1" */
unsigned int version;

/*! Flags indicating behavior of the subsystem. One or more ares_sockfunc_flags_t */
unsigned int flags;

/*! REQUIRED. Create a new socket file descriptor. The file descriptor must
* be opened in non-blocking mode (so that reads and writes never block).
* Recommended other options would be to disable signals on write errors
Expand All @@ -605,13 +639,33 @@ struct ares_socket_functions_ex {
* such as EBADF */
int (*aclose)(ares_socket_t sock, void *user_data);


/*! REQUIRED. Set socket option. This shares a similar syntax to the BSD
* setsockopt() call, however we use our own options. The value is typically
* a pointer to the desired value and each option has its own data type it
* will express in the documentation.
*
* \param[in] sock Socket file descriptor returned from asocket.
* \param[in] opt Option to set.
* \param[in] val Pointer to value for option.
* \param[in] val_size Size of value.
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return Return 0 on success, otherwise -1 should be returned with an
* appropriate errno set. If errno is ENOTIMP an error will not be
* propagated as it will take it to mean it is an intentional
* decision to not support the feature.
*/
int (*asetsockopt)(ares_socket_t sock, ares_socket_opt_t opt, void *val, ares_socklen_t val_size, void *user_data);

/*! REQUIRED. Connect to the remote using the supplied address. For UDP
* sockets this will bind the file descriptor to only send and receive packets
* from the remote address provided.
*
* \param[in] sock Socket file descriptor returned from asocket.
* \param[in] address Address to connect to
* \param[in] address_len Size of address structure passed
* \param[in] flags One or more ares_socket_connect_flags_t
* \param[in] user_data Pointer provided to
* ares_set_socket_functions_ex().
* \return Return 0 upon successful establishement, otherwise -1 should be
Expand All @@ -624,7 +678,8 @@ struct ares_socket_functions_ex {
* retrieve the appropriate error).
*/
int (*aconnect)(ares_socket_t sock, const struct sockaddr *address,
ares_socklen_t address_len, void *user_data);
ares_socklen_t address_len, unsigned int flags,
void *user_data);

/*! REQUIRED. Attempt to read data from the remote.
*
Expand Down Expand Up @@ -681,6 +736,18 @@ struct ares_socket_functions_ex {
ares_socklen_t *address_len, void *user_data);
};

/*! Override the native socket functions for the OS with the provided set.
* An optional user data thunk may be specified which will be passed to
* each registered callback. Replaces ares_set_socket_functions().
*
* \param[in] channel An initialized c-ares channel.
* \param[in] funcs Structure registering the implementations for the
* various functions. See the structure definition.
* This will be duplicated and does not need to exist
* past the life of this call.
* \param[in] user_data User data thunk which will be passed to each call of
* the registered callbacks.
*/
CARES_EXTERN ares_status_t ares_set_socket_functions_ex(
ares_channel_t *channel, const struct ares_socket_functions_ex *funcs,
void *user_data);
Expand Down
35 changes: 17 additions & 18 deletions src/lib/ares_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
ares_channel_t *channel = conn->server->channel;
ares_bool_t is_tfo = ARES_FALSE;
ares_conn_err_t err = ARES_CONN_ERR_SUCCESS;
struct sockaddr_storage sa_storage;
ares_socklen_t salen = 0;
struct sockaddr *sa = NULL;

*written = 0;

Expand All @@ -177,36 +180,33 @@ ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
return ARES_CONN_ERR_WOULDBLOCK;
}

/* On initial write during TFO we need to send an address */
if (conn->flags & ARES_CONN_FLAG_TFO_INITIAL) {
struct sockaddr_storage sa_storage;
ares_socklen_t salen = sizeof(sa_storage);
struct sockaddr *sa = (struct sockaddr *)&sa_storage;
salen = sizeof(sa_storage);
sa = (struct sockaddr *)&sa_storage;

conn->flags &= ~((unsigned int)ARES_CONN_FLAG_TFO_INITIAL);
is_tfo = ARES_TRUE;

if (ares_conn_set_sockaddr(conn, sa, &salen) != ARES_SUCCESS) {
return ARES_CONN_ERR_FAILURE;
}
}

err =
ares_socket_write_tfo(channel, conn->fd, data, len, written, sa, salen);
if (err != ARES_CONN_ERR_SUCCESS) {
goto done;
}
err =
ares_socket_write(channel, conn->fd, data, len, written, sa, salen);
if (err != ARES_CONN_ERR_SUCCESS) {
goto done;
}

if (is_tfo) {
/* If using TFO, we might not have been able to get an IP earlier, since
* we hadn't informed the OS of the destination. When using sendto()
* now we have so we should be able to fetch it */
ares_conn_set_self_ip(conn, ARES_FALSE);
goto done;
}

err = ares_socket_write(channel, conn->fd, data, len, written);
if (err != ARES_CONN_ERR_SUCCESS) {
goto done;
}

done:
if (err == ARES_CONN_ERR_SUCCESS && len == *written) {
/* Wrote all data, make sure we're not listening for write events unless
Expand Down Expand Up @@ -366,10 +366,9 @@ ares_status_t ares_open_connection(ares_conn_t **conn_out,
/* LCOV_EXCL_STOP */
}

/* Enable TFO if the OS supports it and we were passed in data to send during
* the connect. It might be disabled later if an error is encountered. Make
* sure a user isn't overriding anything. */
if (conn->flags & ARES_CONN_FLAG_TCP && ares_socket_tfo_supported(channel)) {
/* Try to enable TFO always if using TCP. it will fail later on if its
* really not supported when we try to enable it on the socket. */
if (conn->flags & ARES_CONN_FLAG_TCP) {
conn->flags |= ARES_CONN_FLAG_TFO;
}

Expand All @@ -386,7 +385,7 @@ ares_status_t ares_open_connection(ares_conn_t **conn_out,
goto done;
}

/* Configure it. */
/* Configure channel configured options */
status = ares_socket_configure(
channel, server->addr.family,
(conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE, conn->fd);
Expand Down
11 changes: 5 additions & 6 deletions src/lib/ares_process.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,9 @@ void ares_process_pending_write(ares_channel_t *channel)

static ares_status_t read_conn_packets(ares_conn_t *conn)
{
ares_bool_t read_again;
ares_conn_err_t err;
ares_bool_t read_again;
ares_conn_err_t err;
const ares_channel_t *channel = conn->server->channel;

do {
size_t count;
Expand Down Expand Up @@ -482,12 +483,10 @@ static ares_status_t read_conn_packets(ares_conn_t *conn)
/* Record amount of data read */
ares_buf_append_finish(conn->in_buf, count);

/* Only loop if we're not overwriting socket functions, and are using UDP
/* Only loop if sockets support non-blocking operation, and are using UDP
* or are using TCP and read the maximum buffer size */
read_again = ARES_FALSE;
if (!(conn->flags & ARES_CONN_FLAG_TCP)) {
read_again = ARES_TRUE;
} else if (count == len) {
if (channel->sock_funcs.flags & ARES_SOCKFUNC_FLAG_NONBLOCKING && (!(conn->flags & ARES_CONN_FLAG_TCP) || count == len)) {
read_again = ARES_TRUE;
}

Expand Down
Loading

0 comments on commit cddab64

Please sign in to comment.