"with-openssl=C:/OpenSSL"
"with-libxml2=C:/libxml2"
"with-libuv=C:/libuv"
+ "with-nghttp2=C:/nghttp2"
"without-python"
"with-system-tests"
x64'
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.
{"libdns.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"libirs.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"libeay32.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
- {"libuv.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
+ {"nghttp2.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
+ {"uv.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
#ifdef HAVE_LIBXML2
{"libxml2.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
#endif
#
# 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"
AC_CHECK_FUNCS([uv_handle_get_data uv_handle_set_data uv_import uv_udp_connect uv_translate_sys_error])
AX_RESTORE_FLAGS([libuv])
+# libnghttp2
+AC_MSG_CHECKING([for libnghttp2])
+PKG_CHECK_MODULES([LIBNGHTTP2], [libnghttp2 >= 1.6.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
#
include/isc/tls.h \
include/isc/tm.h \
include/isc/types.h \
+ include/isc/url.h \
include/isc/utf8.h \
include/isc/util.h \
pthreads/include/isc/condition.h\
$(libisc_la_HEADERS) \
$(pk11_HEADERS) \
$(pkcs11_HEADERS) \
+ netmgr/http.c \
netmgr/netmgr-int.h \
netmgr/netmgr.c \
netmgr/tcp.c \
unix/stdtime.c \
unix/syslog.c \
unix/time.c \
+ url.c \
pk11.c \
pk11_result.c \
aes.c \
* 'cb'.
*/
+typedef void (*isc_nm_http_cb_t)(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *postdata, isc_region_t *getdata,
+ void *cbarg);
+/*%<
+ * Callback function to be used when receiving an HTTP request.
+ *
+ * 'handle' the handle that can be used to send back the answer.
+ * 'eresult' the result of the event.
+ * 'postdata' contains the received POST data, if any. It will be freed
+ * after return by caller.
+ * 'getdata' contains the received GET data (past '?'), if any. It will be
+ * freed after return by caller.
+ * 'cbarg' the callback argument passed to listen function.
+ */
+
+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, isc_ssl_ctx_t *ctx);
+
+isc_result_t
+isc_nm_httpsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
+ const char *uri, isc_nm_cb_t cb, void *cbarg,
+ unsigned int timeout, size_t extrahandlesize);
+
+isc_result_t
+isc_nm_listenhttps(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog,
+ isc_quota_t *quota, isc_ssl_ctx_t *ctx,
+ isc_nmsocket_t **sockp);
+
+isc_result_t
+isc_nm_http_add_endpoint(isc_nmsocket_t *sock, const char *uri,
+ isc_nm_http_cb_t cb, void *cbarg,
+ size_t extrahandlesize);
+
+isc_result_t
+isc_nm_http_add_doh_endpoint(isc_nmsocket_t *sock, const char *uri,
+ isc_nm_recv_cb_t cb, void *cbarg,
+ size_t extrahandlesize);
--- /dev/null
+/*
+ * 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 https://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>
+
+#include <isc/result.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;
+
+isc_result_t
+isc_url_parse(const char *buf, size_t buflen, bool is_connect,
+ isc_url_parser_t *up);
+/*%<
+ * Parse a URL; return nonzero on failure
+ */
--- /dev/null
+/*
+ * 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 https://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/print.h>
+#include <isc/url.h>
+
+#include "netmgr-int.h"
+
+#define AUTHEXTRA 7
+
+typedef struct http2_client_stream {
+ isc_nm_recv_cb_t cb;
+ void *cbarg;
+
+ char *uri;
+ isc_url_parser_t up;
+
+ char *authority;
+ size_t authoritylen;
+ char *path;
+
+ uint8_t rbuf[65535];
+ size_t rbufsize;
+
+ size_t pathlen;
+ int32_t stream_id;
+ isc_region_t *postdata;
+ size_t postdata_pos;
+} http2_client_stream_t;
+
+#define HTTP2_SESSION_MAGIC ISC_MAGIC('H', '2', 'S', 'S')
+#define VALID_HTTP2_SESSION(t) ISC_MAGIC_VALID(t, HTTP2_SESSION_MAGIC)
+
+struct isc_nm_http2_session {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ bool sending;
+ bool closed;
+ bool reading;
+
+ nghttp2_session *ngsession;
+ http2_client_stream_t *cstream;
+ ISC_LIST(isc_nmsocket_h2_t) sstreams;
+
+ isc_nmhandle_t *handle;
+ isc_nmsocket_t *serversocket;
+
+ isc_region_t r;
+ uint8_t buf[65535];
+ size_t bufsize;
+
+ SSL_CTX *ctx;
+};
+
+static bool
+http2_do_bio(isc_nm_http2_session_t *session);
+
+static isc_result_t
+get_http2_client_stream(isc_mem_t *mctx, http2_client_stream_t **streamp,
+ const char *uri, uint16_t *port) {
+ http2_client_stream_t *stream = NULL;
+ int rv;
+
+ REQUIRE(streamp != NULL && *streamp == NULL);
+ REQUIRE(uri != NULL);
+ REQUIRE(port != NULL);
+
+ stream = isc_mem_get(mctx, sizeof(http2_client_stream_t));
+ *stream = (http2_client_stream_t){ .stream_id = -1 };
+
+ 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_client_stream_t));
+ isc_mem_free(mctx, stream->uri);
+ return (ISC_R_FAILURE);
+ }
+
+ stream->authoritylen = stream->up.field_data[ISC_UF_HOST].len;
+ stream->authority = isc_mem_get(mctx, stream->authoritylen + AUTHEXTRA);
+ memmove(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)) {
+ memmove(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] = '?';
+ memmove(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_client_stream(isc_mem_t *mctx, http2_client_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_client_stream_t));
+}
+
+static void
+delete_http2_session(isc_nm_http2_session_t *session) {
+ if (session->handle != NULL) {
+ isc_nm_pauseread(session->handle);
+ isc_nmhandle_detach(&session->handle);
+ }
+ if (session->ngsession != NULL) {
+ nghttp2_session_del(session->ngsession);
+ session->ngsession = NULL;
+ }
+ if (session->cstream != NULL) {
+ put_http2_client_stream(session->mctx, session->cstream);
+ session->cstream = NULL;
+ }
+
+ /*
+ * There might be leftover callbacks waiting to be received
+ */
+ if (session->sending) {
+ session->closed = true;
+ } else if (!session->reading) {
+ session->magic = 0;
+ isc_mem_putanddetach(&session->mctx, session,
+ sizeof(isc_nm_http2_session_t));
+ }
+}
+
+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) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+
+ UNUSED(ngsession);
+ UNUSED(flags);
+
+ if (session->cstream != NULL) {
+ if (session->cstream->stream_id == stream_id) {
+ /* TODO buffer overrun! */
+ memmove(session->cstream->rbuf +
+ session->cstream->rbufsize,
+ data, len);
+ session->cstream->rbufsize += len;
+ }
+ } else {
+ isc_nmsocket_h2_t *sock_h2 = ISC_LIST_HEAD(session->sstreams);
+ while (sock_h2 != NULL) {
+ if (stream_id == sock_h2->stream_id) {
+ memmove(sock_h2->buf + sock_h2->bufsize,
+ data, len);
+ sock_h2->bufsize += len;
+ break;
+ }
+ sock_h2 = ISC_LIST_NEXT(sock_h2, link);
+ }
+ }
+
+ return (0);
+}
+
+static int
+on_stream_close_callback(nghttp2_session *ngsession, int32_t stream_id,
+ uint32_t error_code, void *user_data) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ UNUSED(error_code);
+
+ if (session->cstream != NULL) {
+ if (session->cstream->stream_id == stream_id) {
+ int rv;
+
+ session->cstream->cb(
+ NULL, ISC_R_SUCCESS,
+ &(isc_region_t){ session->cstream->rbuf,
+ session->cstream->rbufsize },
+ session->cstream->cbarg);
+ rv = nghttp2_session_terminate_session(
+ ngsession, NGHTTP2_NO_ERROR);
+ if (rv != 0) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+ }
+ } else {
+ /* XXX */
+ }
+
+ /* 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_client_session(isc_nm_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);
+
+ nghttp2_session_client_new(&session->ngsession, callbacks, session);
+
+ nghttp2_session_callbacks_del(callbacks);
+}
+
+static void
+send_client_connection_header(isc_nm_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 *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \
+ sizeof(NAME) - 1, VALUELEN, NGHTTP2_NV_FLAG_NONE \
+ }
+
+#define MAKE_NV2(NAME, VALUE) \
+ { \
+ (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \
+ sizeof(NAME) - 1, sizeof(VALUE) - 1, \
+ NGHTTP2_NV_FLAG_NONE \
+ }
+
+static ssize_t
+client_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) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+
+ REQUIRE(session->cstream != NULL);
+
+ UNUSED(ngsession);
+ UNUSED(source);
+
+ if (session->cstream->stream_id == stream_id) {
+ size_t len = session->cstream->postdata->length -
+ session->cstream->postdata_pos;
+
+ if (len > length) {
+ len = length;
+ }
+
+ memmove(buf,
+ session->cstream->postdata->base +
+ session->cstream->postdata_pos,
+ len);
+ session->cstream->postdata_pos += len;
+
+ if (session->cstream->postdata_pos ==
+ session->cstream->postdata->length) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+
+ return (len);
+ }
+
+ return (0);
+}
+
+/* Send HTTP request to the remote peer */
+static isc_result_t
+client_submit_request(isc_nm_http2_session_t *session) {
+ int32_t stream_id;
+ http2_client_stream_t *stream = session->cstream;
+ char *uri = stream->uri;
+ isc_url_parser_t *up = &stream->up;
+ nghttp2_data_provider dp;
+ 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)),
+ };
+
+ dp = (nghttp2_data_provider){ .read_callback =
+ client_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
+https_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
+ void *data) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)data;
+ ssize_t readlen;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ UNUSED(handle);
+ UNUSED(result);
+
+ if (result != ISC_R_SUCCESS) {
+ session->reading = false;
+ delete_http2_session(session);
+ /* TODO callback! */
+ return;
+ }
+
+ 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 void
+https_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)arg;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ UNUSED(handle);
+ UNUSED(result);
+
+ session->sending = false;
+ isc_mem_put(session->mctx, session->r.base, session->r.length);
+ session->r.base = NULL;
+ http2_do_bio(session);
+}
+
+static bool
+http2_do_bio(isc_nm_http2_session_t *session) {
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ if (session->closed ||
+ (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, https_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 (!session->sending &&
+ nghttp2_session_want_write(session->ngsession) != 0) {
+ const uint8_t *data = NULL;
+ size_t sz;
+
+ /*
+ * XXXWPK TODO
+ * This function may produce a very small byte string. If
+ * that is the case, and application disables Nagle
+ * algorithm (``TCP_NODELAY``), then writing this small
+ * chunk leads to a 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);
+ INSIST(session->r.base == NULL);
+ session->r.base = isc_mem_get(session->mctx, sz);
+ session->r.length = sz;
+ memmove(session->r.base, data, sz);
+ session->sending = true;
+ isc_nm_send(session->handle, &session->r, https_writecb,
+ session);
+ return (true);
+ }
+
+ return (false);
+}
+
+static void
+https_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ isc_nm_http2_session_t *session = (isc_nm_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) {
+ delete_http2_session(session);
+ return;
+ }
+#endif
+
+ initialize_nghttp2_client_session(session);
+ send_client_connection_header(session);
+ client_submit_request(session);
+ http2_do_bio(session);
+}
+
+isc_result_t
+isc_nm_httpsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
+ const char *uri, isc_nm_cb_t cb, void *cbarg,
+ unsigned int timeout, size_t extrahandlesize) {
+ REQUIRE(VALID_NM(mgr));
+
+ UNUSED(local);
+ UNUSED(peer);
+ UNUSED(uri);
+ UNUSED(cb);
+ UNUSED(cbarg);
+ UNUSED(timeout);
+ UNUSED(extrahandlesize);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+isc_result_t
+isc_nm_doh_request(isc_nm_t *mgr, const char *uri, isc_region_t *region,
+ isc_nm_recv_cb_t cb, void *cbarg, SSL_CTX *ctx) {
+ uint16_t port;
+ char *host = NULL;
+ isc_nm_http2_session_t *session = NULL;
+ http2_client_stream_t *cstream = 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(isc_nm_http2_session_t));
+ *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC,
+ .ctx = ctx };
+ isc_mem_attach(mgr->mctx, &session->mctx);
+
+ result = get_http2_client_stream(mgr->mctx, &cstream, uri, &port);
+ if (result != ISC_R_SUCCESS) {
+ delete_http2_session(session);
+ return (result);
+ }
+
+ cstream->postdata = region;
+ cstream->postdata_pos = 0;
+ cstream->cb = cb;
+ cstream->cbarg = cbarg;
+
+ session->cstream = cstream;
+
+#ifndef WIN32 /* FIXME */
+ hints = (struct addrinfo){ .ai_family = PF_UNSPEC,
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_CANONNAME };
+ host = isc_mem_strndup(mgr->mctx, cstream->authority,
+ cstream->authoritylen + 1);
+
+ s = getaddrinfo(host, NULL, &hints, &res);
+ isc_mem_free(mgr->mctx, host);
+ if (s != 0) {
+ delete_http2_session(session);
+ return (ISC_R_FAILURE);
+ }
+#endif /* WIN32 */
+
+ 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, https_connect_cb,
+ session, ctx, 30000, 0);
+ /* XXX: timeout is hard-coded to 30 seconds - make it a parameter */
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static int
+server_on_begin_headers_callback(nghttp2_session *ngsession,
+ const nghttp2_frame *frame, void *user_data) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+ isc_nmsocket_t *socket = NULL;
+ isc_sockaddr_t iface;
+
+ if (frame->hd.type != NGHTTP2_HEADERS ||
+ frame->headers.cat != NGHTTP2_HCAT_REQUEST)
+ {
+ return (0);
+ }
+
+ socket = isc_mem_get(session->mctx, sizeof(isc_nmsocket_t));
+ iface = isc_nmhandle_localaddr(session->handle);
+ isc__nmsocket_init(socket, session->serversocket->mgr,
+ isc_nm_httpstream, (isc_nmiface_t *)&iface);
+ socket->h2 = (isc_nmsocket_h2_t){ .bufpos = 0,
+ .bufsize = 0,
+ .psock = socket,
+ .handler = NULL,
+ .request_path = NULL,
+ .query_data = NULL,
+ .stream_id = frame->hd.stream_id,
+ .session = session };
+
+ ISC_LINK_INIT(&socket->h2, link);
+ ISC_LIST_APPEND(session->sstreams, &socket->h2, link);
+ nghttp2_session_set_stream_user_data(ngsession, frame->hd.stream_id,
+ socket);
+ return (0);
+}
+
+static int
+server_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
+ const uint8_t *name, size_t namelen,
+ const uint8_t *value, size_t valuelen, uint8_t flags,
+ void *user_data) {
+ isc_nmsocket_t *socket = NULL;
+ const char path[] = ":path";
+
+ UNUSED(flags);
+ UNUSED(user_data);
+
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
+ break;
+ }
+
+ socket = nghttp2_session_get_stream_user_data(
+ session, frame->hd.stream_id);
+ if (socket == NULL || socket->h2.request_path != NULL) {
+ break;
+ }
+
+ if (namelen == sizeof(path) - 1 &&
+ memcmp(path, name, namelen) == 0) {
+ size_t j;
+ for (j = 0; j < valuelen && value[j] != '?'; ++j)
+ ;
+ socket->h2.request_path = isc_mem_strndup(
+ socket->mgr->mctx, (const char *)value, j + 1);
+ if (j < valuelen) {
+ socket->h2.query_data = isc_mem_strndup(
+ socket->mgr->mctx, (char *)value + j,
+ valuelen - j);
+ }
+ }
+ break;
+ }
+
+ return (0);
+}
+
+static ssize_t
+server_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) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+ isc_nmsocket_t *socket = (isc_nmsocket_t *)source->ptr;
+ size_t buflen;
+
+ REQUIRE(socket->h2.stream_id == stream_id);
+
+ UNUSED(ngsession);
+ UNUSED(session);
+
+ buflen = socket->h2.bufsize - socket->h2.bufpos;
+ if (buflen > length) {
+ buflen = length;
+ }
+
+ memmove(buf, socket->h2.buf + socket->h2.bufpos, buflen);
+ socket->h2.bufpos += buflen;
+ if (socket->h2.bufpos == socket->h2.bufsize) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+
+ return (buflen);
+}
+
+static int
+server_send_response(nghttp2_session *ngsession, int32_t stream_id,
+ const nghttp2_nv *nva, size_t nvlen,
+ isc_nmsocket_t *socket) {
+ int rv;
+ nghttp2_data_provider data_prd;
+ data_prd.source.ptr = socket;
+ data_prd.read_callback = server_read_callback;
+
+ rv = nghttp2_submit_response(ngsession, stream_id, nva, nvlen,
+ &data_prd);
+ if (rv != 0) {
+ return (-1);
+ }
+ return (0);
+}
+
+static const char ERROR_HTML[] = "<html><head><title>404</title></head>"
+ "<body><h1>404 Not Found</h1></body></html>";
+
+static int
+error_reply(nghttp2_session *ngsession, isc_nmsocket_t *socket) {
+ const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "404") };
+
+ memmove(socket->h2.buf, ERROR_HTML, sizeof(ERROR_HTML));
+ socket->h2.bufsize = sizeof(ERROR_HTML);
+ socket->h2.bufpos = 0;
+
+ server_send_response(ngsession, socket->h2.stream_id, hdrs,
+ sizeof(hdrs) / sizeof(nghttp2_nv), socket);
+ return (0);
+}
+
+static int
+server_on_request_recv(nghttp2_session *ngsession,
+ isc_nm_http2_session_t *session,
+ isc_nmsocket_t *socket) {
+ isc_nm_http2_server_handler_t *handler = NULL;
+ isc_nmhandle_t *handle = NULL;
+ isc_sockaddr_t addr;
+
+ if (!socket->h2.request_path) {
+ if (error_reply(ngsession, socket) != 0) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+ return (0);
+ }
+
+ for (handler = ISC_LIST_HEAD(session->serversocket->handlers);
+ handler != NULL; handler = ISC_LIST_NEXT(handler, link))
+ {
+ if (!strcmp(socket->h2.request_path, handler->path)) {
+ break;
+ }
+ }
+
+ if (handler == NULL) {
+ if (error_reply(ngsession, socket) != 0) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+ return (0);
+ }
+
+ socket->extrahandlesize = handler->extrahandlesize;
+ addr = isc_nmhandle_peeraddr(session->handle);
+ handle = isc__nmhandle_get(socket, &addr, NULL);
+ handler->cb(handle, ISC_R_SUCCESS,
+ &(isc_region_t){ socket->h2.buf, socket->h2.bufsize },
+ &(isc_region_t){ (unsigned char *)socket->h2.query_data,
+ strlen(socket->h2.query_data) + 1 },
+ handler->cbarg);
+ return (0);
+}
+
+void
+isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
+ const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "200") };
+ isc_nmsocket_t *sock = handle->sock;
+
+ /* TODO FIXME do it asynchronously!!! */
+ memcpy(sock->h2.buf, region->base, region->length);
+ sock->h2.bufsize = region->length;
+ if (server_send_response(handle->httpsession->ngsession,
+ sock->h2.stream_id, hdrs,
+ sizeof(hdrs) / sizeof(nghttp2_nv), sock) != 0)
+ {
+ cb(handle, ISC_R_FAILURE, cbarg);
+ } else {
+ cb(handle, ISC_R_SUCCESS, cbarg);
+ }
+}
+
+static int
+server_on_frame_recv_callback(nghttp2_session *ngsession,
+ const nghttp2_frame *frame, void *user_data) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+ isc_nmsocket_t *socket = NULL;
+
+ switch (frame->hd.type) {
+ case NGHTTP2_DATA:
+ case NGHTTP2_HEADERS:
+ /* Check that the client request has finished */
+ if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ socket = nghttp2_session_get_stream_user_data(
+ ngsession, frame->hd.stream_id);
+
+ /*
+ * For DATA and HEADERS frame, this callback may be
+ * called after on_stream_close_callback. Check that
+ * the stream is still alive.
+ */
+ if (socket == NULL) {
+ return (0);
+ }
+
+ return (server_on_request_recv(ngsession, session,
+ socket));
+ }
+ break;
+ default:
+ break;
+ }
+ return (0);
+}
+
+static void
+initialize_nghttp2_server_session(isc_nm_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);
+
+ nghttp2_session_callbacks_set_on_header_callback(
+ callbacks, server_on_header_callback);
+
+ nghttp2_session_callbacks_set_on_begin_headers_callback(
+ callbacks, server_on_begin_headers_callback);
+
+ nghttp2_session_callbacks_set_on_frame_recv_callback(
+ callbacks, server_on_frame_recv_callback);
+
+ nghttp2_session_server_new(&session->ngsession, callbacks, session);
+
+ nghttp2_session_callbacks_del(callbacks);
+}
+
+static int
+server_send_connection_header(isc_nm_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) {
+ return (-1);
+ }
+ return (0);
+}
+
+static isc_result_t
+httplisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+ isc_nmsocket_t *httplistensock = (isc_nmsocket_t *)cbarg;
+ isc_nm_http2_session_t *session = NULL;
+
+ if (result != ISC_R_SUCCESS) {
+ /* XXXWPK do nothing? */
+ return (result);
+ }
+
+ session = isc_mem_get(httplistensock->mgr->mctx,
+ sizeof(isc_nm_http2_session_t));
+ *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC };
+ initialize_nghttp2_server_session(session);
+
+ isc_mem_attach(httplistensock->mgr->mctx, &session->mctx);
+ isc_nmhandle_attach(handle, &session->handle);
+ isc__nmsocket_attach(httplistensock, &session->serversocket);
+ server_send_connection_header(session);
+
+ /* TODO H2 */
+ http2_do_bio(session);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_nm_listenhttps(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog,
+ isc_quota_t *quota, SSL_CTX *ctx, isc_nmsocket_t **sockp) {
+ isc_nmsocket_t *sock = NULL;
+ isc_result_t result;
+
+ isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_httplistener, iface);
+
+ if (ctx != NULL) {
+ result = isc_nm_listentls(mgr, iface, httplisten_acceptcb, sock,
+ sizeof(isc_nm_http2_session_t),
+ backlog, quota, ctx, &sock->outer);
+ } else {
+ result = isc_nm_listentcp(mgr, iface, httplisten_acceptcb, sock,
+ sizeof(isc_nm_http2_session_t),
+ backlog, quota, &sock->outer);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_detach(&sock);
+ return (result);
+ }
+
+ atomic_store(&sock->listening, true);
+ *sockp = sock;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_nm_http_add_endpoint(isc_nmsocket_t *sock, const char *uri,
+ isc_nm_http_cb_t cb, void *cbarg,
+ size_t extrahandlesize) {
+ isc_nm_http2_server_handler_t *handler = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_httplistener);
+
+ handler = isc_mem_get(sock->mgr->mctx, sizeof(*handler));
+ *handler = (isc_nm_http2_server_handler_t){
+ .cb = cb,
+ .cbarg = cbarg,
+ .extrahandlesize = extrahandlesize,
+ .path = isc_mem_strdup(sock->mgr->mctx, uri)
+ };
+
+ ISC_LINK_INIT(handler, link);
+ ISC_LIST_APPEND(sock->handlers, handler, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+typedef struct {
+ isc_nm_recv_cb_t cb;
+ void *cbarg;
+} cbarg_t;
+
+static unsigned char doh_error[] =
+ "<html><head><title>No request</title></head>"
+ "<body><h1>No request</h1></body></html>";
+
+static const isc_region_t doh_error_r = { doh_error, sizeof(doh_error) };
+
+static void
+https_sendcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+ UNUSED(handle);
+ UNUSED(result);
+ UNUSED(cbarg);
+}
+
+/*
+ * In DoH we just need to intercept the request - the response can be sent
+ * to the client code via the nmhandle directly as it's always just the
+ * http * content.
+ */
+static void
+doh_callback(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *post,
+ isc_region_t *get, void *arg) {
+ cbarg_t *dohcbarg = arg;
+ isc_region_t *data = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ UNUSED(result);
+ UNUSED(get);
+
+ if (result != ISC_R_SUCCESS) {
+ /* Shut down the client, then ourselves */
+ dohcbarg->cb(NULL, result, NULL, dohcbarg->cbarg);
+ /* XXXWPK FREE */
+ return;
+ }
+
+ if (post != NULL) {
+ data = post;
+ } else if (get != NULL) {
+ /* XXXWPK PARSE */
+ data = NULL; /* FIXME */
+ } else {
+ /* Invalid request, just send the error response */
+ isc_nm_send(handle, &doh_error_r, https_sendcb, dohcbarg);
+ return;
+ }
+
+ dohcbarg->cb(handle, result, data, dohcbarg->cbarg);
+}
+
+isc_result_t
+isc_nm_http_add_doh_endpoint(isc_nmsocket_t *sock, const char *uri,
+ isc_nm_recv_cb_t cb, void *cbarg,
+ size_t extrahandlesize) {
+ isc_result_t result;
+ cbarg_t *dohcbarg = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_httplistener);
+
+ dohcbarg = isc_mem_get(sock->mgr->mctx, sizeof(cbarg_t));
+ *dohcbarg = (cbarg_t){ cb, cbarg };
+
+ result = isc_nm_http_add_endpoint(sock, uri, doh_callback, dohcbarg,
+ extrahandlesize);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(sock->mgr->mctx, dohcbarg, sizeof(cbarg_t));
+ }
+
+ return (result);
+}
#define isc__nmsocket_prep_destroy(sock) isc___nmsocket_prep_destroy(sock)
#endif
+typedef struct isc_nm_http2_session isc_nm_http2_session_t;
+
/*
* Single network event loop worker.
*/
isc_nmsocket_t *sock;
size_t ah_pos; /* Position in the socket's 'active handles' array */
+ isc_nm_http2_session_t *httpsession;
+
isc_sockaddr_t peer;
isc_sockaddr_t local;
isc_nm_opaquecb_t doreset; /* reset extra callback, external */
typedef union {
isc_nm_recv_cb_t recv;
+ isc_nm_http_cb_t http;
isc_nm_cb_t send;
isc_nm_cb_t connect;
isc_nm_accept_cb_t accept;
} isc__nm_cb_t;
+typedef struct isc_nm_http2_server_handler isc_nm_http2_server_handler_t;
+
+struct isc_nm_http2_server_handler {
+ char *path;
+ isc_nm_http_cb_t cb;
+ void *cbarg;
+ size_t extrahandlesize;
+ LINK(isc_nm_http2_server_handler_t) link;
+};
+
/*
* Wrapper around uv_req_t with 'our' fields in it. req->data should
* always point to its parent. Note that we always allocate more than
isc_nm_tlslistener,
isc_nm_tlssocket,
isc_nm_tlsdnslistener,
- isc_nm_tlsdnssocket
+ isc_nm_tlsdnssocket,
+ isc_nm_httplistener,
+ isc_nm_httpstream
} isc_nmsocket_type;
/*%
STATID_ACTIVE = 10
};
-
typedef struct isc_nmsocket_tls_send_req {
isc_nmsocket_t *tlssock;
isc_region_t data;
} isc_nmsocket_tls_send_req_t;
+typedef struct isc_nmsocket_h2 {
+ isc_nmsocket_t *psock; /* owner of the structure */
+ char *request_path;
+ char *query_data;
+ isc_nm_http2_server_handler_t *handler;
+
+ uint8_t buf[65535];
+ size_t bufsize;
+ size_t bufpos;
+
+ int32_t stream_id;
+ LINK(struct isc_nmsocket_h2) link;
+} isc_nmsocket_h2_t;
struct isc_nmsocket {
/*% Unlocked, RO */
int magic;
int tid;
isc_nmsocket_type type;
isc_nm_t *mgr;
+
/*% Parent socket for multithreaded listeners */
isc_nmsocket_t *parent;
/*% Listener socket this connection was accepted on */
ISC_LIST(isc__nm_uvreq_t) sends;
} tlsstream;
+ isc_nmsocket_h2_t h2;
/*%
* quota is the TCP client, attached when a TCP connection
* is established. pquota is a non-attached pointer to the
void *accept_cbarg;
atomic_int_fast32_t active_child_connections;
+
+ ISC_LIST(isc_nm_http2_server_handler_t) handlers;
+
#ifdef NETMGR_TRACE
void *backtrace[TRACE_SIZE];
int backtrace_size;
*/
void
-isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
- void *cbarg);
+isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
/*%<
* Back-end implementation of isc_nm_send() for UDP handles.
*/
*/
void
-isc__nm_tcp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
- void *cbarg);
+isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
/*%<
* Back-end implementation of isc_nm_send() for TCP handles.
*/
isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region,
isc_nm_cb_t cb, void *cbarg);
+void
+isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
+
void
isc__nm_tls_cancelread(isc_nmhandle_t *handle);
* Stop reading on a connected TLSDNS handle.
*/
-void
-isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
- void *cbarg);
-
void
isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
void
isc__nm_tls_stoplistening(isc_nmsocket_t *sock);
+void
+isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
+
#define isc__nm_uverr2result(x) \
isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__)
isc_result_t
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_send(handle, region, cb, cbarg);
break;
+ case isc_nm_httpstream:
+ isc__nm_http_send(handle, region, cb, cbarg);
+ break;
default:
INSIST(0);
ISC_UNREACHABLE();
}
void
-isc__nm_tcp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
- void *cbarg) {
+isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
}
void
-isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
- void *cbarg) {
+isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
isc__netievent_tlssend_t *ievent = NULL;
isc__nm_uvreq_t *uvreq = NULL;
isc_nmsocket_t *sock = NULL;
+
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
if (result != ISC_R_SUCCESS) {
goto error;
}
-
return;
error:
tlshandle = isc__nmhandle_get(tlssock, NULL, NULL);
* another thread.
*/
void
-isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
- void *cbarg) {
+isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
isc_nmsocket_t *sock = handle->sock;
isc_nmsocket_t *psock = NULL, *rsock = sock;
isc_sockaddr_t *peer = &handle->peer;
--- /dev/null
+/*
+ * 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 https://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 IS_MARK(c) \
+ ((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || \
+ (c) == '*' || (c) == '\'' || (c) == '(' || (c) == ')')
+#define IS_USERINFO_CHAR(c) \
+ (isalnum(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) (isalnum(c) || (c) == '.' || (c) == '-')
+#else
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c) || ((c)&0x80))
+#define IS_HOST_CHAR(c) (isalnum(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 (isalpha(ch)) {
+ return (s_req_schema);
+ }
+
+ break;
+
+ case s_req_schema:
+ if (isalpha(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 (isxdigit(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 (isalnum(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 (isdigit(ch)) {
+ return (s_http_host_port);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return (s_http_host_dead);
+}
+
+static isc_result_t
+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 (ISC_R_FAILURE);
+ }
+
+ 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 (ISC_R_FAILURE);
+ default:
+ break;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+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 (ISC_R_FAILURE);
+ }
+
+ 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 (ISC_R_FAILURE);
+
+ /* Skip delimiters */
+ 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 (ISC_R_FAILURE);
+ }
+
+ if (up->field_set & (1 << ISC_UF_HOST)) {
+ isc_result_t result;
+
+ result = http_parse_host(buf, up, found_at);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ /* CONNECT requests can only contain "hostname:port" */
+ if (is_connect &&
+ up->field_set != ((1 << ISC_UF_HOST) | (1 << ISC_UF_PORT))) {
+ return (ISC_R_FAILURE);
+ }
+
+ 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 (ISC_R_RANGE);
+ }
+ }
+
+ up->port = (uint16_t)v;
+ }
+
+ return (ISC_R_SUCCESS);
+}
isc_nm_closedown
isc_nm_destroy
isc_nm_detach
+isc_nm_listenhttps
isc_nm_listentcpdns
isc_nm_listentls
isc_nm_listentlsdns
isc_tlsctx_free
isc_tm_timegm
isc_tm_strptime
+isc_url_parse
isc_utf8_bom
isc_utf8_valid
isc_win32os_versioncheck
<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>
<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>
@IF PKCS11
<PreprocessorDefinitions>BIND9;@PK11_LIB_LOCATION@WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
- <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ELSE PKCS11
<PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
- <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@END PKCS11
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
- <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile>
<ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary>
</Link>
copy @LIBXML2_DLL@ ..\Build\Debug\
@END LIBXML2
+echo Copying nghttp2 DLL.
+copy @NGHTTP2_DLL@ ..\Build\Debug\
+
@IF GSSAPI
echo Copying the GSSAPI and KRB5 DLLs.
@IF PKCS11
<PreprocessorDefinitions>BIND9;@PK11_LIB_LOCATION@WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
- <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ELSE PKCS11
<PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
- <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@END PKCS11
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<WholeProgramOptimization>false</WholeProgramOptimization>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
- <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile>
<ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
echo Copying libuv DLL.
copy @LIBUV_DLL@ ..\Build\Release\
+echo Copying nghttp2 DLL.
+copy @NGHTTP2_DLL@ ..\Build\Release\
+
@IF LIBXML2
echo Copying the libxml DLL.
<ClInclude Include="..\include\isc\tls.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
<ClCompile Include="..\mem.c" />
<ClCompile Include="..\mutexblock.c" />
<ClCompile Include="..\netaddr.c" />
+ <ClCompile Include="..\netmgr\http.c" />
<ClCompile Include="..\netmgr\netmgr.c" />
<ClCompile Include="..\netmgr\tcp.c" />
- <ClCompile Include="..\netmgr\udp.c" />
- <ClCompile Include="..\netmgr\uverr2result.c" />
- <ClCompile Include="..\netmgr\uv-compat.c" />
<ClCompile Include="..\netmgr\tcpdns.c" />
<ClCompile Include="..\netmgr\tlsstream.c" />
+ <ClCompile Include="..\netmgr\udp.c" />
<ClCompile Include="..\netmgr\tlsdns.c" />
+ <ClCompile Include="..\netmgr\uv-compat.c" />
+ <ClCompile Include="..\netmgr\uverr2result.c" />
<ClCompile Include="..\netscope.c" />
<ClCompile Include="..\nonce.c" />
<ClCompile Include="..\openssl_shim.c" />
<ClCompile Include="..\timer.c" />
<ClCompile Include="..\tls.c" />
<ClCompile Include="..\tm.c" />
+ <ClCompile Include="..\url.c" />
<ClCompile Include="..\utf8.c" />
@IF PKCS11
<ClCompile Include="..\pk11.c" />
"IDN_INC",
"LIBXML2_INC",
"LIBUV_INC",
+ "NGHTTP2_INC",
"OPENSSL_INC",
"READLINE_INC",
"ZLIB_INC");
"KRB5_LIB",
"LIBXML2_LIB",
"LIBUV_LIB",
+ "NGHTTP2_LIB",
"OPENSSL_LIBCRYPTO",
"OPENSSL_LIBSSL",
"READLINE_LIB",
"K5SPRT_DLL",
"LIBXML2_DLL",
"LIBUV_DLL",
+ "NGHTTP2_DLL",
"OPENSSL_DLLCRYPTO",
"OPENSSL_DLLSSL",
"WSHELP_DLL",
"idn",
"openssl",
"libxml2",
+ "nghttp2",
"pkcs11",
"pssuspend",
"python",
" with-samples build with sample programs\n",
" with-openssl[=PATH] build with OpenSSL yes|path (mandatory)\n",
" with-libuv[=PATH] build with libuv yes|path (mandatory)\n",
+" with-nghttp2[=PATH] build with nghttp2 yes|path (mandatory)\n",
" with-pkcs11[=PATH] build with PKCS#11 support yes|no|provider-path\n",
" with-gssapi[=PATH] build with MIT KfW GSSAPI yes|no|path\n",
" with-libxml2[=PATH] build with libxml2 library yes|no|path\n",
my $use_samples = "no";
my $use_libuv = "auto";
my $libuv_path = "../../";
+my $nghttp2_path = "../../";
+my $use_nghttp2 = "auto";
my $use_openssl = "auto";
my $openssl_path = "../../";
my $use_pkcs11 = "no";
$use_libuv = "yes";
$libuv_path = $val;
}
+ } elsif ($key =~ /^nghttp2$/i) {
+ if ($val =~ /^no$/i) {
+ die "nghttp2 is required\n";
+ } elsif ($val !~ /^yes$/i) {
+ $use_nghttp2 = "yes";
+ $nghttp2_path = $val;
+ }
} elsif ($key =~ /^pkcs11$/i) {
if ($val =~ /^yes$/i) {
$use_pkcs11 = "yes";
print "querytrace: disabled\n";
}
print "libuv-path: $libuv_path\n";
+ print "nghttp2-path: $nghttp2_path\n";
print "openssl-path: $openssl_path\n";
if ($use_tests eq "yes") {
print "tests: enabled\n";
# $configdefh{"HAVE_UV_IMPORT"} = 1;
}
+# with-nghttp2
+if ($use_nghttp2 eq "auto") {
+ if ($verbose) {
+ print "checking for an nghttp2 built directory at sibling root\n";
+ }
+ opendir DIR, $nghttp2_path || die "No Directory: $!\n";
+ my @dirlist = grep (/^nghttp2-[0-9]+\.[0-9]+\.[0-9]+$/i, readdir(DIR));
+ closedir(DIR);
+
+ # Make sure we have something
+ if (scalar(@dirlist) == 0) {
+ die "can't find an nghttp2 at sibling root\n";
+ }
+ # Now see if we have a directory or just a file.
+ # Make sure we are case insensitive
+ my $file;
+ foreach $file (sort {uc($b) cmp uc($a)} @dirlist) {
+ if (-f File::Spec->catfile($nghttp2_path,
+ $file,
+ "include", "nghttp2", "nghttp2.h")) {
+ $nghttp2_path = File::Spec->catdir($nghttp2_path, $file);
+ $use_nghttp2 = "yes";
+ last;
+ }
+ }
+
+ # If we have one use it otherwise report the error
+ if ($use_nghttp2 eq "auto") {
+ die "can't find an nghttp2 built directory at sibling root\n";
+ }
+}
+
+if ($use_nghttp2 eq "yes") {
+ $nghttp2_path = File::Spec->rel2abs($nghttp2_path);
+ if ($verbose) {
+ print "checking for nghttp2 directory at \"$nghttp2_path\"\n";
+ }
+ if (!-f File::Spec->catfile($nghttp2_path,
+ "include", "nghttp2", "nghttp2.h")) {
+ die "can't find nghttp2 nghttp2.h include\n";
+ }
+ my $nghttp2_inc = File::Spec->catdir($nghttp2_path, "include");
+ my $nghttp2_bindir = File::Spec->catdir($nghttp2_path, "bin");
+ my $nghttp2_libdir = File::Spec->catdir($nghttp2_path, "lib");
+ my $nghttp2_dll = File::Spec->catfile($nghttp2_bindir, "nghttp2.dll");
+ my $nghttp2_lib = File::Spec->catfile($nghttp2_libdir, "nghttp2.lib");
+ if (!-f $nghttp2_lib) {
+ die "can't find nghttp2.lib library\n";
+ }
+ if (!-f $nghttp2_dll) {
+ die "can't find nghttp2.dll library\n";
+ }
+ $configinc{"NGHTTP2_INC"} = "$nghttp2_inc";
+ $configlib{"NGHTTP2_LIB"} = "$nghttp2_lib";
+ $configdll{"NGHTTP2_DLL"} = "$nghttp2_dll";
+}
+
# with-openssl
if ($use_openssl eq "auto") {
if ($verbose) {
print LOUT "libdns.dll-BCFT\n";
print LOUT "libirs.dll-BCFT\n";
print LOUT "libns.dll-BCFT\n";
+ print LOUT "nghttp2.dll-BCFT\n";
print LOUT "uv.dll-BCFT\n";
if ($use_openssl eq "yes") {
my $v;