#include <isc/netmgr.h>
#include <isc/nonce.h>
#include <isc/random.h>
+#include <isc/refcount.h>
#include <isc/result.h>
#include <isc/stdtime.h>
#include <isc/string.h>
#include <named/main.h>
#include <named/server.h>
+#undef NAMED_CONTROLCONF_TRACE
+
typedef struct controlkey controlkey_t;
typedef ISC_LIST(controlkey_t) controlkeylist_t;
};
struct controlconnection {
- isc_nmhandle_t *readhandle;
- isc_nmhandle_t *sendhandle;
- isc_nmhandle_t *cmdhandle;
+ isc_refcount_t references;
isccc_ccmsg_t ccmsg;
- bool reading;
- bool sending;
controllistener_t *listener;
isccc_sexpr_t *ctrl;
isc_buffer_t *buffer;
isc_stdtime_t now;
isc_result_t result;
ISC_LINK(controlconnection_t) link;
+ bool shuttingdown;
};
struct controllistener {
isc_nmsocket_t *sock;
dns_acl_t *acl;
bool exiting;
- isc_refcount_t refs;
+ isc_refcount_t references;
controlkeylist_t keys;
isc_mutex_t connections_lock;
controlconnectionlist_t connections;
struct named_controls {
named_server_t *server;
controllistenerlist_t listeners;
- atomic_bool shuttingdown;
+ bool shuttingdown;
isc_mutex_t symtab_lock;
isccc_symtab_t *symtab;
};
control_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg);
static void
control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg);
+static void
+conn_cleanup(controlconnection_t *conn);
+static void
+conn_free(controlconnection_t *conn);
+
+#if NAMED_CONTROLCONF_TRACE
+#define controllistener_ref(ptr) \
+ controllistener__ref(ptr, __func__, __FILE__, __LINE__)
+#define controllistener_unref(ptr) \
+ controllistener__unref(ptr, __func__, __FILE__, __LINE__)
+#define controllistener_attach(ptr, ptrp) \
+ controllistener__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
+#define controllistener_detach(ptrp) \
+ controllistener__detach(ptrp, __func__, __FILE__, __LINE__)
+ISC_REFCOUNT_TRACE_DECL(controllistener);
+
+#define controlconnection_ref(ptr) \
+ controlconnection__ref(ptr, __func__, __FILE__, __LINE__)
+#define controlconnection_unref(ptr) \
+ controlconnection__unref(ptr, __func__, __FILE__, __LINE__)
+#define controlconnection_attach(ptr, ptrp) \
+ controlconnection__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
+#define controlconnection_detach(ptrp) \
+ controlconnection__detach(ptrp, __func__, __FILE__, __LINE__)
+ISC_REFCOUNT_TRACE_DECL(controlconnection);
+#else
+ISC_REFCOUNT_DECL(controllistener);
+ISC_REFCOUNT_DECL(controlconnection);
+#endif
#define CLOCKSKEW 300
INSIST(listener->exiting);
INSIST(ISC_LIST_EMPTY(listener->connections));
- isc_refcount_destroy(&listener->refs);
+ isc_refcount_destroy(&listener->references);
REQUIRE(listener->sock == NULL);
isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
}
-static void
-maybe_free_listener(controllistener_t *listener) {
- if (isc_refcount_decrement(&listener->refs) == 1) {
- free_listener(listener);
- }
-}
+#if NAMED_CONTROLCONF_TRACE
+ISC_REFCOUNT_TRACE_IMPL(controllistener, free_listener);
+ISC_REFCOUNT_TRACE_IMPL(controlconnection, conn_free);
+#else
+ISC_REFCOUNT_IMPL(controllistener, free_listener);
+ISC_REFCOUNT_IMPL(controlconnection, conn_free);
+#endif
static void
shutdown_listener(controllistener_t *listener) {
if (!listener->exiting) {
char socktext[ISC_SOCKADDR_FORMATSIZE];
+ for (controlconnection_t *conn =
+ ISC_LIST_HEAD(listener->connections);
+ conn != NULL; conn = ISC_LIST_HEAD(listener->connections))
+ {
+ control_recvmessage(conn->ccmsg.handle,
+ ISC_R_SHUTTINGDOWN, conn);
+ }
+
ISC_LIST_UNLINK(listener->controls->listeners, listener, link);
isc_sockaddr_format(&listener->address, socktext,
isc_nm_stoplistening(listener->sock);
isc_nmsocket_close(&listener->sock);
- maybe_free_listener(listener);
+ controllistener_detach(&listener);
}
static bool
controllistener_t *listener = conn->listener;
isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(handle);
- REQUIRE(conn->sending);
-
- conn->sending = false;
-
if (conn->result == ISC_R_SHUTTINGDOWN) {
isc_loopmgr_shutdown(named_g_loopmgr);
goto cleanup_sendhandle;
}
- if (atomic_load_acquire(&listener->controls->shuttingdown) ||
- result == ISC_R_SHUTTINGDOWN)
- {
+ if (listener->controls->shuttingdown || result == ISC_R_SHUTTINGDOWN) {
goto cleanup_sendhandle;
} else if (result != ISC_R_SUCCESS) {
char socktext[ISC_SOCKADDR_FORMATSIZE];
goto cleanup_sendhandle;
}
- isc_nmhandle_attach(handle, &conn->readhandle);
- conn->reading = true;
-
- isc_nmhandle_detach(&conn->sendhandle);
-
isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn);
- return;
cleanup_sendhandle:
- isc_nmhandle_detach(&conn->sendhandle);
+ if (result != ISC_R_SUCCESS) {
+ control_recvmessage(handle, result, conn);
+ }
+ controlconnection_detach(&conn);
}
static void
}
static void
-control_respond(isc_nmhandle_t *handle, controlconnection_t *conn) {
+control_respond(controlconnection_t *conn) {
controllistener_t *listener = conn->listener;
isccc_sexpr_t *data = NULL;
isc_buffer_t b;
result = isccc_cc_towire(conn->response, &conn->buffer, conn->alg,
&conn->secret);
if (result != ISC_R_SUCCESS) {
- goto cleanup;
+ return;
}
isc_buffer_init(&b, conn->buffer->base, 4);
r.base = conn->buffer->base;
r.length = conn->buffer->used;
- isc_nmhandle_attach(handle, &conn->sendhandle);
- conn->sending = true;
- conn_cleanup(conn);
-
- isc_nmhandle_detach(&conn->cmdhandle);
-
- isc_nm_send(conn->sendhandle, &r, control_senddone, conn);
-
- return;
+ controlconnection_ref(conn);
+ isccc_ccmsg_sendmessage(&conn->ccmsg, &r, control_senddone, conn);
cleanup:
conn_cleanup(conn);
- isc_nmhandle_detach(&conn->cmdhandle);
}
static void
controlconnection_t *conn = (controlconnection_t *)arg;
controllistener_t *listener = conn->listener;
- if (atomic_load_acquire(&listener->controls->shuttingdown)) {
- conn_cleanup(conn);
- isc_nmhandle_detach(&conn->cmdhandle);
- return;
+ if (!listener->controls->shuttingdown) {
+ conn->result = named_control_docommand(
+ conn->request, listener->readonly, &conn->text);
+ control_respond(conn);
}
-
- conn->result = named_control_docommand(conn->request,
- listener->readonly, &conn->text);
- control_respond(conn->cmdhandle, conn);
+ controlconnection_detach(&conn);
}
static void
-control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result,
+ void *arg) {
controlconnection_t *conn = (controlconnection_t *)arg;
controllistener_t *listener = conn->listener;
controlkey_t *key = NULL;
isccc_time_t exp;
uint32_t nonce;
- conn->reading = false;
+ if (conn->shuttingdown) {
+ return;
+ }
/* Is the server shutting down? */
- if (atomic_load_acquire(&listener->controls->shuttingdown)) {
- goto cleanup_readhandle;
+ if (listener->controls->shuttingdown) {
+ result = ISC_R_SHUTTINGDOWN;
}
if (result != ISC_R_SUCCESS) {
if (result == ISC_R_SHUTTINGDOWN) {
- atomic_store_release(&listener->controls->shuttingdown,
- true);
+ listener->controls->shuttingdown = true;
} else if (result != ISC_R_EOF) {
log_invalid(&conn->ccmsg, result);
}
-
- goto cleanup_readhandle;
+ goto cleanup;
}
for (key = ISC_LIST_HEAD(listener->keys); key != NULL;
{
isccc_region_t ccregion;
- ccregion.rstart = isc_buffer_base(conn->ccmsg.buffer);
- ccregion.rend = isc_buffer_used(conn->ccmsg.buffer);
+ isccc_ccmsg_toregion(&conn->ccmsg, &ccregion);
conn->secret.rstart = isc_mem_get(listener->mctx,
key->secret.length);
memmove(conn->secret.rstart, key->secret.base,
isc_buffer_allocate(listener->mctx, &conn->text, 2 * 2048);
- isc_nmhandle_attach(handle, &conn->cmdhandle);
- isc_nmhandle_detach(&conn->readhandle);
-
if (conn->nonce == 0) {
/*
* Establish nonce.
isc_nonce_buf(&conn->nonce, sizeof(conn->nonce));
}
conn->result = ISC_R_SUCCESS;
- control_respond(handle, conn);
+ control_respond(conn);
return;
}
/*
* Trigger the command.
*/
-
+ controlconnection_ref(conn);
isc_async_run(named_g_mainloop, control_command, conn);
return;
cleanup:
- conn_cleanup(conn);
-
-cleanup_readhandle:
- /*
- * readhandle could be NULL if we're shutting down,
- * but if not we need to detach it.
- */
- if (conn->readhandle != NULL) {
- isc_nmhandle_detach(&conn->readhandle);
- }
+ conn->shuttingdown = true;
+ controlconnection_detach(&conn);
}
static void
-conn_reset(void *arg) {
- controlconnection_t *conn = (controlconnection_t *)arg;
+conn_free(controlconnection_t *conn) {
controllistener_t *listener = conn->listener;
+ conn_cleanup(conn);
+
if (conn->buffer != NULL) {
isc_buffer_free(&conn->buffer);
}
- if (conn->reading) {
- isccc_ccmsg_cancelread(&conn->ccmsg);
- return;
- }
-
LOCK(&listener->connections_lock);
ISC_LIST_UNLINK(listener->connections, conn, link);
UNLOCK(&listener->connections_lock);
#endif /* ifdef ENABLE_AFL */
isccc_ccmsg_invalidate(&conn->ccmsg);
-}
-
-static void
-conn_put(void *arg) {
- controlconnection_t *conn = (controlconnection_t *)arg;
- controllistener_t *listener = conn->listener;
isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3),
isc_mem_put(listener->mctx, conn, sizeof(*conn));
- maybe_free_listener(listener);
+ controllistener_detach(&listener);
}
static void
newconnection(controllistener_t *listener, isc_nmhandle_t *handle) {
- controlconnection_t *conn = NULL;
+ controlconnection_t *conn = isc_mem_get(listener->mctx, sizeof(*conn));
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3),
+ "allocate new control connection");
- conn = isc_nmhandle_getdata(handle);
- if (conn == NULL) {
- conn = isc_mem_get(listener->mctx, sizeof(*conn));
- isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
- NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3),
- "allocate new control connection");
- isc_nmhandle_setdata(handle, conn, conn_reset, conn_put);
- isc_refcount_increment(&listener->refs);
- }
+ *conn = (controlconnection_t){ .alg = DST_ALG_UNKNOWN };
- *conn = (controlconnection_t){ .listener = listener,
- .reading = false,
- .alg = DST_ALG_UNKNOWN };
+ isc_refcount_init(&conn->references, 1);
+ controllistener_attach(listener, &conn->listener);
+ /* isccc_ccmsg_init() attaches to the handle */
isccc_ccmsg_init(listener->mctx, handle, &conn->ccmsg);
/* Set a 32 KiB upper limit on incoming message. */
ISC_LIST_INITANDAPPEND(listener->connections, conn, link);
UNLOCK(&listener->connections_lock);
- isc_nmhandle_attach(handle, &conn->readhandle);
- conn->reading = true;
-
isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn);
}
void
named_controls_shutdown(named_controls_t *controls) {
controls_shutdown(controls);
- atomic_store_release(&controls->shuttingdown, true);
+ controls->shuttingdown = true;
}
static isc_result_t
ISC_LINK_INIT(listener, link);
ISC_LIST_INIT(listener->keys);
ISC_LIST_INIT(listener->connections);
- isc_refcount_init(&listener->refs, 1);
+ isc_refcount_init(&listener->references, 1);
/*
* Make the ACL.
return;
cleanup:
- isc_refcount_decrement(&listener->refs);
+ isc_refcount_decrement(&listener->references);
listener->exiting = true;
free_listener(listener);
}
*listenerp = NULL;
-
- /* XXXDCL return error results? fail hard? */
}
isc_result_t
ISC_LIST_INIT(controls->listeners);
- atomic_init(&controls->shuttingdown, false);
isc_mutex_init(&controls->symtab_lock);
LOCK(&controls->symtab_lock);
result = isccc_cc_createsymtab(&controls->symtab);
#include <stdbool.h>
#include <stdlib.h>
-#include <isc/atomic.h>
#include <isc/attributes.h>
#include <isc/buffer.h>
#include <isc/commandline.h>
static bool failed = false;
static bool c_flag = false;
static isc_mem_t *rndc_mctx = NULL;
-static atomic_uint_fast32_t sends = 0;
-static atomic_uint_fast32_t recvs = 0;
-static atomic_uint_fast32_t connects = 0;
static char *command = NULL;
static char *args = NULL;
static char program[256];
static uint32_t serial;
static bool quiet = false;
static bool showresult = false;
-static bool shuttingdown = false;
-static isc_nmhandle_t *recvdone_handle = NULL;
-static isc_nmhandle_t *recvnonce_handle = NULL;
static void
rndc_startconnect(isc_sockaddr_t *addr);
}
static void
-rndc_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
- isc_nmhandle_t *sendhandle = (isc_nmhandle_t *)arg;
-
+rndc_senddone(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result,
+ void *arg ISC_ATTR_UNUSED) {
if (result != ISC_R_SUCCESS) {
fatal("send failed: %s", isc_result_totext(result));
}
-
- REQUIRE(sendhandle == handle);
- isc_nmhandle_detach(&sendhandle);
-
- if (atomic_fetch_sub_release(&sends, 1) == 1 &&
- atomic_load_acquire(&recvs) == 0)
- {
- shuttingdown = true;
- isc_loopmgr_shutdown(loopmgr);
- }
}
static void
char *errormsg = NULL;
char *textmsg = NULL;
+ REQUIRE(handle != NULL);
REQUIRE(ccmsg != NULL);
- if (shuttingdown && (result == ISC_R_EOF || result == ISC_R_CANCELED)) {
- atomic_fetch_sub_release(&recvs, 1);
- if (handle != NULL) {
- REQUIRE(recvdone_handle == handle);
- isc_nmhandle_detach(&recvdone_handle);
- }
- return;
- } else if (result == ISC_R_EOF) {
+ if (result == ISC_R_EOF) {
fatal("connection to remote host closed.\n"
"* This may indicate that the\n"
"* remote server is using an older\n"
isccc_sexpr_free(&response);
- REQUIRE(recvdone_handle == handle);
- isc_nmhandle_detach(&recvdone_handle);
-
- if (atomic_fetch_sub_release(&recvs, 1) == 1 &&
- atomic_load_acquire(&sends) == 0)
- {
- shuttingdown = true;
- isc_loopmgr_shutdown(loopmgr);
- }
+ isccc_ccmsg_invalidate(ccmsg);
+ isc_loopmgr_shutdown(loopmgr);
}
static void
-rndc_recvnonce(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+rndc_recvnonce(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result,
+ void *arg) {
isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg;
isccc_sexpr_t *response = NULL;
- isc_nmhandle_t *sendhandle = NULL;
isccc_sexpr_t *_ctrl = NULL;
isccc_region_t source;
uint32_t nonce;
REQUIRE(ccmsg != NULL);
- if (shuttingdown && (result == ISC_R_EOF || result == ISC_R_CANCELED)) {
- atomic_fetch_sub_release(&recvs, 1);
- if (handle != NULL) {
- REQUIRE(recvnonce_handle == handle);
- isc_nmhandle_detach(&recvnonce_handle);
- }
- return;
- } else if (result == ISC_R_EOF) {
+ if (result == ISC_R_EOF) {
fatal("connection to remote host closed.\n"
"* This may indicate that the\n"
"* remote server is using an older\n"
r.base = databuf->base;
r.length = databuf->used;
- isc_nmhandle_attach(handle, &recvdone_handle);
- atomic_fetch_add_relaxed(&recvs, 1);
isccc_ccmsg_readmessage(ccmsg, rndc_recvdone, ccmsg);
-
- isc_nmhandle_attach(handle, &sendhandle);
- atomic_fetch_add_relaxed(&sends, 1);
- isc_nm_send(handle, &r, rndc_senddone, sendhandle);
-
- REQUIRE(recvnonce_handle == handle);
- isc_nmhandle_detach(&recvnonce_handle);
- atomic_fetch_sub_release(&recvs, 1);
+ isccc_ccmsg_sendmessage(ccmsg, &r, rndc_senddone, NULL);
isccc_sexpr_free(&response);
isccc_sexpr_free(&request);
isccc_time_t now = isc_stdtime_now();
isc_region_t r;
isc_buffer_t b;
- isc_nmhandle_t *connhandle = NULL;
- isc_nmhandle_t *sendhandle = NULL;
REQUIRE(ccmsg != NULL);
if (result != ISC_R_SUCCESS) {
- atomic_fetch_sub_release(&connects, 1);
isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
sizeof(socktext));
if (++currentaddr < nserveraddrs) {
isc_result_totext(result));
}
- isc_nmhandle_attach(handle, &connhandle);
-
DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
now, now + 60, &request));
data = isccc_alist_lookup(request, "_data");
r.base = databuf->base;
r.length = databuf->used;
+ /* isccc_ccmsg_init() attaches to the handle */
isccc_ccmsg_init(rndc_mctx, handle, ccmsg);
isccc_ccmsg_setmaxsize(ccmsg, 1024 * 1024);
- isc_nmhandle_attach(handle, &recvnonce_handle);
- atomic_fetch_add_relaxed(&recvs, 1);
isccc_ccmsg_readmessage(ccmsg, rndc_recvnonce, ccmsg);
-
- isc_nmhandle_attach(handle, &sendhandle);
- atomic_fetch_add_relaxed(&sends, 1);
- isc_nm_send(handle, &r, rndc_senddone, sendhandle);
-
- isc_nmhandle_detach(&connhandle);
- atomic_fetch_sub_release(&connects, 1);
+ isccc_ccmsg_sendmessage(ccmsg, &r, rndc_senddone, NULL);
isccc_sexpr_free(&request);
}
UNREACHABLE();
}
- atomic_fetch_add_relaxed(&connects, 1);
isc_nm_tcpconnect(netmgr, local, addr, rndc_connected, &rndc_ccmsg,
60000);
}
isc_loopmgr_run(loopmgr);
- /*
- * Note: when TCP connections are shut down, there will be a final
- * call to the isccc callback routine with &rndc_ccmsg as its
- * argument. We therefore need to delay invalidating it until
- * after the netmgr is closed down.
- */
- isccc_ccmsg_invalidate(&rndc_ccmsg);
-
isc_log_destroy(&log);
isc_log_setcontext(NULL);
associated object's 'put' callback will be called to free any resources
it allocated.
-## UDP listening
+## Streaming Protocols
-UDP listener sockets automatically create an array of 'child' sockets,
-each associated with one networker, and all listening on the same address
-via `SO_REUSEADDR`. (The parent's reference counter is used for all the
-parent and child sockets together; none are destroyed until there are no
-remaining references to any of tem.)
+Currently, we have two streaming protocols available in Network Manager - TCP
+and TLS. The underlying premise is that they both expose the same interface to
+the clients.
-## TCP listening
+### Servers (Listening)
-A TCP listener socket cannot listen on multiple threads in parallel,
-so receiving a TCP connection can cause a context switch, but this is
-expected to be rare enough not to impact performance significantly.
+The users of the API calls ``isc_nm_listentcp()`` or ``isc_nm_listentls()`` with
+the accept callback as argument.
-When connected, a TCP socket will attach to the system-wide TCP clients
-quota.
+When connection is accepted, the accept callback is called with a handle and
+status and it can return a non-``ISC_R_RESULT`` to abort the connection.
-## TCP listening for DNS
+The accept callback should generally immediately call ``isc_nm_read()`` to setup
+the read callback. Not doing so, can lead to a data race - if the NM is shut
+down before the ``isc_nm_read()`` call, the socket can become dangling until
+``isc_nm_read()`` is finally called.
-A TCPDNS listener is a wrapper around a TCP socket which specifically
-handles DNS traffic, including the two-byte length field that prepends DNS
-messages over TCP.
+When ``isc_nm_read()`` is called, the read callback will receive:
-Other wrapper socket types can be added in the future, such as a TLS socket
-wrapper to implement encryption or an HTTP wrapper to implement the HTTP
-protocol. This will enable the system to have a transport-neutral network
-manager socket over which DNS can be sent without knowing anything about
-transport, encryption, etc.
+- 0-<n> calls with ``ISC_R_SUCCESS`` state
+- exactly 1 call with non-``ISC_R_SUCCESS`` state when the connection is
+ interrupted (locally closed, remotely closed, NM shutting down, etc.)
+
+The ``isc_nm_read_stop()`` can be used to pause reading from the socket and only
+the final non-``ISC_R_SUCCESS`` callback will be received in such case.
+
+### Clients (Connecting)
+
+The users of the API calls ``isc_nm_tcpconnect()`` or ``isc_nm_tlsconnect()``
+with the connect callback as argument.
+
+When connection is established, the connect callback is called with a handle and
+status.
+
+The connect callback should generally immediately call ``isc_nm_read()`` - see
+the same caveat in the accepting part.
+
+When ``isc__nm_read()`` is called on the connected socket, the read callback
+will receive:
+
+- 0-<n> calls with ``ISC_R_SUCCESS`` state
+- exactly 1 call with non-``ISC_R_SUCCESS`` state when the connection is
+ interrupted (locally closed, remotely closed, NM shutting down, etc.)
+
+The ``isc_nm_read_stop()`` can be used to pause reading from the socket and only
+the final non-``ISC_R_SUCCESS`` callback will be received in such case.
+
+## DNS Message Protocols
+
+Currently, we have three (four) DNS Message Protocols implemented in the Network Manager:
+
+- UDP
+- StreamDNS (TCPDNS and TLSDNS)
+- HTTP
+
+### Servers (Listening)
+
+The users of the API calls ``isc_nm_listenudp()`` or
+``isc_nm_listenstreamdns()`` with:
+
+- accept callback
+- read callback
+
+The StreamDNS accepts an optional TLS context for DoT (otherwise DNS over TCP
+will be used).
+
+The HTTP listening is more complicated - the users need to setup the endpoints
+with the read callback and pass the 1-<n> endpoints to the
+``isc_nm_listenhttp()`` call.
+
+The accept callback is used only to implement "firewall"-like functionality, it
+could be used to tear down the connection early in the process.
+
+After the connection has been accepted, the read callback will receive:
+
+- 0-<n> calls with ``ISC_R_SUCCESS`` state
+- exactly 1 call with non-``ISC_R_SUCCESS`` state when the connection is
+ interrupted (locally closed, remotely closed, NM shutting down, etc.)
+
+Each read callback will contain a full assembled DNS message.
+
+### Clients (Connecting)
+
+The users of the API calls ``isc_nm_udpconnect()``,
+``isc_nm_streamdnsconnect()``, or ``isc_nm_httpconnect()`` with a connect
+callback.
+
+When connection is established, the connect callback is called with a handle and
+status.
+
+The connect callback should generally immediately call ``isc_nm_read()`` - see
+the caveat in the previous parts.
+
+After the connection has been connected, the read callback will receive exactly
+1 call for each ``isc_nm_read()`` call - either with ``ISC_R_SUCCESS`` if the
+DNS message was successfully read or non-``ISC_R_SUCCESS`` indicating the error
+condition. The read callback either needs to issue new ``isc_nm_read()`` call
+or detach from the handle if no further messages are required.
isc_httpdmgr_t *mgr; /*%< our parent */
ISC_LINK(isc_httpd_t) link;
- isc_nmhandle_t *handle; /* Permanent pointer to handle */
- isc_nmhandle_t *readhandle; /* Waiting for a read callback */
+ isc_nmhandle_t *handle; /* Permanent pointer to handle */
int flags;
typedef struct isc_httpd_sendreq {
isc_mem_t *mctx;
isc_httpd_t *httpd;
- isc_nmhandle_t *handle;
/*%
* Transmit data state.
static void
httpd_senddone(isc_nmhandle_t *, isc_result_t, void *);
static void
-httpd_reset(void *);
-static void
-httpd_put(void *);
+httpd_free(isc_httpd_t *httpd);
static void
httpd_addheader(isc_httpd_sendreq_t *, const char *, const char *);
}
static void
-httpd_reset(void *arg) {
- isc_httpd_t *httpd = (isc_httpd_t *)arg;
+httpd_free(isc_httpd_t *httpd) {
isc_httpdmgr_t *httpdmgr = NULL;
REQUIRE(VALID_HTTPD(httpd));
httpd->path = NULL;
httpd->up = (isc_url_parser_t){ 0 };
isc_time_set(&httpd->if_modified_since, 0, 0);
+
+ httpd->magic = 0;
+ httpd->mgr = NULL;
+
+ isc_mem_put(httpdmgr->mctx, httpd, sizeof(*httpd));
+
+ httpdmgr_detach(&httpdmgr);
+
+#if ENABLE_AFL
+ if (finishhook != NULL) {
+ finishhook();
+ }
+#endif /* ENABLE_AFL */
}
static void
return (req);
}
-static void
-httpd_put(void *arg) {
- isc_httpd_t *httpd = (isc_httpd_t *)arg;
- isc_httpdmgr_t *mgr = NULL;
-
- REQUIRE(VALID_HTTPD(httpd));
-
- mgr = httpd->mgr;
- REQUIRE(VALID_HTTPDMGR(mgr));
-
- httpd->magic = 0;
- httpd->mgr = NULL;
-
- isc_mem_put(mgr->mctx, httpd, sizeof(*httpd));
-
- httpdmgr_detach(&mgr);
-
-#if ENABLE_AFL
- if (finishhook != NULL) {
- finishhook();
- }
-#endif /* ENABLE_AFL */
-}
-
static void
new_httpd(isc_httpdmgr_t *httpdmgr, isc_nmhandle_t *handle) {
isc_httpd_t *httpd = NULL;
REQUIRE(VALID_HTTPDMGR(httpdmgr));
- httpd = isc_nmhandle_getdata(handle);
- if (httpd == NULL) {
- httpd = isc_mem_get(httpdmgr->mctx, sizeof(*httpd));
- *httpd = (isc_httpd_t){ .handle = NULL };
- httpdmgr_attach(httpdmgr, &httpd->mgr);
- }
-
- if (httpd->handle == NULL) {
- isc_nmhandle_setdata(handle, httpd, httpd_reset, httpd_put);
- httpd->handle = handle;
- } else {
- INSIST(httpd->handle == handle);
- }
+ httpd = isc_mem_get(httpdmgr->mctx, sizeof(*httpd));
+ *httpd = (isc_httpd_t){
+ .magic = HTTPD_MAGIC,
+ .link = ISC_LINK_INITIALIZER,
+ };
- ISC_LINK_INIT(httpd, link);
+ isc_nmhandle_attach(handle, &httpd->handle);
- httpd->magic = HTTPD_MAGIC;
+ httpdmgr_attach(httpdmgr, &httpd->mgr);
LOCK(&httpdmgr->lock);
ISC_LIST_APPEND(httpdmgr->running, httpd, link);
UNLOCK(&httpdmgr->lock);
- isc_nmhandle_attach(httpd->handle, &httpd->readhandle);
- isc_nm_read(handle, httpd_request, httpdmgr);
+ isc_nm_read(handle, httpd_request, httpd);
}
static isc_result_t
static void
httpd_request(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *arg) {
- isc_result_t result;
- isc_httpdmgr_t *mgr = arg;
- isc_httpd_t *httpd = NULL;
+ isc_httpd_t *httpd = arg;
+ isc_httpdmgr_t *mgr = httpd->mgr;
isc_httpd_sendreq_t *req = NULL;
isc_region_t r;
size_t last_len = 0;
-
- httpd = isc_nmhandle_getdata(handle);
+ isc_result_t result;
REQUIRE(VALID_HTTPD(httpd));
goto close_readhandle;
}
- REQUIRE(httpd->readhandle == handle);
REQUIRE((mgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) == 0);
- isc_nm_read_stop(httpd->readhandle);
+ isc_nm_read_stop(handle);
/*
* If we are being called from httpd_senddone(), the last HTTP request
goto close_readhandle;
}
- /* Wait for more data, the readhandle is still attached */
+ /* Wait for more data, the handle is still attached */
isc_nm_read(handle, httpd_request, arg);
return;
}
*/
isc_buffer_usedregion(req->sendbuffer, &r);
- isc_nmhandle_attach(httpd->handle, &req->handle);
- isc_nm_send(httpd->handle, &r, httpd_senddone, req);
-
- isc_nmhandle_detach(&httpd->readhandle);
+ isc_nmhandle_ref(handle);
+ isc_nm_send(handle, &r, httpd_senddone, req);
return;
close_readhandle:
- isc_nm_read_stop(httpd->readhandle);
- isc_nmhandle_close(httpd->readhandle);
- isc_nmhandle_detach(&httpd->readhandle);
+ isc_nmhandle_close(httpd->handle);
+ isc_nmhandle_detach(&httpd->handle);
+
+ httpd_free(httpd);
}
void
httpd = ISC_LIST_HEAD(httpdmgr->running);
while (httpd != NULL) {
- if (httpd->readhandle != NULL) {
- httpd_request(httpd->readhandle, ISC_R_SHUTTINGDOWN,
- NULL, httpdmgr);
+ if (httpd->handle != NULL) {
+ httpd_request(httpd->handle, ISC_R_SUCCESS, NULL,
+ httpd);
}
httpd = ISC_LIST_NEXT(httpd, link);
}
goto detach;
}
- if (eresult == ISC_R_SUCCESS && (httpd->flags & HTTPD_CLOSE) == 0) {
- /*
- * Calling httpd_request() with region NULL restarts
- * reading.
- */
- isc_nmhandle_attach(handle, &httpd->readhandle);
- httpd_request(handle, ISC_R_SUCCESS, NULL, httpd->mgr);
- } else {
- isc_nmhandle_close(handle);
+ if (eresult == ISC_R_SUCCESS && (httpd->flags & HTTPD_CLOSE) != 0) {
+ eresult = ISC_R_EOF;
}
+ /*
+ * Calling httpd_request() with region NULL restarts
+ * reading.
+ */
+ httpd_request(handle, eresult, NULL, httpd);
+
detach:
isc_nmhandle_detach(&handle);
isc__httpd_sendreq_free(req);
#include <unistd.h>
#include <isc/mem.h>
+#include <isc/refcount.h>
#include <isc/region.h>
#include <isc/result.h>
#include <isc/tls.h>
*/
#if ISC_NETMGR_TRACE
-#define isc_nmhandle_attach(handle, dest) \
- isc__nmhandle_attach(handle, dest, __FILE__, __LINE__, __func__)
-#define isc_nmhandle_detach(handlep) \
- isc__nmhandle_detach(handlep, __FILE__, __LINE__, __func__)
-#define FLARG_PASS , file, line, func
-#define FLARG \
- , const char *file ISC_ATTR_UNUSED, unsigned int line ISC_ATTR_UNUSED, \
- const char *func ISC_ATTR_UNUSED
+#define isc_nmhandle_ref(ptr) \
+ isc_nmhandle__ref(ptr, __func__, __FILE__, __LINE__)
+#define isc_nmhandle_unref(ptr) \
+ isc_nmhandle__unref(ptr, __func__, __FILE__, __LINE__)
+#define isc_nmhandle_attach(ptr, ptrp) \
+ isc_nmhandle__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
+#define isc_nmhandle_detach(ptrp) \
+ isc_nmhandle__detach(ptrp, __func__, __FILE__, __LINE__)
+ISC_REFCOUNT_TRACE_DECL(isc_nmhandle);
#else
-#define isc_nmhandle_attach(handle, dest) isc__nmhandle_attach(handle, dest)
-#define isc_nmhandle_detach(handlep) isc__nmhandle_detach(handlep)
-#define FLARG_PASS
-#define FLARG
+ISC_REFCOUNT_DECL(isc_nmhandle);
#endif
-
-void
-isc__nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **dest FLARG);
-void
-isc__nmhandle_detach(isc_nmhandle_t **handlep FLARG);
/*%<
* Increment/decrement the reference counter in a netmgr handle.
*
* event loop. When references go to zero, the associated socket will be
* closed and deleted.
*/
-#undef FLARG
void *
isc_nmhandle_getdata(isc_nmhandle_t *handle);
* active handles with a result code of ISC_R_CANCELED.
*
* Requires:
- * \li 'sock' is a valid datagram-like netmgr socket
+ * \li 'handle' is a valid netmgr handle
* \li ...for which a read/recv callback has been defined.
*/
void
isc_nmhandle_close(isc_nmhandle_t *handle);
+/*%<
+ * Close the active handle - no further read callbacks will happen.
+ *
+ * Requires:
+ * 'li 'handle' is a valid netmgr handle
+ */
void
isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void
isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
- isc_nm_cb_t cb, void *cbarg, unsigned int timeout);
+ isc_nm_cb_t connect_cb, void *connect_cbarg,
+ unsigned int timeout);
/*%<
* Create a socket using netmgr 'mgr', bind it to the address 'local',
* and connect it to the address 'peer'.
void
isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
- isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx,
+ isc_nm_cb_t connect_cb, void *connect_cbarg,
+ isc_tlsctx_t *ctx,
isc_tlsctx_client_session_cache_t *client_sess_cache,
unsigned int timeout);
* Read callback from TLS socket.
*/
static void
-http_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
- void *data) {
+http_readcb(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result,
+ isc_region_t *region, void *data) {
isc_nm_http_session_t *session = (isc_nm_http_session_t *)data;
ssize_t readlen;
REQUIRE(VALID_HTTP2_SESSION(session));
- UNUSED(handle);
-
if (result != ISC_R_SUCCESS) {
if (result != ISC_R_TIMEDOUT) {
session->reading = false;
#if defined(__linux__)
#include <syscall.h>
-#define gettid() (uint32_t) syscall(SYS_gettid)
+#define gettid() (uint64_t) syscall(SYS_gettid)
+#elif defined(__FreeBSD__)
+#include <pthread_np.h>
+#define gettid() (uint64_t)(pthread_getthreadid_np())
+#elif defined(__OpenBSD__)
+#include <unistd.h>
+#define gettid() (uint64_t)(getthrid())
+#elif defined(__NetBSD__)
+#include <lwp.h>
+#define gettid() (uint64_t)(_lwp_self())
+#elif defined(__DragonFly__)
+#include <unistd.h>
+#define gettid() (uint64_t)(lwp_gettid())
#else
-#define gettid() (uint32_t) pthread_self()
+#define gettid() (uint64_t)(pthread_self())
#endif
#define NETMGR_TRACE_LOG(format, ...) \
- fprintf(stderr, "%" PRIu32 ":%d:%s:%u:%s:" format, gettid(), \
+ fprintf(stderr, "%" PRIu64 ":%d:%s:%u:%s:" format, gettid(), \
isc_tid(), file, line, func, __VA_ARGS__)
-#define FLARG \
- , const char *file ISC_ATTR_UNUSED, unsigned int line ISC_ATTR_UNUSED, \
- const char *func ISC_ATTR_UNUSED
-#define FLARG_PASS , file, line, func
-#define FLARG_IEVENT(ievent) \
- const char *file = ievent->file; \
- unsigned int line = ievent->line; \
- const char *func = ievent->func;
-#define FLARG_IEVENT_PASS(ievent) \
- ievent->file = file; \
- ievent->line = line; \
- ievent->func = func;
+#define FLARG \
+ , const char *func ISC_ATTR_UNUSED, const char *file ISC_ATTR_UNUSED, \
+ unsigned int line ISC_ATTR_UNUSED
+
+#define FLARG_PASS , func, file, line
#define isc__nm_uvreq_get(sock) \
- isc___nm_uvreq_get(sock, __FILE__, __LINE__, __func__)
+ isc___nm_uvreq_get(sock, __func__, __FILE__, __LINE__)
#define isc__nm_uvreq_put(req) \
- isc___nm_uvreq_put(req, __FILE__, __LINE__, __func__)
+ isc___nm_uvreq_put(req, __func__, __FILE__, __LINE__)
#define isc__nmsocket_init(sock, mgr, type, iface, parent) \
- isc___nmsocket_init(sock, mgr, type, iface, parent, __FILE__, \
- __LINE__, __func__)
+ isc___nmsocket_init(sock, mgr, type, iface, parent, __func__, \
+ __FILE__, __LINE__)
#define isc__nmsocket_put(sockp) \
- isc___nmsocket_put(sockp, __FILE__, __LINE__, __func__)
+ isc___nmsocket_put(sockp, __func__, __FILE__, __LINE__)
#define isc__nmsocket_attach(sock, target) \
- isc___nmsocket_attach(sock, target, __FILE__, __LINE__, __func__)
+ isc___nmsocket_attach(sock, target, __func__, __FILE__, __LINE__)
#define isc__nmsocket_detach(socketp) \
- isc___nmsocket_detach(socketp, __FILE__, __LINE__, __func__)
+ isc___nmsocket_detach(socketp, __func__, __FILE__, __LINE__)
#define isc__nmsocket_close(socketp) \
- isc___nmsocket_close(socketp, __FILE__, __LINE__, __func__)
+ isc___nmsocket_close(socketp, __func__, __FILE__, __LINE__)
#define isc__nmhandle_get(sock, peer, local) \
- isc___nmhandle_get(sock, peer, local, __FILE__, __LINE__, __func__)
+ isc___nmhandle_get(sock, peer, local, __func__, __FILE__, __LINE__)
#define isc__nmsocket_prep_destroy(sock) \
- isc___nmsocket_prep_destroy(sock, __FILE__, __LINE__, __func__)
+ isc___nmsocket_prep_destroy(sock, __func__, __FILE__, __LINE__)
+#define isc__nm_get_read_req(sock, sockaddr) \
+ isc___nm_get_read_req(sock, sockaddr, __func__, __FILE__, __LINE__)
#else
#define NETMGR_TRACE_LOG(format, ...)
#define FLARG
#define FLARG_PASS
-#define FLARG_IEVENT(ievent)
-#define FLARG_IEVENT_PASS(ievent)
#define isc__nm_uvreq_get(sock) isc___nm_uvreq_get(sock)
#define isc__nm_uvreq_put(req) isc___nm_uvreq_put(req)
#define isc__nmsocket_init(sock, mgr, type, iface, parent) \
#define isc__nmhandle_get(sock, peer, local) \
isc___nmhandle_get(sock, peer, local)
#define isc__nmsocket_prep_destroy(sock) isc___nmsocket_prep_destroy(sock)
+#define isc__nm_get_read_req(sock, sockaddr) \
+ isc___nm_get_read_req(sock, sockaddr)
#endif
typedef struct isc__nm_uvreq isc__nm_uvreq_t;
size_t nsending;
bool tcp_nodelay_value;
isc_nmsocket_tls_send_req_t *send_req; /*%< Send req to reuse */
+ bool reading;
} tlsstream;
#if HAVE_LIBNGHTTP2
isc_nmhandle_t *recv_handle;
isc_nm_recv_cb_t recv_cb;
void *recv_cbarg;
- bool recv_read;
isc_nm_cb_t connect_cb;
void *connect_cbarg;
* Close a UDP socket.
*/
-void
-isc__nm_udp_cancelread(isc_nmhandle_t *handle);
-/*%<
- * Stop reading on a connected UDP handle.
- */
-
void
isc__nm_udp_shutdown(isc_nmsocket_t *sock);
/*%<
* sockets.
*/
-void
-isc__nm_tcp_cancelread(isc_nmhandle_t *handle);
-/*%<
- * Stop reading on a connected TCP handle.
- */
-
void
isc__nm_tcp_stoplistening(isc_nmsocket_t *sock);
/*%<
void
isc__nm_streamdns_cleanup_data(isc_nmsocket_t *sock);
-void
-isc__nm_streamdns_cancelread(isc_nmhandle_t *handle);
-
void
isc__nmhandle_streamdns_cleartimeout(isc_nmhandle_t *handle);
bool async);
isc__nm_uvreq_t *
-isc__nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr);
+isc___nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr FLARG);
void
isc__nm_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf);
}
}
-void
-isc_nmhandle_close(isc_nmhandle_t *handle) {
- REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(VALID_NMSOCK(handle->sock));
-
- isc__nmsocket_clearcb(handle->sock);
- isc__nm_failed_read_cb(handle->sock, ISC_R_EOF, false);
-}
-
void
isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) {
REQUIRE(sock->parent == NULL);
return (handle);
}
-void
-isc__nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **handlep FLARG) {
- REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(handlep != NULL && *handlep == NULL);
-
- NETMGR_TRACE_LOG("isc__nmhandle_attach():handle %p->references = "
- "%" PRIuFAST32 "\n",
- handle, isc_refcount_current(&handle->references) + 1);
-
- isc_refcount_increment(&handle->references);
- *handlep = handle;
-}
-
bool
isc_nmhandle_is_stream(isc_nmhandle_t *handle) {
REQUIRE(VALID_NMHANDLE(handle));
handle);
}
-void
-isc__nmhandle_detach(isc_nmhandle_t **handlep FLARG) {
- isc_nmhandle_t *handle = NULL;
-
- REQUIRE(handlep != NULL);
- REQUIRE(VALID_NMHANDLE(*handlep));
-
- handle = *handlep;
- *handlep = NULL;
-
- REQUIRE(handle->sock->tid == isc_tid());
-
- NETMGR_TRACE_LOG("isc__nmhandle_detach():%p->references = %" PRIuFAST32
- "\n",
- handle, isc_refcount_current(&handle->references) - 1);
-
- if (isc_refcount_decrement(&handle->references) == 1) {
- nmhandle_destroy(handle);
- }
-}
+#if ISC_NETMGR_TRACE
+ISC_REFCOUNT_TRACE_IMPL(isc_nmhandle, nmhandle_destroy)
+#else
+ISC_REFCOUNT_IMPL(isc_nmhandle, nmhandle_destroy);
+#endif
void *
isc_nmhandle_getdata(isc_nmhandle_t *handle) {
*/
REQUIRE(!sock->timedout);
sock->timedout = true;
- isc__nmsocket_clearcb(sock);
isc__nmsocket_shutdown(sock);
}
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_tid());
- REQUIRE(sock->reading);
if (sock->client) {
uv_timer_stop(timer);
- sock->recv_read = false;
-
if (sock->recv_cb != NULL) {
isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
isc__nm_readcb(sock, req, ISC_R_TIMEDOUT, false);
if (!isc__nmsocket_timer_running(sock)) {
isc__nmsocket_clearcb(sock);
- isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
+ isc__nm_failed_read_cb(sock, ISC_R_TIMEDOUT, false);
}
} else {
isc__nm_failed_read_cb(sock, ISC_R_TIMEDOUT, false);
}
isc__nm_uvreq_t *
-isc__nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr) {
+isc___nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr FLARG) {
isc__nm_uvreq_t *req = NULL;
req = isc__nm_uvreq_get(sock);
switch (sock->type) {
case isc_nm_tcpsocket:
case isc_nm_tlssocket:
+#if ISC_NETMGR_TRACE
+ isc_nmhandle__attach(sock->statichandle,
+ &req->handle FLARG_PASS);
+#else
isc_nmhandle_attach(sock->statichandle, &req->handle);
+#endif
break;
case isc_nm_streamdnssocket:
+#if ISC_NETMGR_TRACE
+ isc_nmhandle__attach(sock->recv_handle,
+ &req->handle FLARG_PASS);
+#else
isc_nmhandle_attach(sock->recv_handle, &req->handle);
+#endif
break;
default:
if (sock->client && sock->statichandle != NULL) {
+#if ISC_NETMGR_TRACE
+ isc_nmhandle__attach(sock->statichandle,
+ &req->handle FLARG_PASS);
+#else
isc_nmhandle_attach(sock->statichandle, &req->handle);
+#endif
} else {
- req->handle = isc__nmhandle_get(sock, sockaddr, NULL);
+ req->handle = isc___nmhandle_get(sock, sockaddr,
+ NULL FLARG_PASS);
}
break;
}
isc_result_t result = ISC_R_SUCCESS;
int r;
- if (sock->reading) {
+ if (uv_is_active(&sock->uv_handle.handle)) {
return (ISC_R_SUCCESS);
}
}
if (r != 0) {
result = isc_uverr2result(r);
- } else {
- sock->reading = true;
}
return (result);
isc__nm_stop_reading(isc_nmsocket_t *sock) {
int r;
- if (!sock->reading) {
+ if (!uv_is_active(&sock->uv_handle.handle)) {
return;
}
default:
UNREACHABLE();
}
- sock->reading = false;
}
bool
ISC_LIST_UNLINK(sock->active_uvreqs, req, active_link);
if (handle != NULL) {
- isc__nmhandle_detach(&handle FLARG_PASS);
+#if ISC_NETMGR_TRACE
+ isc_nmhandle__detach(&handle, func, file, line);
+#else
+ isc_nmhandle_detach(&handle);
+#endif
}
isc_mempool_put(sock->worker->uvreq_pool, req);
}
}
-void
-isc_nm_cancelread(isc_nmhandle_t *handle) {
+static void
+cancelread_cb(void *arg) {
+ isc_nmhandle_t *handle = arg;
+
REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->tid == isc_tid());
+
+ REQUIRE(handle->sock->tid == isc_tid());
switch (handle->sock->type) {
case isc_nm_udpsocket:
- isc__nm_udp_cancelread(handle);
- break;
case isc_nm_streamdnssocket:
- isc__nm_streamdns_cancelread(handle);
+ case isc_nm_httpsocket:
+ isc__nm_failed_read_cb(handle->sock, ISC_R_CANCELED, false);
break;
default:
UNREACHABLE();
}
+
+ isc_nmhandle_detach(&handle);
+}
+
+void
+isc_nm_cancelread(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ /* Running this directly could cause a dead-lock */
+ isc_nmhandle_ref(handle);
+ isc_async_run(handle->sock->worker->loop, cancelread_cb, handle);
}
void
}
}
+void
+isc_nmhandle_close(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ isc__nmsocket_clearcb(handle->sock);
+ isc__nmsocket_prep_destroy(handle->sock);
+}
+
void
isc_nm_stoplistening(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
"Parent %p, listener %p, server %p, statichandle = "
"%p\n",
sock->parent, sock->listener, sock->server, sock->statichandle);
- fprintf(stderr, "Flags:%s%s%s%s%s\n",
- atomic_load_acquire(&sock->active) ? " active" : "",
+ fprintf(stderr, "Flags:%s%s%s%s%s\n", sock->active ? " active" : "",
sock->closing ? " closing" : "",
sock->destroying ? " destroying" : "",
sock->connecting ? " connecting" : "",
*/
bool stop = sock->client;
- sock->recv_read = false;
+ sock->reading = false;
if (sock->recv_cb != NULL) {
if (!sock->client) {
/*
streamdns_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
const isc_result_t result) {
sock->connecting = false;
- if (sock->connect_cb == NULL) {
- return;
- }
+ INSIST(sock->connect_cb != NULL);
sock->connect_cb(handle, result, sock->connect_cbarg);
if (result != ISC_R_SUCCESS) {
isc__nmsocket_clearcb(handle->sock);
}
}
-static bool
-streamdns_waiting_for_msg(isc_nmsocket_t *sock) {
- /* There is an unsatisfied read operation pending */
- return (sock->recv_read);
-}
-
bool
isc__nmsocket_streamdns_timer_running(isc_nmsocket_t *sock) {
isc_nmsocket_t *transp_sock;
static void
streamdns_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result,
const bool async) {
- bool destroy = true;
-
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(result != ISC_R_SUCCESS);
- if (sock->recv_cb != NULL && sock->recv_handle != NULL &&
- (streamdns_waiting_for_msg(sock) || result == ISC_R_TIMEDOUT))
- {
- isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
-
- INSIST(VALID_NMHANDLE(sock->recv_handle));
+ /* Nobody is reading from the socket yet */
+ if (sock->recv_handle == NULL) {
+ goto destroy;
+ }
- if (result != ISC_R_TIMEDOUT) {
- sock->recv_read = false;
- isc_dnsstream_assembler_clear(sock->streamdns.input);
- isc__nmsocket_clearcb(sock);
- } else if (sock->client) {
- sock->recv_read = false;
+ if (sock->client && result == ISC_R_TIMEDOUT) {
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nm_readcb(sock, req, ISC_R_TIMEDOUT, false);
}
- isc__nm_readcb(sock, req, result, async);
- if (result == ISC_R_TIMEDOUT &&
- isc__nmsocket_streamdns_timer_running(sock))
- {
- destroy = false;
+
+ if (isc__nmsocket_timer_running(sock)) {
+ /* Timer was restarted, bail-out */
+ return;
}
+
+ isc__nmsocket_clearcb(sock);
+
+ goto destroy;
}
- if (destroy) {
- isc__nmsocket_prep_destroy(sock);
+ isc_dnsstream_assembler_clear(sock->streamdns.input);
+
+ /* Nobody expects the callback if isc_nm_read() wasn't called */
+ if (!sock->client || sock->reading) {
+ sock->reading = false;
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nmsocket_clearcb(sock);
+ isc__nm_readcb(sock, req, result, async);
+ }
}
+
+destroy:
+ isc__nmsocket_prep_destroy(sock);
}
void
* The socket is unused after calling the callback. Let's close
* the underlying connection.
*/
+ /* FIXME: call failed_read_cb(?) */
+ if (sock->outerhandle != NULL) {
+ isc_nmhandle_detach(&sock->outerhandle);
+ }
isc__nmsocket_prep_destroy(sock);
}
}
sock->recv_cb = cb;
sock->recv_cbarg = cbarg;
- sock->recv_read = true;
+ sock->reading = true;
isc_nmhandle_attach(handle, &sock->recv_handle);
/*
isc__nmsocket_stop(sock);
}
-static void
-streamdns_cancelread_cb(void *arg) {
- isc_nmsocket_t *sock = arg;
- REQUIRE(VALID_NMSOCK(sock));
-
- streamdns_failed_read_cb(sock, ISC_R_EOF, false);
- isc__nmsocket_detach(&sock);
-}
-
-void
-isc__nm_streamdns_cancelread(isc_nmhandle_t *handle) {
- isc_nmsocket_t *sock = NULL;
-
- REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(VALID_NMSOCK(handle->sock));
- REQUIRE(handle->sock->type == isc_nm_streamdnssocket);
-
- sock = handle->sock;
-
- isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
- isc_async_run(sock->worker->loop, streamdns_cancelread_cb, sock);
-}
-
void
isc__nmhandle_streamdns_cleartimeout(isc_nmhandle_t *handle) {
isc_nmsocket_t *sock = NULL;
REQUIRE(VALID_UVREQ(req));
REQUIRE(VALID_NMHANDLE(req->handle));
- if (sock->timedout) {
+ INSIST(sock->connecting);
+
+ if (sock->timedout || status == UV_ETIMEDOUT) {
+ /* Connection timed-out */
result = ISC_R_TIMEDOUT;
goto error;
- } else if (!sock->connecting) {
- /*
- * The connect was cancelled from timeout; just clean up
- * the req.
- */
- isc__nm_uvreq_put(&req);
- return;
} else if (isc__nm_closing(worker)) {
/* Network manager shutting down */
result = ISC_R_SHUTTINGDOWN;
/* Connection canceled */
result = ISC_R_CANCELED;
goto error;
- } else if (status == UV_ETIMEDOUT) {
- /* Timeout status code here indicates hard error */
- result = ISC_R_TIMEDOUT;
- goto error;
} else if (status == UV_EADDRINUSE) {
/*
* On FreeBSD the TCP connect() call sometimes results in a
void
isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
- isc_nm_cb_t cb, void *cbarg, unsigned int timeout) {
+ isc_nm_cb_t connect_cb, void *connect_cbarg,
+ unsigned int timeout) {
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *sock = NULL;
isc__nm_uvreq_t *req = NULL;
REQUIRE(peer != NULL);
if (isc__nm_closing(worker)) {
- cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
+ connect_cb(NULL, ISC_R_SHUTTINGDOWN, connect_cbarg);
return;
}
result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &fd);
if (result != ISC_R_SUCCESS) {
- cb(NULL, result, cbarg);
+ connect_cb(NULL, result, connect_cbarg);
return;
}
sock->client = true;
req = isc__nm_uvreq_get(sock);
- req->cb.connect = cb;
- req->cbarg = cbarg;
+ req->cb.connect = connect_cb;
+ req->cbarg = connect_cbarg;
req->peer = *peer;
req->local = *local;
req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
/* Stop the parent */
sock->closed = true;
+
isc__nmsocket_prep_destroy(sock);
}
isc__nmsocket_timer_stop(sock);
isc__nm_stop_reading(sock);
- if (!sock->recv_read) {
- goto destroy;
- }
- sock->recv_read = false;
-
if (sock->recv_cb != NULL) {
isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
isc__nmsocket_clearcb(sock);
isc__nm_readcb(sock, req, result, async);
}
-destroy:
isc__nmsocket_prep_destroy(sock);
}
sock->recv_cb = cb;
sock->recv_cbarg = cbarg;
- sock->recv_read = true;
/* Initialize the timer */
if (sock->read_timeout == 0) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_tid());
- REQUIRE(sock->reading);
REQUIRE(buf != NULL);
netmgr = sock->worker->netmgr;
*/
isc_nmhandle_detach(&handle);
+ if (csock->statichandle != NULL) {
+ INSIST(csock->recv_cb != NULL);
+ }
+
/*
* sock is now attached to the handle.
*/
void
isc__nm_tcp_shutdown(isc_nmsocket_t *sock) {
- isc__networker_t *worker = NULL;
-
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_tid());
REQUIRE(sock->type == isc_nm_tcpsocket);
- worker = sock->worker;
-
/*
* If the socket is active, mark it inactive and
* continue. If it isn't active, stop now.
}
sock->active = false;
- if (sock->accepting) {
- return;
- }
+ INSIST(!sock->accepting);
if (sock->connecting) {
isc_nmsocket_t *tsock = NULL;
return;
}
- if (sock->statichandle != NULL) {
- if (isc__nm_closing(worker)) {
- isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false);
- } else {
- isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
- }
+ /* There's a handle attached to the socket (from accept or connect) */
+ if (sock->statichandle) {
+ isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false);
return;
}
REQUIRE(sock->type == isc_nm_tcpsocket);
REQUIRE(sock->tid == isc_tid());
REQUIRE(!sock->reading);
- REQUIRE(!sock->recv_read);
sock->manual_read_timer = manual;
}
#define TLS_MAX_SEND_BUF_SIZE (UINT16_MAX + UINT16_MAX / 2)
+#ifdef ISC_NETMGR_TRACE
+ISC_ATTR_UNUSED static const char *
+tls_status2str(int tls_status) {
+ switch (tls_status) {
+ case SSL_ERROR_NONE:
+ return ("SSL_ERROR_NONE");
+ case SSL_ERROR_ZERO_RETURN:
+ return ("SSL_ERROR_ZERO_RETURN");
+ case SSL_ERROR_WANT_WRITE:
+ return ("SSL_ERROR_WANT_WRITE");
+ case SSL_ERROR_WANT_READ:
+ return ("SSL_ERROR_WANT_READ");
+ case SSL_ERROR_SSL:
+ return ("SSL_ERROR_SSL");
+ default:
+ UNREACHABLE();
+ }
+}
+
+ISC_ATTR_UNUSED static const char *
+state2str(int state) {
+ switch (state) {
+ case TLS_INIT:
+ return ("TLS_INIT");
+ case TLS_HANDSHAKE:
+ return ("TLS_HANDSHAKE");
+ case TLS_IO:
+ return ("TLS_IO");
+ case TLS_CLOSED:
+ return ("TLS_CLOSED");
+ default:
+ UNREACHABLE();
+ }
+}
+#endif /* ISC_NETMGR_TRACE */
+
static isc_result_t
tls_error_to_result(const int tls_err, const int tls_state, isc_tls_t *tls) {
switch (tls_err) {
}
}
+static void
+tls_read_start(isc_nmsocket_t *restrict sock);
+
static void
tls_read_stop(isc_nmsocket_t *sock);
tls_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
void *cbarg);
-static void
-tls_close_direct(void *arg);
-
static void
async_tls_do_bio(isc_nmsocket_t *sock);
static void
tls_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
const isc_result_t result) {
- if (sock->connect_cb == NULL) {
- return;
- }
+ INSIST(sock->connect_cb != NULL);
sock->connect_cb(handle, result, sock->connect_cbarg);
if (result != ISC_R_SUCCESS) {
isc__nmsocket_clearcb(handle->sock);
}
}
- if (finish && eresult == ISC_R_SUCCESS && tlssock->reading) {
+ if (finish) {
+ /*
+ * If wrapping up, call tls_failed_read() - it will care of
+ * socket de-initialisation and calling the read callback, if
+ * necessary.
+ */
tls_failed_read_cb(tlssock, ISC_R_EOF);
} else if (eresult == ISC_R_SUCCESS) {
tls_do_bio(tlssock, NULL, NULL, false);
}
static void
-tls_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result) {
- bool destroy = true;
-
+tls_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(result != ISC_R_SUCCESS);
+ /* This is TLS counterpart of isc__nm_failed_connect_cb() */
if (!sock->tlsstream.server &&
(sock->tlsstream.state == TLS_INIT ||
sock->tlsstream.state == TLS_HANDSHAKE) &&
tls_call_connect_cb(sock, handle, result);
isc__nmsocket_clearcb(sock);
isc_nmhandle_detach(&handle);
- } else if (sock->reading && sock->recv_cb != NULL &&
- sock->statichandle != NULL &&
- (sock->recv_read || result == ISC_R_TIMEDOUT))
- {
- sock->recv_read = false;
- sock->recv_cb(sock->statichandle, result, NULL,
- sock->recv_cbarg);
- if (result == ISC_R_TIMEDOUT &&
- (sock->outerhandle == NULL ||
- isc__nmsocket_timer_running(sock->outerhandle->sock)))
- {
- destroy = false;
+ goto destroy;
+ }
+
+ isc__nmsocket_timer_stop(sock);
+
+ /* Nobody is reading from the socket yet */
+ if (sock->statichandle == NULL) {
+ goto destroy;
+ }
+
+ /* This is TLS counterpart of isc__nmsocket_readtimeout_cb() */
+ if (sock->client && result == ISC_R_TIMEDOUT) {
+ INSIST(sock->statichandle != NULL);
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nm_readcb(sock, req, ISC_R_TIMEDOUT, false);
+ }
+
+ if (isc__nmsocket_timer_running(sock)) {
+ /* Timer was restarted, bail-out */
+ return;
}
+
+ isc__nmsocket_clearcb(sock);
+
+ goto destroy;
}
- if (destroy) {
- isc__nmsocket_prep_destroy(sock);
+ /*
+ * We don't need to check for .nsending, as the callbacks will be
+ * cleared at the time the tls_senddone() tries to call it for the
+ * second time.
+ */
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nmsocket_clearcb(sock);
+ isc__nm_readcb(sock, req, result, false);
}
+
+destroy:
+ isc__nmsocket_prep_destroy(sock);
}
void
isc__nm_tls_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result,
- bool async) {
- UNUSED(async);
-
+ bool async ISC_ATTR_UNUSED) {
if (!inactive(sock) && sock->tlsstream.state == TLS_IO) {
tls_do_bio(sock, NULL, NULL, true);
- } else if (sock->recv_read) {
- tls_read_stop(sock);
- tls_failed_read_cb(sock, result);
+ return;
}
+
+ tls_failed_read_cb(sock, result);
}
static void
int rv = 0;
size_t len = 0;
int saved_errno = 0;
- bool was_reading;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_tid());
- was_reading = sock->reading;
-
/*
* Clear the TLS error queue so that SSL_get_error() and SSL I/O
* routine calls will not get affected by prior error statuses.
/* Decrypt and pass data from network to client */
if (sock->tlsstream.state >= TLS_IO && sock->recv_cb != NULL &&
- was_reading && sock->statichandle != NULL && !finish)
+ sock->statichandle != NULL && sock->reading && !finish)
{
bool was_new_data = false;
uint8_t recv_buf[TLS_BUF_SIZE];
pending = tls_process_outgoing(sock, finish, send_data);
if (pending > 0 && tls_status != SSL_ERROR_SSL) {
- /* We'll continue in tls_senddone */
return;
}
{
return;
} else if (sock->reading == false &&
- sock->tlsstream.state > TLS_HANDSHAKE)
+ sock->tlsstream.state == TLS_HANDSHAKE)
{
- /* We need to read data when doing handshake even if
- * 'sock->reading == false' */
+ /*
+ * We need to read data when doing handshake even if
+ * 'sock->reading == false'. It will be stopped when
+ * handshake is completed.
+ */
+ tls_read_start(sock);
+ return;
+ } else if (sock->reading == false) {
return;
}
- INSIST(VALID_NMHANDLE(sock->outerhandle));
-
- isc_nm_read(sock->outerhandle, tls_readcb, sock);
- if (!sock->manual_read_timer) {
- isc__nmsocket_timer_start(sock);
- }
+ tls_read_start(sock);
return;
default:
result = tls_error_to_result(tls_status, sock->tlsstream.state,
return;
}
+ REQUIRE(handle == tlssock->outerhandle);
tls_do_bio(tlssock, region, NULL, false);
}
sock->recv_cb = cb;
sock->recv_cbarg = cbarg;
- sock->recv_read = true;
sock->reading = true;
async_tls_do_bio(sock);
}
static void
-tls_read_stop(isc_nmsocket_t *sock) {
- sock->reading = false;
+tls_read_start(isc_nmsocket_t *restrict sock) {
+ if (sock->tlsstream.reading) {
+ return;
+ }
+ sock->tlsstream.reading = true;
+
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+
+ isc_nm_read(sock->outerhandle, tls_readcb, sock);
+ if (!sock->manual_read_timer) {
+ isc__nmsocket_timer_start(sock);
+ }
+}
+static void
+tls_read_stop(isc_nmsocket_t *sock) {
+ sock->tlsstream.reading = false;
if (sock->outerhandle != NULL) {
isc_nm_read_stop(sock->outerhandle);
}
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
+ handle->sock->reading = false;
+
tls_read_stop(handle->sock);
}
-static void
-tls_close_direct(void *arg) {
- isc_nmsocket_t *sock = arg;
+void
+isc__nm_tls_close(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlssocket);
+ REQUIRE(!sock->closing);
REQUIRE(sock->tid == isc_tid());
+ REQUIRE(!sock->closed);
+ REQUIRE(!sock->closing);
+
+ sock->closing = true;
+
/*
* At this point we're certain that there are no
* external references, we can close everything.
*/
+ tls_read_stop(sock);
if (sock->outerhandle != NULL) {
- sock->reading = false;
isc_nm_read_stop(sock->outerhandle);
-
isc_nmhandle_close(sock->outerhandle);
isc_nmhandle_detach(&sock->outerhandle);
}
sock->tlsstream.state = TLS_CLOSED;
}
-void
-isc__nm_tls_close(isc_nmsocket_t *sock) {
- REQUIRE(VALID_NMSOCK(sock));
- REQUIRE(sock->type == isc_nm_tlssocket);
- REQUIRE(!sock->closing);
-
- sock->closing = true;
-
- if (sock->tid == isc_tid()) {
- /* no point in attempting to make the call asynchronous */
- tls_close_direct(sock);
- } else {
- isc_async_run(sock->worker->loop, tls_close_direct, sock);
- }
-}
-
void
isc__nm_tls_stoplistening(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
void
isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
- isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx,
+ isc_nm_cb_t connect_cb, void *connect_cbarg,
+ isc_tlsctx_t *ctx,
isc_tlsctx_client_session_cache_t *client_sess_cache,
unsigned int timeout) {
- isc_nmsocket_t *nsock = NULL;
+ isc_nmsocket_t *sock = NULL;
isc__networker_t *worker = &mgr->workers[isc_tid()];
REQUIRE(VALID_NM(mgr));
if (isc__nm_closing(worker)) {
- cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
+ connect_cb(NULL, ISC_R_SHUTTINGDOWN, connect_cbarg);
return;
}
- nsock = isc_mem_get(worker->mctx, sizeof(*nsock));
- isc__nmsocket_init(nsock, worker, isc_nm_tlssocket, local, NULL);
- nsock->connect_cb = cb;
- nsock->connect_cbarg = cbarg;
- nsock->connect_timeout = timeout;
- isc_tlsctx_attach(ctx, &nsock->tlsstream.ctx);
- nsock->client = true;
+ sock = isc_mem_get(worker->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, worker, isc_nm_tlssocket, local, NULL);
+ sock->connect_cb = connect_cb;
+ sock->connect_cbarg = connect_cbarg;
+ sock->connect_timeout = timeout;
+ isc_tlsctx_attach(ctx, &sock->tlsstream.ctx);
+ sock->client = true;
if (client_sess_cache != NULL) {
INSIST(isc_tlsctx_client_session_cache_getctx(
client_sess_cache) == ctx);
isc_tlsctx_client_session_cache_attach(
- client_sess_cache, &nsock->tlsstream.client_sess_cache);
+ client_sess_cache, &sock->tlsstream.client_sess_cache);
}
- isc_nm_tcpconnect(mgr, local, peer, tcp_connected, nsock,
- nsock->connect_timeout);
+ isc_nm_tcpconnect(mgr, local, peer, tcp_connected, sock,
+ sock->connect_timeout);
}
static void
goto done;
}
- sock->reading = true;
done:
result = isc_uverr2result(r);
req->uvbuf.base = buf->base;
req->uvbuf.len = nrecv;
- sock->recv_read = false;
+ sock->reading = false;
/*
* The client isc_nm_read() expects just a single message, so we need to
if (sock->client) {
isc__nmsocket_timer_stop(sock);
isc__nm_stop_reading(sock);
+ isc__nmsocket_clearcb(sock);
}
REQUIRE(!sock->processing);
REQUIRE(result != ISC_R_SUCCESS);
REQUIRE(sock->tid == isc_tid());
+ /*
+ * For UDP server socket, we don't have child socket via
+ * "accept", so we:
+ * - we continue to read
+ * - we don't clear the callbacks
+ * - we don't destroy it (only stoplistening could do that)
+ */
+
if (sock->client) {
isc__nmsocket_timer_stop(sock);
isc__nm_stop_reading(sock);
+ }
- /* Nobody expects the callback if isc_nm_read() wasn't called */
- if (!sock->recv_read) {
- goto destroy;
- }
+ /* Nobody expects the callback if isc_nm_read() wasn't called */
+ if (sock->reading) {
+ sock->reading = false;
if (sock->recv_cb != NULL) {
isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
- isc__nmsocket_clearcb(sock);
isc__nm_readcb(sock, req, result, async);
}
-
- sock->recv_read = false;
-
- destroy:
- isc__nmsocket_prep_destroy(sock);
- return;
}
- /*
- * For UDP server socket, we don't have child socket via
- * "accept", so we:
- * - we continue to read
- * - we don't clear the callbacks
- * - we don't destroy it (only stoplistening could do that)
- */
- if (!sock->recv_read) {
+ if (sock->client) {
+ isc__nmsocket_clearcb(sock);
+ isc__nmsocket_prep_destroy(sock);
return;
}
- sock->recv_read = false;
-
- if (sock->recv_cb != NULL) {
- isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
- isc__nm_readcb(sock, req, result, async);
- }
}
void
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_udpsocket);
REQUIRE(sock->statichandle == handle);
- REQUIRE(!sock->recv_read);
REQUIRE(sock->tid == isc_tid());
/*
*/
sock->recv_cb = cb;
sock->recv_cbarg = cbarg;
- sock->recv_read = true;
+ sock->reading = true;
if (isc__nm_closing(sock->worker)) {
result = ISC_R_SHUTTINGDOWN;
void
isc__nm_udp_shutdown(isc_nmsocket_t *sock) {
- isc__networker_t *worker = NULL;
-
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_tid());
REQUIRE(sock->type == isc_nm_udpsocket);
- worker = sock->worker;
-
/*
* If the socket is active, mark it inactive and
* continue. If it isn't active, stop now.
* interested in the callback.
*/
if (sock->statichandle != NULL) {
- if (isc__nm_closing(worker)) {
- isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false);
- } else {
- isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
- }
+ isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false);
return;
}
isc__nmsocket_prep_destroy(sock->parent);
}
}
-
-static void
-udp_cancelread_cb(void *arg) {
- isc_nmsocket_t *sock = arg;
-
- REQUIRE(VALID_NMSOCK(sock));
- REQUIRE(sock->tid == isc_tid());
- REQUIRE(sock->client);
-
- isc__nm_failed_read_cb(sock, ISC_R_EOF, false);
- isc__nmsocket_detach(&sock);
-}
-
-void
-isc__nm_udp_cancelread(isc_nmhandle_t *handle) {
- REQUIRE(VALID_NMHANDLE(handle));
-
- isc_nmsocket_t *sock = handle->sock;
-
- REQUIRE(VALID_NMSOCK(sock));
- REQUIRE(sock->type == isc_nm_udpsocket);
-
- isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
- isc_async_run(sock->worker->loop, udp_cancelread_cb, sock);
-}
recv_data(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
void *arg) {
isccc_ccmsg_t *ccmsg = arg;
- size_t size;
- INSIST(VALID_CCMSG(ccmsg));
+ REQUIRE(VALID_CCMSG(ccmsg));
- switch (eresult) {
- case ISC_R_SHUTTINGDOWN:
- case ISC_R_CANCELED:
- case ISC_R_EOF:
- ccmsg->result = eresult;
- goto done;
- case ISC_R_SUCCESS:
- if (region == NULL) {
- ccmsg->result = ISC_R_EOF;
- goto done;
- }
- ccmsg->result = ISC_R_SUCCESS;
- break;
- default:
- ccmsg->result = eresult;
+ REQUIRE(handle == ccmsg->handle);
+ if (eresult != ISC_R_SUCCESS) {
goto done;
}
+ REQUIRE(region != NULL);
+
if (!ccmsg->length_received) {
if (region->length < sizeof(uint32_t)) {
- ccmsg->result = ISC_R_UNEXPECTEDEND;
+ eresult = ISC_R_UNEXPECTEDEND;
goto done;
}
ccmsg->size = ntohl(*(uint32_t *)region->base);
if (ccmsg->size == 0) {
- ccmsg->result = ISC_R_UNEXPECTEDEND;
+ eresult = ISC_R_UNEXPECTEDEND;
goto done;
}
if (ccmsg->size > ccmsg->maxsize) {
- ccmsg->result = ISC_R_RANGE;
+ eresult = ISC_R_RANGE;
goto done;
}
/* We have some data in the buffer, read it */
- size = ISC_MIN(isc_buffer_availablelength(ccmsg->buffer),
- region->length);
+ size_t size = ISC_MIN(isc_buffer_availablelength(ccmsg->buffer),
+ region->length);
isc_buffer_putmem(ccmsg->buffer, region->base, size);
isc_region_consume(region, size);
if (isc_buffer_usedlength(ccmsg->buffer) == ccmsg->size) {
- ccmsg->result = ISC_R_SUCCESS;
goto done;
}
done:
isc_nm_read_stop(handle);
- ccmsg->cb(handle, ccmsg->result, ccmsg->cbarg);
+ if (ccmsg->reading) {
+ ccmsg->reading = false;
+ ccmsg->recv_cb(handle, eresult, ccmsg->recv_cbarg);
+ }
+
+ return;
}
void
.magic = CCMSG_MAGIC,
.maxsize = 0xffffffffU, /* Largest message possible. */
.mctx = mctx,
- .handle = handle,
- .result = ISC_R_UNEXPECTED /* None yet. */
};
+
+ isc_nmhandle_attach(handle, &ccmsg->handle);
}
void
isc_buffer_free(&ccmsg->buffer);
}
- ccmsg->cb = cb;
- ccmsg->cbarg = cbarg;
- ccmsg->result = ISC_R_UNEXPECTED; /* unknown right now */
+ ccmsg->recv_cb = cb;
+ ccmsg->recv_cbarg = cbarg;
ccmsg->length_received = false;
- isc_nm_read(ccmsg->handle, recv_data, ccmsg);
ccmsg->reading = true;
+ isc_nm_read(ccmsg->handle, recv_data, ccmsg);
}
-void
-isccc_ccmsg_cancelread(isccc_ccmsg_t *ccmsg) {
+static void
+ccmsg_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
+ isccc_ccmsg_t *ccmsg = arg;
+
REQUIRE(VALID_CCMSG(ccmsg));
- if (ccmsg->reading) {
- isc_nm_read_stop(ccmsg->handle);
- ccmsg->reading = false;
+ INSIST(ccmsg->send_cb != NULL);
+ ccmsg->send_cb(handle, eresult, ccmsg->send_cbarg);
+ ccmsg->send_cb = NULL;
+
+ if (eresult != ISC_R_SUCCESS && ccmsg->reading) {
+ recv_data(handle, eresult, NULL, ccmsg);
}
+
+ isc_nmhandle_detach(&handle);
+}
+
+void
+isccc_ccmsg_sendmessage(isccc_ccmsg_t *ccmsg, isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
+ REQUIRE(VALID_CCMSG(ccmsg));
+ REQUIRE(ccmsg->send_cb == NULL);
+
+ ccmsg->send_cb = cb;
+ ccmsg->send_cbarg = cbarg;
+
+ isc_nmhandle_ref(ccmsg->handle);
+ isc_nm_send(ccmsg->handle, region, ccmsg_senddone, ccmsg);
}
void
if (ccmsg->buffer != NULL) {
isc_buffer_free(&ccmsg->buffer);
}
+ if (ccmsg->handle != NULL) {
+ isc_nmhandle_close(ccmsg->handle);
+ isc_nmhandle_detach(&ccmsg->handle);
+ }
+}
+
+void
+isccc_ccmsg_toregion(isccc_ccmsg_t *ccmsg, isccc_region_t *ccregion) {
+ REQUIRE(VALID_CCMSG(ccmsg));
+
+ ccregion->rstart = isc_buffer_base(ccmsg->buffer);
+ ccregion->rend = isc_buffer_used(ccmsg->buffer);
}
#include <isc/netmgr.h>
#include <isc/sockaddr.h>
+#include <isccc/types.h>
+
/*% ISCCC Message Structure */
typedef struct isccc_ccmsg {
/* private (don't touch!) */
unsigned int maxsize;
isc_mem_t *mctx;
isc_nmhandle_t *handle;
- isc_nm_cb_t cb;
- void *cbarg;
+ isc_nm_cb_t recv_cb;
+ void *recv_cbarg;
+ isc_nm_cb_t send_cb;
+ void *send_cbarg;
bool reading;
- /* public (read-only) */
- isc_result_t result;
} isccc_ccmsg_t;
ISC_LANG_BEGINDECLS
*/
void
-isccc_ccmsg_cancelread(isccc_ccmsg_t *ccmsg);
+isccc_ccmsg_sendmessage(isccc_ccmsg_t *ccmsg, isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
/*%
- * Cancel a readmessage() call. The event will still be posted with a
- * CANCELED result code.
- *
- * Requires:
+ * Sends region over the command channel message.
*
- *\li "ccmsg" be valid.
+ * CAVEAT: Only a single send message can be scheduled at the time.
*/
void
* sockets, etc.
*/
+void
+isccc_ccmsg_toregion(isccc_ccmsg_t *ccmsg, isccc_region_t *ccregion);
+
ISC_LANG_ENDDECLS
static void
nameserver(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
- void *cbarg) {
+ void *cbarg ISC_ATTR_UNUSED) {
isc_region_t response1, response2;
static unsigned char buf1[16];
static unsigned char buf2[16];
- UNUSED(eresult);
- UNUSED(cbarg);
+ if (eresult != ISC_R_SUCCESS) {
+ return;
+ }
memmove(buf1, region->base, 12);
memset(buf1 + 12, 0, 4);
tcp_test.c \
netmgr_common.h \
netmgr_common.c \
+ stream_shutdown.c \
uv_wrap.h
tcpdns_test_CPPFLAGS = \
tls_test.c \
netmgr_common.h \
netmgr_common.c \
+ stream_shutdown.c \
uv_wrap.h
tlsdns_test_CPPFLAGS = \
int
setup_netmgr_test(void **state) {
- char *env_workers = getenv("ISC_TASK_WORKERS");
- size_t nworkers;
-
tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
tcp_listen_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tcp_listen_addr, &in6addr_loopback, stream_port);
- if (env_workers != NULL) {
- workers = atoi(env_workers);
- } else {
- workers = isc_os_ncpus();
- }
- INSIST(workers > 0);
- nworkers = ISC_MAX(ISC_MIN(workers, 32), 1);
-
- esends = NSENDS * nworkers;
+ esends = NSENDS * workers;
atomic_store(&nsends, esends);
return (0);
}
-static void
+void
stop_listening(void *arg ISC_ATTR_UNUSED) {
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
/* Callbacks */
void
-noop_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
- void *cbarg) {
- UNUSED(handle);
- UNUSED(eresult);
- UNUSED(region);
- UNUSED(cbarg);
+noop_recv_cb(isc_nmhandle_t *handle ISC_ATTR_UNUSED,
+ isc_result_t eresult ISC_ATTR_UNUSED,
+ isc_region_t *region ISC_ATTR_UNUSED,
+ void *cbarg ISC_ATTR_UNUSED) {
+ F();
}
-unsigned int
-noop_accept_cb(isc_nmhandle_t *handle, unsigned int result, void *cbarg) {
- UNUSED(handle);
- UNUSED(cbarg);
+isc_result_t
+noop_accept_cb(isc_nmhandle_t *handle ISC_ATTR_UNUSED, unsigned int eresult,
+ void *cbarg ISC_ATTR_UNUSED) {
+ F();
- if (result == ISC_R_SUCCESS) {
+ if (eresult == ISC_R_SUCCESS) {
(void)atomic_fetch_add(&saccepts, 1);
}
- return (0);
+ return (ISC_R_SUCCESS);
}
void
case ISC_R_SHUTTINGDOWN:
case ISC_R_CANCELED:
case ISC_R_CONNECTIONRESET:
- /* Send failed, we need to stop reading too */
- if (stream) {
- isc_nm_read_stop(handle);
- } else {
+ /* Abort */
+ if (!stream) {
isc_nm_cancelread(handle);
}
break;
return;
}
+ /* This will initiate one more read callback */
+ if (stream) {
+ isc_nmhandle_close(handle);
+ }
break;
case ISC_R_TIMEDOUT:
case ISC_R_EOF:
}
isc_refcount_decrement(&active_creads);
-
- if (stream) {
- isc_nm_read_stop(handle);
- }
isc_nmhandle_detach(&handle);
}
F();
- isc_refcount_decrement(&active_ssends);
-
switch (eresult) {
case ISC_R_CANCELED:
case ISC_R_CONNECTIONRESET:
assert_int_equal(eresult, ISC_R_SUCCESS);
}
+ isc_refcount_decrement(&active_ssends);
isc_nmhandle_detach(&sendhandle);
}
F();
switch (eresult) {
- case ISC_R_CANCELED:
- case ISC_R_CONNECTIONRESET:
- case ISC_R_EOF:
- case ISC_R_SHUTTINGDOWN:
- break;
case ISC_R_SUCCESS:
memmove(&magic, region->base, sizeof(magic));
assert_true(magic == send_magic);
memmove(&magic, region->base, sizeof(magic));
assert_true(magic == send_magic);
- if (magic == send_magic) {
- if (!noanswer) {
- isc_nmhandle_t *sendhandle = NULL;
- isc_nmhandle_attach(handle, &sendhandle);
- isc_refcount_increment0(&active_ssends);
- isc_nmhandle_setwritetimeout(sendhandle,
- T_IDLE);
- isc_nm_send(sendhandle, &send_msg,
- listen_send_cb, cbarg);
- }
- return;
+ if (!noanswer) {
+ /* Answer and continue to listen */
+ isc_nmhandle_t *sendhandle = NULL;
+ isc_nmhandle_attach(handle, &sendhandle);
+ isc_refcount_increment0(&active_ssends);
+ isc_nmhandle_setwritetimeout(sendhandle, T_IDLE);
+ isc_nm_send(sendhandle, &send_msg, listen_send_cb,
+ cbarg);
}
+ /* Continue to listen */
+ return;
+ case ISC_R_CANCELED:
+ case ISC_R_CONNECTIONRESET:
+ case ISC_R_EOF:
+ case ISC_R_SHUTTINGDOWN:
break;
default:
fprintf(stderr, "%s(%p, %s, %p)\n", __func__, handle,
}
isc_refcount_decrement(&active_sreads);
-
isc_nmhandle_detach(&handle);
}
do_saccepts_shutdown(loopmgr);
}
+ isc_nmhandle_attach(handle, &(isc_nmhandle_t *){ NULL });
+ isc_refcount_increment0(&active_sreads);
+
return (eresult);
}
}
isc_refcount_increment0(&active_sreads);
+
isc_nmhandle_attach(handle, &readhandle);
isc_nm_read(handle, listen_read_cb, readhandle);
UNUSED(region);
UNUSED(cbarg);
+ F();
+
assert_true(eresult == ISC_R_CANCELED ||
eresult == ISC_R_CONNECTIONRESET || eresult == ISC_R_EOF);
isc_refcount_decrement(&active_creads);
-
isc_nmhandle_detach(&handle);
isc_loopmgr_shutdown(loopmgr);
UNUSED(cbarg);
UNUSED(eresult);
+ F();
+
assert_non_null(handle);
atomic_fetch_add(&csends, 1);
isc_nmhandle_detach(&handle);
isc_nmhandle_t *readhandle = NULL;
isc_nmhandle_t *sendhandle = NULL;
- UNUSED(handle);
-
F();
isc_refcount_decrement(&active_cconnects);
fprintf(stderr, "%s:%s:%d:%s = %" PRId64 "\n", __func__, __FILE__, \
__LINE__, #v, atomic_load(&v))
#define P(v) fprintf(stderr, #v " = %" PRId64 "\n", v)
-#define F() \
- fprintf(stderr, "%s(%p, %s, %p)\n", __func__, handle, \
+#define F() \
+ fprintf(stderr, "%u:%s(%p, %s, %p)\n", isc_tid(), __func__, handle, \
isc_result_totext(eresult), cbarg)
-#define isc_loopmgr_shutdown(loopmgr) \
- { \
- fprintf(stderr, "%s:%s:%d:isc_loopmgr_shutdown(%p)\n", \
- __func__, __FILE__, __LINE__, loopmgr); \
- isc_loopmgr_shutdown(loopmgr); \
+#define isc_loopmgr_shutdown(loopmgr) \
+ { \
+ fprintf(stderr, "%u:%s:%s:%d:isc_loopmgr_shutdown(%p)\n", \
+ isc_tid(), __func__, __FILE__, __LINE__, loopmgr); \
+ isc_loopmgr_shutdown(loopmgr); \
}
#else
#define X(v)
noop_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
void *cbarg);
-unsigned int
-noop_accept_cb(isc_nmhandle_t *handle, unsigned int result, void *cbarg);
+isc_result_t
+noop_accept_cb(isc_nmhandle_t *handle ISC_ATTR_UNUSED, unsigned int result,
+ void *cbarg ISC_ATTR_UNUSED);
void
connect_send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg);
stream_recv_send_teardown(void **state ISC_ATTR_UNUSED);
void
stream_recv_send_connect(void *arg);
+
+int
+stream_shutdownconnect_setup(void **state ISC_ATTR_UNUSED);
+void
+stream_shutdownconnect(void **state ISC_ATTR_UNUSED);
+int
+stream_shutdownconnect_teardown(void **state ISC_ATTR_UNUSED);
+
+int
+stream_shutdownread_setup(void **state ISC_ATTR_UNUSED);
+void
+stream_shutdownread(void **state ISC_ATTR_UNUSED);
+int
+stream_shutdownread_teardown(void **state ISC_ATTR_UNUSED);
+
+void
+stop_listening(void *arg ISC_ATTR_UNUSED);
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * As a workaround, include an OpenSSL header file before including cmocka.h,
+ * because OpenSSL 3.1.0 uses __attribute__(malloc), conflicting with a
+ * redefined malloc in cmocka.h.
+ */
+#include <openssl/err.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include "netmgr_common.h"
+
+#include <tests/isc.h>
+
+/*
+ * FIXME: This really needs two network managers, so there's predictable result
+ * when shuttingdown the netmgr - right now there's a race whether the listening
+ * or connecting sockets gets shutdown first
+ */
+
+static void
+shutdownconnect_connectcb(isc_nmhandle_t *handle, isc_result_t eresult,
+ void *cbarg) {
+ F();
+
+ assert_non_null(handle);
+ assert_int_equal(eresult, ISC_R_SHUTTINGDOWN);
+ assert_null(cbarg);
+
+ isc_refcount_decrement(&active_cconnects);
+
+ atomic_fetch_add(&cconnects, 1);
+}
+
+int
+stream_shutdownconnect_setup(void **state ISC_ATTR_UNUSED) {
+ int r = setup_netmgr_test(state);
+ return (r);
+}
+
+void
+stream_shutdownconnect(void **state ISC_ATTR_UNUSED) {
+ isc_result_t result = stream_listen(stream_accept_cb, NULL, 128, NULL,
+ &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ isc_loop_teardown(mainloop, stop_listening, listen_sock);
+
+ /* Schedule the shutdown before the connect */
+ isc_loopmgr_shutdown(loopmgr);
+
+ stream_connect(shutdownconnect_connectcb, NULL, T_CONNECT);
+}
+
+int
+stream_shutdownconnect_teardown(void **state ISC_ATTR_UNUSED) {
+ X(cconnects);
+ X(csends);
+ X(creads);
+
+ atomic_assert_int_eq(cconnects, 1);
+ atomic_assert_int_eq(csends, 0);
+ atomic_assert_int_eq(creads, 0);
+
+ return (teardown_netmgr_test(state));
+}
+
+/* Issue the shutdown before reading */
+
+static void
+shutdownread_readcb(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *cbarg) {
+ F();
+ assert_non_null(handle);
+ assert_true(eresult == ISC_R_SHUTTINGDOWN ||
+ eresult == ISC_R_CONNECTIONRESET || eresult == ISC_R_EOF);
+ assert_non_null(region);
+ assert_null(cbarg);
+
+ atomic_fetch_add(&creads, 1);
+ isc_nmhandle_detach(&handle);
+ isc_refcount_decrement(&active_creads);
+}
+
+static void
+shutdownread_sendcb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+ F();
+ assert_non_null(handle);
+ assert_true(eresult == ISC_R_SUCCESS || eresult == ISC_R_SHUTTINGDOWN ||
+ eresult == ISC_R_CONNECTIONRESET || eresult == ISC_R_EOF);
+ assert_null(cbarg);
+
+ atomic_fetch_add(&csends, 1);
+
+ isc_nmhandle_detach(&handle);
+ isc_refcount_decrement(&active_csends);
+}
+
+static void
+shutdownread_connectcb(isc_nmhandle_t *handle, isc_result_t eresult,
+ void *cbarg) {
+ F();
+
+ assert_non_null(handle);
+ assert_int_equal(eresult, ISC_R_SUCCESS);
+ assert_null(cbarg);
+
+ isc_refcount_decrement(&active_cconnects);
+
+ atomic_fetch_add(&cconnects, 1);
+
+ /* Schedule the shutdown before read and send */
+ isc_loopmgr_shutdown(loopmgr);
+
+ isc_refcount_increment0(&active_creads);
+ isc_nmhandle_ref(handle);
+ isc_nm_read(handle, shutdownread_readcb, cbarg);
+
+ isc_refcount_increment0(&active_csends);
+ isc_nmhandle_ref(handle);
+ isc_nm_send(handle, (isc_region_t *)&send_msg, shutdownread_sendcb,
+ cbarg);
+}
+
+int
+stream_shutdownread_setup(void **state ISC_ATTR_UNUSED) {
+ int r = setup_netmgr_test(state);
+ return (r);
+}
+
+void
+stream_shutdownread(void **state ISC_ATTR_UNUSED) {
+ isc_result_t result = stream_listen(stream_accept_cb, NULL, 128, NULL,
+ &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ isc_loop_teardown(mainloop, stop_listening, listen_sock);
+
+ stream_connect(shutdownread_connectcb, NULL, T_CONNECT);
+}
+
+int
+stream_shutdownread_teardown(void **state ISC_ATTR_UNUSED) {
+ X(cconnects);
+ X(csends);
+ X(creads);
+
+ atomic_assert_int_eq(cconnects, 1);
+ atomic_assert_int_eq(csends, 1);
+ atomic_assert_int_eq(creads, 1);
+
+ return (teardown_netmgr_test(state));
+}
return;
}
+ISC_LOOP_TEST_IMPL(tcp_shutdownconnect) {
+ stream_shutdownconnect(arg);
+ return;
+}
+
+ISC_LOOP_TEST_IMPL(tcp_shutdownread) {
+ stream_shutdownread(arg);
+ return;
+}
+
ISC_LOOP_TEST_IMPL(tcp_timeout_recovery) {
stream_timeout_recovery(arg);
return;
ISC_TEST_ENTRY_CUSTOM(tcp_noop, stream_noop_setup, stream_noop_teardown)
ISC_TEST_ENTRY_CUSTOM(tcp_noresponse, stream_noresponse_setup,
stream_noresponse_teardown)
+ISC_TEST_ENTRY_CUSTOM(tcp_shutdownconnect, stream_shutdownconnect_setup,
+ stream_shutdownconnect_teardown)
+ISC_TEST_ENTRY_CUSTOM(tcp_shutdownread, stream_shutdownread_setup,
+ stream_shutdownread_teardown)
ISC_TEST_ENTRY_CUSTOM(tcp_timeout_recovery, stream_timeout_recovery_setup,
stream_timeout_recovery_teardown)
ISC_TEST_ENTRY_CUSTOM(tcp_recv_one, stream_recv_one_setup,
/* TCPDNS */
-static void
-stop_listening(void *arg ISC_ATTR_UNUSED) {
- isc_nm_stoplistening(listen_sock);
- isc_nmsocket_close(&listen_sock);
- assert_null(listen_sock);
-}
-
static void
start_listening(uint32_t nworkers, isc_nm_accept_cb_t accept_cb,
isc_nm_recv_cb_t recv_cb) {
return;
}
+ISC_LOOP_TEST_IMPL(tls_shutdownconnect) {
+ stream_shutdownconnect(arg);
+ return;
+}
+
+ISC_LOOP_TEST_IMPL(tls_shutdownread) {
+ stream_shutdownread(arg);
+ return;
+}
+
ISC_LOOP_TEST_IMPL(tls_timeout_recovery) {
stream_timeout_recovery(arg);
return;
ISC_TEST_ENTRY_CUSTOM(tls_noop, stream_noop_setup, stream_noop_teardown)
ISC_TEST_ENTRY_CUSTOM(tls_noresponse, stream_noresponse_setup,
stream_noresponse_teardown)
+ISC_TEST_ENTRY_CUSTOM(tls_shutdownconnect, stream_shutdownconnect_setup,
+ stream_shutdownconnect_teardown)
+ISC_TEST_ENTRY_CUSTOM(tls_shutdownread, stream_shutdownread_setup,
+ stream_shutdownread_teardown)
ISC_TEST_ENTRY_CUSTOM(tls_timeout_recovery, stream_timeout_recovery_setup,
stream_timeout_recovery_teardown)
ISC_TEST_ENTRY_CUSTOM(tls_recv_one, stream_recv_one_setup,
#include <tests/isc.h>
-static void
-stop_listening(void *arg ISC_ATTR_UNUSED) {
- isc_nm_stoplistening(listen_sock);
- isc_nmsocket_close(&listen_sock);
- assert_null(listen_sock);
-}
-
static void
start_listening(uint32_t nworkers, isc_nm_accept_cb_t accept_cb,
isc_nm_recv_cb_t recv_cb) {
UNUSED(cbarg);
}
+static void
+udp_listen_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *cbarg) {
+ if (eresult != ISC_R_SUCCESS) {
+ isc_refcount_increment0(&active_sreads);
+ }
+ listen_read_cb(handle, eresult, region, cbarg);
+}
+
static void
connect_nomemory_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
UNUSED(handle);
isc_loopmgr_shutdown(loopmgr);
}
-static void
-stop_listening(void *arg ISC_ATTR_UNUSED) {
- isc_nm_stoplistening(listen_sock);
- isc_nmsocket_close(&listen_sock);
- assert_null(listen_sock);
-}
-
static void
start_listening(uint32_t nworkers, isc_nm_recv_cb_t cb) {
isc_result_t result = isc_nm_listenudp(
isc_refcount_increment0(&active_creads);
isc_nmhandle_attach(handle, &readhandle);
isc_nm_read(handle, udp_shutdown_read_read_cb, cbarg);
+ assert_true(handle->sock->reading);
/* Send */
isc_refcount_increment0(&active_csends);
udp_cancel_read_send_cb, cbarg);
}
break;
- case ISC_R_EOF:
+ case ISC_R_CANCELED:
/* The read has been canceled */
atomic_fetch_add(&creads, 1);
isc_loopmgr_shutdown(loopmgr);
}
ISC_LOOP_TEST_IMPL(udp_recv_one) {
- start_listening(ISC_NM_LISTEN_ONE, listen_read_cb);
+ start_listening(ISC_NM_LISTEN_ONE, udp_listen_read_cb);
udp__connect(NULL);
}
}
ISC_LOOP_TEST_IMPL(udp_recv_two) {
- start_listening(ISC_NM_LISTEN_ONE, listen_read_cb);
+ start_listening(ISC_NM_LISTEN_ONE, udp_listen_read_cb);
udp__connect(NULL);
udp__connect(NULL);
}
ISC_LOOP_TEST_IMPL(udp_recv_send) {
- start_listening(ISC_NM_LISTEN_ALL, listen_read_cb);
+ start_listening(ISC_NM_LISTEN_ALL, udp_listen_read_cb);
for (size_t i = 0; i < workers; i++) {
isc_async_run(isc_loop_get(loopmgr, i), udp__connect, NULL);
isc_loop_t *mainloop = NULL;
isc_loopmgr_t *loopmgr = NULL;
isc_nm_t *netmgr = NULL;
-unsigned int workers = -1;
+unsigned int workers = 0;
static void
adjustnofile(void) {
if (env_workers != NULL) {
workers = atoi(env_workers);
}
- if (workers < 2 || workers > 1000) {
+
+ if (workers == 0) {
+ workers = isc_os_ncpus();
+
/* We always need at least two loops for some of the tests */
- workers = isc_os_ncpus() + 1;
+ if (workers < 2) {
+ workers = 2;
+ }
}
isc_loopmgr_create(mctx, workers, &loopmgr);
#include <ns/client.h>
#if ISC_NETMGR_TRACE
-#define FLARG \
- , const char *file ISC_ATTR_UNUSED, unsigned int line ISC_ATTR_UNUSED, \
- const char *func ISC_ATTR_UNUSED
+#define FLARG \
+ , const char *func ISC_ATTR_UNUSED, const char *file ISC_ATTR_UNUSED, \
+ unsigned int line ISC_ATTR_UNUSED
#else
#define FLARG
#endif
atomic_uint_fast32_t client_refs[32];
atomic_uintptr_t client_addrs[32];
+#if ISC_NETMGR_TRACE
+void
+isc_nmhandle__attach(isc_nmhandle_t *source, isc_nmhandle_t **targetp FLARG) {
+#else
void
-isc__nmhandle_attach(isc_nmhandle_t *source, isc_nmhandle_t **targetp FLARG) {
+isc_nmhandle_attach(isc_nmhandle_t *source, isc_nmhandle_t **targetp) {
+#endif
ns_client_t *client = (ns_client_t *)source;
int i;
return;
}
+#if ISC_NETMGR_TRACE
+void
+isc_nmhandle__detach(isc_nmhandle_t **handlep FLARG) {
+#else
void
-isc__nmhandle_detach(isc_nmhandle_t **handlep FLARG) {
+isc_nmhandle_detach(isc_nmhandle_t **handlep) {
+#endif
isc_nmhandle_t *handle = *handlep;
ns_client_t *client = (ns_client_t *)handle;
int i;