Compare commits

...
52 Commits
Author SHA1 Message Date
Evan Hunt 600364259a fixup! Active sockets and handles tracing 2020-09-03 12:37:10 -07:00
Evan Hunt 128acf9fac add readhandle/sendhandle to httpd 2020-09-03 12:33:54 -07:00
Evan Hunt 7e334da824 style, naming, comment clenaup 2020-09-03 12:26:31 -07:00
Evan Hunt d84533af69 clean up reference counting in command channel error cases 2020-09-03 10:59:34 -07:00
Evan Hunt 0d9a516323 dnslisten_readcb needs to detach handle when result is ISC_R_CANCELED 2020-09-03 00:12:16 -07:00
Evan Hunt 6cee82ba3a fixup! Active sockets and handles tracing 2020-09-02 16:52:30 -07:00
Evan Hunt 673c476037 checkpoint: add handles to controlconnection structure and rndc
this also removes the readhandle from isccc_ccmsg_readmessage(); we'll
expect the caller to hold that reference.
2020-09-02 16:07:23 -07:00
Witold Kręcicki c421039402 Active sockets and handles tracing 2020-09-02 17:57:44 +02:00
Evan Hunt f89a9957b3 checkpoint: add a readhandle in lib/isccc 2020-09-01 15:57:30 -07:00
Evan Hunt 7627cdefe9 fixup! checkpoint: separate client handle pointers for different uses 2020-08-31 13:15:22 -07:00
Evan Hunt f9cc0b3639 fixup! checkpoint: separate client handle pointers for different uses 2020-08-31 11:12:41 -07:00
Evan Hunt b3c77ade26 fixup! checkpoint: separate client handle pointers for different uses 2020-08-31 11:12:41 -07:00
Evan Hunt f5b70924e3 checkpoint: separate client handle pointers for different uses 2020-08-31 11:12:41 -07:00
Evan Hunt 670560a223 fixup! XXX WiP: isc_nmhandle attach/detach 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt 2899eb1e8c XXX WiP: isc_nmhandle attach/detach 2020-08-31 11:12:41 -07:00
Evan Hunt 4dde1b4558 fixup! checkpoint: implement isc_nm_read() for UDP 2020-08-31 11:12:41 -07:00
Evan Hunt 725d973ab9 fixup! nghttp2-based HTTP client with DoH support 2020-08-31 11:12:41 -07:00
Evan Hunt 06d6627457 conform url code to isc style
- move url_parser code to lib/isc/url.c
- update style and nomenclature
2020-08-31 11:12:41 -07:00
Evan Hunt 0c41c19cbf fixup! nghttp2-based HTTP client with DoH support 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt bc3747f56b nghttp2-based HTTP client with DoH support 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt 213376b8fa Require libnghttp2 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt 9784baad54 XXX: disable -Wcast-qual for now 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt 7c01178369 netmgr/tls: call tls_do_bio in resumeread
We might have some leftover data in bio buffer that we received
before pausing, we need to do tls_do_bio to make sure we call
the recv() callback on it.
2020-08-31 11:12:41 -07:00
Evan Hunt 3b8e287994 fixup! netmgr/tls: logging 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt dbe5d42ff3 netmgr/tls: logging 2020-08-31 11:12:41 -07:00
Evan Hunt 469b7a5ecb fixup! isc_nm_cancelread returns true if the callback was called 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt ca3a5d1e52 isc_nm_cancelread returns true if the callback was called 2020-08-31 11:12:41 -07:00
Evan Hunt 2779c44e27 add "dig +tls"
- add isc_nm_tlsdnsconnect() function
- add "+[no]tls" option to dig to enable TLS mode
- override the default port number in dig from 53 to 853 when using TLS
2020-08-31 11:12:41 -07:00
Evan Hunt 3a09529d78 clean up some style nits 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt de0c3c3019 checkpoint/test: tcpdns always using TLS, to verify that dig can do DoT 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt f8ef33db6d post-merge-fixes 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt f43af328b6 tls connect: checkpoint 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt 090161e1fc CHANGES note 2020-08-31 11:12:41 -07:00
Witold KręcickiandEvan Hunt 1d32b9799e netmgr tcpdns: add support for DoT
Add an optional SSL_CTX argument to isc_nm_listentcpdns - if not NULL,
use isc_nm_listentls instead of isc_nm_listentcp to listen on a TLS
socket for DoT.
2020-08-31 10:58:37 -07:00
Witold KręcickiandEvan Hunt d7b441a1d5 netmgr: server-side TLS support
Add server-side TLS support to netmgr - that includes moving some of the isc_nm_
functions from tcp.c to a wrapper in netmgr.c calling a proper tcp or tls
function, and a new isc_nm_listentls function.
2020-08-31 10:58:37 -07:00
Witold KręcickiandEvan Hunt dc6811101a Add -lssl to OPENSSL_LIBS 2020-08-31 10:58:37 -07:00
Evan Hunt 5c52feb442 add more logging to the shutdown system test 2020-08-31 10:58:37 -07:00
Evan Hunt 7aa9bbb8ac clear sock->statichandle if it goes to 0 references 2020-08-31 10:58:37 -07:00
Evan Hunt 4c8f28f087 experiment: add cancelread support for UDP, tweak lookup locking 2020-08-31 10:58:37 -07:00
Evan Hunt 1817dadf5d limit the time we wait for netmgr to be destroyed
if more than 10 seconds pass while we wait for netmgr
events to finish running on shutdown, something is almost
certainly wrong and we should assert and crash.
2020-08-31 10:58:37 -07:00
Evan Hunt 34b0bb84ca fixup! checkpoint: convert dig to use netmgr for TCP 2020-08-31 10:58:37 -07:00
Evan Hunt eea946e741 fixup! checkpoint: convert dig to use netmgr for TCP 2020-08-31 10:58:37 -07:00
Evan Hunt d0f3d3f421 fixup! checkpoint: convert dig to use netmgr for UDP 2020-08-31 10:58:37 -07:00
Evan Hunt 9cefc63b4c fixup! checkpoint: convert dig to use netmgr for UDP 2020-08-31 10:58:37 -07:00
Evan Hunt 1cea2ede55 fixup! checkpoint: isc_nm_tcpdnsconnect 2020-08-31 10:58:37 -07:00
Evan Hunt 5316b99628 fixup! checkpoint: implement isc_nm_udpconnect() 2020-08-31 10:58:37 -07:00
Evan Hunt 77bedef71e checkpoint: convert dig to use netmgr for UDP 2020-08-31 10:58:37 -07:00
Evan Hunt db354895e7 checkpoint: implement isc_nm_read() for UDP
isc_nm_read() on a UDP socket will now read a single datagram and
stop until next time.
2020-08-31 10:58:37 -07:00
Evan Hunt 03009e7159 checkpoint: implement isc_nm_udpconnect()
this function sets up a UDP socket, connected to a specified peer
address, then immediately calls a callback with a handle so that
the caller can begin sending.
2020-08-31 10:58:37 -07:00
Evan Hunt c2adc2ac27 checkpoint: convert dig to use netmgr for TCP 2020-08-31 10:58:37 -07:00
Witold KręcickiandEvan Hunt cac6b52672 checkpoint: isc_nm_tcpdnsconnect
add a function to establish outgoing TCPDNS connections.
2020-08-31 10:58:37 -07:00
Witold KręcickiandEvan Hunt 9748d9f4d0 Rename isc_nmsocket_t->tcphandle to statichandle 2020-08-31 10:58:37 -07:00
38 changed files with 3194 additions and 282 deletions
+4
View File
@@ -1,3 +1,7 @@
5495. [func] Add a netmgr TLS layer, enabling server-side DoT
support (not yet available), and client-side DoT
support in dig with "dig +tls". [GL #1840]
5494. [bug] Silence the EPROTO syslog message on older systems.
[GL #1928]
+22
View File
@@ -367,3 +367,25 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-----------------------------------------------------------------------------
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
+35 -16
View File
@@ -68,6 +68,9 @@ struct controlkey {
struct controlconnection {
isc_nmhandle_t *handle;
isc_nmhandle_t *readhandle;
isc_nmhandle_t *sendhandle;
isc_nmhandle_t *cmdhandle;
isccc_ccmsg_t ccmsg;
bool ccmsg_valid;
bool sending;
@@ -221,10 +224,8 @@ control_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
conn->sending = false;
isc_nmhandle_unref(handle);
if (result == ISC_R_CANCELED) {
return;
goto cleanup;
} else if (result != ISC_R_SUCCESS) {
char socktext[ISC_SOCKADDR_FORMATSIZE];
@@ -233,16 +234,23 @@ control_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING,
"error sending command response to %s: %s",
socktext, isc_result_totext(result));
return;
goto cleanup;
}
if (conn->readhandle == NULL) {
isc_nmhandle_attach(handle, &conn->readhandle);
}
result = isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage,
conn);
if (result != ISC_R_SUCCESS) {
isc_nmhandle_detach(&conn->readhandle);
maybe_free_listener(listener);
}
listener->listening = true;
cleanup:
isc_nmhandle_detach(&conn->sendhandle);
}
static inline void
@@ -324,11 +332,11 @@ control_respond(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
r.base = conn->buffer->base;
r.length = conn->buffer->used;
isc_nmhandle_ref(handle);
isc_nmhandle_attach(handle, &conn->sendhandle);
conn->sending = true;
result = isc_nm_send(handle, &r, control_senddone, conn);
result = isc_nm_send(conn->sendhandle, &r, control_senddone, conn);
if (result != ISC_R_SUCCESS) {
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&conn->sendhandle);
conn->sending = false;
}
@@ -356,17 +364,12 @@ control_command(isc_task_t *task, isc_event_t *event) {
UNUSED(task);
/*
* An extra ref and two unrefs are needed here to
* ensure the handle isn't cleaned up if we're running
* an "rndc stop" command.
*/
isc_nmhandle_ref(conn->handle);
conn->result = named_control_docommand(conn->request,
listener->readonly, &conn->text);
control_respond(conn->handle, conn->result, conn);
isc_nmhandle_unref(conn->handle);
isc_nmhandle_unref(conn->handle);
isc_nmhandle_detach(&conn->cmdhandle);
isc_event_free(&event);
}
@@ -399,6 +402,10 @@ control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
log_invalid(&conn->ccmsg, result);
}
if (conn->readhandle != NULL) {
isc_nmhandle_detach(&conn->readhandle);
}
return;
}
@@ -424,6 +431,9 @@ control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
REGION_SIZE(conn->secret));
if (result != ISCCC_R_BADAUTH) {
log_invalid(&conn->ccmsg, result);
if (conn->readhandle != NULL) {
isc_nmhandle_detach(&conn->readhandle);
}
return;
}
}
@@ -513,10 +523,11 @@ control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
/*
* Trigger the command.
*/
isc_nmhandle_ref(handle);
isc_nmhandle_attach(handle, &conn->cmdhandle);
event = isc_event_allocate(listener->mctx, conn, NAMED_EVENT_COMMAND,
control_command, conn, sizeof(isc_event_t));
isc_task_send(named_g_server->task, &event);
isc_nmhandle_detach(&conn->readhandle);
return;
cleanup:
@@ -534,6 +545,10 @@ cleanup:
if (conn->text != NULL) {
isc_buffer_free(&conn->text);
}
if (conn->readhandle != NULL) {
isc_nmhandle_detach(&conn->readhandle);
}
}
static void
@@ -604,9 +619,13 @@ newconnection(controllistener_t *listener, isc_nmhandle_t *handle) {
ISC_LINK_INIT(conn, link);
if (conn->readhandle == NULL) {
isc_nmhandle_attach(handle, &conn->readhandle);
}
result = isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage,
conn);
if (result != ISC_R_SUCCESS) {
isc_nmhandle_detach(&conn->readhandle);
goto cleanup;
}
+3 -2
View File
@@ -14856,14 +14856,15 @@ named_server_zonestatus(named_server_t *server, isc_lex_t *lex,
/* Serial number */
result = dns_zone_getserial(mayberaw, &serial);
/* XXXWPK TODO this is to mirror old behavior with dns_zone_getserial */
/* This is to mirror old behavior with dns_zone_getserial */
if (result != ISC_R_SUCCESS) {
serial = 0;
}
snprintf(serbuf, sizeof(serbuf), "%u", serial);
if (hasraw) {
result = dns_zone_getserial(zone, &signed_serial);
/* XXXWPK TODO ut supra */
if (result != ISC_R_SUCCESS) {
serial = 0;
}
+22 -7
View File
@@ -24,6 +24,7 @@
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/net.h>
#include <isc/netmgr.h>
#include <isc/print.h>
#include <isc/random.h>
#include <isc/refcount.h>
@@ -89,6 +90,7 @@ static uint32_t serial;
static bool quiet = false;
static bool showresult = false;
static bool shuttingdown = false;
isc_nmhandle_t *readhandle = NULL, *sendhandle = NULL;
static void
rndc_startconnect(isc_sockaddr_t *addr);
@@ -288,19 +290,21 @@ get_addresses(const char *host, in_port_t port) {
static void
rndc_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
REQUIRE(handle == sendhandle);
UNUSED(arg);
if (result != ISC_R_SUCCESS) {
fatal("send failed: %s", isc_result_totext(result));
}
isc_nmhandle_detach(&sendhandle);
if (atomic_fetch_sub_release(&sends, 1) == 1 &&
atomic_load_acquire(&recvs) == 0)
{
shuttingdown = true;
isc_task_shutdown(rndc_task);
isc_app_shutdown();
isc_nmhandle_unref(handle);
}
}
@@ -318,7 +322,10 @@ rndc_recvdone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
atomic_fetch_sub_release(&recvs, 1);
if (shuttingdown && (result == ISC_R_EOF || result == ISC_R_CANCELED)) {
isc_nmhandle_unref(handle);
if (readhandle != NULL) {
INSIST(handle == readhandle);
isc_nmhandle_detach(&readhandle);
}
return;
} else if (result == ISC_R_EOF) {
fatal("connection to remote host closed.\n"
@@ -378,8 +385,8 @@ rndc_recvdone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
if (atomic_load_acquire(&sends) == 0 &&
atomic_load_acquire(&recvs) == 0) {
isc_nmhandle_detach(&readhandle);
shuttingdown = true;
isc_nmhandle_unref(handle);
isc_task_shutdown(rndc_task);
isc_app_shutdown();
}
@@ -398,12 +405,13 @@ rndc_recvnonce(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
isccc_sexpr_t *data;
isc_buffer_t b;
REQUIRE(handle == readhandle);
REQUIRE(ccmsg != NULL);
atomic_fetch_sub_release(&recvs, 1);
if (shuttingdown && result == ISC_R_EOF) {
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&readhandle);
return;
} else if (result == ISC_R_EOF) {
fatal("connection to remote host closed.\n"
@@ -471,6 +479,9 @@ rndc_recvnonce(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
isccc_ccmsg_readmessage(ccmsg, rndc_recvdone, ccmsg));
atomic_fetch_add_relaxed(&recvs, 1);
if (sendhandle == NULL) {
isc_nmhandle_attach(handle, &sendhandle);
}
DO("send message", isc_nm_send(handle, &r, rndc_senddone, NULL));
atomic_fetch_add_relaxed(&sends, 1);
@@ -534,12 +545,16 @@ rndc_connected(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
isccc_ccmsg_init(rndc_mctx, handle, ccmsg);
isccc_ccmsg_setmaxsize(ccmsg, 1024 * 1024);
isc_nmhandle_ref(handle);
if (readhandle == NULL) {
isc_nmhandle_attach(handle, &readhandle);
}
DO("schedule recv",
isccc_ccmsg_readmessage(ccmsg, rndc_recvnonce, ccmsg));
atomic_fetch_add_relaxed(&recvs, 1);
if (sendhandle == NULL) {
isc_nmhandle_attach(handle, &sendhandle);
}
DO("send message", isc_nm_send(handle, &r, rndc_senddone, NULL));
atomic_fetch_add_relaxed(&sends, 1);
@@ -1009,7 +1024,7 @@ main(int argc, char **argv) {
isc_mem_create(&rndc_mctx);
netmgr = isc_nm_start(rndc_mctx, 1);
DO("create task manager",
isc_taskmgr_create(rndc_mctx, 1, 0, NULL, &taskmgr));
isc_taskmgr_create(rndc_mctx, 1, 0, netmgr, &taskmgr));
DO("create task", isc_task_create(taskmgr, 0, &rndc_task));
isc_log_create(rndc_mctx, &log, &logconfig);
isc_log_setcontext(log);
+5
View File
@@ -103,15 +103,20 @@ status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "checking drop edns server setup ($n)"
ret=0
echo x
$DIG $DIGOPTS +edns @10.53.0.2 dropedns soa > dig.out.1.test$n && ret=1
echo x
grep "connection timed out; no servers could be reached" dig.out.1.test$n > /dev/null || ret=1
$DIG $DIGOPTS +noedns @10.53.0.2 dropedns soa > dig.out.2.test$n || ret=1
echo x
grep "status: NOERROR" dig.out.2.test$n > /dev/null || ret=1
grep "EDNS: version:" dig.out.2.test$n > /dev/null && ret=1
$DIG $DIGOPTS +noedns +tcp @10.53.0.2 dropedns soa > dig.out.3.test$n || ret=1
echo x
grep "status: NOERROR" dig.out.3.test$n > /dev/null || ret=1
grep "EDNS: version:" dig.out.3.test$n > /dev/null && ret=1
$DIG $DIGOPTS +edns +tcp @10.53.0.2 dropedns soa > dig.out.4.test$n && ret=1
echo x
grep "connection timed out; no servers could be reached" dig.out.4.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
+8 -4
View File
@@ -26,12 +26,10 @@ $DIFF ref output > /dev/null && { ret=1 ; echo_i "diff out of order failed"; }
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
# flush resolver so queries will be from others again
$RNDCCMD 10.53.0.4 flush
sleep 1
echo_i "check pipelined TCP queries using mdig"
ret=0
$RNDCCMD 10.53.0.4 flush
sleep 1
$MDIG $MDIGOPTS +noall +answer +vc -f input -b 10.53.0.4 @10.53.0.4 > raw.mdig
awk '{ print $1 " " $5 }' < raw.mdig > output.mdig
sort < output.mdig > output-sorted.mdig
@@ -42,6 +40,8 @@ status=`expr $status + $ret`
echo_i "check keep-response-order"
ret=0
$RNDCCMD 10.53.0.4 flush
sleep 1
$PIPEQUERIES -p ${PORT} ++ < inputb > rawb || ret=1
awk '{ print $1 " " $5 }' < rawb > outputb
$DIFF refb outputb || ret=1
@@ -50,6 +50,8 @@ status=`expr $status + $ret`
echo_i "check keep-response-order using mdig"
ret=0
$RNDCCMD 10.53.0.4 flush
sleep 1
$MDIG $MDIGOPTS +noall +answer +vc -f inputb -b 10.53.0.7 @10.53.0.4 > rawb.mdig
awk '{ print $1 " " $5 }' < rawb.mdig > outputb.mdig
$DIFF refb outputb.mdig || ret=1
@@ -58,6 +60,8 @@ status=`expr $status + $ret`
echo_i "check mdig -4 -6"
ret=0
$RNDCCMD 10.53.0.4 flush
sleep 1
$MDIG $MDIGOPTS -4 -6 -f input @10.53.0.4 > output46.mdig 2>&1 && ret=1
grep "only one of -4 and -6 allowed" output46.mdig > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
+1
View File
@@ -15,6 +15,7 @@ options {
listen-on { 10.53.0.4; };
listen-on-v6 { none; };
recursion yes;
dnssec-validation yes;
};
view normal {
+2 -3
View File
@@ -8,11 +8,10 @@
# information regarding copyright ownership.
rm -f ns*/*.jnl
rm -f ns*/named.conf
rm -f ns*/named.lock
rm -f ns*/named.memstats
rm -f ns*/named.run
rm -f ns*/rpz*.txt
rm -f resolver/named.conf
rm -f */named.conf
rm -f */named.run
rm -rf __pycache__
rm -f *.status
@@ -1,3 +1,11 @@
logging {
channel basic {
file "named.run";
severity debug 999;
print-time yes;
};
category default { basic; };
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
+11 -2
View File
@@ -131,7 +131,7 @@ AC_CONFIG_FILES([libltdl/Makefile])
#
# Set the default CFLAGS and CPPFLAGS
#
STD_CFLAGS="-Wall -Wextra -Wwrite-strings -Wcast-qual -Wpointer-arith -Wno-missing-field-initializers -Wformat -Wshadow"
STD_CFLAGS="-Wall -Wextra -Wwrite-strings -Wpointer-arith -Wno-missing-field-initializers -Wformat -Wshadow"
# These should be always errors
STD_CFLAGS="$STD_CFLAGS -Werror=implicit-function-declaration -Werror=missing-prototypes -Werror=format-security -Werror=parentheses -Werror=implicit -Werror=strict-prototypes"
@@ -563,6 +563,15 @@ LIBS="$LIBS $LIBUV_LIBS"
AC_CHECK_FUNCS([uv_handle_get_data uv_handle_set_data uv_import])
AX_RESTORE_FLAGS([libuv])
# libnghttp2
AC_MSG_CHECKING([for libnghttp2])
PKG_CHECK_MODULES([LIBNGHTTP2], [libnghttp2 >= 1.40.0], [],
[AC_MSG_ERROR([libnghttp2 not found])])
AX_SAVE_FLAGS([libnghttp2])
CFLAGS="$CFLAGS $LIBNGHTTP2_CFLAGS"
LIBS="$LIBS $LIBNGHTTP2_LIBS"
#
# flockfile is usually provided by pthreads
#
@@ -592,7 +601,7 @@ CRYPTO=OpenSSL
#
# OpenSSL/LibreSSL is mandatory
#
PKG_CHECK_MODULES([OPENSSL], [libcrypto], [],
PKG_CHECK_MODULES([OPENSSL], [libssl libcrypto], [],
[AX_CHECK_OPENSSL([:],[AC_MSG_FAILURE([OpenSSL/LibreSSL not found])])])
AX_SAVE_FLAGS([openssl])
+3
View File
@@ -123,10 +123,12 @@ libisc_la_SOURCES = \
$(libisc_la_HEADERS) \
$(pk11_HEADERS) \
$(pkcs11_HEADERS) \
netmgr/http.c \
netmgr/netmgr-int.h \
netmgr/netmgr.c \
netmgr/tcp.c \
netmgr/tcpdns.c \
netmgr/tls.c \
netmgr/udp.c \
netmgr/uv-compat.c \
netmgr/uv-compat.h \
@@ -147,6 +149,7 @@ libisc_la_SOURCES = \
unix/stdtime.c \
unix/syslog.c \
unix/time.c \
url.c \
pk11.c \
pk11_result.c \
aes.c \
+13 -9
View File
@@ -75,7 +75,10 @@ struct isc_httpd {
isc_httpdmgr_t *mgr; /*%< our parent */
ISC_LINK(isc_httpd_t) link;
isc_nmhandle_t *handle;
isc_nmhandle_t *handle; /* Permanent pointer to handle */
isc_nmhandle_t *readhandle; /* Waiting for a read callback */
isc_nmhandle_t *sendhandle; /* Waiting for a send callback */
state_t state;
int flags;
@@ -636,6 +639,7 @@ new_httpd(isc_httpdmgr_t *httpdmgr, isc_nmhandle_t *handle) {
if (httpd->handle == NULL) {
isc_nmhandle_setdata(handle, httpd, httpd_reset, httpd_put);
httpd->handle = handle;
} else {
INSIST(httpd->handle == handle);
}
@@ -663,10 +667,10 @@ new_httpd(isc_httpdmgr_t *httpdmgr, isc_nmhandle_t *handle) {
ISC_LIST_APPEND(httpdmgr->running, httpd, link);
UNLOCK(&httpdmgr->lock);
isc_nmhandle_ref(handle);
isc_nmhandle_attach(handle, &httpd->readhandle);
result = isc_nm_read(handle, httpd_request, httpdmgr);
if (result != ISC_R_SUCCESS) {
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&httpd->readhandle);
}
return (result);
@@ -958,18 +962,18 @@ httpd_request(isc_nmhandle_t *handle, isc_result_t eresult,
isc_nm_pauseread(handle);
httpd->state = SEND;
isc_nmhandle_ref(handle);
isc_nmhandle_attach(handle, &httpd->sendhandle);
result = isc_nm_send(handle, &r, httpd_senddone, httpd);
if (result != ISC_R_SUCCESS) {
isc_nm_resumeread(handle);
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&httpd->sendhandle);
httpd->state = RECV;
}
return;
done:
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&httpd->readhandle);
}
void
@@ -990,7 +994,7 @@ isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
httpd = ISC_LIST_HEAD(httpdmgr->running);
while (httpd != NULL) {
isc_nmhandle_unref(httpd->handle);
isc_nmhandle_detach(&httpd->readhandle);
httpd = ISC_LIST_NEXT(httpd, link);
}
UNLOCK(&httpdmgr->lock);
@@ -1139,7 +1143,7 @@ httpd_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
httpd->state = RECV;
isc_nm_resumeread(handle);
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&httpd->sendhandle);
}
isc_result_t
@@ -1176,7 +1180,7 @@ void
isc_httpd_setfinishhook(void (*fn)(void)) {
#if ENABLE_AFL
finishhook = fn;
#else /* ENABLE_AFL */
#else /* ENABLE_AFL */
UNUSED(fn);
#endif /* ENABLE_AFL */
}
+63 -9
View File
@@ -16,6 +16,8 @@
#include <isc/result.h>
#include <isc/types.h>
#include <openssl/ssl.h>
/*
* Replacement for isc_sockettype_t provided by socket.h.
*/
@@ -110,9 +112,9 @@ isc_nmsocket_close(isc_nmsocket_t **sockp);
*/
void
isc_nmhandle_ref(isc_nmhandle_t *handle);
isc_nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **dest);
void
isc_nmhandle_unref(isc_nmhandle_t *handle);
isc_nmhandle_detach(isc_nmhandle_t **handlep);
/*%<
* Increment/decrement the reference counter in a netmgr handle,
* but (unlike the attach/detach functions) do not change the pointer
@@ -176,8 +178,21 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
* as its argument.
*
* When handles are allocated for the socket, 'extrasize' additional bytes
* will be allocated along with the handle for an associated object
* (typically ns_client).
* can be allocated along with the handle for an associated object, which
* can then be freed automatically when the handle is destroyed.
*/
isc_result_t
isc_nm_udpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, size_t extrahandlesize);
/*%<
* Open a UDP socket, bind to 'local' and connect to 'peer', and
* immediately call 'cb' with a handle so that the caller can begin
* sending packets over UDP.
*
* When handles are allocated for the socket, 'extrasize' additional bytes
* can be allocated along with the handle for an associated object, which
* can then be freed automatically when the handle is destroyed.
*/
void
@@ -196,12 +211,17 @@ isc_nm_pause(isc_nm_t *mgr);
void
isc_nm_resume(isc_nm_t *mgr);
/*%<
* Resume paused processing. It will return immediately
* after signalling workers to resume.
* Resume paused processing. It will return immediately after signalling
* workers to resume.
*/
isc_result_t
isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
/*
* Begin (or continue) reading on the socket associated with 'handle', and
* update its recv callback to 'cb', which will be called as soon as there
* is data to process.
*/
isc_result_t
isc_nm_pauseread(isc_nmhandle_t *handle);
@@ -212,11 +232,12 @@ isc_nm_pauseread(isc_nmhandle_t *handle);
* \li 'handle' is a valid netmgr handle.
*/
void
bool
isc_nm_cancelread(isc_nmhandle_t *handle);
/*%<
* Cancel reading on a connected socket. Calls the read/recv callback on
* active handles with a result code of ISC_R_CANCELED.
* active handles with a result code of ISC_R_CANCELED. Returns true if
* the callback was called
*
* Requires:
* \li 'sock' is a valid netmgr socket
@@ -289,7 +310,8 @@ isc_result_t
isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
void *cbarg, isc_nm_accept_cb_t accept_cb,
void *accept_cbarg, size_t extrahandlesize, int backlog,
isc_quota_t *quota, isc_nmsocket_t **sockp);
isc_quota_t *quota, SSL_CTX *sslctx,
isc_nmsocket_t **sockp);
/*%<
* Start listening for DNS messages over the TCP interface 'iface', using
* net manager 'mgr'.
@@ -367,6 +389,17 @@ isc_nm_tcp_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
* \li 'mgr' is a valid netmgr.
*/
isc_result_t
isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface,
isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
size_t extrahandlesize, int backlog, isc_quota_t *quota,
SSL_CTX *sslctx, isc_nmsocket_t **sockp);
isc_result_t
isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, SSL_CTX *ctx,
size_t extrahandlesize);
void
isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp);
/*%<
@@ -385,3 +418,24 @@ isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats);
*\li stats is a valid set of statistics counters supporting the
* full range of socket-related stats counter numbers.
*/
isc_result_t
isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, size_t extrahandlesize);
isc_result_t
isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, size_t extrahandlesize);
/*%<
* Establish a DNS client connection over either a TCP or a TLS socket.
*/
isc_result_t
isc_nm_doh_request(isc_nm_t *mgr, const char *uri, isc_region_t *message,
isc_nm_recv_cb_t cb, void *cbarg, SSL_CTX *ctx);
/*%<
* Perform a DoH request on the uri provided. message is the DNS message
* that will be POSTed to URI, cb is called with a NULL handle,
* ctx can be NULL (it will be created).
*/
+1
View File
@@ -88,6 +88,7 @@
#define ISC_R_DISCFULL 67 /*%< disc full */
#define ISC_R_DEFAULT 68 /*%< default */
#define ISC_R_IPV4PREFIX 69 /*%< IPv4 prefix */
#define ISC_R_TLSERROR 70 /*%< TLS error */
/*% Not a result code: the number of results. */
#define ISC_R_NRESULTS 70
+78
View File
@@ -0,0 +1,78 @@
/*
* 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.
*/
/*
* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <stdbool.h>
#include <stdint.h>
/*
* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
#define HTTP_PARSER_STRICT 1
#endif
typedef enum {
ISC_UF_SCHEMA = 0,
ISC_UF_HOST = 1,
ISC_UF_PORT = 2,
ISC_UF_PATH = 3,
ISC_UF_QUERY = 4,
ISC_UF_FRAGMENT = 5,
ISC_UF_USERINFO = 6,
ISC_UF_MAX = 7
} isc_url_field_t;
/* Result structure for isc_url_parse().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
typedef struct {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[ISC_UF_MAX];
} isc_url_parser_t;
int
isc_url_parse(const char *buf, size_t buflen, bool is_connect,
isc_url_parser_t *up);
/*%<
* Parse a URL; return nonzero on failure
*/
+642
View File
@@ -0,0 +1,642 @@
/*
* 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 <nghttp2/nghttp2.h>
#include <signal.h>
#include <string.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <isc/base64.h>
#include <isc/netmgr.h>
#include <isc/url.h>
#include "netmgr-int.h"
#define AUTHEXTRA 7
typedef struct {
char *uri;
isc_url_parser_t up;
char *authority;
size_t authoritylen;
char *path;
size_t pathlen;
int32_t stream_id;
isc_region_t *postdata;
size_t postdata_pos;
} http2_stream_t;
typedef struct {
isc_mem_t *mctx;
nghttp2_session *ngsession;
http2_stream_t *stream;
isc_nmhandle_t *handle;
isc_region_t r;
uint8_t buf[65535];
size_t bufsize;
uint8_t rbuf[65535];
size_t rbufsize;
isc_nm_recv_cb_t cb;
void *cbarg;
SSL_CTX *ctx;
bool reading;
} http2_session_t;
static bool
http2_do_bio(http2_session_t *session);
static void
writecb(isc_nmhandle_t *handle, isc_result_t result, void *ptr);
static isc_result_t
get_http2_stream(isc_mem_t *mctx, http2_stream_t **streamp, const char *uri,
uint16_t *port) {
http2_stream_t *stream = NULL;
int rv;
REQUIRE(streamp != NULL && *streamp == NULL);
REQUIRE(uri != NULL);
REQUIRE(port != NULL);
stream = isc_mem_get(mctx, sizeof(http2_stream_t));
stream->uri = isc_mem_strdup(mctx, uri);
rv = isc_url_parse(stream->uri, strlen(stream->uri), 0, &stream->up);
if (rv != 0) {
isc_mem_put(mctx, stream, sizeof(http2_stream_t));
isc_mem_free(mctx, stream->uri);
return (ISC_R_FAILURE);
}
stream->stream_id = -1;
stream->authoritylen = stream->up.field_data[ISC_UF_HOST].len;
stream->authority = isc_mem_get(mctx, stream->authoritylen + AUTHEXTRA);
memcpy(stream->authority, &uri[stream->up.field_data[ISC_UF_HOST].off],
stream->up.field_data[ISC_UF_HOST].len);
if (stream->up.field_set & (1 << ISC_UF_PORT)) {
stream->authoritylen += (size_t)snprintf(
stream->authority +
stream->up.field_data[ISC_UF_HOST].len,
AUTHEXTRA, ":%u", stream->up.port);
}
/* If we don't have path in URI, we use "/" as path. */
stream->pathlen = 1;
if (stream->up.field_set & (1 << ISC_UF_PATH)) {
stream->pathlen = stream->up.field_data[ISC_UF_PATH].len;
}
if (stream->up.field_set & (1 << ISC_UF_QUERY)) {
/* +1 for '?' character */
stream->pathlen +=
(size_t)(stream->up.field_data[ISC_UF_QUERY].len + 1);
}
stream->path = isc_mem_get(mctx, stream->pathlen);
if (stream->up.field_set & (1 << ISC_UF_PATH)) {
memcpy(stream->path,
&uri[stream->up.field_data[ISC_UF_PATH].off],
stream->up.field_data[ISC_UF_PATH].len);
} else {
stream->path[0] = '/';
}
if (stream->up.field_set & (1 << ISC_UF_QUERY)) {
stream->path[stream->pathlen -
stream->up.field_data[ISC_UF_QUERY].len - 1] = '?';
memcpy(stream->path + stream->pathlen -
stream->up.field_data[ISC_UF_QUERY].len,
&uri[stream->up.field_data[ISC_UF_QUERY].off],
stream->up.field_data[ISC_UF_QUERY].len);
}
*port = 443;
if ((stream->up.field_set & (1 << ISC_UF_PORT)) != 0) {
*port = stream->up.port;
}
*streamp = stream;
return (ISC_R_SUCCESS);
}
static void
put_http2_stream(isc_mem_t *mctx, http2_stream_t *stream) {
isc_mem_put(mctx, stream->path, stream->pathlen);
isc_mem_put(mctx, stream->authority, stream->authoritylen + AUTHEXTRA);
isc_mem_put(mctx, stream, sizeof(http2_stream_t));
}
static void
delete_http2_session(http2_session_t *session) {
if (session->handle != NULL) {
isc_nmhandle_detach(&session->handle);
}
if (session->ngsession != NULL) {
nghttp2_session_del(session->ngsession);
session->ngsession = NULL;
}
if (session->stream != NULL) {
put_http2_stream(session->mctx, session->stream);
session->stream = NULL;
}
isc_mem_putanddetach(&session->mctx, session, sizeof(http2_session_t));
}
#if 0
/* XXXWPK do we need these callback? We might want to verify headers */
on_header_callback(nghttp2_session *ngsession, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen, const uint8_t *value,
size_t valuelen, uint8_t flags, void *user_data) {
http2_session_t *session = (http2_session_t *)user_data;
UNUSED(ngsession);
UNUSED(flags);
switch (frame->hd.type) {
case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session->stream->stream_id == frame->hd.stream_id)
{
break;
}
}
return (0);
}
static int
on_begin_headers_callback(nghttp2_session *ngsession,
const nghttp2_frame *frame, void *user_data) {
http2_session_t *session = (http2_session_t *)user_data;
UNUSED(ngsession);
switch (frame->hd.type) {
case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session->stream->stream_id == frame->hd.stream_id)
{
/* XXX */
}
break;
}
return (0);
}
on_frame_recv_callback(nghttp2_session *ngsession, const nghttp2_frame *frame,
void *user_data) {
http2_session_t *session = (http2_session_t *)user_data;
UNUSED(ngsession);
switch (frame->hd.type) {
case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session->stream->stream_id == frame->hd.stream_id)
{
/* XXX */
}
break;
}
return (0);
}
#endif
static int
on_data_chunk_recv_callback(nghttp2_session *ngsession, uint8_t flags,
int32_t stream_id, const uint8_t *data, size_t len,
void *user_data) {
http2_session_t *session = (http2_session_t *)user_data;
UNUSED(ngsession);
UNUSED(flags);
if (session->stream->stream_id == stream_id) {
/* TODO buffer overrun! */
memmove(session->rbuf + session->rbufsize, data, len);
session->rbufsize += len;
}
return (0);
}
static int
on_stream_close_callback(nghttp2_session *ngsession, int32_t stream_id,
uint32_t error_code, void *user_data) {
http2_session_t *session = NULL;
UNUSED(error_code);
session = (http2_session_t *)user_data;
if (session->stream->stream_id == stream_id) {
int rv = nghttp2_session_terminate_session(ngsession,
NGHTTP2_NO_ERROR);
if (rv != 0) {
return (NGHTTP2_ERR_CALLBACK_FAILURE);
}
}
session->cb(NULL, ISC_R_SUCCESS,
&(isc_region_t){ session->rbuf, session->rbufsize },
session->cbarg);
/* XXXWPK TODO we need to close the session */
return (0);
}
#ifndef OPENSSL_NO_NEXTPROTONEG
/*
* NPN TLS extension client callback. We check that server advertised
* the HTTP/2 protocol the nghttp2 library supports. If not, exit the
* program.
*/
static int
select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg) {
UNUSED(ssl);
UNUSED(arg);
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
/* TODO */
}
return (SSL_TLSEXT_ERR_OK);
}
#endif /* !OPENSSL_NO_NEXTPROTONEG */
/* Create SSL_CTX. */
static SSL_CTX *
create_ssl_ctx(void) {
SSL_CTX *ssl_ctx = NULL;
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
RUNTIME_CHECK(ssl_ctx != NULL);
SSL_CTX_set_options(
ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
#ifndef OPENSSL_NO_NEXTPROTONEG
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
#endif /* !OPENSSL_NO_NEXTPROTONEG */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
return (ssl_ctx);
}
static void
initialize_nghttp2_session(http2_session_t *session) {
nghttp2_session_callbacks *callbacks = NULL;
nghttp2_session_callbacks_new(&callbacks);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);
#if 0
/* Do we need it ? */
nghttp2_session_callbacks_set_on_header_callback(callbacks,
on_header_callback);
nghttp2_session_callbacks_set_on_begin_headers_callback(
callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(
callbacks, on_frame_recv_callback);
#endif
nghttp2_session_client_new(&session->ngsession, callbacks, session);
nghttp2_session_callbacks_del(callbacks);
}
static void
send_client_connection_header(http2_session_t *session) {
nghttp2_settings_entry iv[1] = {
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
};
int rv;
rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv,
1);
if (rv != 0) {
/* TODO */
}
http2_do_bio(session);
}
#define MAKE_NV(NAME, VALUE, VALUELEN) \
{ \
(uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \
NGHTTP2_NV_FLAG_NONE \
}
#define MAKE_NV2(NAME, VALUE) \
{ \
(uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, \
sizeof(VALUE) - 1, NGHTTP2_NV_FLAG_NONE \
}
static ssize_t
post_read_callback(nghttp2_session *ngsession, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data) {
http2_session_t *session = (http2_session_t *)user_data;
UNUSED(ngsession);
UNUSED(source);
if (session->stream->stream_id == stream_id) {
size_t len = session->stream->postdata->length -
session->stream->postdata_pos;
if (len > length) {
len = length;
}
memcpy(buf,
session->stream->postdata->base +
session->stream->postdata_pos,
len);
session->stream->postdata_pos += len;
if (session->stream->postdata_pos ==
session->stream->postdata->length) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
return (len);
}
return (0);
}
/* Send HTTP request to the remote peer */
static isc_result_t
submit_request(http2_session_t *session) {
int32_t stream_id;
http2_stream_t *stream = session->stream;
char *uri = stream->uri;
isc_url_parser_t *up = &stream->up;
char p[64];
snprintf(p, 64, "%u", stream->postdata->length);
nghttp2_nv hdrs[] = {
MAKE_NV2(":method", "POST"),
MAKE_NV(":scheme", &uri[up->field_data[ISC_UF_SCHEMA].off],
up->field_data[ISC_UF_SCHEMA].len),
MAKE_NV(":authority", stream->authority, stream->authoritylen),
MAKE_NV(":path", stream->path, stream->pathlen),
MAKE_NV2("content-type", "application/dns-message"),
MAKE_NV2("accept", "application/dns-message"),
MAKE_NV("content-length", p, strlen(p))
};
nghttp2_data_provider dp = { .read_callback = post_read_callback };
stream_id = nghttp2_submit_request(session->ngsession, NULL, hdrs, 7,
&dp, stream);
if (stream_id < 0) {
return (ISC_R_FAILURE);
}
stream->stream_id = stream_id;
http2_do_bio(session);
return (ISC_R_SUCCESS);
}
/*
* read callback from TLS socket.
*/
static void
readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
void *data) {
http2_session_t *session = NULL;
ssize_t readlen;
UNUSED(handle);
UNUSED(result);
session = (http2_session_t *)data;
readlen = nghttp2_session_mem_recv(session->ngsession, region->base,
region->length);
if (readlen < 0) {
delete_http2_session(session);
/* TODO callback! */
return;
}
if (readlen < region->length) {
INSIST(session->bufsize == 0);
INSIST(region->length - readlen < 65535);
memmove(session->buf, region->base, region->length - readlen);
session->bufsize = region->length - readlen;
isc_nm_pauseread(session->handle);
}
/* We might have something to receive or send, do IO */
http2_do_bio(session);
}
static bool
http2_do_bio(http2_session_t *session) {
if (nghttp2_session_want_read(session->ngsession) == 0 &&
nghttp2_session_want_write(session->ngsession) == 0)
{
delete_http2_session(session);
return (false);
}
if (nghttp2_session_want_read(session->ngsession) != 0) {
if (!session->reading) {
/* We have not yet started reading from this handle */
isc_nm_read(session->handle, readcb, session);
session->reading = true;
} else if (session->bufsize > 0) {
/* Leftover data in the buffer, use it */
size_t readlen = nghttp2_session_mem_recv(
session->ngsession, session->buf,
session->bufsize);
if (readlen == session->bufsize) {
session->bufsize = 0;
} else {
memmove(session->buf, session->buf + readlen,
session->bufsize - readlen);
session->bufsize -= readlen;
}
http2_do_bio(session);
return (false);
} else {
/* Resume reading, it's idempotent, wait for more */
isc_nm_resumeread(session->handle);
}
} else {
/* We don't want more data, stop reading for now */
isc_nm_pauseread(session->handle);
}
if (nghttp2_session_want_write(session->ngsession) != 0) {
isc_result_t result;
const uint8_t *data;
size_t sz;
/*
* XXXWPK TODO
* This function may produce very small byte string. If that
* is the case, and application disables Nagle algorithm
* (``TCP_NODELAY``), then writing this small chunk leads to
* very small packet, and it is very inefficient. An
* application should be responsible to buffer up small chunks
* of data as necessary to avoid this situation.
*/
sz = nghttp2_session_mem_send(session->ngsession, &data);
session->r.base = isc_mem_get(session->mctx, sz);
session->r.length = sz;
memcpy(session->r.base, data, sz);
result = isc_nm_send(session->handle, &session->r, writecb,
session);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
return (true);
}
return (false);
}
static void
writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
http2_session_t *session = (http2_session_t *)arg;
UNUSED(handle);
UNUSED(result);
http2_do_bio(session);
isc_mem_put(session->mctx, session->r.base, session->r.length);
}
static void
connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
http2_session_t *session = (http2_session_t *)arg;
if (result != ISC_R_SUCCESS) {
delete_http2_session(session);
return;
}
isc_nmhandle_attach(handle, &session->handle);
#if 0
/* TODO H2 */
#ifndef OPENSSL_NO_NEXTPROTONEG
SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
if (alpn == NULL) {
SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
}
#endif
if (alpn == NULL || alpnlen != 2 ||
memcmp("h2", alpn, 2) != 0)
{
fprintf(stderr, "h2 is not negotiated\n");
delete_http2_session(session);
return;
}
#endif
initialize_nghttp2_session(session);
send_client_connection_header(session);
submit_request(session);
http2_do_bio(session);
}
isc_result_t
isc_nm_doh_request(isc_nm_t *mgr, const char *uri, isc_region_t *message,
isc_nm_recv_cb_t cb, void *cbarg, SSL_CTX *ctx) {
uint16_t port;
char *host = NULL;
http2_session_t *session = NULL;
struct addrinfo hints;
struct addrinfo *res = NULL;
isc_sockaddr_t local, peer;
isc_result_t result;
int s;
if (ctx == NULL) {
ctx = create_ssl_ctx();
}
session = isc_mem_get(mgr->mctx, sizeof(http2_session_t));
*session = (http2_session_t){ .cb = cb, .cbarg = cbarg, .ctx = ctx };
isc_mem_attach(mgr->mctx, &session->mctx);
result = get_http2_stream(mgr->mctx, &session->stream, uri, &port);
if (result != ISC_R_SUCCESS) {
delete_http2_session(session);
return (result);
}
session->stream->postdata = message;
session->stream->postdata_pos = 0;
/* TODO do this properly!!! */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags |= AI_CANONNAME;
host = strndup(
&session->stream
->uri[session->stream->up.field_data[ISC_UF_HOST].off],
session->stream->up.field_data[ISC_UF_HOST].len);
s = getaddrinfo(host, NULL, &hints, &res);
free(host);
if (s != 0) {
return (ISC_R_FAILURE);
}
isc_sockaddr_fromsockaddr(&peer, res->ai_addr);
isc_sockaddr_setport(&peer, port);
isc_sockaddr_anyofpf(&local, res->ai_family);
freeaddrinfo(res);
result = isc_nm_tlsconnect(mgr, (isc_nmiface_t *)&local,
(isc_nmiface_t *)&peer, connect_cb, session,
ctx, 0);
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_SUCCESS);
}
+164 -18
View File
@@ -14,6 +14,9 @@
#include <unistd.h>
#include <uv.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <isc/astack.h>
#include <isc/atomic.h>
#include <isc/buffer.h>
@@ -49,6 +52,16 @@
#define ISC_NETMGR_RECVBUF_SIZE (65536)
#endif
/*
* Define NETMGR_TRACE to activate tracing of handles and sockets.
* This will impair performance but enables us to quickly determine,
* if netmgr resources haven't been cleaned up on shutdown, which ones
* are still in use.
*/
#ifdef NETMGR_TRACE
#define TRACE_SIZE 8
#endif
/*
* Single network event loop worker.
*/
@@ -104,6 +117,11 @@ struct isc_nmhandle {
isc_sockaddr_t local;
isc_nm_opaquecb_t doreset; /* reset extra callback, external */
isc_nm_opaquecb_t dofree; /* free extra callback, external */
#ifdef NETMGR_TRACE
void *backtrace[TRACE_SIZE];
int backtrace_size;
LINK(isc_nmhandle_t) active_link;
#endif
void *opaque;
char extra[];
};
@@ -116,13 +134,14 @@ struct isc_nmiface {
};
typedef enum isc__netievent_type {
netievent_udpconnect,
netievent_udpsend,
netievent_udprecv,
netievent_udpread,
netievent_udpstop,
netievent_udpclose,
netievent_tcpconnect,
netievent_tcpsend,
netievent_tcprecv,
netievent_tcpstartread,
netievent_tcppauseread,
netievent_tcpchildaccept,
@@ -130,8 +149,14 @@ typedef enum isc__netievent_type {
netievent_tcpstop,
netievent_tcpclose,
netievent_tcpdnsclose,
netievent_tcpdnssend,
netievent_tcpdnsread,
netievent_tcpdnsclose,
netievent_tlsclose,
netievent_tlssend,
netievent_tlsstartread,
netievent_tlsconnect,
netievent_closecb,
netievent_shutdown,
@@ -146,20 +171,12 @@ typedef enum isc__netievent_type {
netievent_tcplisten,
} isc__netievent_type;
/*
* We have to split it because we can read and write on a socket
* simultaneously.
*/
typedef union {
isc_nm_recv_cb_t recv;
isc_nm_cb_t connect;
isc_nm_accept_cb_t accept;
} isc__nm_readcb_t;
typedef union {
isc_nm_cb_t send;
isc_nm_cb_t connect;
} isc__nm_writecb_t;
typedef union {
isc_nm_recv_cb_t recv;
isc_nm_accept_cb_t accept;
@@ -206,13 +223,18 @@ typedef struct isc__netievent__socket {
} isc__netievent__socket_t;
typedef isc__netievent__socket_t isc__netievent_udplisten_t;
typedef isc__netievent__socket_t isc__netievent_udpread_t;
typedef isc__netievent__socket_t isc__netievent_udpstop_t;
typedef isc__netievent__socket_t isc__netievent_udpclose_t;
typedef isc__netievent__socket_t isc__netievent_tcpstop_t;
typedef isc__netievent__socket_t isc__netievent_tcpclose_t;
typedef isc__netievent__socket_t isc__netievent_tlsclose_t;
typedef isc__netievent__socket_t isc__netievent_tcpdnsclose_t;
typedef isc__netievent__socket_t isc__netievent_startread_t;
typedef isc__netievent__socket_t isc__netievent_pauseread_t;
typedef isc__netievent__socket_t isc__netievent_closecb_t;
typedef isc__netievent__socket_t isc__netievent_tcpdnsclose_t;
typedef isc__netievent__socket_t isc__netievent_tcpdnsread_t;
typedef struct isc__netievent__socket_req {
isc__netievent_type type;
@@ -256,6 +278,20 @@ typedef struct isc__netievent_udpsend {
isc__nm_uvreq_t *req;
} isc__netievent_udpsend_t;
typedef struct isc__netievent_udpconnect {
isc__netievent_type type;
isc_nmsocket_t *sock;
isc_sockaddr_t peer;
} isc__netievent_udpconnect_t;
typedef struct isc__netievent_tlsconnect {
isc__netievent_type type;
isc_nmsocket_t *sock;
SSL_CTX *ctx;
isc_sockaddr_t local; /* local address */
isc_sockaddr_t peer; /* peer address */
} isc__netievent_tlsconnect_t;
typedef struct isc__netievent {
isc__netievent_type type;
} isc__netievent_t;
@@ -325,6 +361,10 @@ struct isc_nm {
uint32_t idle;
uint32_t keepalive;
uint32_t advertised;
#ifdef NETMGR_TRACE
ISC_LIST(isc_nmsocket_t) active_sockets;
#endif
};
typedef enum isc_nmsocket_type {
@@ -333,7 +373,9 @@ typedef enum isc_nmsocket_type {
isc_nm_tcpsocket,
isc_nm_tcplistener,
isc_nm_tcpdnslistener,
isc_nm_tcpdnssocket
isc_nm_tcpdnssocket,
isc_nm_tlslistener,
isc_nm_tlssocket
} isc_nmsocket_type;
/*%
@@ -371,6 +413,16 @@ struct isc_nmsocket {
/*% Self, for self-contained unreferenced sockets (tcpdns) */
isc_nmsocket_t *self;
/*% TLS stuff */
struct tls {
bool server;
BIO *app_bio;
SSL *ssl;
SSL_CTX *ctx;
BIO *ssl_bio;
enum { INIT, HANDSHAKE, IO, CLOSING } state;
} tls;
/*%
* quota is the TCP client, attached when a TCP connection
* is established. pquota is a non-attached pointer to the
@@ -403,7 +455,7 @@ struct isc_nmsocket {
isc_nmsocket_t *children;
int nchildren;
isc_nmiface_t *iface;
isc_nmhandle_t *tcphandle;
isc_nmhandle_t *statichandle;
isc_nmhandle_t *outerhandle;
/*% Extra data allocated at the end of each isc_nmhandle_t */
@@ -445,7 +497,12 @@ struct isc_nmsocket {
isc_refcount_t references;
/*%
* TCPDNS socket has been set not to pipeliine.
* Established an outgoing connection, as client not server.
*/
atomic_bool client;
/*%
* TCPDNS socket has been set not to pipeline.
*/
atomic_bool sequential;
@@ -536,6 +593,12 @@ struct isc_nmsocket {
isc__nm_cb_t accept_cb;
void *accept_cbarg;
#ifdef NETMGR_TRACE
void *backtrace[TRACE_SIZE];
int backtrace_size;
LINK(isc_nmsocket_t) active_link;
ISC_LIST(isc_nmhandle_t) active_handles;
#endif
};
bool
@@ -663,16 +726,40 @@ isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
* Back-end implementation of isc_nm_send() for UDP handles.
*/
isc_result_t
isc__nm_udp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
/*
* Back-end implementation of isc_nm_read() for UDP handles.
*/
void
isc__nm_udp_close(isc_nmsocket_t *sock);
/*%<
* Close a UDP socket.
*/
void
isc__nm_udp_stoplistening(isc_nmsocket_t *sock);
bool
isc__nm_udp_cancelread(isc_nmhandle_t *handle);
/*%<
* Stop reading on a pending UDP handle.
*/
void
isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_udpconnect(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_udpsend(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_udpread(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_udpclose(isc__networker_t *worker, isc__netievent_t *ev0);
/*%<
* Callback handlers for asynchronous UDP events (listen, stoplisten, send).
*/
@@ -686,6 +773,9 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
isc_result_t
isc__nm_tcp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
/*
* Back-end implementation of isc_nm_read() for TCP handles.
*/
void
isc__nm_tcp_close(isc_nmsocket_t *sock);
@@ -712,10 +802,10 @@ isc__nm_tcp_shutdown(isc_nmsocket_t *sock);
* sockets.
*/
void
isc__nm_tcp_cancelread(isc_nmsocket_t *sock);
bool
isc__nm_tcp_cancelread(isc_nmhandle_t *handle);
/*%<
* Stop reading on a connected socket.
* Stop reading on a connected TCP handle.
*/
void
@@ -748,6 +838,22 @@ isc__nm_async_tcpclose(isc__networker_t *worker, isc__netievent_t *ev0);
* stoplisten, send, read, pause, close).
*/
void
isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tls_startread(isc__networker_t *worker, isc__netievent_t *ev0);
/*%<
* Callback handlers for asynchronouse TLS events.
*/
isc_result_t
isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region,
isc_nm_cb_t cb, void *cbarg);
@@ -770,6 +876,46 @@ isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0);
isc_result_t
isc__nm_tcpdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
bool
isc__nm_tcpdns_cancelread(isc_nmhandle_t *handle);
/*%<
* Stop reading on a connected TCPDNS handle.
*/
isc_result_t
isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void *cbarg);
isc_result_t
isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
void
isc__nm_tls_close(isc_nmsocket_t *sock);
/*%<
* Close a TLS socket.
*/
isc_result_t
isc__nm_tls_pauseread(isc_nmsocket_t *sock);
/*%<
* Pause reading on this socket, while still remembering the callback.
*/
isc_result_t
isc__nm_tls_resumeread(isc_nmsocket_t *sock);
/*%<
* Resume reading from socket.
*
*/
void
isc__nm_tls_stoplistening(isc_nmsocket_t *sock);
#define isc__nm_uverr2result(x) \
isc___nm_uverr2result(x, true, __FILE__, __LINE__)
isc_result_t
+129 -17
View File
@@ -33,6 +33,10 @@
#include "netmgr-int.h"
#include "uv-compat.h"
#ifdef NETMGR_TRACE
#include <execinfo.h>
#endif
/*%
* How many isc_nmhandles and isc_nm_uvreqs will we be
* caching for reuse in a socket.
@@ -157,6 +161,10 @@ isc_nm_start(isc_mem_t *mctx, uint32_t workers) {
atomic_init(&mgr->paused, false);
atomic_init(&mgr->interlocked, false);
#ifdef NETMGR_TRACE
ISC_LIST_INIT(mgr->active_sockets);
#endif
/*
* Default TCP timeout values.
* May be updated by isc_nm_tcptimeouts().
@@ -393,6 +401,7 @@ isc_nm_closedown(isc_nm_t *mgr) {
void
isc_nm_destroy(isc_nm_t **mgr0) {
isc_nm_t *mgr = NULL;
int counter = 0;
REQUIRE(mgr0 != NULL);
REQUIRE(VALID_NM(*mgr0));
@@ -407,7 +416,7 @@ isc_nm_destroy(isc_nm_t **mgr0) {
/*
* Wait for the manager to be dereferenced elsewhere.
*/
while (isc_refcount_current(&mgr->references) > 1) {
while (isc_refcount_current(&mgr->references) > 1 && counter++ < 1000) {
/*
* Sometimes libuv gets stuck, pausing and unpausing
* netmgr goes over all events in async queue for all
@@ -418,10 +427,14 @@ isc_nm_destroy(isc_nm_t **mgr0) {
isc_nm_resume(mgr);
#ifdef WIN32
_sleep(10);
#else /* ifdef WIN32 */
#else /* ifdef WIN32 */
usleep(10000);
#endif /* ifdef WIN32 */
}
#ifdef NETMGR_TRACE
INSIST(ISC_LIST_EMPTY(mgr->active_sockets));
#endif
INSIST(counter <= 1000);
/*
* Detach final reference.
@@ -546,7 +559,7 @@ nm_thread(isc_threadarg_t worker0) {
*/
#ifdef WIN32
_sleep(100);
#else /* ifdef WIN32 */
#else /* ifdef WIN32 */
usleep(100000);
#endif /* ifdef WIN32 */
}
@@ -591,6 +604,10 @@ process_queue(isc__networker_t *worker, isc_queue_t *queue) {
uv_stop(&worker->loop);
isc_mempool_put(worker->mgr->evpool, ievent);
return;
case netievent_udpconnect:
isc__nm_async_udpconnect(worker, ievent);
break;
case netievent_udplisten:
isc__nm_async_udplisten(worker, ievent);
break;
@@ -600,6 +617,14 @@ process_queue(isc__networker_t *worker, isc_queue_t *queue) {
case netievent_udpsend:
isc__nm_async_udpsend(worker, ievent);
break;
case netievent_udpread:
isc__nm_async_udpread(worker, ievent);
break;
case netievent_udpclose:
isc__nm_async_udpclose(worker, ievent);
break;
case netievent_tcpconnect:
isc__nm_async_tcpconnect(worker, ievent);
break;
@@ -630,9 +655,27 @@ process_queue(isc__networker_t *worker, isc_queue_t *queue) {
case netievent_tcpclose:
isc__nm_async_tcpclose(worker, ievent);
break;
case netievent_tlsstartread:
isc__nm_async_tls_startread(worker, ievent);
break;
case netievent_tlssend:
isc__nm_async_tlssend(worker, ievent);
break;
case netievent_tlsclose:
isc__nm_async_tlsclose(worker, ievent);
break;
case netievent_tlsconnect:
isc__nm_async_tlsconnect(worker, ievent);
break;
case netievent_tcpdnsclose:
isc__nm_async_tcpdnsclose(worker, ievent);
break;
case netievent_tcpdnsread:
isc__nm_async_tcpdnsread(worker, ievent);
break;
case netievent_closecb:
isc__nm_async_closecb(worker, ievent);
break;
@@ -739,11 +782,10 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree) {
isc__nm_decstats(sock->mgr, sock->statsindex[STATID_ACTIVE]);
}
sock->tcphandle = NULL;
sock->statichandle = NULL;
if (sock->outerhandle != NULL) {
isc_nmhandle_unref(sock->outerhandle);
sock->outerhandle = NULL;
isc_nmhandle_detach(&sock->outerhandle);
}
if (sock->outer != NULL) {
@@ -786,7 +828,11 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree) {
isc_mem_free(sock->mgr->mctx, sock->ah_handles);
isc_mutex_destroy(&sock->lock);
isc_condition_destroy(&sock->cond);
#ifdef NETMGR_TRACE
LOCK(&sock->mgr->lock);
ISC_LIST_UNLINK(sock->mgr->active_sockets, sock, active_link);
UNLOCK(&sock->mgr->lock);
#endif
if (dofree) {
isc_nm_t *mgr = sock->mgr;
isc_mem_put(mgr->mctx, sock, sizeof(*sock));
@@ -833,7 +879,7 @@ nmsocket_maybe_destroy(isc_nmsocket_t *sock) {
}
}
if (active_handles == 0 || sock->tcphandle != NULL) {
if (active_handles == 0 || sock->statichandle != NULL) {
destroy = true;
}
@@ -875,12 +921,18 @@ isc__nmsocket_prep_destroy(isc_nmsocket_t *sock) {
*/
if (!atomic_load(&sock->closed)) {
switch (sock->type) {
case isc_nm_udpsocket:
isc__nm_udp_close(sock);
return;
case isc_nm_tcpsocket:
isc__nm_tcp_close(sock);
return;
case isc_nm_tcpdnssocket:
isc__nm_tcpdns_close(sock);
return;
case isc_nm_tlssocket:
isc__nm_tls_close(sock);
break;
default:
break;
}
@@ -944,6 +996,15 @@ isc__nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
.inactivereqs = isc_astack_new(
mgr->mctx, ISC_NM_REQS_STACK_SIZE) };
#ifdef NETMGR_TRACE
sock->backtrace_size = backtrace(sock->backtrace, TRACE_SIZE);
ISC_LINK_INIT(sock, active_link);
ISC_LIST_INIT(sock->active_handles);
LOCK(&mgr->lock);
ISC_LIST_APPEND(mgr->active_sockets, sock, active_link);
UNLOCK(&mgr->lock);
#endif
isc_nm_attach(mgr, &sock->mgr);
sock->uv_handle.handle.data = sock;
@@ -1032,6 +1093,9 @@ alloc_handle(isc_nmsocket_t *sock) {
sizeof(isc_nmhandle_t) + sock->extrahandlesize);
*handle = (isc_nmhandle_t){ .magic = NMHANDLE_MAGIC };
#ifdef NETMGR_TRACE
ISC_LINK_INIT(handle, active_link);
#endif
isc_refcount_init(&handle->references, 1);
return (handle);
@@ -1057,6 +1121,10 @@ isc__nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
isc__nmsocket_attach(sock, &handle->sock);
#ifdef NETMGR_TRACE
handle->backtrace_size = backtrace(handle->backtrace, TRACE_SIZE);
#endif
if (peer != NULL) {
memcpy(&handle->peer, peer, sizeof(isc_sockaddr_t));
} else {
@@ -1097,21 +1165,29 @@ isc__nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
INSIST(sock->ah_handles[pos] == NULL);
sock->ah_handles[pos] = handle;
handle->ah_pos = pos;
#ifdef NETMGR_TRACE
ISC_LIST_APPEND(sock->active_handles, handle, active_link);
#endif
UNLOCK(&sock->lock);
if (sock->type == isc_nm_tcpsocket) {
INSIST(sock->tcphandle == NULL);
sock->tcphandle = handle;
if (sock->type == isc_nm_tcpsocket || sock->type == isc_nm_tlssocket ||
(sock->type == isc_nm_udpsocket && sock->client))
{
INSIST(sock->statichandle == NULL);
/* Note: statichandle is assigned, not attached. */
sock->statichandle = handle;
}
return (handle);
}
void
isc_nmhandle_ref(isc_nmhandle_t *handle) {
isc_nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **handlep) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(handlep != NULL && *handlep == NULL);
isc_refcount_increment(&handle->references);
*handlep = handle;
}
bool
@@ -1153,6 +1229,10 @@ nmhandle_deactivate(isc_nmsocket_t *sock, isc_nmhandle_t *handle) {
INSIST(sock->ah_size > handle->ah_pos);
INSIST(atomic_load(&sock->ah) > 0);
#ifdef NETMGR_TRACE
ISC_LIST_UNLINK(sock->active_handles, handle, active_link);
#endif
sock->ah_handles[handle->ah_pos] = NULL;
handlenum = atomic_fetch_sub(&sock->ah, 1) - 1;
sock->ah_frees[handlenum] = handle->ah_pos;
@@ -1167,14 +1247,20 @@ nmhandle_deactivate(isc_nmsocket_t *sock, isc_nmhandle_t *handle) {
}
void
isc_nmhandle_unref(isc_nmhandle_t *handle) {
isc_nmhandle_detach(isc_nmhandle_t **handlep) {
isc_nmsocket_t *sock = NULL;
isc_nmhandle_t *handle = NULL;
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(handlep != NULL);
REQUIRE(VALID_NMHANDLE(*handlep));
handle = *handlep;
*handlep = NULL;
if (isc_refcount_decrement(&handle->references) > 1) {
return;
}
/* We need an acquire memory barrier here */
(void)isc_refcount_current(&handle->references);
@@ -1208,6 +1294,11 @@ isc_nmhandle_unref(isc_nmhandle_t *handle) {
}
}
if (handle == sock->statichandle) {
/* statichandle is assigned, not attached. */
sock->statichandle = NULL;
}
isc__nmsocket_detach(&sock);
}
@@ -1309,7 +1400,7 @@ isc__nm_uvreq_put(isc__nm_uvreq_t **req0, isc_nmsocket_t *sock) {
}
if (handle != NULL) {
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&handle);
}
isc__nmsocket_detach(&sock);
@@ -1328,6 +1419,8 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
return (isc__nm_tcp_send(handle, region, cb, cbarg));
case isc_nm_tcpdnssocket:
return (isc__nm_tcpdns_send(handle, region, cb, cbarg));
case isc_nm_tlssocket:
return (isc__nm_tls_send(handle, region, cb, cbarg));
default:
INSIST(0);
ISC_UNREACHABLE();
@@ -1339,21 +1432,33 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
REQUIRE(VALID_NMHANDLE(handle));
switch (handle->sock->type) {
case isc_nm_udpsocket:
return (isc__nm_udp_read(handle, cb, cbarg));
case isc_nm_tcpsocket:
return (isc__nm_tcp_read(handle, cb, cbarg));
case isc_nm_tcpdnssocket:
return (isc__nm_tcpdns_read(handle, cb, cbarg));
case isc_nm_tlssocket:
return (isc__nm_tls_read(handle, cb, cbarg));
default:
INSIST(0);
ISC_UNREACHABLE();
}
}
void
bool
isc_nm_cancelread(isc_nmhandle_t *handle) {
REQUIRE(VALID_NMHANDLE(handle));
switch (handle->sock->type) {
case isc_nm_udpsocket:
return (isc__nm_udp_cancelread(handle));
break;
case isc_nm_tcpsocket:
isc__nm_tcp_cancelread(handle->sock);
return (isc__nm_tcp_cancelread(handle));
break;
case isc_nm_tcpdnssocket:
return (isc__nm_tcpdns_cancelread(handle));
break;
default:
INSIST(0);
@@ -1370,6 +1475,8 @@ isc_nm_pauseread(isc_nmhandle_t *handle) {
switch (sock->type) {
case isc_nm_tcpsocket:
return (isc__nm_tcp_pauseread(sock));
case isc_nm_tlssocket:
return (isc__nm_tls_pauseread(sock));
default:
INSIST(0);
ISC_UNREACHABLE();
@@ -1385,6 +1492,8 @@ isc_nm_resumeread(isc_nmhandle_t *handle) {
switch (sock->type) {
case isc_nm_tcpsocket:
return (isc__nm_tcp_resumeread(sock));
case isc_nm_tlssocket:
return (isc__nm_tls_resumeread(sock));
default:
INSIST(0);
ISC_UNREACHABLE();
@@ -1405,6 +1514,9 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) {
case isc_nm_tcplistener:
isc__nm_tcp_stoplistening(sock);
break;
case isc_nm_tlslistener:
isc__nm_tls_stoplistening(sock);
break;
default:
INSIST(0);
ISC_UNREACHABLE();
+55 -53
View File
@@ -147,49 +147,46 @@ done:
static void
tcp_connect_cb(uv_connect_t *uvreq, int status) {
isc_result_t result;
isc__nm_uvreq_t *req = (isc__nm_uvreq_t *)uvreq->data;
isc_nmsocket_t *sock = NULL;
struct sockaddr_storage ss;
isc_nmhandle_t *handle = NULL;
sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
REQUIRE(VALID_UVREQ(req));
if (status == 0) {
isc_result_t result;
struct sockaddr_storage ss;
isc_nmhandle_t *handle = NULL;
sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CONNECT]);
uv_tcp_getpeername(&sock->uv_handle.tcp, (struct sockaddr *)&ss,
&(int){ sizeof(ss) });
result = isc_sockaddr_fromsockaddr(&sock->peer,
(struct sockaddr *)&ss);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
handle = isc__nmhandle_get(sock, NULL, NULL);
req->cb.connect(handle, ISC_R_SUCCESS, req->cbarg);
isc__nm_uvreq_put(&req, sock);
/*
* The sock is now attached to the handle.
*/
isc__nmsocket_detach(&sock);
/*
* If the connect callback wants to hold on to the handle,
* it needs to attach to it.
*/
isc_nmhandle_unref(handle);
} else {
/*
* TODO:
* Handle the connect error properly and free the socket.
*/
if (status != 0) {
req->cb.connect(NULL, isc__nm_uverr2result(status), req->cbarg);
isc__nm_uvreq_put(&req, sock);
return;
}
sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CONNECT]);
uv_tcp_getpeername(&sock->uv_handle.tcp, (struct sockaddr *)&ss,
&(int){ sizeof(ss) });
result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
handle = isc__nmhandle_get(sock, NULL, NULL);
req->cb.connect(handle, ISC_R_SUCCESS, req->cbarg);
isc__nm_uvreq_put(&req, sock);
sock->client = true;
/*
* The sock is now attached to the handle.
*/
isc__nmsocket_detach(&sock);
/*
* If the connect callback wants to hold on to the handle,
* it needs to attach to it.
*/
isc_nmhandle_detach(&handle);
}
isc_result_t
@@ -201,6 +198,8 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(VALID_NM(mgr));
REQUIRE(local != NULL);
REQUIRE(peer != NULL);
nsock = isc_mem_get(mgr->mctx, sizeof(*nsock));
isc__nmsocket_init(nsock, mgr, isc_nm_tcpsocket, local);
@@ -211,6 +210,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
req->cb.connect = cb;
req->cbarg = cbarg;
req->peer = peer->addr;
req->local = local->addr;
ievent = isc__nm_get_ievent(mgr, netievent_tcpconnect);
ievent->sock = nsock;
@@ -501,7 +501,7 @@ isc__nm_async_tcpchildaccept(isc__networker_t *worker, isc__netievent_t *ev0) {
* If the accept callback wants to hold on to the handle,
* it needs to attach to it.
*/
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&handle);
return;
error:
@@ -604,7 +604,7 @@ readtimeout_cb(uv_timer_t *handle) {
isc_quota_detach(&sock->quota);
}
if (sock->rcb.recv != NULL) {
sock->rcb.recv(sock->tcphandle, ISC_R_TIMEDOUT, NULL,
sock->rcb.recv(sock->statichandle, ISC_R_TIMEDOUT, NULL,
sock->rcbarg);
isc__nmsocket_clearcb(sock);
}
@@ -766,8 +766,8 @@ read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
.length = nread };
if (sock->rcb.recv != NULL) {
sock->rcb.recv(sock->tcphandle, ISC_R_SUCCESS, &region,
sock->rcbarg);
sock->rcb.recv(sock->statichandle, ISC_R_SUCCESS,
&region, sock->rcbarg);
}
sock->read_timeout = (atomic_load(&sock->keepalive)
@@ -792,7 +792,8 @@ read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
*/
if (sock->rcb.recv != NULL) {
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_RECVFAIL]);
sock->rcb.recv(sock->tcphandle, ISC_R_EOF, NULL, sock->rcbarg);
sock->rcb.recv(sock->statichandle, ISC_R_EOF, NULL,
sock->rcbarg);
isc__nmsocket_clearcb(sock);
}
@@ -958,8 +959,7 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
uvreq = isc__nm_uvreq_get(sock->mgr, sock);
uvreq->uvbuf.base = (char *)region->base;
uvreq->uvbuf.len = region->length;
uvreq->handle = handle;
isc_nmhandle_ref(uvreq->handle);
isc_nmhandle_attach(handle, &uvreq->handle);
uvreq->cb.send = cb;
uvreq->cbarg = cbarg;
@@ -1002,7 +1002,6 @@ tcp_send_cb(uv_write_t *req, int status) {
uvreq->cb.send(uvreq->handle, result, uvreq->cbarg);
sock = uvreq->handle->sock;
isc_nmhandle_unref(uvreq->handle);
isc__nm_uvreq_put(&uvreq, sock);
}
@@ -1034,8 +1033,6 @@ tcp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
REQUIRE(sock->tid == isc_nm_tid());
REQUIRE(sock->type == isc_nm_tcpsocket);
isc_nmhandle_ref(req->handle);
r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, &req->uvbuf,
1, tcp_send_cb);
if (r < 0) {
@@ -1125,24 +1122,29 @@ void
isc__nm_tcp_shutdown(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
if (sock->type == isc_nm_tcpsocket && sock->tcphandle != NULL &&
if (sock->type == isc_nm_tcpsocket && sock->statichandle != NULL &&
sock->rcb.recv != NULL)
{
sock->rcb.recv(sock->tcphandle, ISC_R_CANCELED, NULL,
sock->rcb.recv(sock->statichandle, ISC_R_CANCELED, NULL,
sock->rcbarg);
isc__nmsocket_clearcb(sock);
}
}
void
isc__nm_tcp_cancelread(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
bool
isc__nm_tcp_cancelread(isc_nmhandle_t *handle) {
isc_nmsocket_t *sock = NULL;
if (sock->type == isc_nm_tcpsocket && sock->tcphandle != NULL &&
sock->rcb.recv != NULL)
{
sock->rcb.recv(sock->tcphandle, ISC_R_CANCELED, NULL,
sock->rcbarg);
REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(sock->type == isc_nm_tcpsocket);
if (sock->client && sock->rcb.recv != NULL) {
sock->rcb.recv(handle, ISC_R_EOF, NULL, sock->rcbarg);
isc__nmsocket_clearcb(sock);
return (true);
}
return (false);
}
+252 -38
View File
@@ -82,7 +82,9 @@ alloc_dnsbuf(isc_nmsocket_t *sock, size_t len) {
static void
timer_close_cb(uv_handle_t *handle) {
isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
INSIST(VALID_NMSOCK(sock));
REQUIRE(VALID_NMSOCK(sock));
atomic_store(&sock->closed, true);
tcpdns_close_direct(sock);
}
@@ -94,9 +96,9 @@ dnstcp_readtimeout(uv_timer_t *timer) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_nm_tid());
/* Close the TCP connection, it's closing should fire 'our' closing */
isc_nmhandle_unref(sock->outerhandle);
sock->outerhandle = NULL;
/* Close the TCP connection; its closure should fire ours. */
isc_nmhandle_detach(&sock->outerhandle);
}
/*
@@ -106,6 +108,7 @@ static isc_result_t
dnslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *dnslistensock = (isc_nmsocket_t *)cbarg;
isc_nmsocket_t *dnssock = NULL;
isc_nmhandle_t *readhandle = NULL;
REQUIRE(VALID_NMSOCK(dnslistensock));
REQUIRE(dnslistensock->type == isc_nm_tcpdnslistener);
@@ -132,8 +135,7 @@ dnslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc__nmsocket_attach(dnssock, &dnssock->self);
dnssock->outerhandle = handle;
isc_nmhandle_ref(dnssock->outerhandle);
isc_nmhandle_attach(handle, &dnssock->outerhandle);
dnssock->peer = handle->sock->peer;
dnssock->read_timeout = handle->sock->mgr->init;
@@ -147,10 +149,14 @@ dnslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
uv_timer_start(&dnssock->timer, dnstcp_readtimeout,
dnssock->read_timeout, 0);
isc_nmhandle_ref(handle);
result = isc_nm_read(handle, dnslisten_readcb, dnssock);
/*
* Add a reference to handle to keep it from being freed by
* the caller; it will be detached in dnslisten_readcb().
*/
isc_nmhandle_attach(handle, &readhandle);
result = isc_nm_read(readhandle, dnslisten_readcb, dnssock);
if (result != ISC_R_SUCCESS) {
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&readhandle);
}
isc__nmsocket_detach(&dnssock);
@@ -187,8 +193,13 @@ processbuffer(isc_nmsocket_t *dnssock, isc_nmhandle_t **handlep) {
*/
len = dnslen(dnssock->buf);
if (len <= dnssock->buf_len - 2) {
isc_nmhandle_t *dnshandle = isc__nmhandle_get(dnssock, NULL,
NULL);
isc_nmhandle_t *dnshandle = NULL;
if (dnssock->client && dnssock->statichandle != NULL) {
isc_nmhandle_attach(dnssock->statichandle, &dnshandle);
} else {
dnshandle = isc__nmhandle_get(dnssock, NULL, NULL);
}
isc_nmsocket_t *listener = dnssock->listener;
if (listener != NULL && listener->rcb.recv != NULL) {
@@ -197,6 +208,20 @@ processbuffer(isc_nmsocket_t *dnssock, isc_nmhandle_t **handlep) {
&(isc_region_t){ .base = dnssock->buf + 2,
.length = len },
listener->rcbarg);
} else if (dnssock->rcb.recv != NULL) {
isc_nm_recv_cb_t cb = dnssock->rcb.recv;
void *cbarg = dnssock->rcbarg;
/*
* We need to clear the read callback *before*
* calling it, because it might make another
* call to isc_nm_read() and set up a new callback.
*/
isc__nmsocket_clearcb(dnssock);
cb(dnshandle, ISC_R_SUCCESS,
&(isc_region_t){ .base = dnssock->buf + 2,
.length = len },
cbarg);
}
len += 2;
@@ -227,20 +252,18 @@ dnslisten_readcb(isc_nmhandle_t *handle, isc_result_t eresult,
REQUIRE(VALID_NMSOCK(dnssock));
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(dnssock->tid == isc_nm_tid());
if (region == NULL || eresult != ISC_R_SUCCESS) {
/* Connection closed */
isc_nmhandle_unref(handle);
dnssock->result = eresult;
if (dnssock->self != NULL) {
isc__nmsocket_detach(&dnssock->self);
}
isc__nmsocket_clearcb(dnssock);
if (dnssock->outerhandle != NULL) {
isc_nmhandle_unref(dnssock->outerhandle);
dnssock->outerhandle = NULL;
isc_nmhandle_detach(&dnssock->outerhandle);
}
isc_nmhandle_detach(&handle);
return;
}
@@ -277,11 +300,14 @@ dnslisten_readcb(isc_nmhandle_t *handle, isc_result_t eresult,
uv_timer_stop(&dnssock->timer);
}
if (atomic_load(&dnssock->sequential)) {
if (atomic_load(&dnssock->sequential) ||
dnssock->rcb.recv == NULL) {
/*
* We're in sequential mode and we processed
* one packet, so we're done until the next read
* completes.
* There are two reasons we might want to pause here:
* - We're in sequential mode and we've received
* a whole packet, so we're done until it's been
* processed; or
* - We no longer have a read callback.
*/
isc_nm_pauseread(dnssock->outerhandle);
done = true;
@@ -300,7 +326,7 @@ dnslisten_readcb(isc_nmhandle_t *handle, isc_result_t eresult,
}
}
isc_nmhandle_unref(dnshandle);
isc_nmhandle_detach(&dnshandle);
} while (!done);
}
@@ -313,8 +339,8 @@ isc_result_t
isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
void *cbarg, isc_nm_accept_cb_t accept_cb,
void *accept_cbarg, size_t extrahandlesize, int backlog,
isc_quota_t *quota, isc_nmsocket_t **sockp) {
/* A 'wrapper' socket object with outer set to true TCP socket */
isc_quota_t *quota, SSL_CTX *sslctx,
isc_nmsocket_t **sockp) {
isc_nmsocket_t *dnslistensock = isc_mem_get(mgr->mctx,
sizeof(*dnslistensock));
isc_result_t result;
@@ -328,10 +354,21 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
dnslistensock->accept_cbarg = accept_cbarg;
dnslistensock->extrahandlesize = extrahandlesize;
/* We set dnslistensock->outer to a true listening socket */
result = isc_nm_listentcp(mgr, iface, dnslisten_acceptcb, dnslistensock,
extrahandlesize, backlog, quota,
&dnslistensock->outer);
/*
* dnslistensock will be a DNS 'wrapper' around a connected
* stream. We set dnslistensock->outer to a socket listening
* for a TCP or TLS connection.
*/
if (sslctx != NULL) {
result = isc_nm_listentls(mgr, iface, dnslisten_acceptcb,
dnslistensock, extrahandlesize,
backlog, quota, sslctx,
&dnslistensock->outer);
} else {
result = isc_nm_listentcp(
mgr, iface, dnslisten_acceptcb, dnslistensock,
extrahandlesize, backlog, quota, &dnslistensock->outer);
}
if (result == ISC_R_SUCCESS) {
atomic_store(&dnslistensock->listening, true);
*sockp = dnslistensock;
@@ -429,12 +466,11 @@ resume_processing(void *arg) {
if (sock->timer_initialized) {
uv_timer_stop(&sock->timer);
}
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&handle);
} else if (sock->outerhandle != NULL) {
result = isc_nm_resumeread(sock->outerhandle);
if (result != ISC_R_SUCCESS) {
isc_nmhandle_unref(sock->outerhandle);
sock->outerhandle = NULL;
isc_nmhandle_detach(&sock->outerhandle);
}
}
@@ -464,7 +500,7 @@ resume_processing(void *arg) {
uv_timer_stop(&sock->timer);
}
atomic_store(&sock->outerhandle->sock->processing, true);
isc_nmhandle_unref(dnshandle);
isc_nmhandle_detach(&dnshandle);
} while (atomic_load(&sock->ah) < TCPDNS_CLIENTS_PER_CONN);
}
@@ -495,8 +531,7 @@ isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) {
r.base = (unsigned char *)req->uvbuf.base;
r.length = req->uvbuf.len;
result = isc__nm_tcp_send(sock->outerhandle, &r, tcpdnssend_cb,
req);
result = isc_nm_send(sock->outerhandle, &r, tcpdnssend_cb, req);
}
if (result != ISC_R_SUCCESS) {
@@ -522,8 +557,7 @@ isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region,
REQUIRE(sock->type == isc_nm_tcpdnssocket);
uvreq = isc__nm_uvreq_get(sock->mgr, sock);
uvreq->handle = handle;
isc_nmhandle_ref(uvreq->handle);
isc_nmhandle_attach(handle, &uvreq->handle);
uvreq->cb.send = cb;
uvreq->cbarg = cbarg;
@@ -538,8 +572,8 @@ isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region,
r.base = (unsigned char *)uvreq->uvbuf.base;
r.length = uvreq->uvbuf.len;
return (isc__nm_tcp_send(sock->outerhandle, &r, tcpdnssend_cb,
uvreq));
return (isc_nm_send(sock->outerhandle, &r, tcpdnssend_cb,
uvreq));
} else {
isc__netievent_tcpdnssend_t *ievent = NULL;
@@ -579,8 +613,7 @@ tcpdns_close_direct(isc_nmsocket_t *sock) {
*/
if (sock->outerhandle != NULL) {
isc__nmsocket_clearcb(sock->outerhandle->sock);
isc_nmhandle_unref(sock->outerhandle);
sock->outerhandle = NULL;
isc_nmhandle_detach(&sock->outerhandle);
}
if (sock->listener != NULL) {
isc__nmsocket_detach(&sock->listener);
@@ -616,3 +649,184 @@ isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
tcpdns_close_direct(ievent->sock);
}
typedef struct {
isc_mem_t *mctx;
isc_nm_cb_t cb;
void *cbarg;
size_t extrahandlesize;
} tcpconnect_t;
static void
tcpdnsconnect_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
tcpconnect_t *conn = (tcpconnect_t *)arg;
isc_nm_cb_t cb = conn->cb;
void *cbarg = conn->cbarg;
size_t extrahandlesize = conn->extrahandlesize;
isc_nmsocket_t *dnssock = NULL;
isc_mem_putanddetach(&conn->mctx, conn, sizeof(*conn));
if (result != ISC_R_SUCCESS) {
cb(NULL, result, cbarg);
return;
}
REQUIRE(VALID_NMHANDLE(handle));
dnssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*dnssock));
isc__nmsocket_init(dnssock, handle->sock->mgr, isc_nm_tcpdnssocket,
handle->sock->iface);
dnssock->extrahandlesize = extrahandlesize;
isc_nmhandle_attach(handle, &dnssock->outerhandle);
dnssock->peer = handle->sock->peer;
dnssock->read_timeout = handle->sock->mgr->init;
dnssock->tid = isc_nm_tid();
dnssock->client = true;
dnssock->statichandle = isc__nmhandle_get(dnssock, NULL, NULL);
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);
/*
* The connection is now established; we start reading immediately,
* before we've been asked to. We'll read and buffer at most one
* packet.
*/
result = isc_nm_read(handle, dnslisten_readcb, dnssock);
cb(dnssock->statichandle, result, cbarg);
isc_nmhandle_detach(&handle);
isc__nmsocket_detach(&dnssock);
}
isc_result_t
isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, size_t extrahandlesize) {
tcpconnect_t *conn = isc_mem_get(mgr->mctx, sizeof(tcpconnect_t));
*conn = (tcpconnect_t){ .cb = cb,
.cbarg = cbarg,
.extrahandlesize = extrahandlesize };
isc_mem_attach(mgr->mctx, &conn->mctx);
return (isc_nm_tcpconnect(mgr, local, peer, tcpdnsconnect_cb, conn, 0));
}
isc_result_t
isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, size_t extrahandlesize) {
tcpconnect_t *conn = isc_mem_get(mgr->mctx, sizeof(tcpconnect_t));
SSL_CTX *ctx = NULL;
*conn = (tcpconnect_t){ .cb = cb,
.cbarg = cbarg,
.extrahandlesize = extrahandlesize };
isc_mem_attach(mgr->mctx, &conn->mctx);
ctx = SSL_CTX_new(SSLv23_client_method());
return (isc_nm_tlsconnect(mgr, local, peer, tcpdnsconnect_cb, conn, ctx,
0));
}
isc_result_t
isc__nm_tcpdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
isc_nmsocket_t *sock = handle->sock;
isc__netievent_tcpdnsread_t *ievent = NULL;
isc_nmhandle_t *eventhandle = NULL;
REQUIRE(handle == sock->statichandle);
REQUIRE(sock->rcb.recv == NULL);
REQUIRE(sock->client);
/*
* This MUST be done asynchronously, no matter which thread we're
* in. The callback function for isc_nm_read() often calls
* isc_nm_read() again; if we tried to do that synchronously
* we'd clash in processbuffer() and grow the stack indefinitely.
*/
ievent = isc__nm_get_ievent(sock->mgr, netievent_tcpdnsread);
ievent->sock = sock;
sock->rcb.recv = cb;
sock->rcbarg = cbarg;
/*
* Add a reference to the handle to keep it from being freed by
* the caller; it will be detached in in isc__nm_async_tcpdnsread().
*/
isc_nmhandle_attach(handle, &eventhandle);
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
return (ISC_R_SUCCESS);
}
void
isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0) {
isc_result_t result;
isc__netievent_tcpdnsread_t *ievent =
(isc__netievent_tcpdnsclose_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
isc_nmhandle_t *handle = NULL, *newhandle = NULL;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(worker->id == sock->tid);
handle = sock->statichandle;
if (sock->type != isc_nm_tcpdnssocket || sock->outerhandle == NULL) {
sock->rcb.recv(handle, ISC_R_NOTCONNECTED, NULL, sock->rcbarg);
isc_nmhandle_detach(&handle);
return;
}
/*
* Maybe we have a packet already?
*/
result = processbuffer(sock, &newhandle);
if (result == ISC_R_SUCCESS) {
atomic_store(&sock->outerhandle->sock->processing, true);
if (sock->timer_initialized) {
uv_timer_stop(&sock->timer);
}
isc_nmhandle_detach(&newhandle);
} else if (sock->outerhandle != NULL) {
/* Restart reading, wait for the callback */
atomic_store(&sock->outerhandle->sock->processing, false);
if (sock->timer_initialized) {
uv_timer_start(&sock->timer, dnstcp_readtimeout,
sock->read_timeout, 0);
}
isc_nm_resumeread(sock->outerhandle);
} else {
isc_nm_recv_cb_t cb = sock->rcb.recv;
void *cbarg = sock->rcbarg;
isc__nmsocket_clearcb(sock);
cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg);
}
isc_nmhandle_detach(&handle);
}
bool
isc__nm_tcpdns_cancelread(isc_nmhandle_t *handle) {
isc_nmsocket_t *sock = NULL;
REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(sock->type == isc_nm_tcpdnssocket);
if (sock->client && sock->rcb.recv != NULL) {
sock->rcb.recv(handle, ISC_R_CANCELED, NULL, sock->rcbarg);
isc__nmsocket_clearcb(sock);
isc__nm_tcp_cancelread(sock->outerhandle);
return (true);
}
return (false);
}
+580
View File
@@ -0,0 +1,580 @@
/*
* 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 <libgen.h>
#include <unistd.h>
#include <uv.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <isc/atomic.h>
#include <isc/buffer.h>
#include <isc/condition.h>
#include <isc/log.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/netmgr.h>
#include <isc/quota.h>
#include <isc/random.h>
#include <isc/refcount.h>
#include <isc/region.h>
#include <isc/result.h>
#include <isc/sockaddr.h>
#include <isc/stdtime.h>
#include <isc/thread.h>
#include <isc/util.h>
#include "netmgr-int.h"
#include "uv-compat.h"
#define TLS_CHECK_RV INT_MAX
static isc_result_t
tls_error_to_result(int tls_err) {
switch (tls_err) {
case SSL_ERROR_ZERO_RETURN:
return (ISC_R_EOF);
default:
return (ISC_R_UNEXPECTED);
}
}
static void
tls_do_bio(isc_nmsocket_t *sock, int rv);
static void
tls_close_direct(isc_nmsocket_t *sock);
static isc_result_t
tls_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);
static void
tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
UNUSED(handle);
UNUSED(eresult);
tls_do_bio(sock, TLS_CHECK_RV);
}
static void
tls_do_bio(isc_nmsocket_t *sock, int rv) {
isc_result_t result = ISC_R_SUCCESS;
int pending, tls_err = 0;
REQUIRE(sock->tid == isc_nm_tid());
if (rv == TLS_CHECK_RV) {
char buf[1];
rv = SSL_peek(sock->tls.ssl, buf, 1);
if (rv == 1) {
if (sock->rcb.recv != NULL &&
!atomic_load(&sock->readpaused)) {
isc_region_t region = {
isc_mem_get(sock->mgr->mctx, 4096), 4096
};
isc_region_t dregion;
memset(region.base, 0, region.length);
rv = SSL_read(sock->tls.ssl, region.base,
region.length);
dregion = (isc_region_t){ region.base, rv };
sock->rcb.recv(sock->statichandle,
ISC_R_SUCCESS, &dregion,
sock->rcbarg);
isc_mem_put(sock->mgr->mctx, region.base, 4096);
}
}
}
pending = BIO_pending(sock->tls.app_bio);
if (pending > 0) {
char *p = malloc(pending);
rv = BIO_read(sock->tls.app_bio, p, pending);
result = isc_nm_send(sock->outerhandle,
&(isc_region_t){ (unsigned char *)p, rv },
tls_senddone, sock);
if (result != ISC_R_SUCCESS) {
goto error;
}
}
tls_err = SSL_get_error(sock->tls.ssl, rv);
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
ISC_LOG_DEBUG(30), "SSL error code in BIO: %d", tls_err);
if (tls_err == 0) {
return;
} else if (tls_err == SSL_ERROR_WANT_WRITE) {
isc_nm_pauseread(sock->outerhandle);
pending = BIO_pending(sock->tls.app_bio);
if (pending > 0) {
char *p = malloc(pending);
rv = BIO_read(sock->tls.app_bio, p, pending);
result = isc_nm_send(
sock->outerhandle,
&(isc_region_t){ (unsigned char *)p, rv },
tls_senddone, sock);
if (result != ISC_R_SUCCESS) {
goto error;
}
}
} else if (tls_err == SSL_ERROR_WANT_READ) {
isc_nm_resumeread(sock->outerhandle);
} else {
result = tls_error_to_result(tls_err);
goto error;
}
return;
error:
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
ISC_LOG_ERROR, "SSL error in BIO: %d", tls_err);
if (sock->rcb.recv != NULL) {
sock->rcb.recv(sock->statichandle, result, NULL, sock->rcbarg);
} else {
tls_close_direct(sock);
}
}
static void
tls_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
void *cbarg) {
isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
int rv;
REQUIRE(VALID_NMSOCK(tlssock));
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(tlssock->tid == isc_nm_tid());
if (result != ISC_R_SUCCESS) {
/* Connection closed */
/*
* TODO accept_cb should be called if we're not
* initialized yet!
*/
if (tlssock->rcb.recv != NULL) {
tlssock->rcb.recv(tlssock->statichandle, result, region,
tlssock->rcbarg);
}
isc__nm_tls_close(tlssock);
return;
}
rv = BIO_write(tlssock->tls.app_bio, region->base, region->length);
INSIST(rv > 0 && (unsigned int)rv == region->length);
tls_do_bio(tlssock, TLS_CHECK_RV);
if (tlssock->tls.state != IO) {
if (SSL_is_init_finished(tlssock->tls.ssl) == 1) {
if (tlssock->server) {
tlssock->listener->accept_cb.accept(
tlssock->statichandle, ISC_R_SUCCESS,
tlssock->listener->accept_cbarg);
} else {
isc_nmhandle_t *tlshandle =
isc__nmhandle_get(tlssock, NULL, NULL);
tlssock->accept_cb.connect(
tlshandle, ISC_R_SUCCESS,
tlssock->accept_cbarg);
}
tlssock->tls.state = IO;
/* We need to do it again - to flush incoming buffer */
tls_do_bio(tlssock, TLS_CHECK_RV);
}
}
}
static isc_result_t
initialize_tls(isc_nmsocket_t *sock, bool server) {
REQUIRE(sock->tid == isc_nm_tid());
sock->tls.ssl = SSL_new(sock->tls.ctx);
if (sock->tls.ssl == NULL) {
return (ISC_R_TLSERROR);
}
if (BIO_new_bio_pair(&(sock->tls.ssl_bio), 0, &(sock->tls.app_bio),
0) != 1) {
SSL_free(sock->tls.ssl);
return (ISC_R_TLSERROR);
}
SSL_set_bio(sock->tls.ssl, sock->tls.ssl_bio, sock->tls.ssl_bio);
if (server) {
SSL_set_accept_state(sock->tls.ssl);
} else {
SSL_set_connect_state(sock->tls.ssl);
}
isc_nm_read(sock->outerhandle, tls_readcb, sock);
tls_do_bio(sock, TLS_CHECK_RV);
return (ISC_R_SUCCESS);
}
static isc_result_t
tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *tlslistensock = (isc_nmsocket_t *)cbarg;
isc_nmsocket_t *tlssock = NULL;
REQUIRE(VALID_NMSOCK(tlslistensock));
REQUIRE(tlslistensock->type == isc_nm_tlslistener);
/* If accept() was unsuccessful we can't do anything */
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* We need to create a 'wrapper' tlssocket for this connection.
*/
tlssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*tlssock));
isc__nmsocket_init(tlssock, handle->sock->mgr, isc_nm_tlssocket,
handle->sock->iface);
tlssock->extrahandlesize = tlslistensock->extrahandlesize;
isc__nmsocket_attach(tlslistensock, &tlssock->listener);
isc_nmhandle_attach(handle, &tlssock->outerhandle);
tlssock->peer = handle->sock->peer;
tlssock->read_timeout = handle->sock->mgr->init;
tlssock->tid = isc_nm_tid();
tlssock->tls.server = true;
isc__nmhandle_get(tlssock, NULL, NULL);
uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
&tlssock->timer);
tlssock->timer.data = tlssock;
tlssock->timer_initialized = true;
tlssock->tls.ctx = tlslistensock->tls.ctx;
result = initialize_tls(tlssock, true);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
/* TODO: catch failure code, detach tlssock, and log the error */
return (result);
}
isc_result_t
isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface,
isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
size_t extrahandlesize, int backlog, isc_quota_t *quota,
SSL_CTX *sslctx, isc_nmsocket_t **sockp) {
isc_result_t result;
isc_nmsocket_t *tlssock = isc_mem_get(mgr->mctx, sizeof(*tlssock));
isc__nmsocket_init(tlssock, mgr, isc_nm_tlslistener, iface);
tlssock->accept_cb.accept = accept_cb;
tlssock->accept_cbarg = accept_cbarg;
tlssock->extrahandlesize = extrahandlesize;
tlssock->tls.ctx = sslctx;
/*
* tlssock will be a TLS 'wrapper' around an unencrypted stream.
* We set tlssock->outer to a socket listening for a TCP connection.
*/
result = isc_nm_listentcp(mgr, iface, tlslisten_acceptcb, tlssock,
extrahandlesize, backlog, quota,
&tlssock->outer);
if (result == ISC_R_SUCCESS) {
atomic_store(&tlssock->listening, true);
*sockp = tlssock;
return (ISC_R_SUCCESS);
} else {
atomic_store(&tlssock->closed, true);
isc__nmsocket_detach(&tlssock);
return (result);
}
}
void
isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0) {
isc_result_t result;
isc__netievent_tcpsend_t *ievent = (isc__netievent_tcpsend_t *)ev0;
REQUIRE(worker->id == ievent->sock->tid);
if (!atomic_load(&ievent->sock->active)) {
return;
}
result = tls_send_direct(ievent->sock, ievent->req);
if (result != ISC_R_SUCCESS) {
ievent->req->cb.send(ievent->req->handle, result,
ievent->req->cbarg);
isc__nm_uvreq_put(&ievent->req, ievent->req->handle->sock);
}
}
static isc_result_t
tls_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
int rv;
REQUIRE(sock->tid == isc_nm_tid());
REQUIRE(sock->type == isc_nm_tlssocket);
rv = SSL_write(sock->tls.ssl, req->uvbuf.base, req->uvbuf.len);
if (rv < 0) {
isc__nm_uvreq_put(&req, sock);
return (isc__nm_uverr2result(rv));
}
INSIST((unsigned)rv == req->uvbuf.len);
tls_do_bio(sock, TLS_CHECK_RV);
req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
isc__nm_uvreq_put(&req, sock);
return (ISC_R_SUCCESS);
}
isc_result_t
isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void *cbarg) {
isc_nmsocket_t *sock = handle->sock;
isc__netievent_tcpsend_t *ievent = NULL;
isc__nm_uvreq_t *uvreq = NULL;
REQUIRE(sock->type == isc_nm_tlssocket);
uvreq = isc__nm_uvreq_get(sock->mgr, sock);
uvreq->uvbuf.base = (char *)region->base;
uvreq->uvbuf.len = region->length;
isc_nmhandle_attach(handle, &uvreq->handle);
uvreq->cb.send = cb;
uvreq->cbarg = cbarg;
/*
* We need to create an event and pass it using async channel
*/
ievent = isc__nm_get_ievent(sock->mgr, netievent_tlssend);
ievent->sock = sock;
ievent->req = uvreq;
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
return (ISC_R_SUCCESS);
}
void
isc__nm_async_tls_startread(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_startread_t *ievent = (isc__netievent_startread_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
REQUIRE(worker->id == isc_nm_tid());
tls_do_bio(sock, TLS_CHECK_RV);
}
isc_result_t
isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
isc_nmsocket_t *sock = NULL;
isc__netievent_startread_t *ievent = NULL;
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
sock = handle->sock;
sock->rcb.recv = cb;
sock->rcbarg = cbarg;
ievent = isc__nm_get_ievent(sock->mgr, netievent_tlsstartread);
ievent->sock = sock;
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
return (ISC_R_SUCCESS);
}
isc_result_t
isc__nm_tls_pauseread(isc_nmsocket_t *sock) {
atomic_store(&sock->readpaused, true);
return (ISC_R_SUCCESS);
}
isc_result_t
isc__nm_tls_resumeread(isc_nmsocket_t *sock) {
atomic_store(&sock->readpaused, false);
tls_do_bio(sock, TLS_CHECK_RV);
return (ISC_R_SUCCESS);
}
static void
timer_close_cb(uv_handle_t *handle) {
isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
INSIST(VALID_NMSOCK(sock));
isc__nmsocket_detach(&sock);
}
static void
tls_close_direct(isc_nmsocket_t *sock) {
REQUIRE(sock->tid == isc_nm_tid());
/* We don't need atomics here, it's all in single network thread */
if (sock->timer_initialized) {
/*
* We need to fire the timer callback to clean it up,
* it will then call us again (via detach) so that we
* can finally close the socket.
*/
sock->timer_initialized = false;
uv_timer_stop(&sock->timer);
uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
} else {
/*
* At this point we're certain that there are no external
* references, we can close everything.
*/
if (sock->outerhandle != NULL) {
isc_nm_pauseread(sock->outerhandle);
isc_nmhandle_detach(&sock->outerhandle);
}
if (sock->listener != NULL) {
isc__nmsocket_detach(&sock->listener);
}
atomic_store(&sock->closed, true);
}
}
void
isc__nm_tls_close(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlssocket);
if (sock->tid == isc_nm_tid()) {
tls_close_direct(sock);
} else {
isc__netievent_tlsclose_t *ievent =
isc__nm_get_ievent(sock->mgr, netievent_tlsclose);
ievent->sock = sock;
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
}
void
isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlsclose_t *ievent = (isc__netievent_tlsclose_t *)ev0;
REQUIRE(worker->id == ievent->sock->tid);
tls_close_direct(ievent->sock);
}
void
isc__nm_tls_stoplistening(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlslistener);
atomic_store(&sock->listening, false);
atomic_store(&sock->closed, true);
sock->rcb.recv = NULL;
sock->rcbarg = NULL;
if (sock->outer != NULL) {
isc_nm_stoplistening(sock->outer);
isc__nmsocket_detach(&sock->outer);
}
}
isc_result_t
isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, SSL_CTX *ctx,
size_t extrahandlesize) {
isc_nmsocket_t *nsock = NULL, *tmp = NULL;
isc__netievent_tlsconnect_t *ievent = NULL;
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(VALID_NM(mgr));
nsock = isc_mem_get(mgr->mctx, sizeof(*nsock));
isc__nmsocket_init(nsock, mgr, isc_nm_tlssocket, local);
nsock->extrahandlesize = extrahandlesize;
nsock->result = ISC_R_SUCCESS;
nsock->accept_cb.connect = cb;
nsock->accept_cbarg = cbarg;
nsock->tls.ctx = ctx;
ievent = isc__nm_get_ievent(mgr, netievent_tlsconnect);
ievent->sock = nsock;
ievent->local = local->addr;
ievent->peer = peer->addr;
ievent->ctx = ctx;
/*
* Async callbacks can dereference the socket in the meantime,
* we need to hold an additional reference to it.
*/
isc__nmsocket_attach(nsock, &tmp);
if (isc__nm_in_netthread()) {
nsock->tid = isc_nm_tid();
isc__nm_async_tlsconnect(&mgr->workers[nsock->tid],
(isc__netievent_t *)ievent);
isc__nm_put_ievent(mgr, ievent);
} else {
nsock->tid = isc_random_uniform(mgr->nworkers);
isc__nm_enqueue_ievent(&mgr->workers[nsock->tid],
(isc__netievent_t *)ievent);
}
isc__nmsocket_detach(&tmp);
return (result);
}
static void
tls_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
REQUIRE(VALID_NMSOCK(tlssock));
if (result != ISC_R_SUCCESS) {
tlssock->accept_cb.connect(NULL, result, tlssock->accept_cbarg);
return;
}
INSIST(VALID_NMHANDLE(handle));
isc_nmhandle_attach(handle, &tlssock->outerhandle);
result = initialize_tls(tlssock, false);
if (result != ISC_R_SUCCESS) {
tlssock->accept_cb.connect(NULL, result, tlssock->accept_cbarg);
/* TODO CLOSE! */
return;
}
}
void
isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlsconnect_t *ievent =
(isc__netievent_tlsconnect_t *)ev0;
isc_nmsocket_t *tlssock = ievent->sock;
isc_result_t result;
UNUSED(worker);
tlssock->tid = isc_nm_tid();
uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
&tlssock->timer);
tlssock->timer.data = tlssock;
tlssock->timer_initialized = true;
result = isc_nm_tcpconnect(worker->mgr, (isc_nmiface_t *)&ievent->local,
(isc_nmiface_t *)&ievent->peer,
tls_connect_cb, tlssock, 0);
if (result != ISC_R_SUCCESS) {
tlssock->accept_cb.connect(NULL, result, tlssock->accept_cbarg);
/* TODO CLOSE! */
return;
}
}
+310 -29
View File
@@ -40,6 +40,9 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
static void
udp_send_cb(uv_udp_send_t *req, int status);
static void
udp_close_cb(uv_handle_t *uvhandle);
isc_result_t
isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
void *cbarg, size_t extrahandlesize, isc_nmsocket_t **sockp) {
@@ -80,7 +83,7 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
csock->rcb.recv = cb;
csock->rcbarg = cbarg;
csock->fd = socket(family, SOCK_DGRAM, 0);
INSIST(csock->fd >= 0);
RUNTIME_CHECK(csock->fd >= 0);
/*
* This is SO_REUSE**** hell:
@@ -159,7 +162,7 @@ udp_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
}
/*
* handle 'udplisten' async call - start listening on a socket.
* Asynchrnous 'udplisten' call handler: start listening on a UDP socket.
*/
void
isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) {
@@ -223,7 +226,7 @@ isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) {
}
static void
udp_close_cb(uv_handle_t *handle) {
udp_stop_cb(uv_handle_t *handle) {
isc_nmsocket_t *sock = uv_handle_get_data(handle);
atomic_store(&sock->closed, true);
@@ -236,7 +239,7 @@ stop_udp_child(isc_nmsocket_t *sock) {
REQUIRE(sock->tid == isc_nm_tid());
uv_udp_recv_stop(&sock->uv_handle.udp);
uv_close((uv_handle_t *)&sock->uv_handle.udp, udp_close_cb);
uv_close((uv_handle_t *)&sock->uv_handle.udp, udp_stop_cb);
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CLOSE]);
@@ -311,7 +314,7 @@ isc__nm_udp_stoplistening(isc_nmsocket_t *sock) {
}
/*
* handle 'udpstop' async call - stop listening on a socket.
* Asynchrnous 'udpstop' call handler: stop listening on a UDP socket.
*/
void
isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0) {
@@ -375,9 +378,9 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
#endif
/*
* Three reasons to return now without processing:
* - If addr == NULL that's the end of stream - we can
* free the buffer and bail.
* Three possible reasons to return now without processing:
* - If addr == NULL, in which case it's the end of stream;
* we can free the buffer and bail.
* - If we're simulating a firewall blocking UDP packets
* bigger than 'maxudp' bytes for testing purposes.
* - If the socket is no longer active.
@@ -395,7 +398,11 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
result = isc_sockaddr_fromsockaddr(&sockaddr, addr);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
nmhandle = isc__nmhandle_get(sock, &sockaddr, NULL);
if (!sock->connected) {
nmhandle = isc__nmhandle_get(sock, &sockaddr, NULL);
} else {
nmhandle = sock->statichandle;
}
region.base = (unsigned char *)buf->base;
region.length = nrecv;
@@ -414,24 +421,24 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
* If the recv callback wants to hold on to the handle,
* it needs to attach to it.
*/
isc_nmhandle_unref(nmhandle);
isc_nmhandle_detach(&nmhandle);
}
/*
* isc__nm_udp_send sends buf to a peer on a socket.
* It tries to find a proper sibling/child socket so that we won't have
* to jump to another thread.
* Send the data in 'region' to a peer via a UDP socket. We try to find
* a proper sibling/child socket so that we won't have to jump to another
* thread.
*/
isc_result_t
isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void *cbarg) {
isc_nmsocket_t *psock = NULL, *rsock = NULL;
isc_nmsocket_t *sock = handle->sock;
isc_nmsocket_t *psock = NULL, *rsock = sock;
isc_sockaddr_t *peer = &handle->peer;
isc__netievent_udpsend_t *ievent = NULL;
isc__nm_uvreq_t *uvreq = NULL;
int ntid;
uint32_t maxudp = atomic_load(&sock->mgr->maxudp);
int ntid;
/*
* We're simulating a firewall blocking UDP packets bigger than
@@ -442,16 +449,16 @@ isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
* we need to do so here.
*/
if (maxudp != 0 && region->length > maxudp) {
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&handle);
return (ISC_R_SUCCESS);
}
if (sock->type == isc_nm_udpsocket) {
if (sock->type == isc_nm_udpsocket && !sock->client) {
INSIST(sock->parent != NULL);
psock = sock->parent;
} else if (sock->type == isc_nm_udplistener) {
psock = sock;
} else {
} else if (!sock->client) {
INSIST(0);
ISC_UNREACHABLE();
}
@@ -467,20 +474,21 @@ isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
*/
if (isc__nm_in_netthread()) {
ntid = isc_nm_tid();
} else if (sock->type == isc_nm_udpsocket) {
} else if (sock->type == isc_nm_udpsocket && !sock->client) {
ntid = sock->tid;
} else {
ntid = (int)isc_random_uniform(sock->nchildren);
}
rsock = &psock->children[ntid];
if (psock != NULL) {
rsock = &psock->children[ntid];
}
uvreq = isc__nm_uvreq_get(sock->mgr, sock);
uvreq->uvbuf.base = (char *)region->base;
uvreq->uvbuf.len = region->length;
uvreq->handle = handle;
isc_nmhandle_ref(uvreq->handle);
isc_nmhandle_attach(handle, &uvreq->handle);
uvreq->cb.send = cb;
uvreq->cbarg = cbarg;
@@ -507,7 +515,7 @@ isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
}
/*
* handle 'udpsend' async event - send a packet on the socket
* Asynchronous 'udpsend' event handler: send a packet on a UDP socket.
*/
void
isc__nm_async_udpsend(isc__networker_t *worker, isc__netievent_t *ev0) {
@@ -524,9 +532,6 @@ isc__nm_async_udpsend(isc__networker_t *worker, isc__netievent_t *ev0) {
}
}
/*
* udp_send_cb - callback
*/
static void
udp_send_cb(uv_udp_send_t *req, int status) {
isc_result_t result = ISC_R_SUCCESS;
@@ -542,7 +547,6 @@ udp_send_cb(uv_udp_send_t *req, int status) {
}
uvreq->cb.send(uvreq->handle, result, uvreq->cbarg);
isc_nmhandle_unref(uvreq->handle);
isc__nm_uvreq_put(&uvreq, uvreq->sock);
}
@@ -553,6 +557,7 @@ udp_send_cb(uv_udp_send_t *req, int status) {
static isc_result_t
udp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
isc_sockaddr_t *peer) {
const struct sockaddr *sa = NULL;
int rv;
REQUIRE(sock->tid == isc_nm_tid());
@@ -561,9 +566,10 @@ udp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
if (!isc__nmsocket_active(sock)) {
return (ISC_R_CANCELED);
}
isc_nmhandle_ref(req->handle);
sa = sock->connected ? NULL : &peer->type.sa;
rv = uv_udp_send(&req->uv_req.udp_send, &sock->uv_handle.udp,
&req->uvbuf, 1, &peer->type.sa, udp_send_cb);
&req->uvbuf, 1, sa, udp_send_cb);
if (rv < 0) {
isc__nm_incstats(req->sock->mgr,
req->sock->statsindex[STATID_SENDFAIL]);
@@ -572,3 +578,278 @@ udp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
return (ISC_R_SUCCESS);
}
/*
* Asynchronous 'udpconnect' call handler: open a new UDP socket and call
* the 'open' callback with a handle.
*/
void
isc__nm_async_udpconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_udpconnect_t *ievent =
(isc__netievent_udpconnect_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
isc_nmhandle_t *handle = NULL;
int uv_bind_flags = UV_UDP_REUSEADDR;
int r;
REQUIRE(sock->type == isc_nm_udpsocket);
REQUIRE(sock->iface != NULL);
REQUIRE(sock->parent == NULL);
REQUIRE(sock->tid == isc_nm_tid());
uv_udp_init(&worker->loop, &sock->uv_handle.udp);
uv_handle_set_data(&sock->uv_handle.handle, NULL);
uv_handle_set_data(&sock->uv_handle.handle, sock);
handle = isc__nmhandle_get(sock, &ievent->peer, &sock->iface->addr);
r = uv_udp_open(&sock->uv_handle.udp, sock->fd);
if (r != 0) {
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_OPENFAIL]);
atomic_store(&sock->closed, true);
atomic_store(&sock->connect_error, true);
sock->result = isc__nm_uverr2result(r);
goto done;
}
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_OPEN]);
if (sock->iface->addr.type.sa.sa_family == AF_INET6) {
uv_bind_flags |= UV_UDP_IPV6ONLY;
}
r = uv_udp_bind(&sock->uv_handle.udp, &sock->iface->addr.type.sa,
uv_bind_flags);
if (r != 0) {
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]);
atomic_store(&sock->connect_error, true);
sock->result = isc__nm_uverr2result(r);
goto done;
}
r = uv_udp_connect(&sock->uv_handle.udp, &ievent->peer.type.sa);
if (r != 0) {
isc__nm_incstats(sock->mgr,
sock->statsindex[STATID_CONNECTFAIL]);
atomic_store(&sock->connect_error, true);
sock->result = isc__nm_uverr2result(r);
goto done;
}
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CONNECT]);
#ifdef ISC_RECV_BUFFER_SIZE
uv_recv_buffer_size(&sock->uv_handle.handle,
&(int){ ISC_RECV_BUFFER_SIZE });
#endif
#ifdef ISC_SEND_BUFFER_SIZE
uv_send_buffer_size(&sock->uv_handle.handle,
&(int){ ISC_SEND_BUFFER_SIZE });
#endif
atomic_store(&sock->connected, true);
sock->result = ISC_R_SUCCESS;
done:
sock->rcb.connect(handle, sock->result, sock->rcbarg);
LOCK(&sock->lock);
SIGNAL(&sock->cond);
UNLOCK(&sock->lock);
isc__nmsocket_detach(&sock);
}
isc_result_t
isc_nm_udpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, size_t extrahandlesize) {
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *sock = NULL, *tmp = NULL;
isc__netievent_udpconnect_t *event = NULL;
int r = 0;
REQUIRE(VALID_NM(mgr));
REQUIRE(local != NULL);
REQUIRE(peer != NULL);
sock = isc_mem_get(mgr->mctx, sizeof(isc_nmsocket_t));
isc__nmsocket_init(sock, mgr, isc_nm_udpsocket, local);
INSIST(sock->rcb.recv == NULL && sock->rcbarg == NULL);
sock->rcb.connect = cb;
sock->rcbarg = cbarg;
sock->extrahandlesize = extrahandlesize;
sock->peer = peer->addr;
sock->client = true;
sock->fd = socket(peer->addr.type.sa.sa_family, SOCK_DGRAM, 0);
RUNTIME_CHECK(sock->fd >= 0);
/*
* Set up SO_REUSE* (see comments in isc_nm_listenudp() for
* details).
*/
#if defined(SO_REUSEADDR)
r = setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 },
sizeof(int));
RUNTIME_CHECK(r == 0);
#endif
#if defined(SO_REUSEPORT_LB)
r = setsockopt(sock->fd, SOL_SOCKET, SO_REUSEPORT_LB, &(int){ 1 },
sizeof(int));
RUNTIME_CHECK(r == 0);
#elif defined(SO_REUSEPORT)
r = setsockopt(sock->fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 },
sizeof(int));
RUNTIME_CHECK(r == 0);
#endif
#ifdef SO_INCOMING_CPU
(void)setsockopt(sock->fd, SOL_SOCKET, SO_INCOMING_CPU, &(int){ 1 },
sizeof(int));
#endif
event = isc__nm_get_ievent(mgr, netievent_udpconnect);
event->sock = sock;
event->peer = *((isc_sockaddr_t *)peer);
/*
* Hold an additional sock reference so async callbacks
* can't destroy it until we're ready.
*/
isc__nmsocket_attach(sock, &tmp);
r = isc_random_uniform(mgr->nworkers);
if (r == isc_nm_tid()) {
isc__nm_async_udpconnect(&mgr->workers[r],
(isc__netievent_t *)event);
isc__nm_put_ievent(mgr, event);
} else {
isc__nm_enqueue_ievent(&mgr->workers[r],
(isc__netievent_t *)event);
LOCK(&sock->lock);
while (!atomic_load(&sock->connected) &&
!atomic_load(&sock->connect_error)) {
WAIT(&sock->cond, &sock->lock);
}
UNLOCK(&sock->lock);
}
if (sock->result != ISC_R_SUCCESS) {
result = sock->result;
isc__nmsocket_detach(&sock);
}
isc__nmsocket_detach(&tmp);
return (result);
}
static void
udp_read_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
const struct sockaddr *addr, unsigned flags) {
isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)handle);
udp_recv_cb(handle, nrecv, buf, addr, flags);
uv_udp_recv_stop(&sock->uv_handle.udp);
}
/*
* Asynchronous 'udpread' call handler: start or resume reading on a socket;
* pause reading and call the 'recv' callback after each datagram.
*/
void
isc__nm_async_udpread(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_udpread_t *ievent = (isc__netievent_udpread_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
UNUSED(worker);
uv_udp_recv_start(&sock->uv_handle.udp, udp_alloc_cb, udp_read_cb);
}
isc_result_t
isc__nm_udp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
isc_nmsocket_t *sock = NULL;
isc__netievent_startread_t *ievent = NULL;
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
REQUIRE(handle->sock->type == isc_nm_udpsocket);
sock = handle->sock;
sock->rcb.recv = cb;
sock->rcbarg = cbarg;
ievent = isc__nm_get_ievent(sock->mgr, netievent_udpread);
ievent->sock = sock;
if (sock->tid == isc_nm_tid()) {
isc__nm_async_udpread(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
isc__nm_put_ievent(sock->mgr, ievent);
} else {
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
return (ISC_R_SUCCESS);
}
static void
udp_close_cb(uv_handle_t *uvhandle) {
isc_nmsocket_t *sock = uv_handle_get_data(uvhandle);
REQUIRE(VALID_NMSOCK(sock));
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CLOSE]);
atomic_store(&sock->closed, true);
isc__nmsocket_prep_destroy(sock);
}
void
isc__nm_async_udpclose(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_udpclose_t *ievent = (isc__netievent_udpclose_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
REQUIRE(worker->id == ievent->sock->tid);
uv_close(&sock->uv_handle.handle, udp_close_cb);
}
void
isc__nm_udp_close(isc_nmsocket_t *sock) {
isc__netievent_udpclose_t *ievent = NULL;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_udpsocket);
ievent = isc__nm_get_ievent(sock->mgr, netievent_udpclose);
ievent->sock = sock;
if (sock->tid == isc_nm_tid()) {
isc__nm_async_udpclose(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
isc__nm_put_ievent(sock->mgr, ievent);
} else {
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
}
bool
isc__nm_udp_cancelread(isc_nmhandle_t *handle) {
isc_nmsocket_t *sock = NULL;
REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(sock->type == isc_nm_udpsocket);
if (sock->client && sock->rcb.recv != NULL) {
sock->rcb.recv(handle, ISC_R_CANCELED, NULL, sock->rcbarg);
isc__nmsocket_clearcb(sock);
return (true);
}
return (false);
}
+672
View File
@@ -0,0 +1,672 @@
/*
* 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.
*/
/*
* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <isc/url.h>
#include <isc/util.h>
#ifndef BIT_AT
#define BIT_AT(a, i) \
(!!((unsigned int)(a)[(unsigned int)(i) >> 3] & \
(1 << ((unsigned int)(i)&7))))
#endif
#if HTTP_PARSER_STRICT
#define T(v) 0
#else
#define T(v) v
#endif
static const uint8_t normal_url_char[32] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
};
#undef T
typedef enum {
s_dead = 1, /* important that this is > 0 */
s_start_req_or_res,
s_res_or_resp_H,
s_start_res,
s_res_H,
s_res_HT,
s_res_HTT,
s_res_HTTP,
s_res_http_major,
s_res_http_dot,
s_res_http_minor,
s_res_http_end,
s_res_first_status_code,
s_res_status_code,
s_res_status_start,
s_res_status,
s_res_line_almost_done,
s_start_req,
s_req_method,
s_req_spaces_before_url,
s_req_schema,
s_req_schema_slash,
s_req_schema_slash_slash,
s_req_server_start,
s_req_server,
s_req_server_with_at,
s_req_path,
s_req_query_string_start,
s_req_query_string,
s_req_fragment_start,
s_req_fragment,
s_req_http_start,
s_req_http_H,
s_req_http_HT,
s_req_http_HTT,
s_req_http_HTTP,
s_req_http_I,
s_req_http_IC,
s_req_http_major,
s_req_http_dot,
s_req_http_minor,
s_req_http_end,
s_req_line_almost_done,
s_header_field_start,
s_header_field,
s_header_value_discard_ws,
s_header_value_discard_ws_almost_done,
s_header_value_discard_lws,
s_header_value_start,
s_header_value,
s_header_value_lws,
s_header_almost_done,
s_chunk_size_start,
s_chunk_size,
s_chunk_parameters,
s_chunk_size_almost_done,
s_headers_almost_done,
s_headers_done,
/*
* Important: 's_headers_done' must be the last 'header' state. All
* states beyond this must be 'body' states. It is used for overflow
* checking. See the PARSING_HEADER() macro.
*/
s_chunk_data,
s_chunk_data_almost_done,
s_chunk_data_done,
s_body_identity,
s_body_identity_eof,
s_message_done
} state_t;
typedef enum {
s_http_host_dead = 1,
s_http_userinfo_start,
s_http_userinfo,
s_http_host_start,
s_http_host_v6_start,
s_http_host,
s_http_host_v6,
s_http_host_v6_end,
s_http_host_v6_zone_start,
s_http_host_v6_zone,
s_http_host_port_start,
s_http_host_port
} host_state_t;
/* Macros for character classes; depends on strict-mode */
#define LOWER(c) (unsigned char)(c | 0x20)
#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
#define IS_MARK(c) \
((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || \
(c) == '*' || (c) == '\'' || (c) == '(' || (c) == ')')
#define IS_USERINFO_CHAR(c) \
(IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || (c) == ';' || \
(c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || (c) == '$' || \
(c) == ',')
#if HTTP_PARSER_STRICT
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
#else
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c) || ((c)&0x80))
#define IS_HOST_CHAR(c) \
(IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
#endif
/*
* Our URL parser.
*
* This is designed to be shared by http_parser_execute() for URL validation,
* hence it has a state transition + byte-for-byte interface. In addition, it
* is meant to be embedded in http_parser_parse_url(), which does the dirty
* work of turning state transitions URL components for its API.
*
* This function should only be invoked with non-space characters. It is
* assumed that the caller cares about (and can detect) the transition between
* URL and non-URL states by looking for these.
*/
static state_t
parse_url_char(state_t s, const char ch) {
if (ch == ' ' || ch == '\r' || ch == '\n') {
return (s_dead);
}
#if HTTP_PARSER_STRICT
if (ch == '\t' || ch == '\f') {
return (s_dead);
}
#endif
switch (s) {
case s_req_spaces_before_url:
/* Proxied requests are followed by scheme of an absolute URI
* (alpha). All methods except CONNECT are followed by '/' or
* '*'.
*/
if (ch == '/' || ch == '*') {
return (s_req_path);
}
if (IS_ALPHA(ch)) {
return (s_req_schema);
}
break;
case s_req_schema:
if (IS_ALPHA(ch)) {
return (s);
}
if (ch == ':') {
return (s_req_schema_slash);
}
break;
case s_req_schema_slash:
if (ch == '/') {
return (s_req_schema_slash_slash);
}
break;
case s_req_schema_slash_slash:
if (ch == '/') {
return (s_req_server_start);
}
break;
case s_req_server_with_at:
if (ch == '@') {
return (s_dead);
}
/* FALLTHROUGH */
case s_req_server_start:
case s_req_server:
if (ch == '/') {
return (s_req_path);
}
if (ch == '?') {
return (s_req_query_string_start);
}
if (ch == '@') {
return (s_req_server_with_at);
}
if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
return (s_req_server);
}
break;
case s_req_path:
if (IS_URL_CHAR(ch)) {
return (s);
}
switch (ch) {
case '?':
return (s_req_query_string_start);
case '#':
return (s_req_fragment_start);
}
break;
case s_req_query_string_start:
case s_req_query_string:
if (IS_URL_CHAR(ch)) {
return (s_req_query_string);
}
switch (ch) {
case '?':
/* allow extra '?' in query string */
return (s_req_query_string);
case '#':
return (s_req_fragment_start);
}
break;
case s_req_fragment_start:
if (IS_URL_CHAR(ch)) {
return (s_req_fragment);
}
switch (ch) {
case '?':
return (s_req_fragment);
case '#':
return (s);
}
break;
case s_req_fragment:
if (IS_URL_CHAR(ch)) {
return (s);
}
switch (ch) {
case '?':
case '#':
return (s);
}
break;
default:
break;
}
/*
* We should never fall out of the switch above unless there's an
* error.
*/
return (s_dead);
}
static host_state_t
http_parse_host_char(host_state_t s, const char ch) {
switch (s) {
case s_http_userinfo:
case s_http_userinfo_start:
if (ch == '@') {
return (s_http_host_start);
}
if (IS_USERINFO_CHAR(ch)) {
return (s_http_userinfo);
}
break;
case s_http_host_start:
if (ch == '[') {
return (s_http_host_v6_start);
}
if (IS_HOST_CHAR(ch)) {
return (s_http_host);
}
break;
case s_http_host:
if (IS_HOST_CHAR(ch)) {
return (s_http_host);
}
/* FALLTHROUGH */
case s_http_host_v6_end:
if (ch == ':') {
return (s_http_host_port_start);
}
break;
case s_http_host_v6:
if (ch == ']') {
return (s_http_host_v6_end);
}
/* FALLTHROUGH */
case s_http_host_v6_start:
if (IS_HEX(ch) || ch == ':' || ch == '.') {
return (s_http_host_v6);
}
if (s == s_http_host_v6 && ch == '%') {
return (s_http_host_v6_zone_start);
}
break;
case s_http_host_v6_zone:
if (ch == ']') {
return (s_http_host_v6_end);
}
/* FALLTHROUGH */
case s_http_host_v6_zone_start:
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' ||
ch == '_' || ch == '~')
{
return (s_http_host_v6_zone);
}
break;
case s_http_host_port:
case s_http_host_port_start:
if (IS_NUM(ch)) {
return (s_http_host_port);
}
break;
default:
break;
}
return (s_http_host_dead);
}
static int
http_parse_host(const char *buf, isc_url_parser_t *up, int found_at) {
host_state_t s;
const char *p = NULL;
size_t buflen = up->field_data[ISC_UF_HOST].off +
up->field_data[ISC_UF_HOST].len;
REQUIRE((up->field_set & (1 << ISC_UF_HOST)) != 0);
up->field_data[ISC_UF_HOST].len = 0;
s = found_at ? s_http_userinfo_start : s_http_host_start;
for (p = buf + up->field_data[ISC_UF_HOST].off; p < buf + buflen; p++) {
host_state_t new_s = http_parse_host_char(s, *p);
if (new_s == s_http_host_dead) {
return (1);
}
switch (new_s) {
case s_http_host:
if (s != s_http_host) {
up->field_data[ISC_UF_HOST].off =
(uint16_t)(p - buf);
}
up->field_data[ISC_UF_HOST].len++;
break;
case s_http_host_v6:
if (s != s_http_host_v6) {
up->field_data[ISC_UF_HOST].off =
(uint16_t)(p - buf);
}
up->field_data[ISC_UF_HOST].len++;
break;
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
up->field_data[ISC_UF_HOST].len++;
break;
case s_http_host_port:
if (s != s_http_host_port) {
up->field_data[ISC_UF_PORT].off =
(uint16_t)(p - buf);
up->field_data[ISC_UF_PORT].len = 0;
up->field_set |= (1 << ISC_UF_PORT);
}
up->field_data[ISC_UF_PORT].len++;
break;
case s_http_userinfo:
if (s != s_http_userinfo) {
up->field_data[ISC_UF_USERINFO].off =
(uint16_t)(p - buf);
up->field_data[ISC_UF_USERINFO].len = 0;
up->field_set |= (1 << ISC_UF_USERINFO);
}
up->field_data[ISC_UF_USERINFO].len++;
break;
default:
break;
}
s = new_s;
}
/* Make sure we don't end somewhere unexpected */
switch (s) {
case s_http_host_start:
case s_http_host_v6_start:
case s_http_host_v6:
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
case s_http_host_port_start:
case s_http_userinfo:
case s_http_userinfo_start:
return (1);
default:
break;
}
return (0);
}
int
isc_url_parse(const char *buf, size_t buflen, bool is_connect,
isc_url_parser_t *up) {
state_t s;
isc_url_field_t uf, old_uf;
int found_at = 0;
const char *p = NULL;
if (buflen == 0) {
return (1);
}
up->port = up->field_set = 0;
s = is_connect ? s_req_server_start : s_req_spaces_before_url;
old_uf = ISC_UF_MAX;
for (p = buf; p < buf + buflen; p++) {
s = parse_url_char(s, *p);
/* Figure out the next field that we're operating on */
switch (s) {
case s_dead:
return (1);
/* Skip delimeters */
case s_req_schema_slash:
case s_req_schema_slash_slash:
case s_req_server_start:
case s_req_query_string_start:
case s_req_fragment_start:
continue;
case s_req_schema:
uf = ISC_UF_SCHEMA;
break;
case s_req_server_with_at:
found_at = 1;
/* FALLTHROUGH*/
case s_req_server:
uf = ISC_UF_HOST;
break;
case s_req_path:
uf = ISC_UF_PATH;
break;
case s_req_query_string:
uf = ISC_UF_QUERY;
break;
case s_req_fragment:
uf = ISC_UF_FRAGMENT;
break;
default:
INSIST(0);
ISC_UNREACHABLE();
}
/* Nothing's changed; soldier on */
if (uf == old_uf) {
up->field_data[uf].len++;
continue;
}
up->field_data[uf].off = (uint16_t)(p - buf);
up->field_data[uf].len = 1;
up->field_set |= (1 << uf);
old_uf = uf;
}
/* host must be present if there is a schema */
/* parsing http:///toto will fail */
if ((up->field_set & (1 << ISC_UF_SCHEMA)) &&
(up->field_set & (1 << ISC_UF_HOST)) == 0)
{
return (1);
}
if (up->field_set & (1 << ISC_UF_HOST)) {
if (http_parse_host(buf, up, found_at) != 0) {
return (1);
}
}
/* CONNECT requests can only contain "hostname:port" */
if (is_connect &&
up->field_set != ((1 << ISC_UF_HOST) | (1 << ISC_UF_PORT))) {
return (1);
}
if (up->field_set & (1 << ISC_UF_PORT)) {
uint16_t off;
uint16_t len;
const char *pp = NULL;
const char *end = NULL;
unsigned long v;
off = up->field_data[ISC_UF_PORT].off;
len = up->field_data[ISC_UF_PORT].len;
end = buf + off + len;
/*
* NOTE: The characters are already validated and are in the
* [0-9] range
*/
INSIST(off + len <= buflen);
v = 0;
for (pp = buf + off; pp < end; p++) {
v *= 10;
v += *pp - '0';
/* Ports have a max value of 2^16 */
if (v > 0xffff) {
return (1);
}
}
up->port = (uint16_t)v;
}
return (0);
}
+6 -2
View File
@@ -436,15 +436,15 @@ isc_netaddr_setzone
isc_netaddr_totext
isc_netaddr_unspec
isc_netscope_pton
isc_nmhandle_attach
isc_nmhandle_detach
isc_nmhandle_getdata
isc_nmhandle_getextra
isc_nmhandle_is_stream
isc_nmhandle_netmgr
isc_nmhandle_localaddr
isc_nmhandle_peeraddr
isc_nmhandle_ref
isc_nmhandle_setdata
isc_nmhandle_unref
isc_nm_cancelread
isc_nm_closedown
isc_nm_destroy
@@ -461,11 +461,14 @@ isc_nm_setstats
isc_nm_start
isc_nm_stoplistening
isc_nm_tcpconnect
isc_nm_tcpdnsconnect
isc_nm_tcp_gettimeouts
isc_nm_tcp_settimeouts
isc_nm_tcpdns_keepalive
isc_nm_tcpdns_sequential
isc_nm_tid
isc_nm_tlsconnect
isc_nm_tlsdnsconnect
isc_nmsocket_close
isc__nm_acquire_interlocked
isc__nm_drop_interlocked
@@ -694,6 +697,7 @@ isc_timermgr_destroy
isc_timermgr_poke
isc_tm_timegm
isc_tm_strptime
isc_url_parse
isc_utf8_bom
isc_utf8_valid
isc_win32os_versioncheck
+6
View File
@@ -260,6 +260,9 @@
<ClInclude Include="..\include\isc\types.h">
<Filter>Library Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\isc\url.h">
<Filter>Library Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\isc\utf8.h">
<Filter>Library Header Files</Filter>
</ClInclude>
@@ -614,6 +617,9 @@
<ClCompile Include="..\tm.c">
<Filter>Library Source Files</Filter>
</ClCompile>
<ClCompile Include="..\url.c">
<Filter>Library Source Files</Filter>
</ClCompile>
<ClCompile Include="..\utf8.c">
<Filter>Library Source Files</Filter>
</ClCompile>
+2
View File
@@ -321,6 +321,7 @@ copy InstallFiles ..\Build\Release\
<ClInclude Include="..\include\isc\timer.h" />
<ClInclude Include="..\include\isc\tm.h" />
<ClInclude Include="..\include\isc\types.h" />
<ClInclude Include="..\include\isc\url.h" />
<ClInclude Include="..\include\isc\utf8.h" />
<ClInclude Include="..\include\isc\util.h" />
@IF PKCS11
@@ -425,6 +426,7 @@ copy InstallFiles ..\Build\Release\
<ClCompile Include="..\taskpool.c" />
<ClCompile Include="..\timer.c" />
<ClCompile Include="..\tm.c" />
<ClCompile Include="..\url.c" />
<ClCompile Include="..\utf8.c" />
@IF PKCS11
<ClCompile Include="..\pk11.c" />
+2 -6
View File
@@ -108,7 +108,6 @@ recv_data(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
done:
isc_nm_pauseread(handle);
ccmsg->cb(handle, ccmsg->result, ccmsg->cbarg);
isc_nmhandle_unref(handle);
}
void
@@ -149,18 +148,14 @@ isccc_ccmsg_readmessage(isccc_ccmsg_t *ccmsg, isc_nm_cb_t cb, void *cbarg) {
ccmsg->result = ISC_R_UNEXPECTED; /* unknown right now */
ccmsg->length_received = false;
isc_nmhandle_ref(ccmsg->handle);
if (ccmsg->reading) {
result = isc_nm_resumeread(ccmsg->handle);
} else {
result = isc_nm_read(ccmsg->handle, recv_data, ccmsg);
ccmsg->reading = true;
}
if (result == ISC_R_CANCELED) {
if (result != ISC_R_SUCCESS) {
ccmsg->reading = false;
} else if (result != ISC_R_SUCCESS) {
ccmsg->reading = false;
isc_nmhandle_unref(ccmsg->handle);
}
return (result);
@@ -172,6 +167,7 @@ isccc_ccmsg_cancelread(isccc_ccmsg_t *ccmsg) {
if (ccmsg->reading) {
isc_nm_cancelread(ccmsg->handle);
ccmsg->reading = false;
}
}
+3 -1
View File
@@ -58,7 +58,9 @@ void
isccc_ccmsg_init(isc_mem_t *mctx, isc_nmhandle_t *handle, isccc_ccmsg_t *ccmsg);
/*%
* Associate a cc message state with a given memory context and
* netmgr handle.
* netmgr handle. (Note that the caller must hold a reference to
* the handle during asynchronous ccmsg operations; the ccmsg code
* does not hold the reference itself.)
*
* Requires:
*
+20 -31
View File
@@ -129,7 +129,7 @@
#if defined(_WIN32) && !defined(_WIN64)
LIBNS_EXTERNAL_DATA atomic_uint_fast32_t ns_client_requests;
#else /* if defined(_WIN32) && !defined(_WIN64) */
#else /* if defined(_WIN32) && !defined(_WIN64) */
LIBNS_EXTERNAL_DATA atomic_uint_fast64_t ns_client_requests;
#endif /* if defined(_WIN32) && !defined(_WIN64) */
@@ -275,7 +275,7 @@ static void
client_senddone(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
ns_client_t *client = cbarg;
REQUIRE(client->handle == handle);
REQUIRE(client->sendhandle == handle);
CTRACE("senddone");
if (result != ISC_R_SUCCESS) {
@@ -284,7 +284,7 @@ client_senddone(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
"send failed: %s", isc_result_totext(result));
}
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&client->sendhandle);
}
static void
@@ -325,13 +325,18 @@ client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
static isc_result_t
client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
isc_result_t result;
isc_region_t r;
REQUIRE(client->sendhandle == NULL);
isc_buffer_usedregion(buffer, &r);
INSIST(client->handle != NULL);
return (isc_nm_send(client->handle, &r, client_senddone, client));
isc_nmhandle_attach(client->handle, &client->sendhandle);
result = isc_nm_send(client->handle, &r, client_senddone, client);
if (result != ISC_R_SUCCESS) {
isc_nmhandle_detach(&client->sendhandle);
}
return (result);
}
void
@@ -383,7 +388,6 @@ done:
}
ns_client_drop(client, result);
isc_nmhandle_unref(client->handle);
}
void
@@ -405,17 +409,13 @@ ns_client_send(ns_client_t *client) {
isc_region_t zr;
#endif /* HAVE_DNSTAP */
REQUIRE(NS_CLIENT_VALID(client));
/*
* XXXWPK TODO
* Delay the response according to the -T delay option
*/
REQUIRE(NS_CLIENT_VALID(client));
/*
* We need to do it to make sure the client and handle
* won't disappear from under us with client_senddone.
*/
env = ns_interfacemgr_getaclenv(client->manager->interface->mgr);
CTRACE("send");
@@ -591,12 +591,7 @@ renderend:
respsize = isc_buffer_usedlength(&buffer);
isc_nmhandle_ref(client->handle);
result = client_sendpkg(client, &buffer);
if (result != ISC_R_SUCCESS) {
/* We won't get a callback to clean it up */
isc_nmhandle_unref(client->handle);
}
switch (isc_sockaddr_pf(&client->peeraddr)) {
case AF_INET:
@@ -626,12 +621,7 @@ renderend:
respsize = isc_buffer_usedlength(&buffer);
isc_nmhandle_ref(client->handle);
result = client_sendpkg(client, &buffer);
if (result != ISC_R_SUCCESS) {
/* We won't get a callback to clean it up */
isc_nmhandle_unref(client->handle);
}
switch (isc_sockaddr_pf(&client->peeraddr)) {
case AF_INET:
@@ -1683,6 +1673,7 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
ns__client_put_cb);
client->handle = handle;
}
if (isc_nmhandle_is_stream(handle)) {
client->attributes |= NS_CLIENTATTR_TCP;
}
@@ -1697,8 +1688,7 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
isc_buffer_add(&tbuffer, region->length);
buffer = &tbuffer;
client->peeraddr = isc_nmhandle_peeraddr(client->handle);
client->peeraddr = isc_nmhandle_peeraddr(handle);
client->peeraddr_valid = true;
reqsize = isc_buffer_usedlength(buffer);
@@ -1968,8 +1958,7 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
isc_netaddr_fromsockaddr(&client->destaddr,
&client->manager->interface->addr);
} else {
isc_sockaddr_t sockaddr =
isc_nmhandle_localaddr(client->handle);
isc_sockaddr_t sockaddr = isc_nmhandle_localaddr(handle);
isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
}
@@ -2174,7 +2163,7 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
&client->requesttime, NULL, buffer);
#endif /* HAVE_DNSTAP */
isc_nmhandle_ref(client->handle);
isc_nmhandle_attach(handle, &client->reqhandle);
ns_query_start(client);
break;
case dns_opcode_update:
@@ -2185,13 +2174,13 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
&client->requesttime, NULL, buffer);
#endif /* HAVE_DNSTAP */
ns_client_settimeout(client, 60);
isc_nmhandle_ref(client->handle);
isc_nmhandle_attach(handle, &client->reqhandle);
ns_update_start(client, sigresult);
break;
case dns_opcode_notify:
CTRACE("notify");
ns_client_settimeout(client, 60);
isc_nmhandle_ref(client->handle);
isc_nmhandle_attach(handle, &client->reqhandle);
ns_notify_start(client);
break;
case dns_opcode_iquery:
+7 -2
View File
@@ -183,7 +183,12 @@ struct ns_client {
isc_task_t * task;
dns_view_t * view;
dns_dispatch_t * dispatch;
isc_nmhandle_t * handle;
isc_nmhandle_t * handle; /* Permanent pointer to handle */
isc_nmhandle_t * sendhandle; /* Waiting for send callback */
isc_nmhandle_t * reqhandle; /* Waiting for request callback
(query, update, notify) */
isc_nmhandle_t * fetchhandle; /* Waiting for recursive fetch */
isc_nmhandle_t * updatehandle; /* Waiting for update callback */
unsigned char * tcpbuf;
dns_message_t * message;
unsigned char * sendbuf;
@@ -274,7 +279,7 @@ struct ns_client {
#if defined(_WIN32) && !defined(_WIN64)
LIBNS_EXTERNAL_DATA extern atomic_uint_fast32_t ns_client_requests;
#else /* if defined(_WIN32) && !defined(_WIN64) */
#else /* if defined(_WIN32) && !defined(_WIN64) */
LIBNS_EXTERNAL_DATA extern atomic_uint_fast64_t ns_client_requests;
#endif /* if defined(_WIN32) && !defined(_WIN64) */
+1 -1
View File
@@ -468,7 +468,7 @@ ns_interface_listentcp(ns_interface_t *ifp) {
result = isc_nm_listentcpdns(
ifp->mgr->nm, (isc_nmiface_t *)&ifp->addr, ns__client_request,
ifp, ns__client_tcpconn, ifp, sizeof(ns_client_t),
ifp->mgr->backlog, &ifp->mgr->sctx->tcpquota,
ifp->mgr->backlog, &ifp->mgr->sctx->tcpquota, NULL,
&ifp->tcplistensocket);
if (result != ISC_R_SUCCESS) {
isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+2 -2
View File
@@ -54,7 +54,7 @@ respond(ns_client_t *client, isc_result_t result) {
}
if (msg_result != ISC_R_SUCCESS) {
ns_client_drop(client, msg_result);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->reqhandle);
return;
}
message->rcode = rcode;
@@ -65,7 +65,7 @@ respond(ns_client_t *client, isc_result_t result) {
}
ns_client_send(client);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->reqhandle);
}
void
+22 -15
View File
@@ -550,7 +550,7 @@ query_send(ns_client_t *client) {
inc_stats(client, counter);
ns_client_send(client);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->reqhandle);
}
static void
@@ -577,7 +577,7 @@ query_error(ns_client_t *client, isc_result_t result, int line) {
log_queryerror(client, result, line, loglevel);
ns_client_error(client, result);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->reqhandle);
}
static void
@@ -590,7 +590,7 @@ query_next(ns_client_t *client, isc_result_t result) {
inc_stats(client, ns_statscounter_failure);
}
ns_client_drop(client, result);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->reqhandle);
}
static inline void
@@ -2475,7 +2475,7 @@ prefetch_done(isc_task_t *task, isc_event_t *event) {
}
free_devent(client, &event, &devent);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->fetchhandle);
}
static void
@@ -2518,7 +2518,7 @@ query_prefetch(ns_client_t *client, dns_name_t *qname,
peeraddr = NULL;
}
isc_nmhandle_ref(client->handle);
isc_nmhandle_attach(client->handle, &client->fetchhandle);
options = client->query.fetchoptions | DNS_FETCHOPT_PREFETCH;
result = dns_resolver_createfetch(
client->view->resolver, qname, rdataset->type, NULL, NULL, NULL,
@@ -2527,7 +2527,7 @@ query_prefetch(ns_client_t *client, dns_name_t *qname,
&client->query.prefetch);
if (result != ISC_R_SUCCESS) {
ns_client_putrdataset(client, &tmprdataset);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->fetchhandle);
}
dns_rdataset_clearprefetch(rdataset);
@@ -2732,7 +2732,7 @@ query_rpzfetch(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t type) {
}
options = client->query.fetchoptions;
isc_nmhandle_ref(client->handle);
isc_nmhandle_attach(client->handle, &client->fetchhandle);
result = dns_resolver_createfetch(
client->view->resolver, qname, type, NULL, NULL, NULL, peeraddr,
client->message->id, options, 0, NULL, client->task,
@@ -2740,7 +2740,7 @@ query_rpzfetch(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t type) {
&client->query.prefetch);
if (result != ISC_R_SUCCESS) {
ns_client_putrdataset(client, &tmprdataset);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->fetchhandle);
}
}
@@ -5178,7 +5178,7 @@ query_trace(query_ctx_t *qctx) {
(int)qctx->client->query.authdbset,
(int)qctx->client->query.isreferral);
CCTRACE(ISC_LOG_DEBUG(3), mbuf);
#else /* ifdef WANT_QUERYTRACE */
#else /* ifdef WANT_QUERYTRACE */
UNUSED(qctx);
#endif /* ifdef WANT_QUERYTRACE */
}
@@ -5703,6 +5703,12 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
}
UNLOCK(&client->manager->reclock);
if (client->reqhandle == NULL) {
isc_nmhandle_attach(client->handle, &client->reqhandle);
}
isc_nmhandle_detach(&client->fetchhandle);
client->query.attributes &= ~NS_QUERYATTR_RECURSING;
client->state = NS_CLIENTSTATE_WORKING;
@@ -5748,7 +5754,6 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
}
dns_resolver_destroyfetch(&fetch);
isc_nmhandle_unref(client->handle);
}
/*%
@@ -5940,14 +5945,14 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
peeraddr = &client->peeraddr;
}
isc_nmhandle_ref(client->handle);
isc_nmhandle_attach(client->handle, &client->fetchhandle);
result = dns_resolver_createfetch(
client->view->resolver, qname, qtype, qdomain, nameservers,
NULL, peeraddr, client->message->id, client->query.fetchoptions,
0, NULL, client->task, fetch_callback, client, rdataset,
sigrdataset, &client->query.fetch);
if (result != ISC_R_SUCCESS) {
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->fetchhandle);
ns_client_putrdataset(client, &rdataset);
if (sigrdataset != NULL) {
ns_client_putrdataset(client, &sigrdataset);
@@ -6194,7 +6199,7 @@ ns__query_sfcache(query_ctx_t *qctx) {
qctx->view->failcache, qctx->client->query.qname,
qctx->qtype, &flags, &qctx->client->tnow);
}
#else /* ifdef ENABLE_AFL */
#else /* ifdef ENABLE_AFL */
failcache = dns_badcache_find(qctx->view->failcache,
qctx->client->query.qname, qctx->qtype,
&flags, &qctx->client->tnow);
@@ -8344,7 +8349,7 @@ query_nodata(query_ctx_t *qctx, isc_result_t res) {
#ifdef dns64_bis_return_excluded_addresses
if (qctx->dns64)
#else /* ifdef dns64_bis_return_excluded_addresses */
#else /* ifdef dns64_bis_return_excluded_addresses */
if (qctx->dns64 && !qctx->dns64_exclude)
#endif /* ifdef dns64_bis_return_excluded_addresses */
{
@@ -11060,7 +11065,9 @@ log_query(ns_client_t *client, unsigned int flags, unsigned int extflags) {
TCP(client) ? "T" : "",
((extflags & DNS_MESSAGEEXTFLAG_DO) != 0) ? "D" : "",
((flags & DNS_MESSAGEFLAG_CD) != 0) ? "C" : "",
HAVECOOKIE(client) ? "V" : WANTCOOKIE(client) ? "K" : "",
HAVECOOKIE(client) ? "V"
: WANTCOOKIE(client) ? "K"
: "",
onbuf, ecsbuf);
}
+31 -10
View File
@@ -1567,7 +1567,9 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
client->nupdates++;
event->ev_arg = client;
isc_nmhandle_ref(client->handle);
if (client->updatehandle == NULL) {
isc_nmhandle_attach(client->handle, &client->updatehandle);
}
dns_zone_gettask(zone, &zonetask);
isc_task_send(zonetask, ISC_EVENT_PTR(&event));
@@ -1585,7 +1587,6 @@ respond(ns_client_t *client, isc_result_t result) {
client->message->rcode = dns_result_torcode(result);
ns_client_send(client);
isc_nmhandle_unref(client->handle);
return;
msg_failure:
@@ -1594,7 +1595,7 @@ msg_failure:
"could not create update response message: %s",
isc_result_totext(msg_result));
ns_client_drop(client, msg_result);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->reqhandle);
}
void
@@ -1673,6 +1674,8 @@ ns_update_start(ns_client_t *client, isc_result_t sigresult) {
default:
FAILC(DNS_R_NOTAUTH, "not authoritative for update zone");
}
isc_nmhandle_detach(&client->reqhandle);
return;
failure:
@@ -1690,6 +1693,7 @@ failure:
if (zone != NULL) {
dns_zone_detach(&zone);
}
isc_nmhandle_detach(&client->reqhandle);
}
/*%
@@ -3497,6 +3501,10 @@ common:
}
uev->ev_type = DNS_EVENT_UPDATEDONE;
uev->ev_action = updatedone_action;
if (client->updatehandle == NULL) {
isc_nmhandle_attach(client->handle, &client->updatehandle);
}
isc_task_send(client->task, &event);
INSIST(ver == NULL);
@@ -3510,8 +3518,9 @@ updatedone_action(isc_task_t *task, isc_event_t *event) {
UNUSED(task);
INSIST(event->ev_type == DNS_EVENT_UPDATEDONE);
INSIST(task == client->task);
REQUIRE(event->ev_type == DNS_EVENT_UPDATEDONE);
REQUIRE(task == client->task);
REQUIRE(client->updatehandle == client->handle);
INSIST(client->nupdates > 0);
switch (uev->result) {
@@ -3528,16 +3537,18 @@ updatedone_action(isc_task_t *task, isc_event_t *event) {
if (uev->zone != NULL) {
dns_zone_detach(&uev->zone);
}
client->nupdates--;
respond(client, uev->result);
isc_event_free(&event);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->updatehandle);
}
/*%
* Update forwarding support.
*/
static void
forward_fail(isc_task_t *task, isc_event_t *event) {
ns_client_t *client = (ns_client_t *)event->ev_arg;
@@ -3548,7 +3559,7 @@ forward_fail(isc_task_t *task, isc_event_t *event) {
client->nupdates--;
respond(client, DNS_R_SERVFAIL);
isc_event_free(&event);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->updatehandle);
}
static void
@@ -3568,6 +3579,10 @@ forward_callback(void *arg, isc_result_t result, dns_message_t *answer) {
uev->answer = answer;
inc_stats(client, zone, ns_statscounter_updaterespfwd);
}
if (client->updatehandle == NULL) {
isc_nmhandle_attach(client->handle, &client->updatehandle);
}
isc_task_send(client->task, ISC_EVENT_PTR(&uev));
dns_zone_detach(&zone);
}
@@ -3584,7 +3599,7 @@ forward_done(isc_task_t *task, isc_event_t *event) {
ns_client_sendraw(client, uev->answer);
dns_message_destroy(&uev->answer);
isc_event_free(&event);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->updatehandle);
}
static void
@@ -3594,6 +3609,10 @@ forward_action(isc_task_t *task, isc_event_t *event) {
ns_client_t *client = (ns_client_t *)event->ev_arg;
isc_result_t result;
if (client->updatehandle == NULL) {
isc_nmhandle_attach(client->handle, &client->updatehandle);
}
result = dns_zone_forwardupdate(zone, client->message, forward_callback,
event);
if (result != ISC_R_SUCCESS) {
@@ -3636,7 +3655,9 @@ send_forward_event(ns_client_t *client, dns_zone_t *zone) {
namebuf, classbuf);
dns_zone_gettask(zone, &zonetask);
isc_nmhandle_ref(client->handle);
if (client->updatehandle == NULL) {
isc_nmhandle_attach(client->handle, &client->updatehandle);
}
isc_task_send(zonetask, ISC_EVENT_PTR(&event));
if (event != NULL) {
+4 -4
View File
@@ -1192,7 +1192,7 @@ failure:
NS_LOGMODULE_XFER_OUT, ISC_LOG_DEBUG(3),
"zone transfer setup failed");
ns_client_error(client, result);
isc_nmhandle_unref(client->handle);
isc_nmhandle_detach(&client->handle);
}
}
@@ -1580,7 +1580,7 @@ sendstream(xfrout_ctx_t *xfr) {
xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response");
ns_client_send(xfr->client);
xfr->stream->methods->pause(xfr->stream);
isc_nmhandle_unref(xfr->client->handle);
isc_nmhandle_detach(&xfr->client->handle);
xfrout_ctx_destroy(&xfr);
return;
}
@@ -1718,7 +1718,7 @@ xfrout_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
xfrout_ctx_destroy(&xfr);
/* We're done, unreference the handle */
isc_nmhandle_unref(handle);
isc_nmhandle_detach(&handle);
}
}
@@ -1744,7 +1744,7 @@ xfrout_maybe_destroy(xfrout_ctx_t *xfr) {
} else {
#endif /* if 0 */
ns_client_drop(xfr->client, ISC_R_CANCELED);
isc_nmhandle_unref(xfr->client->handle);
isc_nmhandle_detach(&xfr->client->handle);
xfrout_ctx_destroy(&xfr);
#if 0
}
+1 -1
View File
@@ -68,7 +68,7 @@ AC_DEFUN([AX_CHECK_OPENSSL], [
AS_IF([test -f "$ssldir/include/openssl/ssl.h"],
[
OPENSSL_CFLAGS="-I$ssldir/include"
OPENSSL_LIBS="-L$ssldir/lib -lcrypto"
OPENSSL_LIBS="-L$ssldir/lib -lcrypto -lssl"
found=true
AC_MSG_RESULT([yes])
break
+4
View File
@@ -1861,6 +1861,7 @@
./lib/isc/include/isc/timer.h C 1998,1999,2000,2001,2002,2004,2005,2006,2007,2008,2009,2012,2013,2014,2016,2018,2019,2020
./lib/isc/include/isc/tm.h C 2014,2016,2018,2019,2020
./lib/isc/include/isc/types.h C 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2012,2013,2014,2016,2017,2018,2019,2020
./lib/isc/include/isc/url.h C 2020
./lib/isc/include/isc/utf8.h C 2020
./lib/isc/include/isc/util.h C 1998,1999,2000,2001,2004,2005,2006,2007,2010,2011,2012,2015,2016,2017,2018,2019,2020
./lib/isc/include/pk11/constants.h C 2014,2016,2017,2018,2019,2020
@@ -1878,10 +1879,12 @@
./lib/isc/mem_p.h C 2018,2019,2020
./lib/isc/mutexblock.c C 1999,2000,2001,2004,2005,2007,2011,2012,2016,2018,2019,2020
./lib/isc/netaddr.c C 1999,2000,2001,2002,2004,2005,2007,2010,2011,2012,2014,2015,2016,2017,2018,2019,2020
./lib/isc/netmgr/http.c C 2020
./lib/isc/netmgr/netmgr-int.h C 2019,2020
./lib/isc/netmgr/netmgr.c C 2019,2020
./lib/isc/netmgr/tcp.c C 2019,2020
./lib/isc/netmgr/tcpdns.c C 2019,2020
./lib/isc/netmgr/tls.c C 2020
./lib/isc/netmgr/udp.c C 2019,2020
./lib/isc/netmgr/uv-compat.c C 2020
./lib/isc/netmgr/uv-compat.h C 2019,2020
@@ -1985,6 +1988,7 @@
./lib/isc/unix/stdtime.c C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020
./lib/isc/unix/syslog.c C 2001,2004,2005,2007,2016,2018,2019,2020
./lib/isc/unix/time.c C 1998,1999,2000,2001,2003,2004,2005,2006,2007,2008,2011,2012,2014,2015,2016,2017,2018,2019,2020
./lib/isc/url.c C 2020
./lib/isc/utf8.c C 2020
./lib/isc/win32/DLLMain.c C 2001,2004,2007,2016,2018,2019,2020
./lib/isc/win32/condition.c C 1998,1999,2000,2001,2004,2006,2007,2016,2018,2019,2020