2019-11-05 13:55:54 -08:00
|
|
|
/*
|
|
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*
|
|
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
|
|
* information regarding copyright ownership.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <uv.h>
|
|
|
|
|
|
|
|
#include <isc/atomic.h>
|
|
|
|
#include <isc/buffer.h>
|
|
|
|
#include <isc/condition.h>
|
|
|
|
#include <isc/magic.h>
|
|
|
|
#include <isc/mem.h>
|
|
|
|
#include <isc/netmgr.h>
|
|
|
|
#include <isc/random.h>
|
|
|
|
#include <isc/refcount.h>
|
|
|
|
#include <isc/region.h>
|
|
|
|
#include <isc/result.h>
|
|
|
|
#include <isc/sockaddr.h>
|
|
|
|
#include <isc/thread.h>
|
|
|
|
#include <isc/util.h>
|
|
|
|
|
|
|
|
#include "netmgr-int.h"
|
2020-02-12 13:59:18 +01:00
|
|
|
#include "uv-compat.h"
|
2019-11-05 13:55:54 -08:00
|
|
|
|
2019-11-08 10:52:49 -08:00
|
|
|
#define TCPDNS_CLIENTS_PER_CONN 23
|
|
|
|
/*%<
|
|
|
|
*
|
|
|
|
* Maximum number of simultaneous handles in flight supported for a single
|
|
|
|
* connected TCPDNS socket. This value was chosen arbitrarily, and may be
|
|
|
|
* changed in the future.
|
|
|
|
*/
|
|
|
|
|
2019-11-05 13:55:54 -08:00
|
|
|
static void
|
|
|
|
dnslisten_readcb(isc_nmhandle_t *handle, isc_region_t *region, void *arg);
|
|
|
|
|
2019-11-08 10:52:49 -08:00
|
|
|
static void
|
|
|
|
resume_processing(void *arg);
|
|
|
|
|
2019-11-05 13:55:54 -08:00
|
|
|
static inline size_t
|
2020-02-12 13:59:18 +01:00
|
|
|
dnslen(unsigned char *base)
|
|
|
|
{
|
2019-11-05 13:55:54 -08:00
|
|
|
return ((base[0] << 8) + (base[1]));
|
|
|
|
}
|
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
/*
|
|
|
|
* Regular TCP buffer, should suffice in most cases.
|
|
|
|
*/
|
2019-11-05 13:55:54 -08:00
|
|
|
#define NM_REG_BUF 4096
|
2019-11-19 11:56:00 +01:00
|
|
|
/*
|
|
|
|
* Two full DNS packets with lengths.
|
|
|
|
* netmgr receives 64k at most so there's no risk
|
|
|
|
* of overrun.
|
|
|
|
*/
|
2020-02-12 13:59:18 +01:00
|
|
|
#define NM_BIG_BUF (65535 + 2) * 2
|
2019-11-05 13:55:54 -08:00
|
|
|
static inline void
|
2020-02-12 13:59:18 +01:00
|
|
|
alloc_dnsbuf(isc_nmsocket_t *sock, size_t len)
|
|
|
|
{
|
2019-11-05 13:55:54 -08:00
|
|
|
REQUIRE(len <= NM_BIG_BUF);
|
|
|
|
|
|
|
|
if (sock->buf == NULL) {
|
|
|
|
/* We don't have the buffer at all */
|
|
|
|
size_t alloc_len = len < NM_REG_BUF ? NM_REG_BUF : NM_BIG_BUF;
|
2019-11-08 10:52:49 -08:00
|
|
|
sock->buf = isc_mem_allocate(sock->mgr->mctx, alloc_len);
|
2019-11-05 13:55:54 -08:00
|
|
|
sock->buf_size = alloc_len;
|
|
|
|
} else {
|
|
|
|
/* We have the buffer but it's too small */
|
|
|
|
sock->buf = isc_mem_reallocate(sock->mgr->mctx, sock->buf,
|
|
|
|
NM_BIG_BUF);
|
|
|
|
sock->buf_size = NM_BIG_BUF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
static void
|
2020-02-12 13:59:18 +01:00
|
|
|
timer_close_cb(uv_handle_t *handle)
|
|
|
|
{
|
|
|
|
isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
|
2019-11-19 11:56:00 +01:00
|
|
|
INSIST(VALID_NMSOCK(sock));
|
|
|
|
atomic_store(&sock->closed, true);
|
|
|
|
isc_nmsocket_detach(&sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-02-12 13:59:18 +01:00
|
|
|
dnstcp_readtimeout(uv_timer_t *timer)
|
|
|
|
{
|
2019-12-03 00:07:59 -08:00
|
|
|
isc_nmsocket_t *sock =
|
2020-02-12 13:59:18 +01:00
|
|
|
(isc_nmsocket_t *)uv_handle_get_data((uv_handle_t *)timer);
|
2019-11-20 22:33:35 +01:00
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
2019-12-07 23:43:52 +01:00
|
|
|
REQUIRE(sock->tid == isc_nm_tid());
|
2019-11-20 22:33:35 +01:00
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
isc_nmsocket_detach(&sock->outer);
|
2020-02-12 13:59:18 +01:00
|
|
|
uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
|
2019-11-19 11:56:00 +01:00
|
|
|
}
|
|
|
|
|
2019-11-05 13:55:54 -08:00
|
|
|
/*
|
2019-11-25 18:36:14 -03:00
|
|
|
* Accept callback for TCP-DNS connection.
|
2019-11-05 13:55:54 -08:00
|
|
|
*/
|
|
|
|
static void
|
2020-02-12 13:59:18 +01:00
|
|
|
dnslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg)
|
|
|
|
{
|
|
|
|
isc_nmsocket_t *dnslistensock = (isc_nmsocket_t *)cbarg;
|
2019-11-05 13:55:54 -08:00
|
|
|
isc_nmsocket_t *dnssock = NULL;
|
|
|
|
|
|
|
|
REQUIRE(VALID_NMSOCK(dnslistensock));
|
|
|
|
REQUIRE(dnslistensock->type == isc_nm_tcpdnslistener);
|
|
|
|
|
|
|
|
/* If accept() was unnsuccessful we can't do anything */
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-25 18:36:14 -03:00
|
|
|
if (dnslistensock->accept_cb.accept != NULL) {
|
|
|
|
dnslistensock->accept_cb.accept(handle, ISC_R_SUCCESS,
|
|
|
|
dnslistensock->accept_cbarg);
|
|
|
|
}
|
|
|
|
|
2019-11-05 13:55:54 -08:00
|
|
|
/* We need to create a 'wrapper' dnssocket for this connection */
|
|
|
|
dnssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*dnssock));
|
2020-02-12 13:59:18 +01:00
|
|
|
isc__nmsocket_init(dnssock, handle->sock->mgr, isc_nm_tcpdnssocket,
|
|
|
|
handle->sock->iface);
|
2019-11-05 13:55:54 -08:00
|
|
|
|
|
|
|
dnssock->extrahandlesize = dnslistensock->extrahandlesize;
|
2020-01-15 14:53:42 +01:00
|
|
|
isc_nmsocket_attach(dnslistensock, &dnssock->listener);
|
2019-11-05 13:55:54 -08:00
|
|
|
isc_nmsocket_attach(handle->sock, &dnssock->outer);
|
|
|
|
dnssock->peer = handle->sock->peer;
|
2019-11-20 22:33:35 +01:00
|
|
|
dnssock->read_timeout = handle->sock->mgr->init;
|
2019-11-19 11:56:00 +01:00
|
|
|
dnssock->tid = isc_nm_tid();
|
2019-11-08 10:52:49 -08:00
|
|
|
dnssock->closehandle_cb = resume_processing;
|
2019-11-05 13:55:54 -08:00
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
uv_timer_init(&dnssock->mgr->workers[isc_nm_tid()].loop,
|
|
|
|
&dnssock->timer);
|
|
|
|
dnssock->timer.data = dnssock;
|
|
|
|
dnssock->timer_initialized = true;
|
|
|
|
uv_timer_start(&dnssock->timer, dnstcp_readtimeout,
|
|
|
|
dnssock->read_timeout, 0);
|
|
|
|
|
2019-11-05 13:55:54 -08:00
|
|
|
isc_nm_read(handle, dnslisten_readcb, dnssock);
|
|
|
|
}
|
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
/*
|
|
|
|
* Process a single packet from the incoming buffer.
|
|
|
|
*
|
|
|
|
* Return ISC_R_SUCCESS and attach 'handlep' to a handle if something
|
|
|
|
* was processed; return ISC_R_NOMORE if there isn't a full message
|
|
|
|
* to be processed.
|
|
|
|
*
|
|
|
|
* The caller will need to unreference the handle.
|
|
|
|
*/
|
|
|
|
static isc_result_t
|
2020-02-12 13:59:18 +01:00
|
|
|
processbuffer(isc_nmsocket_t *dnssock, isc_nmhandle_t **handlep)
|
|
|
|
{
|
2019-11-19 11:56:00 +01:00
|
|
|
size_t len;
|
2019-11-08 10:52:49 -08:00
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
REQUIRE(VALID_NMSOCK(dnssock));
|
|
|
|
REQUIRE(handlep != NULL && *handlep == NULL);
|
2019-11-08 10:52:49 -08:00
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
/*
|
|
|
|
* If we don't even have the length yet, we can't do
|
|
|
|
* anything.
|
|
|
|
*/
|
|
|
|
if (dnssock->buf_len < 2) {
|
|
|
|
return (ISC_R_NOMORE);
|
2019-11-08 10:52:49 -08:00
|
|
|
}
|
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
/*
|
|
|
|
* Process the first packet from the buffer, leaving
|
|
|
|
* the rest (if any) for later.
|
|
|
|
*/
|
|
|
|
len = dnslen(dnssock->buf);
|
|
|
|
if (len <= dnssock->buf_len - 2) {
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_nmhandle_t *dnshandle =
|
|
|
|
isc__nmhandle_get(dnssock, NULL, NULL);
|
2020-01-15 14:53:42 +01:00
|
|
|
isc_nmsocket_t *listener = dnssock->listener;
|
|
|
|
|
|
|
|
if (listener != NULL && listener->rcb.recv != NULL) {
|
2020-02-12 13:59:18 +01:00
|
|
|
listener->rcb.recv(
|
|
|
|
dnshandle,
|
|
|
|
&(isc_region_t){ .base = dnssock->buf + 2,
|
|
|
|
.length = len },
|
|
|
|
listener->rcbarg);
|
2020-01-15 14:53:42 +01:00
|
|
|
}
|
2019-11-08 10:52:49 -08:00
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
len += 2;
|
2019-11-08 10:52:49 -08:00
|
|
|
dnssock->buf_len -= len;
|
|
|
|
if (len > 0) {
|
|
|
|
memmove(dnssock->buf, dnssock->buf + len,
|
|
|
|
dnssock->buf_len);
|
|
|
|
}
|
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
*handlep = dnshandle;
|
|
|
|
return (ISC_R_SUCCESS);
|
2019-11-08 10:52:49 -08:00
|
|
|
}
|
2019-11-19 11:56:00 +01:00
|
|
|
|
|
|
|
return (ISC_R_NOMORE);
|
2019-11-08 10:52:49 -08:00
|
|
|
}
|
|
|
|
|
2019-11-05 13:55:54 -08:00
|
|
|
/*
|
|
|
|
* We've got a read on our underlying socket, need to check if we have
|
|
|
|
* a complete DNS packet and, if so - call the callback
|
|
|
|
*/
|
|
|
|
static void
|
2020-02-12 13:59:18 +01:00
|
|
|
dnslisten_readcb(isc_nmhandle_t *handle, isc_region_t *region, void *arg)
|
|
|
|
{
|
|
|
|
isc_nmsocket_t *dnssock = (isc_nmsocket_t *)arg;
|
|
|
|
unsigned char * base = NULL;
|
|
|
|
bool done = false;
|
|
|
|
size_t len;
|
2019-11-05 13:55:54 -08:00
|
|
|
|
|
|
|
REQUIRE(VALID_NMSOCK(dnssock));
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
2019-12-07 23:43:52 +01:00
|
|
|
REQUIRE(dnssock->tid == isc_nm_tid());
|
2019-11-05 13:55:54 -08:00
|
|
|
|
|
|
|
if (region == NULL) {
|
|
|
|
/* Connection closed */
|
2019-11-19 11:56:00 +01:00
|
|
|
isc__nm_tcpdns_close(dnssock);
|
2019-11-05 13:55:54 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
base = region->base;
|
|
|
|
len = region->length;
|
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
if (dnssock->buf_len + len > dnssock->buf_size) {
|
|
|
|
alloc_dnsbuf(dnssock, dnssock->buf_len + len);
|
2019-11-08 10:52:49 -08:00
|
|
|
}
|
2019-11-19 11:56:00 +01:00
|
|
|
memmove(dnssock->buf + dnssock->buf_len, base, len);
|
|
|
|
dnssock->buf_len += len;
|
2019-11-08 10:52:49 -08:00
|
|
|
|
2019-11-20 22:33:35 +01:00
|
|
|
dnssock->read_timeout = (atomic_load(&dnssock->keepalive)
|
2020-02-12 13:59:18 +01:00
|
|
|
? dnssock->mgr->keepalive
|
|
|
|
: dnssock->mgr->idle);
|
2019-11-20 22:33:35 +01:00
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
do {
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_result_t result;
|
2019-11-19 11:56:00 +01:00
|
|
|
isc_nmhandle_t *dnshandle = NULL;
|
2019-11-08 10:52:49 -08:00
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
result = processbuffer(dnssock, &dnshandle);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
2019-11-08 10:52:49 -08:00
|
|
|
/*
|
2019-11-19 11:56:00 +01:00
|
|
|
* There wasn't anything in the buffer to process.
|
2019-11-08 10:52:49 -08:00
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
/*
|
|
|
|
* We have a packet: stop timeout timers
|
|
|
|
*/
|
|
|
|
atomic_store(&dnssock->outer->processing, true);
|
|
|
|
uv_timer_stop(&dnssock->timer);
|
2019-11-05 13:55:54 -08:00
|
|
|
|
2019-11-20 22:33:35 +01:00
|
|
|
if (atomic_load(&dnssock->sequential)) {
|
2019-11-05 13:55:54 -08:00
|
|
|
/*
|
2019-11-19 11:56:00 +01:00
|
|
|
* We're in sequential mode and we processed
|
|
|
|
* one packet, so we're done until the next read
|
|
|
|
* completes.
|
2019-11-05 13:55:54 -08:00
|
|
|
*/
|
2019-11-19 11:56:00 +01:00
|
|
|
isc_nm_pauseread(dnssock->outer);
|
|
|
|
done = true;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We're pipelining, so we now resume processing
|
|
|
|
* packets until the clients-per-connection limit
|
|
|
|
* is reached (as determined by the number of
|
|
|
|
* active handles on the socket). When the limit
|
|
|
|
* is reached, pause reading.
|
|
|
|
*/
|
|
|
|
if (atomic_load(&dnssock->ah) >=
|
2020-02-12 13:59:18 +01:00
|
|
|
TCPDNS_CLIENTS_PER_CONN) {
|
2019-11-19 11:56:00 +01:00
|
|
|
isc_nm_pauseread(dnssock->outer);
|
|
|
|
done = true;
|
|
|
|
}
|
2019-11-05 13:55:54 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_nmhandle_unref(dnshandle);
|
2019-11-19 11:56:00 +01:00
|
|
|
} while (!done);
|
2019-11-05 13:55:54 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* isc_nm_listentcpdns listens for connections and accepts
|
|
|
|
* them immediately, then calls the cb for each incoming DNS packet
|
|
|
|
* (with 2-byte length stripped) - just like for UDP packet.
|
|
|
|
*/
|
|
|
|
isc_result_t
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
|
|
|
|
void *cbarg, isc_nm_cb_t accept_cb, void *accept_cbarg,
|
2019-11-25 18:36:14 -03:00
|
|
|
size_t extrahandlesize, int backlog, isc_quota_t *quota,
|
2019-11-20 22:33:35 +01:00
|
|
|
isc_nmsocket_t **sockp)
|
2019-11-05 13:55:54 -08:00
|
|
|
{
|
|
|
|
/* A 'wrapper' socket object with outer set to true TCP socket */
|
|
|
|
isc_nmsocket_t *dnslistensock =
|
|
|
|
isc_mem_get(mgr->mctx, sizeof(*dnslistensock));
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
REQUIRE(VALID_NM(mgr));
|
|
|
|
|
2020-01-05 01:02:12 -08:00
|
|
|
isc__nmsocket_init(dnslistensock, mgr, isc_nm_tcpdnslistener, iface);
|
2019-11-05 13:55:54 -08:00
|
|
|
dnslistensock->rcb.recv = cb;
|
|
|
|
dnslistensock->rcbarg = cbarg;
|
2019-11-25 18:36:14 -03:00
|
|
|
dnslistensock->accept_cb.accept = accept_cb;
|
|
|
|
dnslistensock->accept_cbarg = accept_cbarg;
|
2019-11-05 13:55:54 -08:00
|
|
|
dnslistensock->extrahandlesize = extrahandlesize;
|
|
|
|
|
|
|
|
/* We set dnslistensock->outer to a true listening socket */
|
2020-02-12 13:59:18 +01:00
|
|
|
result = isc_nm_listentcp(mgr, iface, dnslisten_acceptcb, dnslistensock,
|
|
|
|
extrahandlesize, backlog, quota,
|
|
|
|
&dnslistensock->outer);
|
2019-12-02 13:54:44 +01:00
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
atomic_store(&dnslistensock->listening, true);
|
|
|
|
*sockp = dnslistensock;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
} else {
|
|
|
|
atomic_store(&dnslistensock->closed, true);
|
|
|
|
isc_nmsocket_detach(&dnslistensock);
|
|
|
|
return (result);
|
|
|
|
}
|
2019-11-05 13:55:54 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_nm_tcpdns_stoplistening(isc_nmsocket_t *sock)
|
|
|
|
{
|
2019-11-05 13:55:54 -08:00
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
REQUIRE(sock->type == isc_nm_tcpdnslistener);
|
|
|
|
|
|
|
|
atomic_store(&sock->listening, false);
|
|
|
|
atomic_store(&sock->closed, true);
|
2020-01-15 14:53:42 +01:00
|
|
|
sock->rcb.recv = NULL;
|
|
|
|
sock->rcbarg = NULL;
|
2019-11-05 13:55:54 -08:00
|
|
|
|
|
|
|
if (sock->outer != NULL) {
|
|
|
|
isc_nm_tcp_stoplistening(sock->outer);
|
|
|
|
isc_nmsocket_detach(&sock->outer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_nm_tcpdns_sequential(isc_nmhandle_t *handle)
|
|
|
|
{
|
2019-11-05 13:55:54 -08:00
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
|
|
|
|
if (handle->sock->type != isc_nm_tcpdnssocket ||
|
2020-02-12 13:59:18 +01:00
|
|
|
handle->sock->outer == NULL) {
|
2019-11-05 13:55:54 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't want pipelining on this connection. That means
|
2019-11-08 10:52:49 -08:00
|
|
|
* that we need to pause after reading each request, and
|
|
|
|
* resume only after the request has been processed. This
|
|
|
|
* is done in resume_processing(), which is the socket's
|
|
|
|
* closehandle_cb callback, called whenever a handle
|
|
|
|
* is released.
|
2019-11-05 13:55:54 -08:00
|
|
|
*/
|
|
|
|
isc_nm_pauseread(handle->sock->outer);
|
|
|
|
atomic_store(&handle->sock->sequential, true);
|
|
|
|
}
|
|
|
|
|
2019-11-20 22:33:35 +01:00
|
|
|
void
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_nm_tcpdns_keepalive(isc_nmhandle_t *handle)
|
|
|
|
{
|
2019-11-20 22:33:35 +01:00
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
|
|
|
|
if (handle->sock->type != isc_nm_tcpdnssocket ||
|
2020-02-12 13:59:18 +01:00
|
|
|
handle->sock->outer == NULL) {
|
2019-11-20 22:33:35 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_store(&handle->sock->keepalive, true);
|
|
|
|
atomic_store(&handle->sock->outer->keepalive, true);
|
|
|
|
}
|
|
|
|
|
2019-11-05 13:55:54 -08:00
|
|
|
typedef struct tcpsend {
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_mem_t * mctx;
|
|
|
|
isc_nmhandle_t *handle;
|
|
|
|
isc_region_t region;
|
|
|
|
isc_nmhandle_t *orighandle;
|
|
|
|
isc_nm_cb_t cb;
|
|
|
|
void * cbarg;
|
2019-11-05 13:55:54 -08:00
|
|
|
} tcpsend_t;
|
|
|
|
|
2019-11-08 10:52:49 -08:00
|
|
|
static void
|
2020-02-12 13:59:18 +01:00
|
|
|
resume_processing(void *arg)
|
|
|
|
{
|
|
|
|
isc_nmsocket_t *sock = (isc_nmsocket_t *)arg;
|
|
|
|
isc_result_t result;
|
2019-11-08 10:52:49 -08:00
|
|
|
|
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
2019-11-19 11:56:00 +01:00
|
|
|
REQUIRE(sock->tid == isc_nm_tid());
|
2019-11-08 10:52:49 -08:00
|
|
|
|
|
|
|
if (sock->type != isc_nm_tcpdnssocket || sock->outer == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-19 11:56:00 +01:00
|
|
|
if (atomic_load(&sock->ah) == 0) {
|
|
|
|
/* Nothing is active; sockets can timeout now */
|
|
|
|
atomic_store(&sock->outer->processing, false);
|
|
|
|
uv_timer_start(&sock->timer, dnstcp_readtimeout,
|
|
|
|
sock->read_timeout, 0);
|
|
|
|
}
|
|
|
|
|
2019-11-08 10:52:49 -08:00
|
|
|
/*
|
2019-11-19 11:56:00 +01:00
|
|
|
* For sequential sockets: Process what's in the buffer, or
|
|
|
|
* if there aren't any messages buffered, resume reading.
|
2019-11-08 10:52:49 -08:00
|
|
|
*/
|
2019-11-20 22:33:35 +01:00
|
|
|
if (atomic_load(&sock->sequential)) {
|
2019-11-19 11:56:00 +01:00
|
|
|
isc_nmhandle_t *handle = NULL;
|
|
|
|
|
|
|
|
result = processbuffer(sock, &handle);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
atomic_store(&sock->outer->processing, true);
|
|
|
|
uv_timer_stop(&sock->timer);
|
|
|
|
isc_nmhandle_unref(handle);
|
|
|
|
} else if (sock->outer != NULL) {
|
|
|
|
isc_nm_resumeread(sock->outer);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2019-11-08 10:52:49 -08:00
|
|
|
}
|
2019-11-19 11:56:00 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For pipelined sockets: If we're under the clients-per-connection
|
|
|
|
* limit, resume processing until we reach the limit again.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
isc_nmhandle_t *dnshandle = NULL;
|
|
|
|
|
|
|
|
result = processbuffer(sock, &dnshandle);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
/*
|
|
|
|
* Nothing in the buffer; resume reading.
|
|
|
|
*/
|
|
|
|
if (sock->outer != NULL) {
|
|
|
|
isc_nm_resumeread(sock->outer);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
uv_timer_stop(&sock->timer);
|
|
|
|
atomic_store(&sock->outer->processing, true);
|
|
|
|
isc_nmhandle_unref(dnshandle);
|
|
|
|
} while (atomic_load(&sock->ah) < TCPDNS_CLIENTS_PER_CONN);
|
2019-11-08 10:52:49 -08:00
|
|
|
}
|
|
|
|
|
2019-11-05 13:55:54 -08:00
|
|
|
static void
|
2020-02-12 13:59:18 +01:00
|
|
|
tcpdnssend_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg)
|
|
|
|
{
|
|
|
|
tcpsend_t *ts = (tcpsend_t *)cbarg;
|
2019-11-05 13:55:54 -08:00
|
|
|
|
|
|
|
UNUSED(handle);
|
|
|
|
|
|
|
|
ts->cb(ts->orighandle, result, ts->cbarg);
|
|
|
|
isc_mem_put(ts->mctx, ts->region.base, ts->region.length);
|
|
|
|
|
|
|
|
isc_nmhandle_unref(ts->orighandle);
|
|
|
|
isc_mem_putanddetach(&ts->mctx, ts, sizeof(*ts));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* isc__nm_tcp_send sends buf to a peer on a socket.
|
|
|
|
*/
|
|
|
|
isc_result_t
|
|
|
|
isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region,
|
|
|
|
isc_nm_cb_t cb, void *cbarg)
|
|
|
|
{
|
|
|
|
tcpsend_t *t = NULL;
|
|
|
|
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
|
|
|
|
isc_nmsocket_t *sock = handle->sock;
|
|
|
|
|
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
REQUIRE(sock->type == isc_nm_tcpdnssocket);
|
|
|
|
|
|
|
|
if (sock->outer == NULL) {
|
2020-01-17 11:42:35 +01:00
|
|
|
/* The socket is closed */
|
2019-11-05 13:55:54 -08:00
|
|
|
return (ISC_R_NOTCONNECTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
t = isc_mem_get(sock->mgr->mctx, sizeof(*t));
|
2020-02-12 13:59:18 +01:00
|
|
|
*t = (tcpsend_t){
|
2019-11-05 13:55:54 -08:00
|
|
|
.cb = cb,
|
|
|
|
.cbarg = cbarg,
|
|
|
|
.handle = handle->sock->outer->tcphandle,
|
|
|
|
};
|
|
|
|
|
|
|
|
isc_mem_attach(sock->mgr->mctx, &t->mctx);
|
|
|
|
t->orighandle = handle;
|
|
|
|
isc_nmhandle_ref(t->orighandle);
|
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
t->region = (isc_region_t){ .base = isc_mem_get(t->mctx,
|
|
|
|
region->length + 2),
|
|
|
|
.length = region->length + 2 };
|
2019-11-05 13:55:54 -08:00
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
*(uint16_t *)t->region.base = htons(region->length);
|
2019-11-05 13:55:54 -08:00
|
|
|
memmove(t->region.base + 2, region->base, region->length);
|
|
|
|
|
|
|
|
return (isc__nm_tcp_send(t->handle, &t->region, tcpdnssend_cb, t));
|
|
|
|
}
|
|
|
|
|
2019-12-06 22:25:52 +01:00
|
|
|
static void
|
2020-02-12 13:59:18 +01:00
|
|
|
tcpdns_close_direct(isc_nmsocket_t *sock)
|
|
|
|
{
|
2019-12-07 23:43:52 +01:00
|
|
|
REQUIRE(sock->tid == isc_nm_tid());
|
2019-11-05 13:55:54 -08:00
|
|
|
if (sock->outer != NULL) {
|
2019-12-08 23:09:16 +01:00
|
|
|
sock->outer->rcb.recv = NULL;
|
2019-11-05 13:55:54 -08:00
|
|
|
isc_nmsocket_detach(&sock->outer);
|
|
|
|
}
|
2020-01-15 14:53:42 +01:00
|
|
|
if (sock->listener != NULL) {
|
|
|
|
isc_nmsocket_detach(&sock->listener);
|
|
|
|
}
|
2019-12-06 22:25:52 +01:00
|
|
|
/* We don't need atomics here, it's all in single network thread */
|
|
|
|
if (sock->timer_initialized) {
|
|
|
|
sock->timer_initialized = false;
|
|
|
|
uv_timer_stop(&sock->timer);
|
2020-02-12 13:59:18 +01:00
|
|
|
uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
|
2019-12-06 22:25:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-12 13:59:18 +01:00
|
|
|
isc__nm_tcpdns_close(isc_nmsocket_t *sock)
|
|
|
|
{
|
2019-12-06 22:25:52 +01:00
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
REQUIRE(sock->type == isc_nm_tcpdnssocket);
|
|
|
|
|
|
|
|
if (sock->tid == isc_nm_tid()) {
|
|
|
|
tcpdns_close_direct(sock);
|
|
|
|
} else {
|
|
|
|
isc__netievent_tcpdnsclose_t *ievent =
|
|
|
|
isc__nm_get_ievent(sock->mgr, netievent_tcpdnsclose);
|
|
|
|
|
|
|
|
ievent->sock = sock;
|
|
|
|
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
|
2020-02-12 13:59:18 +01:00
|
|
|
(isc__netievent_t *)ievent);
|
2019-12-06 22:25:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-12 13:59:18 +01:00
|
|
|
isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0)
|
|
|
|
{
|
2019-12-06 22:25:52 +01:00
|
|
|
isc__netievent_tcpdnsclose_t *ievent =
|
2020-02-12 13:59:18 +01:00
|
|
|
(isc__netievent_tcpdnsclose_t *)ev0;
|
2019-12-06 22:25:52 +01:00
|
|
|
|
|
|
|
REQUIRE(worker->id == ievent->sock->tid);
|
|
|
|
|
|
|
|
tcpdns_close_direct(ievent->sock);
|
2019-11-05 13:55:54 -08:00
|
|
|
}
|