From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

/* MIT License
*
* Copyright (c) 2024 Brad House
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#ifndef __ARES_CONN_H
#define __ARES_CONN_H
#include "ares_socket.h"
struct ares_conn;
typedef struct ares_conn ares_conn_t;
struct ares_server;
typedef struct ares_server ares_server_t;
typedef enum {
/*! No flags */
ARES_CONN_FLAG_NONE = 0,
/*! TCP connection, not UDP */
ARES_CONN_FLAG_TCP = 1 << 0,
/*! TCP Fast Open is enabled and being used if supported by the OS */
ARES_CONN_FLAG_TFO = 1 << 1,
/*! TCP Fast Open has not yet sent its first packet. Gets unset on first
* write to a connection */
ARES_CONN_FLAG_TFO_INITIAL = 1 << 2
} ares_conn_flags_t;
typedef enum {
ARES_CONN_STATE_NONE = 0,
ARES_CONN_STATE_READ = 1 << 0,
ARES_CONN_STATE_WRITE = 1 << 1,
ARES_CONN_STATE_CONNECTED = 1 << 2, /* This doesn't get a callback */
ARES_CONN_STATE_CBFLAGS = ARES_CONN_STATE_READ | ARES_CONN_STATE_WRITE
} ares_conn_state_flags_t;
struct ares_conn {
ares_server_t *server;
ares_socket_t fd;
struct ares_addr self_ip;
ares_conn_flags_t flags;
ares_conn_state_flags_t state_flags;
/*! Outbound buffered data that is not yet sent. Exists as one contiguous
* stream in TCP format (big endian 16bit length prefix followed by DNS
* wire-format message). For TCP this can be sent as-is, UDP this must
* be sent per-packet (stripping the length prefix) */
ares_buf_t *out_buf;
/*! Inbound buffered data that is not yet parsed. Exists as one contiguous
* stream in TCP format (big endian 16bit length prefix followed by DNS
* wire-format message). TCP may have partial data and this needs to be
* handled gracefully, but UDP will always have a full message */
ares_buf_t *in_buf;
/* total number of queries run on this connection since it was established */
size_t total_queries;
/* list of outstanding queries to this connection */
ares_llist_t *queries_to_conn;
};
/*! Various buckets for grouping history */
typedef enum {
ARES_METRIC_1MINUTE = 0, /*!< Bucket for tracking over the last minute */
ARES_METRIC_15MINUTES, /*!< Bucket for tracking over the last 15 minutes */
ARES_METRIC_1HOUR, /*!< Bucket for tracking over the last hour */
ARES_METRIC_1DAY, /*!< Bucket for tracking over the last day */
ARES_METRIC_INCEPTION, /*!< Bucket for tracking since inception */
ARES_METRIC_COUNT /*!< Count of buckets, not a real bucket */
} ares_server_bucket_t;
/*! Data metrics collected for each bucket */
typedef struct {
time_t ts; /*!< Timestamp divided by bucket divisor */
unsigned int latency_min_ms; /*!< Minimum latency for queries */
unsigned int latency_max_ms; /*!< Maximum latency for queries */
ares_uint64_t total_ms; /*!< Cumulative query time for bucket */
ares_uint64_t total_count; /*!< Number of queries for bucket */
time_t prev_ts; /*!< Previous period bucket timestamp */
ares_uint64_t
prev_total_ms; /*!< Previous period bucket cumulative query time */
ares_uint64_t prev_total_count; /*!< Previous period bucket query count */
} ares_server_metrics_t;
typedef enum {
ARES_COOKIE_INITIAL = 0,
ARES_COOKIE_GENERATED = 1,
ARES_COOKIE_SUPPORTED = 2,
ARES_COOKIE_UNSUPPORTED = 3
} ares_cookie_state_t;
/*! Structure holding tracking data for RFC 7873/9018 DNS cookies.
* Implementation plan for this feature is here:
*/
typedef struct {
/*! starts at INITIAL, transitions as needed. */
ares_cookie_state_t state;
/*! randomly-generate client cookie */
unsigned char client[8];
/*! timestamp client cookie was generated, used for rotation purposes */
ares_timeval_t client_ts;
/*! IP address last used for client to connect to server. If this changes
* The client cookie gets invalidated */
struct ares_addr client_ip;
/*! Server Cookie last received, 8-32 bytes in length */
unsigned char server[32];
/*! Length of server cookie on file. */
size_t server_len;
/*! Timestamp of last attempt to use cookies, but it was determined that the
* server didn't support them */
ares_timeval_t unsupported_ts;
} ares_cookie_t;
struct ares_server {
/* Configuration */
size_t idx; /* index for server in system configuration */
struct ares_addr addr;
unsigned short udp_port; /* host byte order */
unsigned short tcp_port; /* host byte order */
char ll_iface[64]; /* IPv6 Link Local Interface */
unsigned int ll_scope; /* IPv6 Link Local Scope */
size_t consec_failures; /* Consecutive query failure count
* can be hard errors or timeouts
*/
ares_bool_t probe_pending; /* Whether a probe is pending for this
* server due to prior failures */
ares_llist_t *connections;
ares_conn_t *tcp_conn;
/* The next time when we will retry this server if it has hit failures */
ares_timeval_t next_retry_time;
/*! Buckets for collecting metrics about the server */
ares_server_metrics_t metrics[ARES_METRIC_COUNT];
/*! RFC 7873/9018 DNS Cookies */
ares_cookie_t cookie;
/* Link back to owning channel */
ares_channel_t *channel;
};
void ares_close_connection(ares_conn_t *conn, ares_status_t requeue_status);
void ares_close_sockets(ares_server_t *server);
void ares_check_cleanup_conns(const ares_channel_t *channel);
void ares_destroy_servers_state(ares_channel_t *channel);
ares_status_t ares_open_connection(ares_conn_t **conn_out,
ares_channel_t *channel,
ares_server_t *server, ares_bool_t is_tcp);
ares_conn_err_t ares_conn_write(ares_conn_t *conn, const void *data, size_t len,
size_t *written);
ares_status_t ares_conn_flush(ares_conn_t *conn);
ares_conn_err_t ares_conn_read(ares_conn_t *conn, void *data, size_t len,
size_t *read_bytes);
ares_conn_t *ares_conn_from_fd(const ares_channel_t *channel, ares_socket_t fd);
void ares_conn_sock_state_cb_update(ares_conn_t *conn,
ares_conn_state_flags_t flags);
ares_conn_err_t ares_socket_recv(ares_channel_t *channel, ares_socket_t s,
ares_bool_t is_tcp, void *data,
size_t data_len, size_t *read_bytes);
ares_conn_err_t ares_socket_recvfrom(ares_channel_t *channel, ares_socket_t s,
ares_bool_t is_tcp, void *data,
size_t data_len, int flags,
struct sockaddr *from,
ares_socklen_t *from_len,
size_t *read_bytes);
void ares_destroy_server(ares_server_t *server);
#endif