Commit 4f0e4313 authored by Daniel Salzman's avatar Daniel Salzman

net: add TCP connect timeout support

parent 8dec3a87
......@@ -142,8 +142,8 @@ server:
tcp\-workers: INT
background\-workers: INT
async\-start: BOOL
tcp\-idle\-timeout: TIME
tcp\-handshake\-timeout: TIME
tcp\-idle\-timeout: TIME
tcp\-reply\-timeout: TIME
max\-tcp\-clients: INT
max\-udp\-payload: SIZE
......@@ -213,12 +213,6 @@ If enabled, server doesn\(aqt wait for the zones to be loaded and starts
responding immediately with SERVFAIL answers until the zone loads.
.sp
Default: off
.SS tcp\-idle\-timeout
.sp
Maximum idle time between requests on a TCP connection. This also limits
receiving of a single query, each query must be received in this time limit.
.sp
Default: 20
.SS tcp\-handshake\-timeout
.sp
Maximum time between newly accepted TCP connection and the first query.
......@@ -226,9 +220,17 @@ This is useful to disconnect inactive connections faster than connections
that already made at least 1 meaningful query.
.sp
Default: 5
.SS tcp\-idle\-timeout
.sp
Maximum idle time between requests on a TCP connection. This also limits
receiving of a single query, each query must be received in this time limit.
.sp
Default: 20
.SS tcp\-reply\-timeout
.sp
Maximum time to wait for a reply to an issued SOA query.
Maximum time to wait for an outgoing connection or for a reply to an issued
request (SOA, NOTIFY, AXFR...). This limit also applies to knotc remote
operation over an internet socket.
.sp
Default: 10
.SS max\-tcp\-clients
......
......@@ -95,8 +95,8 @@ General options related to the server.
tcp-workers: INT
background-workers: INT
async-start: BOOL
tcp-idle-timeout: TIME
tcp-handshake-timeout: TIME
tcp-idle-timeout: TIME
tcp-reply-timeout: TIME
max-tcp-clients: INT
max-udp-payload: SIZE
......@@ -203,16 +203,6 @@ responding immediately with SERVFAIL answers until the zone loads.
Default: off
.. _server_tcp-idle-timeout:
tcp-idle-timeout
----------------
Maximum idle time between requests on a TCP connection. This also limits
receiving of a single query, each query must be received in this time limit.
Default: 20
.. _server_tcp-handshake-timeout:
tcp-handshake-timeout
......@@ -224,12 +214,24 @@ that already made at least 1 meaningful query.
Default: 5
.. _server_tcp-idle-timeout:
tcp-idle-timeout
----------------
Maximum idle time between requests on a TCP connection. This also limits
receiving of a single query, each query must be received in this time limit.
Default: 20
.. _server_tcp-reply-timeout:
tcp-reply-timeout
-----------------
Maximum time to wait for a reply to an issued SOA query.
Maximum time to wait for an outgoing connection or for a reply to an issued
request (SOA, NOTIFY, AXFR...). This limit also applies to knotc remote
operation over an internet socket.
Default: 10
......
......@@ -84,8 +84,8 @@ static const yp_item_t desc_server[] = {
{ C_BG_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } },
{ C_ASYNC_START, YP_TBOOL, YP_VNONE },
{ C_TCP_HSHAKE_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 5, YP_STIME } },
{ C_TCP_REPLY_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 10, YP_STIME } },
{ C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 20, YP_STIME } },
{ C_TCP_REPLY_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 10, YP_STIME } },
{ C_MAX_TCP_CLIENTS, YP_TINT, YP_VINT = { 0, INT32_MAX, 100 } },
{ C_MAX_UDP_PAYLOAD, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_UDP_PAYLOAD,
KNOT_EDNS_MAX_UDP_PAYLOAD,
......
......@@ -148,7 +148,7 @@ static int cmd_remote_print_reply(const knot_rrset_t *rr)
return KNOT_EOK;
}
static int cmd_remote_reply(int c)
static int cmd_remote_reply(int c, struct timeval *timeout)
{
knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
if (!pkt) {
......@@ -156,7 +156,7 @@ static int cmd_remote_reply(int c)
}
/* Read response packet. */
int n = tcp_recv_msg(c, pkt->wire, pkt->max_size, NULL);
int n = tcp_recv_msg(c, pkt->wire, pkt->max_size, timeout);
if (n <= 0) {
dbg_server("remote: couldn't receive response = %s\n", knot_strerror(n));
knot_pkt_free(&pkt);
......@@ -248,28 +248,20 @@ static int cmd_remote(struct sockaddr_storage *addr, knot_tsig_key_t *key,
dbg_server("%s: sending query size %zu\n", __func__, pkt->size);
/* Connect to remote. */
char addr_str[SOCKADDR_STRLEN] = {0};
char addr_str[SOCKADDR_STRLEN] = { 0 };
sockaddr_tostr(addr_str, sizeof(addr_str), addr);
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
struct timeval tv = { conf_int(&val), 0 };
int s = net_connected_socket(SOCK_STREAM, addr, NULL, 0);
int s = net_connected_socket(SOCK_STREAM, addr, NULL, 0, &tv);
if (s < 0) {
log_error("failed to connect to remote host '%s'", addr_str);
knot_pkt_free(&pkt);
return 1;
}
/* Wait for availability. */
struct pollfd pfd = { s, POLLOUT, 0 };
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
if (poll(&pfd, 1, conf_int(&val)) != 1) {
log_error("failed to connect to remote host '%s'", addr_str);
close(s);
knot_pkt_free(&pkt);
return 1;
}
/* Send and free packet. */
int ret = tcp_send_msg(s, pkt->wire, pkt->size, NULL);
int ret = tcp_send_msg(s, pkt->wire, pkt->size, &tv);
knot_pkt_free(&pkt);
/* Evaluate and wait for reply. */
......@@ -282,7 +274,7 @@ static int cmd_remote(struct sockaddr_storage *addr, knot_tsig_key_t *key,
/* Wait for reply. */
ret = KNOT_EOK;
while (ret == KNOT_EOK) {
ret = cmd_remote_reply(s);
ret = cmd_remote_reply(s, &tv);
if (ret != KNOT_EOK) {
if (ret != KNOT_ECONN) {
log_notice("remote command reply: %s",
......
......@@ -85,7 +85,7 @@ static int dnsproxy_fwd(int state, knot_pkt_t *pkt, struct query_data *qdata, vo
/* Forward request. */
ret = knot_requestor_enqueue(&re, req);
if (ret == KNOT_EOK) {
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_HSHAKE_TIMEOUT);
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv);
} else {
......
......@@ -114,9 +114,8 @@ static int tcp_handle(tcp_context_t *tcp, int fd,
/* Timeout. */
rcu_read_lock();
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
int64_t max_conn_reply = conf_int(&val);
struct timeval tmout = { conf_int(&val), 0 };
rcu_read_unlock();
struct timeval tmout = { max_conn_reply, 0 };
/* Receive data. */
struct timeval recv_tmout = tmout;
......
......@@ -266,7 +266,7 @@ static uint32_t soa_graceful_expire(const knot_rdataset_t *soa)
{
// Allow for timeouts. Otherwise zones with very short
// expiry may expire before the timeout is reached.
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_IDLE_TIMEOUT);
conf_val_t val = conf_get(conf(), C_SRV, C_TCP_REPLY_TIMEOUT);
return knot_soa_expire(soa) + 2 * conf_int(&val);
}
......
......@@ -154,7 +154,8 @@ int net_bound_socket(int type, const struct sockaddr_storage *ss,
}
int net_connected_socket(int type, const struct sockaddr_storage *dst_addr,
const struct sockaddr_storage *src_addr, unsigned flags)
const struct sockaddr_storage *src_addr, unsigned flags,
struct timeval *timeout)
{
if (dst_addr == NULL) {
return KNOT_EINVAL;
......@@ -164,7 +165,7 @@ int net_connected_socket(int type, const struct sockaddr_storage *dst_addr,
/* Check port. */
if (sockaddr_port(dst_addr) == 0) {
return KNOT_ECONN;
return KNOT_NET_EADDR;
}
/* Bind to specific source address - if set. */
......@@ -177,16 +178,33 @@ int net_connected_socket(int type, const struct sockaddr_storage *dst_addr,
return socket;
}
/* Set socket flags. */
if (fcntl(socket, F_SETFL, flags) < 0)
;
/* Set specified socket flags + non-blocking flag. */
if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) < 0) {
close(socket);
return KNOT_NET_ESOCKET;
}
/* Connect to destination. */
const struct sockaddr *sa = (const struct sockaddr *)dst_addr;
int ret = connect(socket, sa, sockaddr_len(sa));
if (ret != 0 && errno != EINPROGRESS) {
close(socket);
return knot_map_errno();
if (ret != 0) {
if (errno != EINPROGRESS) {
close(socket);
return KNOT_NET_ECONNECT;
}
fd_set set;
FD_ZERO(&set);
FD_SET(socket, &set);
ret = select(socket + 1, NULL, &set, NULL, timeout);
if (ret == 0) {
close(socket);
return KNOT_NET_ETIMEOUT;
} else if (ret < 0) {
close(socket);
return KNOT_NET_ECONNECT;
}
}
return socket;
......
......@@ -66,12 +66,16 @@ int net_bound_socket(int type, const struct sockaddr_storage *ss,
* \param type Socket transport type (SOCK_STREAM, SOCK_DGRAM).
* \param dst_addr Destination address.
* \param src_addr Source address (can be NULL).
* \param flags Socket flags (O_NONBLOCK for example).
* \param flags Socket flags.
* \param timeout Connection establishment timeout.
*
* \note The O_NONBLOCK flag is added automatically.
*
* \return socket or error code
*/
int net_connected_socket(int type, const struct sockaddr_storage *dst_addr,
const struct sockaddr_storage *src_addr, unsigned flags);
const struct sockaddr_storage *src_addr, unsigned flags,
struct timeval *timeout);
/*!
* \brief Return true if the socket is connected.
......
......@@ -40,12 +40,17 @@ static struct knot_request *request_make(mm_ctx_t *mm)
}
/*! \brief Ensure a socket is connected. */
static int request_ensure_connected(struct knot_request *request)
static int request_ensure_connected(struct knot_request *request,
const struct timeval *timeout)
{
/* Each request has unique timeout. */
struct timeval tv = *timeout;
/* Connect the socket if not already connected. */
if (request->fd < 0) {
int sock_type = use_tcp(request) ? SOCK_STREAM : SOCK_DGRAM;
request->fd = net_connected_socket(sock_type, &request->remote, &request->origin, 0);
request->fd = net_connected_socket(sock_type, &request->remote,
&request->origin, 0, &tv);
if (request->fd < 0) {
return KNOT_ECONN;
}
......@@ -54,10 +59,14 @@ static int request_ensure_connected(struct knot_request *request)
return KNOT_EOK;
}
static int request_send(struct knot_request *request, struct timeval *timeout)
static int request_send(struct knot_request *request,
const struct timeval *timeout)
{
/* Each request has unique timeout. */
struct timeval tv = *timeout;
/* Wait for writeability or error. */
int ret = request_ensure_connected(request);
int ret = request_ensure_connected(request, &tv);
if (ret != KNOT_EOK) {
return ret;
}
......@@ -69,7 +78,7 @@ static int request_send(struct knot_request *request, struct timeval *timeout)
/* Send query. */
if (use_tcp(request)) {
ret = tcp_send_msg(request->fd, wire, wire_len, timeout);
ret = tcp_send_msg(request->fd, wire, wire_len, &tv);
} else {
ret = udp_send_msg(request->fd, wire, wire_len, NULL);
}
......@@ -87,10 +96,10 @@ static int request_recv(struct knot_request *request,
knot_pkt_clear(resp);
/* Each request has unique timeout. */
struct timeval tv = { timeout->tv_sec, timeout->tv_usec };
struct timeval tv = *timeout;
/* Wait for readability */
int ret = request_ensure_connected(request);
int ret = request_ensure_connected(request, &tv);
if (ret != KNOT_EOK) {
return ret;
}
......
......@@ -81,12 +81,10 @@ int main(int argc, char *argv[])
// create TCP client
int client = net_connected_socket(SOCK_STREAM, &addr, NULL, 0);
struct timeval tv = { 5, 0 };
int client = net_connected_socket(SOCK_STREAM, &addr, NULL, O_NONBLOCK, &tv);
ok(client >= 0, "client: connect to server");
r = fcntl(client, F_SETFL, O_NONBLOCK);
ok(r == 0, "client: set non-blocking mode");
int optval = 8192;
socklen_t optlen = sizeof(optval);
r = setsockopt(client, SOL_SOCKET, SO_SNDBUF, &optval, optlen);
......
......@@ -157,7 +157,8 @@ int main(int argc, char *argv[])
/*! \todo #243 TSIG secured requests test should be implemented. */
/* Terminate responder. */
int responder = net_connected_socket(SOCK_STREAM, &remote.addr, NULL, 0);
struct timeval tv = { 5, 0 };
int responder = net_connected_socket(SOCK_STREAM, &remote.addr, NULL, 0, &tv);
assert(responder > 0);
tcp_send_msg(responder, (const uint8_t *)"", 1, NULL);
(void) pthread_join(thread, 0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment