diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index bd8d844989..a57222c367 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1337,6 +1337,14 @@ isc__nm_async_tcpstop(isc__networker_t *worker, isc__netievent_t *ev0); * stoplisten, send, read, pause, close). */ +void +isc__nm_tcp_senddns(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg); +/*%< + * The same as 'isc__nm_tcp_send()', but with data length sent + * ahead of data (two bytes (16 bit) in big-endian format). + */ + void isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0); @@ -1993,3 +2001,11 @@ isc__nmhandle_get_selected_alpn(isc_nmhandle_t *handle, * not negotiated of the underlying protocol of the connection * represented via the given handle does not support ALPN. */ + +void +isc__nm_senddns(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, + void *cbarg); +/*%< + * The same as 'isc_nm_send()', but with data length sent + * ahead of data (two bytes (16 bit) in big-endian format). + */ diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index eb10ce67f2..80bcca310d 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -1873,6 +1873,20 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, } } +void +isc__nm_senddns(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, + void *cbarg) { + REQUIRE(VALID_NMHANDLE(handle)); + + switch (handle->sock->type) { + case isc_nm_tcpsocket: + isc__nm_tcp_senddns(handle, region, cb, cbarg); + break; + default: + UNREACHABLE(); + } +} + void isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { REQUIRE(VALID_NMHANDLE(handle)); diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index c994622d0c..cd49da70b4 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -996,9 +996,9 @@ failure: return (result); } -void -isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, - isc_nm_cb_t cb, void *cbarg) { +static void +tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, isc_nm_cb_t cb, + void *cbarg, const bool dnsmsg) { REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); @@ -1011,6 +1011,9 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, REQUIRE(sock->tid == isc_tid()); uvreq = isc__nm_uvreq_get(sock->worker, sock); + if (dnsmsg) { + *(uint16_t *)uvreq->tcplen = htons(region->length); + } uvreq->uvbuf.base = (char *)region->base; uvreq->uvbuf.len = region->length; @@ -1034,6 +1037,18 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, return; } +void +isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { + tcp_send(handle, region, cb, cbarg, false); +} + +void +isc__nm_tcp_senddns(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { + tcp_send(handle, region, cb, cbarg, true); +} + static void tcp_send_cb(uv_write_t *req, int status) { isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data; @@ -1065,27 +1080,59 @@ tcp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { REQUIRE(sock->type == isc_nm_tcpsocket); int r; + uv_buf_t bufs[2] = { { 0 }, { 0 } }; /* ugly, but required for old GCC + versions */ + size_t nbufs = 1; if (isc__nmsocket_closing(sock)) { return (ISC_R_CANCELED); } - uv_buf_t uvbuf = { .base = req->uvbuf.base, .len = req->uvbuf.len }; + /* Check if we are not trying to send a DNS message */ + if (*(uint16_t *)req->tcplen == 0) { + bufs[0].base = req->uvbuf.base; + bufs[0].len = req->uvbuf.len; - r = uv_try_write(&sock->uv_handle.stream, &uvbuf, 1); + r = uv_try_write(&sock->uv_handle.stream, bufs, nbufs); - if (r == (int)(uvbuf.len)) { - /* Wrote everything */ - isc__nm_sendcb(sock, req, ISC_R_SUCCESS, true); - return (ISC_R_SUCCESS); - } else if (r > 0) { - uvbuf.base += (size_t)r; - uvbuf.len -= (size_t)r; - } else if (!(r == UV_ENOSYS || r == UV_EAGAIN)) { - return (isc_uverr2result(r)); + if (r == (int)(bufs[0].len)) { + /* Wrote everything */ + isc__nm_sendcb(sock, req, ISC_R_SUCCESS, true); + return (ISC_R_SUCCESS); + } else if (r > 0) { + bufs[0].base += (size_t)r; + bufs[0].len -= (size_t)r; + } else if (!(r == UV_ENOSYS || r == UV_EAGAIN)) { + return (isc_uverr2result(r)); + } + } else { + nbufs = 2; + bufs[0].base = req->tcplen; + bufs[0].len = 2; + bufs[1].base = req->uvbuf.base; + bufs[1].len = req->uvbuf.len; + + r = uv_try_write(&sock->uv_handle.stream, bufs, nbufs); + + if (r == (int)(bufs[0].len + bufs[1].len)) { + /* Wrote everything */ + isc__nm_sendcb(sock, req, ISC_R_SUCCESS, true); + return (ISC_R_SUCCESS); + } else if (r == 1) { + /* Partial write of DNSMSG length */ + bufs[0].base = req->tcplen + 1; + bufs[0].len = 1; + } else if (r > 0) { + /* Partial write of DNSMSG */ + nbufs = 1; + bufs[0].base = req->uvbuf.base + (r - 2); + bufs[0].len = req->uvbuf.len - (r - 2); + } else if (!(r == UV_ENOSYS || r == UV_EAGAIN)) { + return (isc_uverr2result(r)); + } } - r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, &uvbuf, 1, + r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, bufs, nbufs, tcp_send_cb); if (r < 0) { return (isc_uverr2result(r));