- style, cleanup, and removal of unnecessary code.
- combined isc_nm_http_add_endpoint() and isc_nm_http_add_doh_endpoint()
into one function, renamed isc_http_endpoint().
- moved isc_nm_http_connect_send_request() into doh_test.c as a helper
function; remove it from the public API.
- renamed isc_http2 and isc_nm_http2 types and functions to just isc_http
and isc_nm_http, for consistency with other existing names.
- shortened a number of long names.
- the caller is now responsible for determining the peer address.
in isc_nm_httpconnect(); this eliminates the need to parse the URI
and the dependency on an external resolver.
- the caller is also now responsible for creating the SSL client context,
for consistency with isc_nm_tlsdnsconnect().
- added setter functions for HTTP/2 ALPN. instead of setting up ALPN in
isc_tlsctx_createclient(), we now have a function
isc_tlsctx_enable_http2client_alpn() that can be run from
isc_nm_httpconnect().
- refactored isc_nm_httprequest() into separate read and send functions.
isc_nm_send() or isc_nm_read() is called on an http socket, it will
be stored until a corresponding isc_nm_read() or _send() arrives; when
we have both halves of the pair the HTTP request will be initiated.
- isc_nm_httprequest() is renamed isc__nm_http_request() for use as an
internal helper function by the DoH unit test. (eventually doh_test
should be rewritten to use read and send, and this function should
be removed.)
- added implementations of isc__nm_tls_settimeout() and
isc__nm_http_settimeout().
- increased NGHTTP2 header block length for client connections to 128K.
- use isc_mem_t for internal memory allocations inside nghttp2, to
help track memory leaks.
- send "Cache-Control" header in requests and responses. (note:
currently we try to bypass HTTP caching proxies, but ideally we should
interact with them: https://tools.ietf.org/html/rfc8484#section-5.1)
create_name(dohid, &dohname);
- transport = dns_transport_new(&dohname, DNS_TRANSPORT_DOH,
+ transport = dns_transport_new(&dohname, DNS_TRANSPORT_HTTP,
list);
parse_transport_option(doh, transport, "key-file",
DNS_TRANSPORT_UDP = 1,
DNS_TRANSPORT_TCP = 2,
DNS_TRANSPORT_TLS = 3,
- DNS_TRANSPORT_DOH = 4,
+ DNS_TRANSPORT_HTTP = 4,
DNS_TRANSPORT_COUNT = 5,
} dns_transport_type_t;
typedef enum {
- DNS_DOH_GET = 0,
- DNS_DOH_POST = 1,
-} dns_doh_mode_t;
+ DNS_HTTP_GET = 0,
+ DNS_HTTP_POST = 1,
+} dns_http_mode_t;
typedef struct dns_transport dns_transport_t;
typedef struct dns_transport_list dns_transport_list_t;
dns_transport_get_hostname(dns_transport_t *transport);
char *
dns_transport_get_endpoint(dns_transport_t *transport);
-dns_doh_mode_t
+dns_http_mode_t
dns_transport_get_mode(dns_transport_t *transport);
/*%<
* Getter functions: return the type, cert file, key file, CA file,
- * hostname, DoH endpoint, or DoH mode (GET or POST) for 'transport'.
+ * hostname, HTTP endpoint, or HTTP mode (GET or POST) for 'transport'.
*/
void
void
dns_transport_set_endpoint(dns_transport_t *transport, const char *endpoint);
void
-dns_transport_set_mode(dns_transport_t *transport, dns_doh_mode_t mode);
+dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode);
/*%<
* Setter functions: set the type, cert file, key file, CA file,
- * hostname, DoH endpoint, or DoH mode (GET or POST) for 'transport'.
+ * hostname, HTTP endpoint, or HTTP mode (GET or POST) for 'transport'.
*
* Requires:
*\li 'transport' is valid.
- *\li 'transport' is of type DNS_TRANSPORT_TLS or DNS_TRANSPORT_DOH
+ *\li 'transport' is of type DNS_TRANSPORT_TLS or DNS_TRANSPORT_HTTP
* (for certfile, keyfile, cafile, or hostname).
- *\li 'transport' is of type DNS_TRANSPORT_DOH (for endpoint or mode).
+ *\li 'transport' is of type DNS_TRANSPORT_HTTP (for endpoint or mode).
*/
void
} tls;
struct {
char *endpoint;
- dns_doh_mode_t mode;
+ dns_http_mode_t mode;
} doh;
};
return (transport->doh.endpoint);
}
-dns_doh_mode_t
+dns_http_mode_t
dns_transport_get_mode(dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
dns_transport_set_certfile(dns_transport_t *transport, const char *certfile) {
REQUIRE(VALID_TRANSPORT(transport));
REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
- transport->type == DNS_TRANSPORT_DOH);
+ transport->type == DNS_TRANSPORT_HTTP);
if (certfile != NULL) {
transport->tls.certfile = isc_mem_strdup(transport->mctx,
dns_transport_set_keyfile(dns_transport_t *transport, const char *keyfile) {
REQUIRE(VALID_TRANSPORT(transport));
REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
- transport->type == DNS_TRANSPORT_DOH);
+ transport->type == DNS_TRANSPORT_HTTP);
if (keyfile != NULL) {
transport->tls.keyfile = isc_mem_strdup(transport->mctx,
dns_transport_set_cafile(dns_transport_t *transport, const char *cafile) {
REQUIRE(VALID_TRANSPORT(transport));
REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
- transport->type == DNS_TRANSPORT_DOH);
+ transport->type == DNS_TRANSPORT_HTTP);
if (cafile != NULL) {
transport->tls.cafile = isc_mem_strdup(transport->mctx, cafile);
dns_transport_set_hostname(dns_transport_t *transport, const char *hostname) {
REQUIRE(VALID_TRANSPORT(transport));
REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
- transport->type == DNS_TRANSPORT_DOH);
+ transport->type == DNS_TRANSPORT_HTTP);
if (hostname != NULL) {
transport->tls.hostname = isc_mem_strdup(transport->mctx,
void
dns_transport_set_endpoint(dns_transport_t *transport, const char *endpoint) {
REQUIRE(VALID_TRANSPORT(transport));
- REQUIRE(transport->type == DNS_TRANSPORT_DOH);
+ REQUIRE(transport->type == DNS_TRANSPORT_HTTP);
if (endpoint != NULL) {
transport->doh.endpoint = isc_mem_strdup(transport->mctx,
}
void
-dns_transport_set_mode(dns_transport_t *transport, dns_doh_mode_t mode) {
+dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode) {
REQUIRE(VALID_TRANSPORT(transport));
- REQUIRE(transport->type == DNS_TRANSPORT_DOH);
+ REQUIRE(transport->type == DNS_TRANSPORT_HTTP);
transport->doh.mode = mode;
}
* 'cb'.
*/
-typedef void (*isc_nm_http_cb_t)(isc_nmhandle_t *handle, isc_result_t eresult,
- isc_region_t *data, 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.
- * 'data' contains the received data, if any. It will be freed
- * after return by caller.
- * 'cbarg' the callback argument passed to listen function.
- */
-
-isc_result_t
-isc_nm_http_connect_send_request(isc_nm_t *mgr, const char *uri, bool POST,
- isc_region_t *message, isc_nm_recv_cb_t cb,
- void *cbarg, isc_tlsctx_t *ctx,
- unsigned int timeout);
-
isc_result_t
isc_nm_httpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
const char *uri, bool POST, isc_nm_cb_t cb, void *cbarg,
isc_tlsctx_t *ctx, unsigned int timeout,
size_t extrahandlesize);
-isc_result_t
-isc_nm_httprequest(isc_nmhandle_t *handle, isc_region_t *region,
- isc_nm_recv_cb_t reply_cb, void *cbarg);
-
isc_result_t
isc_nm_listenhttp(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog,
isc_quota_t *quota, isc_tlsctx_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);
+isc_nm_http_endpoint(isc_nmsocket_t *sock, const char *uri, isc_nm_recv_cb_t cb,
+ void *cbarg, size_t extrahandlesize);
#define ISC_R_DEFAULT 68 /*%< default */
#define ISC_R_IPV4PREFIX 69 /*%< IPv4 prefix */
#define ISC_R_TLSERROR 70 /*%< TLS error */
+#define ISC_R_HTTP2ALPNERROR 71 /*%< ALPN for HTTP/2 failed */
/*% Not a result code: the number of results. */
-#define ISC_R_NRESULTS 71
+#define ISC_R_NRESULTS 72
ISC_LANG_BEGINDECLS
#include <isc/mem.h>
#include <isc/region.h>
#include <isc/result.h>
-#include <isc/tls.h>
#include <isc/types.h>
typedef struct ssl_ctx_st isc_tlsctx_t;
+typedef struct ssl_st isc_tls_t;
void
isc_tlsctx_free(isc_tlsctx_t **ctpx);
-/*%
- * Free the TLS client/server context.
+/*%<
+ * Free a TLS client or server context.
*
- * Require:
+ * Requires:
*\li 'ctxp' != NULL and '*ctxp' != NULL.
*/
isc_result_t
isc_tlsctx_createserver(const char *keyfile, const char *certfile,
isc_tlsctx_t **ctxp);
-/*%
- * Set up TLS server context.
+/*%<
+ * Set up a TLS server context, using the key and certificate specified in
+ * 'keyfile' and 'certfile', or a self-generated ephemeral key and
+ * certificdate if both 'keyfile' and 'certfile' are NULL.
*
- * Require:
+ * Requires:
*\li 'ctxp' != NULL and '*ctxp' == NULL.
+ *\li 'keyfile' and 'certfile' are either both NULL or both non-NULL.
*/
isc_result_t
isc_tlsctx_createclient(isc_tlsctx_t **ctxp);
-/*%
- * Set up TLS client context.
+/*%<
+ * Set up a TLS client context.
*
- * Require:
+ * Requires:
*\li 'ctxp' != NULL and '*ctxp' == NULL.
*/
+
+isc_tls_t *
+isc_tls_create(isc_tlsctx_t *ctx);
+/*%<
+ * Set up the structure to hold data for a new TLS connection.
+ *
+ * Requires:
+ *\li 'ctx' != NULL.
+ */
+
+void
+isc_tls_free(isc_tls_t **tlsp);
+/*%<
+ * Free a TLS structure.
+ *
+ * Requires:
+ *\li 'tlsp' != NULL and '*tlsp' != NULL.
+ */
+
+void
+isc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx);
+void
+isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *ctx);
+/*%<
+ *
+ * Enable HTTP/2 Application Layer Protocol Negotation for 'ctx'.
+ *
+ * Requires:
+ *\li 'ctx' is not NULL.
+ */
+
+void
+isc_tls_get_http2_alpn(isc_tls_t *tls, const unsigned char **alpn,
+ unsigned int *alpnlen);
#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>
#define MAX_DNS_MESSAGE_SIZE (UINT16_MAX)
+#define DNS_MEDIA_TYPE "application/dns-message"
+
+#define DEFAULT_CACHE_CONTROL "no-cache, no-store"
+
#define HEADER_MATCH(header, name, namelen) \
(((namelen) == sizeof(header) - 1) && \
(strncasecmp((header), (const char *)(name), (namelen)) == 0))
-typedef struct isc_nm_http2_response_status {
+typedef struct isc_nm_http_response_status {
size_t code;
size_t content_length;
bool content_type_valid;
-} isc_nm_http2_response_status_t;
+} isc_nm_http_response_status_t;
-typedef struct http2_client_stream {
+typedef struct http_cstream {
isc_nm_recv_cb_t read_cb;
void *read_cbarg;
-
isc_nm_cb_t connect_cb;
void *connect_cbarg;
+ bool sending;
+ bool reading;
+
char *uri;
isc_url_parser_t up;
char *GET_path;
size_t GET_path_len;
- isc_nm_http2_response_status_t response_status;
+ isc_nm_http_response_status_t response_status;
- LINK(struct http2_client_stream) link;
-} http2_client_stream_t;
+ LINK(struct http_cstream) link;
+} http_cstream_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 {
+struct isc_nm_http_session {
unsigned int magic;
+ isc_refcount_t references;
isc_mem_t *mctx;
+
bool sending;
- bool closed;
bool reading;
+ bool closed;
nghttp2_session *ngsession;
bool client;
- ISC_LIST(http2_client_stream_t) cstreams;
+ ISC_LIST(http_cstream_t) cstreams;
ISC_LIST(isc_nmsocket_h2_t) sstreams;
-#ifdef NETMGR_TRACE
- size_t sstreams_count;
-#endif /* NETMGR_TRACE */
isc_nmhandle_t *handle;
isc_nmsocket_t *serversocket;
+ isc_nmiface_t server_iface;
isc_region_t r;
uint8_t buf[MAX_DNS_MESSAGE_SIZE];
size_t bufsize;
- bool ssl_ctx_created;
- SSL_CTX *ssl_ctx;
+ isc_tlsctx_t *tlsctx;
};
-typedef enum isc_http2_error_responses {
+typedef enum isc_http_error_responses {
ISC_HTTP_ERROR_SUCCESS, /* 200 */
ISC_HTTP_ERROR_NOT_FOUND, /* 404 */
ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE, /* 413 */
ISC_HTTP_ERROR_NOT_IMPLEMENTED, /* 501 */
ISC_HTTP_ERROR_GENERIC, /* 500 Internal Server Error */
ISC_HTTP_ERROR_MAX
-} isc_http2_error_responses_t;
+} isc_http_error_responses_t;
static void
-http2_do_bio(isc_nm_http2_session_t *session);
+http_do_bio(isc_nm_http_session_t *session);
static void
failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
- isc_nm_http2_session_t *session);
+ isc_nm_http_session_t *session);
static void
-failed_read_cb(isc_result_t result, isc_nm_http2_session_t *session);
+failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
-static int
-server_send_error_response(const isc_http2_error_responses_t error,
+static isc_result_t
+server_send_error_response(const isc_http_error_responses_t error,
nghttp2_session *ngsession, isc_nmsocket_t *socket);
+static isc_result_t
+client_send(isc_nmhandle_t *handle, const isc_region_t *region);
+
+static void
+finish_http_session(isc_nm_http_session_t *session);
+
static bool
inactive(isc_nmsocket_t *sock) {
return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
(sock->server != NULL && !isc__nmsocket_active(sock->server)));
}
-static http2_client_stream_t *
-find_http2_client_stream(int32_t stream_id, isc_nm_http2_session_t *session) {
- http2_client_stream_t *cstream = NULL;
+static void *
+http_malloc(size_t sz, isc_mem_t *mctx) {
+ return (isc_mem_allocate(mctx, sz));
+}
+
+static void *
+http_calloc(size_t n, size_t sz, isc_mem_t *mctx) {
+ const size_t msize = n * sz;
+ void *data = isc_mem_allocate(mctx, msize);
+
+ memset(data, 0, msize);
+ return (data);
+}
+
+static void *
+http_realloc(void *p, size_t newsz, isc_mem_t *mctx) {
+ return (isc_mem_reallocate(mctx, p, newsz));
+}
+
+static void
+http_free(void *p, isc_mem_t *mctx) {
+ if (p == NULL) { /* as standard free() behaves */
+ return;
+ }
+ isc_mem_free(mctx, p);
+}
+
+static void
+init_nghttp2_mem(isc_mem_t *mctx, nghttp2_mem *mem) {
+ *mem = (nghttp2_mem){ .malloc = (nghttp2_malloc)http_malloc,
+ .calloc = (nghttp2_calloc)http_calloc,
+ .realloc = (nghttp2_realloc)http_realloc,
+ .free = (nghttp2_free)http_free,
+ .mem_user_data = mctx };
+}
+
+static void
+new_session(isc_mem_t *mctx, isc_tlsctx_t *tctx,
+ isc_nm_http_session_t **sessionp) {
+ isc_nm_http_session_t *session = NULL;
+
+ REQUIRE(sessionp != NULL && *sessionp == NULL);
+ REQUIRE(mctx != NULL);
+
+ session = isc_mem_get(mctx, sizeof(isc_nm_http_session_t));
+ *session = (isc_nm_http_session_t){ .magic = HTTP2_SESSION_MAGIC,
+ .tlsctx = tctx };
+ isc_refcount_init(&session->references, 1);
+ isc_mem_attach(mctx, &session->mctx);
+ ISC_LIST_INIT(session->cstreams);
+ ISC_LIST_INIT(session->sstreams);
+
+ *sessionp = session;
+}
+
+void
+isc__nm_httpsession_attach(isc_nm_http_session_t *source,
+ isc_nm_http_session_t **targetp) {
+ REQUIRE(VALID_HTTP2_SESSION(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+void
+isc__nm_httpsession_detach(isc_nm_http_session_t **sessionp) {
+ isc_nm_http_session_t *session = NULL;
+
+ REQUIRE(sessionp != NULL);
+
+ session = *sessionp;
+ *sessionp = NULL;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ if (isc_refcount_decrement(&session->references) > 1) {
+ return;
+ }
+
+ finish_http_session(session);
+
+ if (session->r.base) {
+ isc_mem_put(session->mctx, session->r.base, session->r.length);
+ }
+
+ INSIST(ISC_LIST_EMPTY(session->sstreams));
+ INSIST(ISC_LIST_EMPTY(session->cstreams));
+
+ /* We need an acquire memory barrier here */
+ (void)isc_refcount_current(&session->references);
+
+ session->magic = 0;
+ isc_mem_putanddetach(&session->mctx, session,
+ sizeof(isc_nm_http_session_t));
+}
+
+static http_cstream_t *
+find_http_cstream(int32_t stream_id, isc_nm_http_session_t *session) {
+ http_cstream_t *cstream = NULL;
REQUIRE(VALID_HTTP2_SESSION(session));
for (cstream = ISC_LIST_HEAD(session->cstreams); cstream != NULL;
}
static isc_result_t
-get_http2_client_stream(isc_mem_t *mctx, http2_client_stream_t **streamp,
- const char *uri) {
- http2_client_stream_t *stream = NULL;
+new_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
+ isc_mem_t *mctx = sock->mgr->mctx;
+ const char *uri = NULL;
+ bool post;
+ http_cstream_t *stream = NULL;
isc_result_t result;
- REQUIRE(streamp != NULL && *streamp == NULL);
- REQUIRE(uri != NULL);
-
- stream = isc_mem_get(mctx, sizeof(http2_client_stream_t));
- *stream = (http2_client_stream_t){ .stream_id = -1, .rbufsize = 0 };
+ uri = sock->h2.session->handle->sock->h2.connect.uri;
+ post = sock->h2.session->handle->sock->h2.connect.post;
- stream->uri = isc_mem_strdup(mctx, uri);
+ stream = isc_mem_get(mctx, sizeof(http_cstream_t));
+ *stream = (http_cstream_t){ .stream_id = -1,
+ .post = post,
+ .uri = isc_mem_strdup(mctx, uri) };
+ ISC_LINK_INIT(stream, link);
result = isc_url_parse(stream->uri, strlen(stream->uri), 0,
&stream->up);
if (result != ISC_R_SUCCESS) {
isc_mem_free(mctx, stream->uri);
- isc_mem_put(mctx, stream, sizeof(http2_client_stream_t));
+ isc_mem_put(mctx, stream, sizeof(http_cstream_t));
return (result);
}
stream->up.field_data[ISC_UF_QUERY].len);
}
- stream->GET_path = NULL;
- stream->GET_path_len = 0;
- stream->postdata = (isc_region_t){ .base = NULL, .length = 0 };
- memset(&stream->response_status, 0, sizeof(stream->response_status));
-
*streamp = stream;
return (ISC_R_SUCCESS);
}
static void
-put_http2_client_stream(isc_mem_t *mctx, http2_client_stream_t *stream) {
+put_http_cstream(isc_mem_t *mctx, http_cstream_t *stream) {
isc_mem_put(mctx, stream->path, stream->pathlen);
isc_mem_put(mctx, stream->authority,
stream->up.field_data[ISC_UF_HOST].len + AUTHEXTRA);
isc_mem_put(mctx, stream->postdata.base,
stream->postdata.length);
}
- isc_mem_put(mctx, stream, sizeof(http2_client_stream_t));
-}
-
-static void
-delete_http2_session(isc_nm_http2_session_t *session) {
- REQUIRE(ISC_LIST_EMPTY(session->sstreams));
- REQUIRE(ISC_LIST_EMPTY(session->cstreams));
-
- session->magic = 0;
- if (session->ssl_ctx_created) {
- SSL_CTX_free(session->ssl_ctx);
- }
- isc_mem_putanddetach(&session->mctx, session,
- sizeof(isc_nm_http2_session_t));
+ isc_mem_put(mctx, stream, sizeof(http_cstream_t));
}
static void
-finish_http2_session(isc_nm_http2_session_t *session) {
+finish_http_session(isc_nm_http_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 (!ISC_LIST_EMPTY(session->cstreams)) {
- http2_client_stream_t *cstream =
- ISC_LIST_HEAD(session->cstreams);
+ http_cstream_t *cstream = ISC_LIST_HEAD(session->cstreams);
while (cstream != NULL) {
- http2_client_stream_t *next = ISC_LIST_NEXT(cstream,
- link);
+ http_cstream_t *next = ISC_LIST_NEXT(cstream, link);
ISC_LIST_DEQUEUE(session->cstreams, cstream, link);
- put_http2_client_stream(session->mctx, cstream);
-
+ cstream->read_cb(session->handle, ISC_R_UNEXPECTED,
+ &(isc_region_t){ cstream->rbuf,
+ cstream->rbufsize },
+ cstream->read_cbarg);
+ put_http_cstream(session->mctx, cstream);
cstream = next;
}
}
}
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->client) {
- http2_client_stream_t *cstream =
- find_http2_client_stream(stream_id, session);
- if (cstream) {
- size_t new_rbufsize = cstream->rbufsize + len;
- if (new_rbufsize <= MAX_DNS_MESSAGE_SIZE &&
- new_rbufsize <=
- cstream->response_status.content_length)
- {
- memmove(cstream->rbuf + cstream->rbufsize, data,
- len);
- cstream->rbufsize = new_rbufsize;
- } else {
- return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
- }
+on_client_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
+ size_t len, isc_nm_http_session_t *session) {
+ http_cstream_t *cstream = find_http_cstream(stream_id, session);
+
+ if (cstream != NULL) {
+ size_t new_rbufsize = cstream->rbufsize + len;
+ if (new_rbufsize <= MAX_DNS_MESSAGE_SIZE &&
+ new_rbufsize <= cstream->response_status.content_length)
+ {
+ memmove(cstream->rbuf + cstream->rbufsize, data, len);
+ cstream->rbufsize = new_rbufsize;
} else {
- return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
}
} else {
- isc_nmsocket_h2_t *sock_h2 = ISC_LIST_HEAD(session->sstreams);
- while (sock_h2 != NULL) {
- if (stream_id == sock_h2->stream_id) {
- size_t new_bufsize = sock_h2->bufsize + len;
- if (new_bufsize <= MAX_DNS_MESSAGE_SIZE &&
- new_bufsize <= sock_h2->content_length)
- {
- memmove(sock_h2->buf + sock_h2->bufsize,
- data, len);
- sock_h2->bufsize = new_bufsize;
- } else {
- return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
- }
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+
+ return (0);
+}
+
+static int
+on_server_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
+ size_t len, isc_nm_http_session_t *session) {
+ isc_nmsocket_h2_t *h2 = ISC_LIST_HEAD(session->sstreams);
+ while (h2 != NULL) {
+ if (stream_id == h2->stream_id) {
+ size_t new_bufsize = h2->bufsize + len;
+ if (new_bufsize <= MAX_DNS_MESSAGE_SIZE &&
+ new_bufsize <= h2->content_length) {
+ memmove(h2->buf + h2->bufsize, data, len);
+ h2->bufsize = new_bufsize;
break;
}
- sock_h2 = ISC_LIST_NEXT(sock_h2, link);
- }
- if (sock_h2 == NULL) {
- return (NGHTTP2_ERR_CALLBACK_FAILURE);
+
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
}
+ h2 = ISC_LIST_NEXT(h2, link);
+ }
+ if (h2 == NULL) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
}
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;
- int rv = 0;
-
- REQUIRE(VALID_HTTP2_SESSION(session));
+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_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ int rv;
- UNUSED(error_code);
+ UNUSED(ngsession);
+ UNUSED(flags);
- /* NOTE: calling isc_nm_cancelread() or
- * isc__nmsocket_prep_destroy() on a socket will lead to an
- * indirect call to the delete_http2_session() which will, in
- * turn, perform required stream session cleanup.*/
if (session->client) {
- http2_client_stream_t *cstream =
- find_http2_client_stream(stream_id, session);
- if (cstream) {
- isc_result_t result =
- cstream->response_status.code >= 200 &&
- cstream->response_status.code <
- 300
- ? ISC_R_SUCCESS
- : ISC_R_FAILURE;
- cstream->read_cb(session->handle, result,
- &(isc_region_t){ cstream->rbuf,
- cstream->rbufsize },
- cstream->read_cbarg);
- ISC_LIST_UNLINK(session->cstreams, cstream, link);
- put_http2_client_stream(session->mctx, cstream);
- if (ISC_LIST_EMPTY(session->cstreams)) {
- rv = nghttp2_session_terminate_session(
- ngsession, NGHTTP2_NO_ERROR);
- if (rv != 0) {
- return (NGHTTP2_ERR_CALLBACK_FAILURE);
- }
- if (session->handle->sock->h2.session->reading)
- {
- isc_nm_cancelread(
- session->handle->sock->h2
- .session->handle);
- }
- }
- } else {
- return (NGHTTP2_ERR_CALLBACK_FAILURE);
- }
+ rv = on_client_data_chunk_recv_callback(stream_id, data, len,
+ session);
} else {
- isc_nmsocket_t *sock = nghttp2_session_get_stream_user_data(
- ngsession, stream_id);
- if (ISC_LIST_EMPTY(session->sstreams)) {
+ rv = on_server_data_chunk_recv_callback(stream_id, data, len,
+ session);
+ }
+
+ return (rv);
+}
+
+static int
+on_client_stream_close_callback(int32_t stream_id,
+ isc_nm_http_session_t *session) {
+ http_cstream_t *cstream = find_http_cstream(stream_id, session);
+
+ if (cstream != NULL) {
+ isc_result_t result =
+ cstream->response_status.code >= 200 &&
+ cstream->response_status.code < 300
+ ? ISC_R_SUCCESS
+ : ISC_R_FAILURE;
+ cstream->read_cb(
+ session->handle, result,
+ &(isc_region_t){ cstream->rbuf, cstream->rbufsize },
+ cstream->read_cbarg);
+ ISC_LIST_UNLINK(session->cstreams, cstream, link);
+ put_http_cstream(session->mctx, cstream);
+ if (ISC_LIST_EMPTY(session->cstreams)) {
+ int rv = 0;
rv = nghttp2_session_terminate_session(
- ngsession, NGHTTP2_NO_ERROR);
- }
- isc__nmsocket_prep_destroy(sock);
- if (rv != 0) {
- return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ session->ngsession, NGHTTP2_NO_ERROR);
+ if (rv != 0) {
+ return (rv);
+ }
+ if (session->handle->sock->h2.session->reading) {
+ isc_nm_cancelread(session->handle->sock->h2
+ .session->handle);
+ }
}
+ } else {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
}
return (0);
}
-#ifndef OPENSSL_NO_NEXTPROTONEG
-/*
- * NPN TLS extension client callback. We check that server advertised
- * the HTTP/2 protocol the nghttp2 library supports.
- */
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);
+on_server_stream_close_callback(int32_t stream_id,
+ isc_nm_http_session_t *session) {
+ isc_nmsocket_t *sock = nghttp2_session_get_stream_user_data(
+ session->ngsession, stream_id);
+ int rv = 0;
- if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
- return (SSL_TLSEXT_ERR_NOACK);
+ if (ISC_LIST_EMPTY(session->sstreams)) {
+ rv = nghttp2_session_terminate_session(session->ngsession,
+ NGHTTP2_NO_ERROR);
}
- return (SSL_TLSEXT_ERR_OK);
+ isc__nmsocket_prep_destroy(sock);
+ return (rv);
}
-#endif /* !OPENSSL_NO_NEXTPROTONEG */
-/* Create SSL_CTX. */
-static SSL_CTX *
-create_client_ssl_ctx(void) {
- SSL_CTX *ssl_ctx = NULL;
+static int
+on_stream_close_callback(nghttp2_session *ngsession, int32_t stream_id,
+ uint32_t error_code, void *user_data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ int rv = 0;
- RUNTIME_CHECK(isc_tlsctx_createclient(&ssl_ctx) == ISC_R_SUCCESS);
- RUNTIME_CHECK(ssl_ctx != NULL);
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(session->ngsession == ngsession);
-#ifndef OPENSSL_NO_NEXTPROTONEG
- SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
-#endif /* !OPENSSL_NO_NEXTPROTONEG */
+ UNUSED(error_code);
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
- SSL_CTX_set_alpn_protos(ssl_ctx,
- (const unsigned char *)NGHTTP2_PROTO_ALPN,
- NGHTTP2_PROTO_ALPN_LEN);
-#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+ /*
+ * NOTE: calling isc_nm_cancelread() or isc__nmsocket_prep_destroy()
+ * on a socket will lead to an indirect call to detach the session,
+ * which will, in turn, perform required stream cleanup.
+ */
+ if (session->client) {
+ rv = on_client_stream_close_callback(stream_id, session);
+ } else {
+ rv = on_server_stream_close_callback(stream_id, session);
+ }
- ENSURE(ssl_ctx != NULL);
- return (ssl_ctx);
+ return (rv);
}
static void
-client_handle_status_header(http2_client_stream_t *cstream,
- const uint8_t *value, const size_t valuelen) {
+client_handle_status_header(http_cstream_t *cstream, const uint8_t *value,
+ const size_t valuelen) {
char tmp[32] = { 0 };
const size_t tmplen = sizeof(tmp) - 1;
- strncpy(tmp, (const char *)value,
- valuelen > tmplen ? tmplen : valuelen);
+ strncpy(tmp, (const char *)value, ISC_MIN(tmplen, valuelen));
cstream->response_status.code = strtoul(tmp, NULL, 10);
}
static void
-client_handle_content_length_header(http2_client_stream_t *cstream,
+client_handle_content_length_header(http_cstream_t *cstream,
const uint8_t *value,
const size_t valuelen) {
char tmp[32] = { 0 };
const size_t tmplen = sizeof(tmp) - 1;
- strncpy(tmp, (const char *)value,
- valuelen > tmplen ? tmplen : valuelen);
+ strncpy(tmp, (const char *)value, ISC_MIN(tmplen, valuelen));
cstream->response_status.content_length = strtoul(tmp, NULL, 10);
}
static void
-client_handle_content_type_header(http2_client_stream_t *cstream,
- const uint8_t *value, const size_t valuelen) {
- const char type_dns_message[] = "application/dns-message";
+client_handle_content_type_header(http_cstream_t *cstream, const uint8_t *value,
+ const size_t valuelen) {
+ const char type_dns_message[] = DNS_MEDIA_TYPE;
+ const size_t len = sizeof(type_dns_message) - 1;
UNUSED(valuelen);
- if (strncasecmp((const char *)value, type_dns_message,
- sizeof(type_dns_message) - 1) == 0)
- {
+ if (strncasecmp((const char *)value, type_dns_message, len) == 0) {
cstream->response_status.content_type_valid = true;
}
}
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_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ http_cstream_t *cstream = NULL;
const char status[] = ":status";
const char content_length[] = "Content-Length";
const char content_type[] = "Content-Type";
UNUSED(flags);
UNUSED(ngsession);
- http2_client_stream_t *cstream =
- find_http2_client_stream(frame->hd.stream_id, session);
+ cstream = find_http_cstream(frame->hd.stream_id, session);
switch (frame->hd.type) {
case NGHTTP2_HEADERS:
} else if (HEADER_MATCH(content_type, name, namelen)) {
client_handle_content_type_header(cstream, value,
valuelen);
+ if (!cstream->response_status.content_type_valid) {
+ return (NGHTTP2_ERR_HTTP_HEADER);
+ }
}
break;
}
}
static void
-initialize_nghttp2_client_session(isc_nm_http2_session_t *session) {
+initialize_nghttp2_client_session(isc_nm_http_session_t *session) {
nghttp2_session_callbacks *callbacks = NULL;
+ nghttp2_option *option = NULL;
+ nghttp2_mem mem;
+ init_nghttp2_mem(session->mctx, &mem);
RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
+ RUNTIME_CHECK(nghttp2_option_new(&option) == 0);
+
+#if NGHTTP2_VERSION_NUM >= (0x010c00)
+ /* 128K should be enough for headers to allow more space for base64len
+ * encoded GET requests */
+ nghttp2_option_set_max_send_header_block_length(
+ option, MAX_DNS_MESSAGE_SIZE * 2);
+#endif
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_header_callback(
callbacks, client_on_header_callback);
- nghttp2_session_client_new(&session->ngsession, callbacks, session);
+ RUNTIME_CHECK(nghttp2_session_client_new3(&session->ngsession,
+ callbacks, session, option,
+ &mem) == 0);
+ nghttp2_option_del(option);
nghttp2_session_callbacks_del(callbacks);
}
static bool
-send_client_connection_header(isc_nm_http2_session_t *session) {
+send_client_connection_header(isc_nm_http_session_t *session) {
nghttp2_settings_entry iv[] = { { NGHTTP2_SETTINGS_ENABLE_PUSH, 0 } };
int rv;
client_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;
- http2_client_stream_t *cstream;
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ http_cstream_t *cstream = NULL;
REQUIRE(session->client);
REQUIRE(!ISC_LIST_EMPTY(session->cstreams));
UNUSED(ngsession);
UNUSED(source);
- cstream = find_http2_client_stream(stream_id, session);
+ cstream = find_http_cstream(stream_id, session);
if (!cstream || cstream->stream_id != stream_id) {
- /* We haven't found the stream, so we are not reading anything
- */
+ /* We haven't found the stream, so we are not reading */
return (NGHTTP2_ERR_CALLBACK_FAILURE);
}
return (0);
}
-/* Send HTTP request to the remote peer */
+/*
+ * Send HTTP request to the remote peer.
+ */
static isc_result_t
-client_submit_request(isc_nm_http2_session_t *session,
- http2_client_stream_t *stream) {
+client_submit_request(isc_nm_http_session_t *session, http_cstream_t *stream) {
int32_t stream_id;
char *uri = stream->uri;
isc_url_parser_t *up = &stream->up;
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_NV2("content-type", DNS_MEDIA_TYPE),
+ MAKE_NV2("accept", DNS_MEDIA_TYPE),
MAKE_NV("content-length", p, strlen(p)),
+ MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
};
dp = (nghttp2_data_provider){ .read_callback =
stream->authoritylen),
MAKE_NV(":path", stream->GET_path,
stream->GET_path_len),
- MAKE_NV2("accept", "application/dns-message")
+ MAKE_NV2("accept", DNS_MEDIA_TYPE),
+ MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
};
dp = (nghttp2_data_provider){ .read_callback =
}
stream->stream_id = stream_id;
- http2_do_bio(session);
+ http_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;
+http_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
+ void *data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)data;
ssize_t readlen;
REQUIRE(VALID_HTTP2_SESSION(session));
readlen = nghttp2_session_mem_recv(session->ngsession, region->base,
region->length);
if (readlen < 0) {
- failed_read_cb(ISC_R_CANCELED, session);
+ failed_read_cb(ISC_R_UNEXPECTED, session);
return;
}
}
/* We might have something to receive or send, do IO */
- http2_do_bio(session);
+ http_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;
+http_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)arg;
REQUIRE(VALID_HTTP2_SESSION(session));
isc_mem_put(session->mctx, session->r.base, session->r.length);
session->r.base = NULL;
if (result == ISC_R_SUCCESS) {
- http2_do_bio(session);
+ http_do_bio(session);
}
}
static void
-http2_do_bio(isc_nm_http2_session_t *session) {
+http_do_bio(isc_nm_http_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))
{
- finish_http2_session(session);
+ finish_http_session(session);
return;
}
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);
+ isc_nm_read(session->handle, http_readcb, session);
session->reading = true;
} else if (session->bufsize > 0) {
/* Leftover data in the buffer, use it */
session->bufsize -= readlen;
}
- http2_do_bio(session);
+ http_do_bio(session);
return;
} else {
/* Resume reading, it's idempotent, wait for more */
session->r.length = sz;
memmove(session->r.base, data, sz);
session->sending = true;
- isc_nm_send(session->handle, &session->r, https_writecb,
+ isc_nm_send(session->handle, &session->r, http_writecb,
session);
return;
}
return;
}
-typedef struct http_connect_data {
- char *uri;
- isc_nm_cb_t connect_cb;
- void *connect_cbarg;
- bool post;
- bool ssl_ctx_created;
- SSL_CTX *ssl_ctx;
-} http_connect_data_t;
+static isc_result_t
+get_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
+ http_cstream_t *cstream = sock->h2.connect.cstream;
+ isc_result_t result;
+
+ REQUIRE(streamp != NULL && *streamp == NULL);
+
+ sock->h2.connect.cstream = NULL;
+
+ if (cstream == NULL) {
+ result = new_http_cstream(sock, &cstream);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(cstream == NULL);
+ return (result);
+ }
+ }
+
+ *streamp = cstream;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+http_call_connect_cb(isc_nmsocket_t *sock, isc_result_t result) {
+ isc__nm_uvreq_t *req = NULL;
+
+ REQUIRE(sock->connect_cb != NULL);
+
+ req = isc__nm_uvreq_get(sock->mgr, sock);
+ req->cb.connect = sock->connect_cb;
+ req->cbarg = sock->connect_cbarg;
+ req->handle = isc__nmhandle_get(sock, &sock->peer, &sock->iface->addr);
+
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb_force_async(sock, req, result);
+}
static void
transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
- char *uri = NULL;
- http_connect_data_t *pconn_data = (http_connect_data_t *)cbarg;
- http_connect_data_t conn_data;
- isc_nm_http2_session_t *session = NULL;
- isc_mem_t *mctx;
+ isc_nmsocket_t *http_sock = (isc_nmsocket_t *)cbarg;
+ isc_nm_http_session_t *session = NULL;
+ isc_nmsocket_t *transp_sock = NULL;
+ http_cstream_t *cstream = NULL;
+ isc_mem_t *mctx = NULL;
+ REQUIRE(VALID_NMSOCK(http_sock));
REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(cbarg != NULL);
- handle->sock->h2.session = NULL;
- mctx = handle->sock->mgr->mctx;
+ transp_sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(transp_sock));
- conn_data = *pconn_data;
- uri = conn_data.uri;
- isc_mem_put(mctx, pconn_data, sizeof(*pconn_data));
+ mctx = transp_sock->mgr->mctx;
- INSIST(conn_data.connect_cb != NULL);
- INSIST(conn_data.uri != NULL);
+ INSIST(http_sock->h2.connect.uri != NULL);
+ http_sock->tid = transp_sock->tid;
if (result != ISC_R_SUCCESS) {
goto error;
}
- session = isc_mem_get(mctx, sizeof(isc_nm_http2_session_t));
- *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC,
-#ifdef NETMGR_TRACE
- .sstreams_count = 0,
-#endif /* NETMGR_TRACE */
- .ssl_ctx_created =
- conn_data.ssl_ctx_created,
- .ssl_ctx = conn_data.ssl_ctx,
- .handle = NULL,
- .client = true };
- isc_mem_attach(mctx, &session->mctx);
-
- handle->sock->h2.connect.uri = uri;
- handle->sock->h2.connect.post = conn_data.post;
+ new_session(mctx, http_sock->h2.connect.tlsctx, &session);
+ session->client = true;
+ transp_sock->h2.session = session;
+ http_sock->h2.connect.tlsctx = NULL;
- session->ssl_ctx = conn_data.ssl_ctx;
- conn_data.ssl_ctx = NULL;
- session->ssl_ctx_created = conn_data.ssl_ctx_created;
- conn_data.ssl_ctx_created = false;
+ transp_sock->h2.connect.post = http_sock->h2.connect.post;
+ transp_sock->h2.connect.uri = http_sock->h2.connect.uri;
+ http_sock->h2.connect.uri = NULL;
+ isc__nm_httpsession_attach(session, &http_sock->h2.session);
- if (session->ssl_ctx != NULL) {
+ if (session->tlsctx != NULL) {
const unsigned char *alpn = NULL;
unsigned int alpnlen = 0;
- SSL *ssl = NULL;
- REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(VALID_NMSOCK(handle->sock));
- REQUIRE(handle->sock->type == isc_nm_tlssocket);
-
- ssl = handle->sock->tlsstream.ssl;
- INSIST(ssl != NULL);
-#ifndef OPENSSL_NO_NEXTPROTONEG
- SSL_get0_next_proto_negotiated(handle->sock->tlsstream.ssl,
- &alpn, &alpnlen);
-#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
- if (alpn == NULL) {
- SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
- }
-#endif
+ INSIST(transp_sock->type == isc_nm_tlssocket);
- if (alpn == NULL || alpnlen != 2 ||
+ isc_tls_get_http2_alpn(transp_sock->tlsstream.tls, &alpn,
+ &alpnlen);
+ if (alpn == NULL || alpnlen != NGHTTP2_PROTO_VERSION_ID_LEN ||
memcmp(NGHTTP2_PROTO_VERSION_ID, alpn,
NGHTTP2_PROTO_VERSION_ID_LEN) != 0)
{
- result = ISC_R_CANCELED;
+ /*
+ * HTTP/2 negotiation error. Any sensible DoH
+ * client will fail if HTTP/2 cannot be
+ * negotiated via ALPN.
+ */
+ isc__nmsocket_prep_destroy(transp_sock);
+ result = ISC_R_HTTP2ALPNERROR;
goto error;
}
}
isc_nmhandle_attach(handle, &session->handle);
- handle->sock->h2.session = session;
initialize_nghttp2_client_session(session);
if (!send_client_connection_header(session)) {
- handle->sock->h2.session = NULL;
goto error;
}
- conn_data.connect_cb(handle, ISC_R_SUCCESS, conn_data.connect_cbarg);
- if (ISC_LIST_EMPTY(session->cstreams)) {
- delete_http2_session(session);
- isc__nmsocket_prep_destroy(handle->sock);
- } else {
- http2_do_bio(session);
+
+ result = get_http_cstream(http_sock, &cstream);
+ http_sock->h2.connect.cstream = cstream;
+ if (result != ISC_R_SUCCESS) {
+ goto error;
}
+ http_call_connect_cb(http_sock, result);
+ http_do_bio(session);
+ isc__nmsocket_detach(&http_sock);
return;
+
error:
- conn_data.connect_cb(handle, result, conn_data.connect_cbarg);
- if (conn_data.ssl_ctx_created && conn_data.ssl_ctx) {
- SSL_CTX_free(conn_data.ssl_ctx);
- }
+ http_call_connect_cb(http_sock, result);
- if (session != NULL) {
- delete_http2_session(session);
+ if (http_sock->h2.connect.uri != NULL) {
+ isc_mem_free(mctx, http_sock->h2.connect.uri);
}
- if (uri != NULL) {
- isc_mem_free(mctx, uri);
- }
+ isc__nmsocket_detach(&http_sock);
}
isc_result_t
isc_nm_httpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
const char *uri, bool post, isc_nm_cb_t cb, void *cbarg,
- SSL_CTX *ctx, unsigned int timeout, size_t extrahandlesize) {
- isc_nmiface_t resolved_peer, resolved_local;
- http_connect_data_t *pconn_data;
+ isc_tlsctx_t *tlsctx, unsigned int timeout,
+ size_t extrahandlesize) {
isc_result_t result;
- uint16_t port = 80;
- isc_url_parser_t url_parser;
- bool create_ssl_ctx;
- const char http[] = "http";
- const char http_secured[] = "https";
- size_t schema_len;
- const char *schema;
+ isc_nmiface_t local_interface;
+ isc_nmsocket_t *sock = NULL;
REQUIRE(VALID_NM(mgr));
REQUIRE(cb != NULL);
+ REQUIRE(peer != NULL);
REQUIRE(uri != NULL);
REQUIRE(*uri != '\0');
- result = isc_url_parse(uri, strlen(uri), 0, &url_parser);
- if (result != ISC_R_SUCCESS) {
- return (result);
- }
-
- schema_len = url_parser.field_data[ISC_UF_SCHEMA].len;
- INSIST(schema_len > 0);
- schema = &uri[url_parser.field_data[ISC_UF_SCHEMA].off];
-
- if (schema_len == sizeof(http_secured) - 1 &&
- strncasecmp(http_secured, schema, sizeof(http_secured) - 1) == 0)
- {
- create_ssl_ctx = true;
- } else if (schema_len == sizeof(http) - 1 &&
- strncasecmp(http, schema, sizeof(http) - 1) == 0)
- {
- create_ssl_ctx = false;
- } else {
- INSIST(0);
- ISC_UNREACHABLE();
- }
-
- if (ctx == NULL && create_ssl_ctx) {
- port = 443;
- ctx = create_client_ssl_ctx();
+ if (local == NULL) {
+ isc_sockaddr_anyofpf(&local_interface.addr,
+ (peer->addr).type.sa.sa_family);
+ local = &local_interface;
}
- if (peer == NULL || local == NULL) {
- size_t hostlen = 0;
- char *host = NULL;
- struct addrinfo hints;
- struct addrinfo *res = NULL;
-
-#ifndef WIN32 /* FIXME */
- /* extract host name */
- hostlen = url_parser.field_data[ISC_UF_HOST].len + 1;
- host = isc_mem_get(mgr->mctx, hostlen);
- memmove(host, &uri[url_parser.field_data[ISC_UF_HOST].off],
- hostlen - 1);
- host[hostlen - 1] = '\0';
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
- hints.ai_flags = AI_CANONNAME;
-
- result = getaddrinfo(host, NULL, &hints, &res);
- isc_mem_put(mgr->mctx, host, hostlen);
- if (result != 0) {
- return (ISC_R_FAILURE);
- }
-#endif /* WIN32 */
-
- if ((url_parser.field_set & (1 << ISC_UF_PORT)) != 0) {
- port = url_parser.port;
- }
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_httpsocket, local);
- if (peer == NULL) {
- (void)isc_sockaddr_fromsockaddr(&resolved_peer.addr,
- res->ai_addr);
- isc_sockaddr_setport(&resolved_peer.addr, port);
- peer = &resolved_peer;
- }
- if (local == NULL) {
- isc_sockaddr_anyofpf(&resolved_local.addr,
- res->ai_family);
- local = &resolved_local;
- }
+ sock->extrahandlesize = extrahandlesize;
+ sock->connect_timeout = timeout;
+ sock->result = ISC_R_DEFAULT;
+ sock->connect_cb = cb;
+ sock->connect_cbarg = cbarg;
+ sock->h2 = (isc_nmsocket_h2_t){ .connect.uri = isc_mem_strdup(mgr->mctx,
+ uri),
+ .connect.post = post,
+ .connect.tlsctx = tlsctx };
+ ISC_LINK_INIT(&sock->h2, link);
+ atomic_init(&sock->client, true);
- freeaddrinfo(res);
+ /*
+ * We need to prevent the interface object data from going out of
+ * scope too early.
+ */
+ if (local == &local_interface) {
+ sock->h2.connect.local_interface = local_interface;
+ sock->iface = &sock->h2.connect.local_interface;
}
- pconn_data = isc_mem_get(mgr->mctx, sizeof(*pconn_data));
- *pconn_data =
- (http_connect_data_t){ .uri = isc_mem_strdup(mgr->mctx, uri),
- .post = post,
- .connect_cb = cb,
- .connect_cbarg = cbarg,
- .ssl_ctx_created = create_ssl_ctx,
- .ssl_ctx = ctx };
-
- if (ctx != NULL) {
+ if (tlsctx != NULL) {
result = isc_nm_tlsconnect(mgr, local, peer,
- transport_connect_cb, pconn_data,
- ctx, timeout, extrahandlesize);
+ transport_connect_cb, sock, tlsctx,
+ timeout, 0);
} else {
result = isc_nm_tcpconnect(mgr, local, peer,
- transport_connect_cb, pconn_data,
- timeout, extrahandlesize);
+ transport_connect_cb, sock, timeout,
+ 0);
}
return (result);
}
-isc_result_t
-isc_nm_httprequest(isc_nmhandle_t *handle, isc_region_t *region,
- isc_nm_recv_cb_t reply_cb, void *cbarg) {
- http2_client_stream_t *cstream = NULL;
+static isc_result_t
+client_send(isc_nmhandle_t *handle, const isc_region_t *region) {
isc_result_t result = ISC_R_SUCCESS;
- isc_nm_http2_session_t *session = NULL;
- isc_mem_t *mctx;
+ isc_nmsocket_t *sock = handle->sock;
+ isc_mem_t *mctx = sock->mgr->mctx;
+ isc_nm_http_session_t *session = sock->h2.session;
+ http_cstream_t *cstream = sock->h2.connect.cstream;
- REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(VALID_NMSOCK(handle->sock));
- REQUIRE(handle->sock->tid == isc_nm_tid());
REQUIRE(VALID_HTTP2_SESSION(handle->sock->h2.session));
+ REQUIRE(session->client);
REQUIRE(region != NULL);
REQUIRE(region->base != NULL);
- REQUIRE(region->length != 0);
REQUIRE(region->length <= MAX_DNS_MESSAGE_SIZE);
- REQUIRE(reply_cb != NULL);
-
- session = handle->sock->h2.session;
- mctx = handle->sock->mgr->mctx;
-
- result = get_http2_client_stream(mctx, &cstream,
- handle->sock->h2.connect.uri);
- if (result != ISC_R_SUCCESS) {
- goto error;
- }
+ REQUIRE(cstream != NULL);
- cstream->read_cb = reply_cb;
- cstream->read_cbarg = cbarg;
- cstream->post = handle->sock->h2.connect.post;
-
- if (cstream->post) { /* POST */
+ if (cstream->post) {
+ /* POST */
cstream->postdata = (isc_region_t){
.base = isc_mem_get(mctx, region->length),
.length = region->length
};
memmove(cstream->postdata.base, region->base, region->length);
cstream->postdata_pos = 0;
- } else { /* GET */
+ } else {
+ /* GET */
size_t path_size = 0;
char *base64url_data = NULL;
size_t base64url_data_len = 0;
isc_buffer_allocate(mctx, &buf, base64_len);
- if ((result = isc_base64_totext(&data, -1, "", buf)) !=
- ISC_R_SUCCESS) {
+ result = isc_base64_totext(&data, -1, "", buf);
+ if (result != ISC_R_SUCCESS) {
isc_buffer_free(&buf);
goto error;
}
isc_mem_free(mctx, base64url_data);
}
- ISC_LINK_INIT(cstream, link);
- ISC_LIST_APPEND(session->cstreams, cstream, link);
-
- INSIST(cstream != NULL);
+ cstream->sending = true;
+ if (!ISC_LINK_LINKED(cstream, link)) {
+ ISC_LIST_APPEND(session->cstreams, cstream, link);
+ }
+ if (cstream->reading) {
+ sock->h2.connect.cstream = NULL;
+ result = client_submit_request(session, cstream);
+ if (result != ISC_R_SUCCESS) {
+ ISC_LIST_UNLINK(session->cstreams, cstream, link);
+ goto error;
+ }
- result = client_submit_request(session, cstream);
- if (result != ISC_R_SUCCESS) {
- ISC_LIST_UNLINK(session->cstreams, cstream, link);
- goto error;
+ http_do_bio(session);
}
- http2_do_bio(session);
- return (ISC_R_SUCCESS);
error:
- reply_cb(handle, result, NULL, cbarg);
- if (cstream) {
- put_http2_client_stream(mctx, cstream);
- }
return (result);
}
-typedef struct isc_nm_connect_send_data {
- isc_nm_recv_cb_t reply_cb;
- void *cb_arg;
- isc_region_t region;
-} isc_nm_connect_send_data_t;
-
-static void
-https_connect_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
- isc_nm_connect_send_data_t data;
+isc_result_t
+isc__nm_http_request(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_recv_cb_t cb, void *cbarg) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ http_cstream_t *cstream = NULL;
REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->tid == isc_nm_tid());
- memmove(&data, arg, sizeof(data));
- isc_mem_put(handle->sock->mgr->mctx, arg, sizeof(data));
- if (result != ISC_R_SUCCESS) {
- goto error;
- }
+ REQUIRE(cb != NULL);
+
+ sock = handle->sock;
- result = isc_nm_httprequest(handle, &data.region, data.reply_cb,
- data.cb_arg);
+ isc__nm_http_read(handle, cb, cbarg);
+ result = client_send(handle, region);
if (result != ISC_R_SUCCESS) {
goto error;
}
- isc_mem_put(handle->sock->mgr->mctx, data.region.base,
- data.region.length);
- return;
-error:
- data.reply_cb(handle, result, NULL, data.cb_arg);
- isc_mem_put(handle->sock->mgr->mctx, data.region.base,
- data.region.length);
-}
-
-isc_result_t
-isc_nm_http_connect_send_request(isc_nm_t *mgr, const char *uri, bool post,
- isc_region_t *region, isc_nm_recv_cb_t cb,
- void *cbarg, SSL_CTX *ctx,
- unsigned int timeout) {
- isc_region_t copy;
- isc_result_t result;
- isc_nm_connect_send_data_t *data;
-
- REQUIRE(VALID_NM(mgr));
- REQUIRE(uri != NULL);
- REQUIRE(*uri != '\0');
- REQUIRE(region != NULL);
- REQUIRE(region->base != NULL);
- REQUIRE(region->length != 0);
- REQUIRE(region->length <= MAX_DNS_MESSAGE_SIZE);
- REQUIRE(cb != NULL);
-
- copy = (isc_region_t){ .base = isc_mem_get(mgr->mctx, region->length),
- .length = region->length };
- memmove(copy.base, region->base, region->length);
- data = isc_mem_get(mgr->mctx, sizeof(*data));
- *data = (isc_nm_connect_send_data_t){ .reply_cb = cb,
- .cb_arg = cbarg,
- .region = copy };
- result = isc_nm_httpconnect(mgr, NULL, NULL, uri, post,
- https_connect_send_cb, data, ctx, timeout,
- 0);
+ return (ISC_R_SUCCESS);
+error:
+ cstream = sock->h2.connect.cstream;
+ if (cstream->read_cb != NULL) {
+ cstream->read_cb(handle, result, NULL, cstream->read_cbarg);
+ }
return (result);
}
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_nm_http_session_t *session = (isc_nm_http_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)
}
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);
+ isc_nm_httpsocket,
+ (isc_nmiface_t *)&session->server_iface);
socket->h2 = (isc_nmsocket_h2_t){
- .bufpos = 0,
- .bufsize = 0,
.buf = isc_mem_allocate(session->mctx, MAX_DNS_MESSAGE_SIZE),
.psock = socket,
- .handler = NULL,
- .request_path = NULL,
- .query_data = NULL,
.stream_id = frame->hd.stream_id,
- .session = session
};
+ isc__nm_httpsession_attach(session, &socket->h2.session);
socket->tid = session->handle->sock->tid;
ISC_LINK_INIT(&socket->h2, link);
ISC_LIST_APPEND(session->sstreams, &socket->h2, link);
-#ifdef NETMGR_TRACE
- session->sstreams_count++;
- if (session->sstreams_count > 1) {
- fprintf(stderr, "HTTP/2 session %p (active streams: %lu)\n",
- session, session->sstreams_count);
- }
-#endif /* NETMGR_TRACE */
-
nghttp2_session_set_stream_user_data(ngsession, frame->hd.stream_id,
socket);
return (0);
}
-static isc_nm_http2_server_handler_t *
+static isc_nm_httphandler_t *
find_server_request_handler(const char *request_path,
isc_nmsocket_t *serversocket) {
- isc_nm_http2_server_handler_t *handler = NULL;
+ isc_nm_httphandler_t *handler = NULL;
REQUIRE(VALID_NMSOCK(serversocket));
return (NULL);
}
- RWLOCK(&serversocket->h2.handlers_lock, isc_rwlocktype_read);
+ RWLOCK(&serversocket->h2.lock, isc_rwlocktype_read);
if (atomic_load(&serversocket->listening)) {
for (handler = ISC_LIST_HEAD(serversocket->h2.handlers);
handler != NULL; handler = ISC_LIST_NEXT(handler, link))
}
}
}
- RWUNLOCK(&serversocket->h2.handlers_lock, isc_rwlocktype_read);
+ RWUNLOCK(&serversocket->h2.lock, isc_rwlocktype_read);
return (handler);
}
-static isc_http2_error_responses_t
+static isc_http_error_responses_t
server_handle_path_header(isc_nmsocket_t *socket, const uint8_t *value,
const size_t valuelen) {
- isc_nm_http2_server_handler_t *handler = NULL;
+ isc_nm_httphandler_t *handler = NULL;
const uint8_t *qstr = NULL;
size_t vlen = valuelen;
handler = find_server_request_handler(socket->h2.request_path,
socket->h2.session->serversocket);
if (handler != NULL) {
- socket->h2.handler_cb = handler->cb;
- socket->h2.handler_cbarg = handler->cbarg;
+ socket->h2.cb = handler->cb;
+ socket->h2.cbarg = handler->cbarg;
socket->extrahandlesize = handler->extrahandlesize;
} else {
isc_mem_free(socket->mgr->mctx, socket->h2.request_path);
return (ISC_HTTP_ERROR_BAD_REQUEST);
}
- if (isc__nm_parse_doh_query_string((const char *)qstr,
- &dns_value, &dns_value_len))
+ if (isc__nm_parse_httpquery((const char *)qstr, &dns_value,
+ &dns_value_len))
{
const size_t decoded_size = dns_value_len / 4 * 3;
if (decoded_size <= MAX_DNS_MESSAGE_SIZE) {
return (ISC_HTTP_ERROR_SUCCESS);
}
-static isc_http2_error_responses_t
+static isc_http_error_responses_t
server_handle_method_header(isc_nmsocket_t *socket, const uint8_t *value,
const size_t valuelen) {
const char get[] = "GET";
return (ISC_HTTP_ERROR_SUCCESS);
}
-static isc_http2_error_responses_t
+static isc_http_error_responses_t
server_handle_scheme_header(isc_nmsocket_t *socket, const uint8_t *value,
const size_t valuelen) {
const char http[] = "http";
return (ISC_HTTP_ERROR_SUCCESS);
}
-static isc_http2_error_responses_t
+static isc_http_error_responses_t
server_handle_content_length_header(isc_nmsocket_t *socket,
const uint8_t *value,
const size_t valuelen) {
return (ISC_HTTP_ERROR_SUCCESS);
}
-static isc_http2_error_responses_t
+static isc_http_error_responses_t
server_handle_content_type_header(isc_nmsocket_t *socket, const uint8_t *value,
const size_t valuelen) {
- const char type_dns_message[] = "application/dns-message";
+ const char type_dns_message[] = DNS_MEDIA_TYPE;
if (HEADER_MATCH(type_dns_message, value, valuelen)) {
socket->h2.content_type_verified = true;
return (ISC_HTTP_ERROR_SUCCESS);
}
-static isc_http2_error_responses_t
+static isc_http_error_responses_t
server_handle_accept_header(isc_nmsocket_t *socket, const uint8_t *value,
const size_t valuelen) {
const char type_accept_all[] = "*/*";
- const char type_dns_message[] = "application/dns-message";
+ const char type_dns_message[] = DNS_MEDIA_TYPE;
if (HEADER_MATCH(type_dns_message, value, valuelen) ||
HEADER_MATCH(type_accept_all, value, valuelen))
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, uint8_t flags,
void *user_data) {
+ isc_result_t result;
isc_nmsocket_t *socket = NULL;
- isc_http2_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
+ isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
const char path[] = ":path";
const char method[] = ":method";
const char scheme[] = ":scheme";
}
INSIST(socket != NULL);
- if (server_send_error_response(code, session, socket) != 0) {
+ result = server_send_error_response(code, session, socket);
+ if (result != ISC_R_SUCCESS) {
return (NGHTTP2_ERR_CALLBACK_FAILURE);
- };
+ }
+
failed_httpstream_read_cb(socket, ISC_R_CANCELED, socket->h2.session);
return (0);
}
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_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
isc_nmsocket_t *socket = (isc_nmsocket_t *)source->ptr;
size_t buflen;
return (buflen);
}
-static int
+static isc_result_t
server_send_response(nghttp2_session *ngsession, int32_t stream_id,
const nghttp2_nv *nva, size_t nvlen,
isc_nmsocket_t *socket) {
rv = nghttp2_submit_response(ngsession, stream_id, nva, nvlen,
&data_prd);
if (rv != 0) {
- return (-1);
+ return (ISC_R_FAILURE);
}
- return (0);
+ return (ISC_R_SUCCESS);
}
#define MAKE_ERROR_REPLY(tag, code) \
tag, MAKE_NV2(":status", #code) \
}
-static struct http2_error_responses {
- const isc_http2_error_responses_t type;
+static struct http_error_responses {
+ const isc_http_error_responses_t type;
const nghttp2_nv header;
} error_responses[] = {
MAKE_ERROR_REPLY(ISC_HTTP_ERROR_SUCCESS, 200),
MAKE_ERROR_REPLY(ISC_HTTP_ERROR_GENERIC, 500),
};
-static int
-server_send_error_response(const isc_http2_error_responses_t error,
+static isc_result_t
+server_send_error_response(const isc_http_error_responses_t error,
nghttp2_session *ngsession, isc_nmsocket_t *socket) {
- int rv;
-
socket->h2.bufsize = 0;
socket->h2.bufpos = 0;
i < sizeof(error_responses) / sizeof(error_responses[0]); i++)
{
if (error_responses[i].type == error) {
- rv = server_send_response(
+ return (server_send_response(
ngsession, socket->h2.stream_id,
&error_responses[i].header,
- sizeof(error_responses[i].header), socket);
- return (rv);
+ sizeof(error_responses[i].header), socket));
}
}
- rv = server_send_error_response(ISC_HTTP_ERROR_GENERIC, ngsession,
- socket);
- return (rv);
+ return (server_send_error_response(ISC_HTTP_ERROR_GENERIC, ngsession,
+ socket));
}
static int
server_on_request_recv(nghttp2_session *ngsession,
- isc_nm_http2_session_t *session,
- isc_nmsocket_t *socket) {
+ isc_nm_http_session_t *session, isc_nmsocket_t *socket) {
+ isc_result_t result;
isc_nmhandle_t *handle = NULL;
isc_sockaddr_t addr;
- isc_http2_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
+ isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
isc_region_t data;
/*
* Unbound uses.
* (https://blog.nlnetlabs.nl/dns-over-https-in-unbound/)
*/
- if (!socket->h2.request_path || !socket->h2.handler_cb) {
+ if (!socket->h2.request_path || !socket->h2.cb) {
code = ISC_HTTP_ERROR_NOT_FOUND;
} else if ((socket->h2.request_type == ISC_HTTP_REQ_POST &&
!socket->h2.content_type_verified) ||
addr = isc_nmhandle_peeraddr(session->handle);
handle = isc__nmhandle_get(socket, &addr, NULL);
- socket->h2.handler_cb(handle, ISC_R_SUCCESS, &data,
- socket->h2.handler_cbarg);
+ socket->h2.cb(handle, ISC_R_SUCCESS, &data, socket->h2.cbarg);
isc_nmhandle_detach(&handle);
return (0);
error:
- if (server_send_error_response(code, ngsession, socket) != 0) {
+ result = server_send_error_response(code, ngsession, socket);
+ if (result != ISC_R_SUCCESS) {
return (NGHTTP2_ERR_CALLBACK_FAILURE);
}
return (0);
void
isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg) {
- isc_nmsocket_t *sock = handle->sock;
- isc__nm_uvreq_t *uvreq = NULL;
+ isc_nmsocket_t *sock = NULL;
isc__netievent_httpsend_t *ievent = NULL;
+ isc__nm_uvreq_t *uvreq = NULL;
REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(handle->httpsession != NULL);
- REQUIRE(VALID_NMSOCK(sock));
- /* TODO: should be called from within the context of an NM thread? */
- if (inactive(sock) || handle->httpsession->closed) {
- cb(handle, ISC_R_CANCELED, cbarg);
- return;
- }
+ sock = handle->sock;
- INSIST(VALID_NMHANDLE(handle->httpsession->handle));
- INSIST(VALID_NMSOCK(handle->httpsession->handle->sock));
- INSIST(sock->tid == handle->httpsession->handle->sock->tid);
+ REQUIRE(VALID_NMSOCK(sock));
uvreq = isc__nm_uvreq_get(sock->mgr, sock);
isc_nmhandle_attach(handle, &uvreq->handle);
(isc__netievent_t *)ievent);
}
+static void
+failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+ isc_result_t eresult) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+
+ if (req->cb.send != NULL) {
+ isc__nm_sendcb(sock, req, eresult);
+ } else {
+ isc__nm_uvreq_put(&req, sock);
+ }
+}
+
+static void
+client_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock,
+ isc__nm_uvreq_t *req) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nm_cb_t cb = req->cb.send;
+ void *cbarg = req->cbarg;
+
+ result = client_send(
+ handle,
+ &(isc_region_t){ (uint8_t *)req->uvbuf.base, req->uvbuf.len });
+ if (result != ISC_R_SUCCESS) {
+ failed_send_cb(sock, req, result);
+ return;
+ }
+
+ cb(handle, result, cbarg);
+ isc__nm_uvreq_put(&req, sock);
+}
+
+static void
+server_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock,
+ isc__nm_uvreq_t *req) {
+ size_t len;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nm_cb_t cb = req->cb.send;
+ void *cbarg = req->cbarg;
+ if (inactive(sock) || handle->httpsession->closed) {
+ failed_send_cb(sock, req, ISC_R_CANCELED);
+ return;
+ }
+
+ INSIST(handle->httpsession->handle->sock->tid == isc_nm_tid());
+ INSIST(VALID_NMHANDLE(handle->httpsession->handle));
+ INSIST(VALID_NMSOCK(handle->httpsession->handle->sock));
+
+ memmove(sock->h2.buf, req->uvbuf.base, req->uvbuf.len);
+ sock->h2.bufsize = req->uvbuf.len;
+
+ len = snprintf(sock->h2.clenbuf, sizeof(sock->h2.clenbuf), "%lu",
+ (unsigned long)req->uvbuf.len);
+ const nghttp2_nv hdrs[] = {
+ MAKE_NV2(":status", "200"),
+ MAKE_NV2("Content-Type", DNS_MEDIA_TYPE),
+ MAKE_NV("Content-Length", sock->h2.clenbuf, len),
+ /*
+ * TODO: implement Cache-Control: max-age=<seconds>
+ * (https://tools.ietf.org/html/rfc8484#section-5.1)
+ */
+ MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
+ };
+
+ result = server_send_response(handle->httpsession->ngsession,
+ sock->h2.stream_id, hdrs,
+ sizeof(hdrs) / sizeof(nghttp2_nv), sock);
+
+ http_do_bio(handle->httpsession);
+ cb(handle, result, cbarg);
+ isc__nm_uvreq_put(&req, sock);
+}
+
void
isc__nm_async_httpsend(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_httpsend_t *ievent = (isc__netievent_httpsend_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
isc__nm_uvreq_t *req = ievent->req;
isc_nmhandle_t *handle = NULL;
- isc_nm_cb_t cb = NULL;
- void *cbarg = NULL;
- isc_result_t res;
- size_t content_length_str_len;
+ isc_nm_http_session_t *session = NULL;
+
+ UNUSED(worker);
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(VALID_UVREQ(req));
+ REQUIRE(VALID_HTTP2_SESSION(sock->h2.session));
ievent->req = NULL;
handle = req->handle;
- cb = req->cb.send;
- cbarg = req->cbarg;
REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(VALID_NMHANDLE(handle->httpsession->handle));
- REQUIRE(VALID_NMSOCK(handle->httpsession->handle->sock));
- REQUIRE(handle->httpsession->handle->sock->tid == isc_nm_tid());
- UNUSED(worker);
+ session = sock->h2.session;
+ if (session != NULL && session->client) {
+ client_httpsend(handle, sock, req);
+ } else {
+ server_httpsend(handle, sock, req);
+ }
+}
- memmove(sock->h2.buf, req->uvbuf.base, req->uvbuf.len);
- sock->h2.bufsize = req->uvbuf.len;
+void
+isc__nm_http_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ isc_result_t result;
+ http_cstream_t *cstream = NULL;
+ isc_nm_http_session_t *session = NULL;
- content_length_str_len =
- snprintf(sock->h2.response_content_length_str,
- sizeof(sock->h2.response_content_length_str), "%lu",
- (unsigned long)req->uvbuf.len);
- const nghttp2_nv hdrs[] = {
- MAKE_NV2(":status", "200"),
- MAKE_NV2("Content-Type", "application/dns-message"),
- MAKE_NV("Content-Length", sock->h2.response_content_length_str,
- content_length_str_len)
- };
- if (server_send_response(handle->httpsession->ngsession,
- sock->h2.stream_id, hdrs,
- sizeof(hdrs) / sizeof(nghttp2_nv), sock) != 0)
- {
- res = ISC_R_FAILURE;
- } else {
- res = ISC_R_SUCCESS;
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ session = handle->sock->h2.session;
+
+ result = get_http_cstream(handle->sock, &cstream);
+ if (result != ISC_R_SUCCESS) {
+ return;
}
- http2_do_bio(handle->httpsession); /*TODO: Should we call it only
- * on success? */
- cb(handle, res, cbarg);
+ handle->sock->h2.connect.cstream = cstream;
+ cstream->read_cb = cb;
+ cstream->read_cbarg = cbarg;
+ cstream->reading = true;
- isc__nm_uvreq_put(&req, sock);
+ if (!ISC_LINK_LINKED(cstream, link)) {
+ ISC_LIST_APPEND(session->cstreams, cstream, link);
+ }
+
+ if (cstream->sending) {
+ result = client_submit_request(session, cstream);
+ if (result != ISC_R_SUCCESS) {
+ ISC_LIST_UNLINK(session->cstreams, cstream, link);
+ return;
+ }
+
+ http_do_bio(session);
+ }
}
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_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
isc_nmsocket_t *socket = NULL;
switch (frame->hd.type) {
}
static void
-initialize_nghttp2_server_session(isc_nm_http2_session_t *session) {
+initialize_nghttp2_server_session(isc_nm_http_session_t *session) {
nghttp2_session_callbacks *callbacks = NULL;
+ nghttp2_mem mem;
+
+ init_nghttp2_mem(session->mctx, &mem);
RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
nghttp2_session_callbacks_set_on_frame_recv_callback(
callbacks, server_on_frame_recv_callback);
- nghttp2_session_server_new(&session->ngsession, callbacks, session);
+ RUNTIME_CHECK(nghttp2_session_server_new3(&session->ngsession,
+ callbacks, session, NULL,
+ &mem) == 0);
nghttp2_session_callbacks_del(callbacks);
}
static int
-server_send_connection_header(isc_nm_http2_session_t *session) {
+server_send_connection_header(isc_nm_http_session_t *session) {
nghttp2_settings_entry iv[1] = {
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
};
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;
+ isc_nm_http_session_t *session = NULL;
isc_nmsocket_t *listener = NULL, *httpserver = NULL;
REQUIRE(VALID_NMHANDLE(handle));
return (ISC_R_CANCELED);
}
- session = isc_mem_get(httplistensock->mgr->mctx,
- sizeof(isc_nm_http2_session_t));
- *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC,
- .ssl_ctx_created = false,
- .client = false };
+ new_session(httplistensock->mgr->mctx, NULL, &session);
initialize_nghttp2_server_session(session);
handle->sock->h2.session = session;
- isc_mem_attach(httplistensock->mgr->mctx, &session->mctx);
isc_nmhandle_attach(handle, &session->handle);
isc__nmsocket_attach(httplistensock, &session->serversocket);
- session->serversocket = httplistensock;
+ session->server_iface.addr = isc_nmhandle_localaddr(session->handle);
server_send_connection_header(session);
/* TODO H2 */
- http2_do_bio(session);
+ http_do_bio(session);
return (ISC_R_SUCCESS);
}
-#ifndef OPENSSL_NO_NEXTPROTONEG
-static int
-next_proto_cb(SSL *ssl, const unsigned char **data, unsigned int *len,
- void *arg) {
- UNUSED(ssl);
- UNUSED(arg);
-
- *data = (const unsigned char *)NGHTTP2_PROTO_ALPN;
- *len = (unsigned int)NGHTTP2_PROTO_ALPN_LEN;
- return (SSL_TLSEXT_ERR_OK);
-}
-#endif /* !OPENSSL_NO_NEXTPROTONEG */
-
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
-static int
-alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
- const unsigned char *in, unsigned int inlen, void *arg) {
- int ret;
-
- UNUSED(ssl);
- UNUSED(arg);
-
- ret = nghttp2_select_next_protocol((unsigned char **)(uintptr_t)out,
- outlen, in, inlen);
-
- if (ret != 1) {
- return (SSL_TLSEXT_ERR_NOACK);
- }
-
- return (SSL_TLSEXT_ERR_OK);
-}
-#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
-
isc_result_t
isc_nm_listenhttp(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog,
- isc_quota_t *quota, SSL_CTX *ctx, isc_nmsocket_t **sockp) {
+ isc_quota_t *quota, isc_tlsctx_t *ctx,
+ isc_nmsocket_t **sockp) {
isc_nmsocket_t *sock = NULL;
isc_result_t result;
isc__nmsocket_init(sock, mgr, isc_nm_httplistener, iface);
if (ctx != NULL) {
-#ifndef OPENSSL_NO_NEXTPROTONEG
- SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, NULL);
-#endif // OPENSSL_NO_NEXTPROTONEG
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
- SSL_CTX_set_alpn_select_cb(ctx, alpn_select_proto_cb, NULL);
-#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+ isc_tlsctx_enable_http2server_alpn(ctx);
result = isc_nm_listentls(mgr, iface, httplisten_acceptcb, sock,
- sizeof(isc_nm_http2_session_t),
+ sizeof(isc_nm_http_session_t),
backlog, quota, ctx, &sock->outer);
} else {
result = isc_nm_listentcp(mgr, iface, httplisten_acceptcb, sock,
- sizeof(isc_nm_http2_session_t),
+ sizeof(isc_nm_http_session_t),
backlog, quota, &sock->outer);
}
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);
-
- if (find_server_request_handler(uri, sock) == NULL) {
- 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);
-
- RWLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write);
- ISC_LIST_APPEND(sock->h2.handlers, handler, link);
- RWUNLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write);
- }
-
- return (ISC_R_SUCCESS);
-}
-
/*
* 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.
+ * http content.
*/
static void
-doh_callback(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *data,
- void *arg) {
- isc_nm_http_doh_cbarg_t *dohcbarg = arg;
+http_callback(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *data,
+ void *arg) {
+ isc_nm_httpcbarg_t *httpcbarg = arg;
REQUIRE(VALID_NMHANDLE(handle));
if (result != ISC_R_SUCCESS) {
/* Shut down the client, then ourselves */
- dohcbarg->cb(handle, result, NULL, dohcbarg->cbarg);
+ httpcbarg->cb(handle, result, NULL, httpcbarg->cbarg);
/* XXXWPK FREE */
return;
}
- dohcbarg->cb(handle, result, data, dohcbarg->cbarg);
+ httpcbarg->cb(handle, result, data, httpcbarg->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;
- isc_nm_http_doh_cbarg_t *dohcbarg = NULL;
+isc_nm_http_endpoint(isc_nmsocket_t *sock, const char *uri, isc_nm_recv_cb_t cb,
+ void *cbarg, size_t extrahandlesize) {
+ isc_nm_httphandler_t *handler = NULL;
+ isc_nm_httpcbarg_t *httpcbarg = NULL;
+ bool newhandler = false;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_httplistener);
- dohcbarg = isc_mem_get(sock->mgr->mctx,
- sizeof(isc_nm_http_doh_cbarg_t));
- *dohcbarg = (isc_nm_http_doh_cbarg_t){ .cb = cb, .cbarg = cbarg };
- ISC_LINK_INIT(dohcbarg, link);
+ httpcbarg = isc_mem_get(sock->mgr->mctx, sizeof(isc_nm_httpcbarg_t));
+ *httpcbarg = (isc_nm_httpcbarg_t){ .cb = cb, .cbarg = cbarg };
+ ISC_LINK_INIT(httpcbarg, link);
- 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(isc_nm_http_doh_cbarg_t));
+ if (find_server_request_handler(uri, sock) == NULL) {
+ handler = isc_mem_get(sock->mgr->mctx, sizeof(*handler));
+ *handler = (isc_nm_httphandler_t){
+ .cb = http_callback,
+ .cbarg = httpcbarg,
+ .extrahandlesize = extrahandlesize,
+ .path = isc_mem_strdup(sock->mgr->mctx, uri)
+ };
+ ISC_LINK_INIT(handler, link);
+
+ newhandler = true;
}
- RWLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write);
- ISC_LIST_APPEND(sock->h2.handlers_cbargs, dohcbarg, link);
- RWUNLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write);
+ RWLOCK(&sock->h2.lock, isc_rwlocktype_write);
+ if (newhandler) {
+ ISC_LIST_APPEND(sock->h2.handlers, handler, link);
+ }
+ ISC_LIST_APPEND(sock->h2.handler_cbargs, httpcbarg, link);
+ RWUNLOCK(&sock->h2.lock, isc_rwlocktype_write);
- return (result);
+ return (ISC_R_SUCCESS);
}
void
(isc__netievent_t *)ievent);
}
-void
-isc__nm_http_clear_handlers(isc_nmsocket_t *sock) {
- isc_nm_http2_server_handler_t *handler = NULL;
- isc_nm_http_doh_cbarg_t *dohcbarg = NULL;
+static void
+clear_handlers(isc_nmsocket_t *sock) {
+ isc_nm_httphandler_t *handler = NULL;
+ isc_nm_httpcbarg_t *httpcbarg = NULL;
- /* delete all handlers */
- RWLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write);
+ /* Delete all handlers */
+ RWLOCK(&sock->h2.lock, isc_rwlocktype_write);
handler = ISC_LIST_HEAD(sock->h2.handlers);
while (handler != NULL) {
- isc_nm_http2_server_handler_t *next;
+ isc_nm_httphandler_t *next = NULL;
next = ISC_LIST_NEXT(handler, link);
ISC_LIST_DEQUEUE(sock->h2.handlers, handler, link);
handler = next;
}
- dohcbarg = ISC_LIST_HEAD(sock->h2.handlers_cbargs);
- while (dohcbarg != NULL) {
- isc_nm_http_doh_cbarg_t *next;
-
- next = ISC_LIST_NEXT(dohcbarg, link);
- ISC_LIST_DEQUEUE(sock->h2.handlers_cbargs, dohcbarg, link);
- isc_mem_put(sock->mgr->mctx, dohcbarg,
- sizeof(isc_nm_http_doh_cbarg_t));
- dohcbarg = next;
- }
- RWUNLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write);
-}
+ httpcbarg = ISC_LIST_HEAD(sock->h2.handler_cbargs);
+ while (httpcbarg != NULL) {
+ isc_nm_httpcbarg_t *next = NULL;
-void
-isc__nm_http_clear_session(isc_nmsocket_t *sock) {
- if (sock->type != isc_nm_httpstream && sock->h2.session != NULL) {
- isc_nm_http2_session_t *session = sock->h2.session;
- INSIST(ISC_LIST_EMPTY(session->sstreams));
- session->magic = 0;
- if (session->ssl_ctx_created) {
- SSL_CTX_free(session->ssl_ctx);
- }
- isc_mem_putanddetach(&sock->h2.session->mctx, session,
- sizeof(isc_nm_http2_session_t));
- sock->h2.session = NULL;
+ next = ISC_LIST_NEXT(httpcbarg, link);
+ ISC_LIST_DEQUEUE(sock->h2.handler_cbargs, httpcbarg, link);
+ isc_mem_put(sock->mgr->mctx, httpcbarg,
+ sizeof(isc_nm_httpcbarg_t));
+ httpcbarg = next;
}
+ RWUNLOCK(&sock->h2.lock, isc_rwlocktype_write);
}
void
static void
http_close_direct(isc_nmsocket_t *sock) {
bool sessions_empty;
- isc_nm_http2_session_t *session;
+ isc_nm_http_session_t *session = NULL;
REQUIRE(VALID_NMSOCK(sock));
- REQUIRE(VALID_HTTP2_SESSION(sock->h2.session));
atomic_store(&sock->closed, true);
+
+ if (atomic_load(&sock->client)) {
+ return;
+ }
+ INSIST(VALID_HTTP2_SESSION(sock->h2.session));
+
session = sock->h2.session;
if (ISC_LINK_LINKED(&sock->h2, link)) {
ISC_LIST_UNLINK(session->sstreams, &sock->h2, link);
-#ifdef NETMGR_TRACE
- session->sstreams_count--;
-#endif /* NETMGR_TRACE */
}
sessions_empty = ISC_LIST_EMPTY(session->sstreams);
if (!sessions_empty) {
- http2_do_bio(session);
+ http_do_bio(session);
} else if (session->reading) {
session->reading = false;
if (session->handle != NULL) {
isc_nm_cancelread(session->handle);
}
}
- /* If session is closed then the only reference to the socket is
- * the one, created when handling the netievent. */
+
+ /*
+ * If session is closed then the only reference to the
+ * socket is the one created when handling the netievent.
+ */
if (!session->closed) {
INSIST(session->handle != NULL);
isc__nmsocket_detach(&sock);
void
isc__nm_http_close(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
- REQUIRE(sock->type == isc_nm_httpstream);
+ REQUIRE(sock->type == isc_nm_httpsocket);
REQUIRE(!isc__nmsocket_active(sock));
if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
static void
failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
- isc_nm_http2_session_t *session) {
+ isc_nm_http_session_t *session) {
isc_nmhandle_t *handle = NULL;
isc_sockaddr_t addr;
REQUIRE(VALID_NMSOCK(sock));
- INSIST(sock->type == isc_nm_httpstream);
+ INSIST(sock->type == isc_nm_httpsocket);
if (!sock->h2.request_path) {
return;
}
- INSIST(sock->h2.handler_cbarg != NULL);
+ INSIST(sock->h2.cbarg != NULL);
(void)nghttp2_submit_rst_stream(
session->ngsession, NGHTTP2_FLAG_END_STREAM, sock->h2.stream_id,
NGHTTP2_REFUSED_STREAM);
addr = isc_nmhandle_peeraddr(session->handle);
handle = isc__nmhandle_get(sock, &addr, NULL);
- sock->h2.handler_cb(handle, result,
- &(isc_region_t){ sock->h2.buf, sock->h2.bufsize },
- sock->h2.handler_cbarg);
+ sock->h2.cb(handle, result,
+ &(isc_region_t){ sock->h2.buf, sock->h2.bufsize },
+ sock->h2.cbarg);
isc_nmhandle_detach(&handle);
}
static void
-failed_read_cb(isc_result_t result, isc_nm_http2_session_t *session) {
+failed_read_cb(isc_result_t result, isc_nm_http_session_t *session) {
REQUIRE(VALID_HTTP2_SESSION(session));
if (session->client) {
- http2_client_stream_t *cstream = NULL;
+ http_cstream_t *cstream = NULL;
cstream = ISC_LIST_HEAD(session->cstreams);
while (cstream != NULL) {
- http2_client_stream_t *next = ISC_LIST_NEXT(cstream,
- link);
+ http_cstream_t *next = ISC_LIST_NEXT(cstream, link);
ISC_LIST_DEQUEUE(session->cstreams, cstream, link);
-
cstream->read_cb(session->handle, result,
&(isc_region_t){ cstream->rbuf,
cstream->rbufsize },
cstream->read_cbarg);
-
- put_http2_client_stream(session->mctx, cstream);
+ put_http_cstream(session->mctx, cstream);
cstream = next;
}
} else {
h2data = next;
}
}
- finish_http2_session(session);
+
+ finish_http_session(session);
}
static const bool base64url_validation_table[256] = {
goto end;
break;
default:
- /* All other characters from the alphabet are the same
+ /*
+ * All other characters from the alphabet are the same
* for both base64 and base64url, so we can reuse the
- * validation table for the rest of the characters. */
+ * validation table for the rest of the characters.
+ */
if (base64[i] != '-' && base64[i] != '_' &&
base64url_validation_table[(size_t)base64[i]])
{
return (res);
}
-/* DoH GET Query String Scanner-less Recursive Descent Parser/Verifier
+void
+isc__nm_http_initsocket(isc_nmsocket_t *sock) {
+ REQUIRE(sock != NULL);
-It is based on the following grammar (using WSN/EBNF):
+ sock->h2 = (isc_nmsocket_h2_t){
+ .request_type = ISC_HTTP_REQ_UNSUPPORTED,
+ .request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED,
+ };
-S = query-string.
-query-string = ['?'] { key-value-pair } EOF.
-key-value-pair = key '=' value [ '&' ].
-key = ('_' | alpha) { '_' | alnum}.
-value = value-char {value-char}.
-value-char = unreserved-char | percent-charcode.
-unreserved-char = alnum |'_' | '.' | '-' | '~'. (* RFC3986, Section 2.3 *)
-percent-charcode = '%' hexdigit hexdigit.
-...
+ if (sock->type == isc_nm_httplistener) {
+ ISC_LIST_INIT(sock->h2.handlers);
+ ISC_LIST_INIT(sock->h2.handler_cbargs);
+ isc_rwlock_init(&sock->h2.lock, 0, 1);
+ }
+}
-Should be good enough.
-*/
+void
+isc__nm_http_cleanup_data(isc_nmsocket_t *sock) {
+ if (sock->type == isc_nm_httplistener ||
+ sock->type == isc_nm_httpsocket) {
+ if (sock->type == isc_nm_httplistener) {
+ clear_handlers(sock);
+ isc_rwlock_destroy(&sock->h2.lock);
+ }
+
+ if (sock->h2.request_path != NULL) {
+ isc_mem_free(sock->mgr->mctx, sock->h2.request_path);
+ sock->h2.request_path = NULL;
+ }
+
+ if (sock->h2.query_data != NULL) {
+ isc_mem_free(sock->mgr->mctx, sock->h2.query_data);
+ sock->h2.query_data = NULL;
+ }
+
+ if (sock->h2.connect.cstream != NULL) {
+ put_http_cstream(sock->mgr->mctx,
+ sock->h2.connect.cstream);
+ sock->h2.connect.cstream = NULL;
+ }
+
+ if (sock->h2.buf != NULL) {
+ isc_mem_free(sock->mgr->mctx, sock->h2.buf);
+ sock->h2.buf = NULL;
+ }
+ }
+
+ if ((sock->type == isc_nm_httplistener ||
+ sock->type == isc_nm_httpsocket ||
+ sock->type == isc_nm_tcpsocket ||
+ sock->type == isc_nm_tlssocket) &&
+ sock->h2.session != NULL)
+ {
+ if (sock->h2.connect.uri != NULL) {
+ isc_mem_free(sock->mgr->mctx, sock->h2.connect.uri);
+ sock->h2.connect.uri = NULL;
+ }
+ isc__nm_httpsession_detach(&sock->h2.session);
+ }
+}
+
+void
+isc__nm_http_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
+ isc_nmsocket_t *sock = NULL;
-typedef struct isc_doh_query_parser_state {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_httpsocket);
+
+ sock = handle->sock;
+ if (sock->h2.session != NULL && sock->h2.session->handle) {
+ INSIST(VALID_HTTP2_SESSION(sock->h2.session));
+ INSIST(VALID_NMHANDLE(sock->h2.session->handle));
+ isc_nmhandle_settimeout(sock->h2.session->handle, timeout);
+ }
+}
+
+/*
+ * DoH GET Query String Scanner-less Recursive Descent Parser/Verifier
+ *
+ * It is based on the following grammar (using WSN/EBNF):
+ *
+ * S = query-string.
+ * query-string = ['?'] { key-value-pair } EOF.
+ * key-value-pair = key '=' value [ '&' ].
+ * key = ('_' | alpha) { '_' | alnum}.
+ * value = value-char {value-char}.
+ * value-char = unreserved-char | percent-charcode.
+ * unreserved-char = alnum |'_' | '.' | '-' | '~'. (* RFC3986, Section 2.3 *)
+ * percent-charcode = '%' hexdigit hexdigit.
+ * ...
+ *
+ * Should be good enough.
+ */
+typedef struct isc_httpparser_state {
const char *str;
const char *last_key;
const char *last_value;
size_t last_value_len;
- bool doh_query_found;
- const char *doh_query;
- size_t doh_query_length;
-} isc_doh_query_parser_state_t;
+ bool query_found;
+ const char *query;
+ size_t query_len;
+} isc_httpparser_state_t;
#define MATCH(ch) (st->str[0] == (ch))
#define MATCH_ALPHA() isalpha(st->str[0])
#define GETP() (st->str)
static bool
-rule_query_string(isc_doh_query_parser_state_t *st);
+rule_query_string(isc_httpparser_state_t *st);
bool
-isc__nm_parse_doh_query_string(const char *query_string, const char **start,
- size_t *len) {
- isc_doh_query_parser_state_t state;
+isc__nm_parse_httpquery(const char *query_string, const char **start,
+ size_t *len) {
+ isc_httpparser_state_t state;
REQUIRE(start != NULL);
- REQUIRE(len != 0);
+ REQUIRE(len != NULL);
- if (query_string == NULL || *query_string == '\0' || start == NULL ||
- len == 0) {
+ if (query_string == NULL || query_string[0] == '\0') {
return (false);
}
- memset(&state, 0, sizeof(state));
- state.str = query_string;
+ state = (isc_httpparser_state_t) { .str = query_string };
if (!rule_query_string(&state)) {
return (false);
}
- if (!state.doh_query_found) {
+ if (!state.query_found) {
return (false);
}
- *start = state.doh_query;
- *len = state.doh_query_length;
+ *start = state.query;
+ *len = state.query_len;
return (true);
}
static bool
-rule_key_value_pair(isc_doh_query_parser_state_t *st);
+rule_key_value_pair(isc_httpparser_state_t *st);
static bool
-rule_key(isc_doh_query_parser_state_t *st);
+rule_key(isc_httpparser_state_t *st);
static bool
-rule_value(isc_doh_query_parser_state_t *st);
+rule_value(isc_httpparser_state_t *st);
static bool
-rule_value_char(isc_doh_query_parser_state_t *st);
+rule_value_char(isc_httpparser_state_t *st);
static bool
-rule_percent_charcode(isc_doh_query_parser_state_t *st);
+rule_percent_charcode(isc_httpparser_state_t *st);
static bool
-rule_unreserved_char(isc_doh_query_parser_state_t *st);
+rule_unreserved_char(isc_httpparser_state_t *st);
static bool
-rule_query_string(isc_doh_query_parser_state_t *st) {
+rule_query_string(isc_httpparser_state_t *st) {
if (MATCH('?')) {
ADVANCE();
}
}
static bool
-rule_key_value_pair(isc_doh_query_parser_state_t *st) {
+rule_key_value_pair(isc_httpparser_state_t *st) {
if (!rule_key(st)) {
return (false);
}
if (st->last_key_len == sizeof(dns) - 1 &&
memcmp(st->last_key, dns, sizeof(dns) - 1) == 0)
{
- st->doh_query_found = true;
- st->doh_query = st->last_value;
- st->doh_query_length = st->last_value_len;
+ st->query_found = true;
+ st->query = st->last_value;
+ st->query_len = st->last_value_len;
}
} else {
return (false);
}
static bool
-rule_key(isc_doh_query_parser_state_t *st) {
+rule_key(isc_httpparser_state_t *st) {
if (MATCH('_') || MATCH_ALPHA()) {
st->last_key = GETP();
ADVANCE();
}
static bool
-rule_value(isc_doh_query_parser_state_t *st) {
+rule_value(isc_httpparser_state_t *st) {
const char *s = GETP();
if (!rule_value_char(st)) {
return (false);
}
static bool
-rule_value_char(isc_doh_query_parser_state_t *st) {
+rule_value_char(isc_httpparser_state_t *st) {
if (rule_unreserved_char(st)) {
return (true);
}
}
static bool
-rule_unreserved_char(isc_doh_query_parser_state_t *st) {
+rule_unreserved_char(isc_httpparser_state_t *st) {
if (MATCH_ALNUM() || MATCH('_') || MATCH('.') || MATCH('-') ||
MATCH('~')) {
ADVANCE();
}
static bool
-rule_percent_charcode(isc_doh_query_parser_state_t *st) {
+rule_percent_charcode(isc_httpparser_state_t *st) {
if (MATCH('%')) {
ADVANCE();
} else {
#include <isc/sockaddr.h>
#include <isc/stats.h>
#include <isc/thread.h>
+#include <isc/tls.h>
#include <isc/util.h>
#include "uv-compat.h"
#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.
*/
atomic_load(&(t)->references) > 0)
typedef void (*isc__nm_closecb)(isc_nmhandle_t *);
+typedef struct isc_nm_http_session isc_nm_http_session_t;
struct isc_nmhandle {
int magic;
isc_nmsocket_t *sock;
size_t ah_pos; /* Position in the socket's 'active handles' array */
- isc_nm_http2_session_t *httpsession;
+ isc_nm_http_session_t *httpsession;
isc_sockaddr_t peer;
isc_sockaddr_t local;
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 {
+typedef struct isc_nm_httphandler isc_nm_httphandler_t;
+struct isc_nm_httphandler {
char *path;
- isc_nm_http_cb_t cb;
+ isc_nm_recv_cb_t cb;
void *cbarg;
size_t extrahandlesize;
- LINK(isc_nm_http2_server_handler_t) link;
+ LINK(isc_nm_httphandler_t) link;
};
/*
isc_nm_tlsdnslistener,
isc_nm_tlsdnssocket,
isc_nm_httplistener,
- isc_nm_httpstream
+ isc_nm_httpsocket
} isc_nmsocket_type;
/*%
typedef struct isc_nmsocket_tls_send_req {
isc_nmsocket_t *tlssock;
isc_region_t data;
+ isc_nm_cb_t cb;
+ void *cbarg;
+ isc_nmhandle_t *handle;
+ bool finish;
} isc_nmsocket_tls_send_req_t;
-typedef enum isc_doh_request_type {
+typedef enum isc_http_request_type {
ISC_HTTP_REQ_GET,
ISC_HTTP_REQ_POST,
ISC_HTTP_REQ_UNSUPPORTED
-} isc_http2_request_type_t;
+} isc_http_request_type_t;
-typedef enum isc_http2_scheme_type {
+typedef enum isc_http_scheme_type {
ISC_HTTP_SCHEME_HTTP,
ISC_HTTP_SCHEME_HTTP_SECURE,
ISC_HTTP_SCHEME_UNSUPPORTED
-} isc_http2_scheme_type_t;
+} isc_http_scheme_type_t;
-typedef struct isc_nm_http_doh_cbarg {
+typedef struct isc_nm_httpcbarg {
isc_nm_recv_cb_t cb;
void *cbarg;
- LINK(struct isc_nm_http_doh_cbarg) link;
-} isc_nm_http_doh_cbarg_t;
+ LINK(struct isc_nm_httpcbarg) link;
+} isc_nm_httpcbarg_t;
typedef struct isc_nmsocket_h2 {
isc_nmsocket_t *psock; /* owner of the structure */
char *query_data;
size_t query_data_len;
bool query_too_large;
- isc_nm_http2_server_handler_t *handler;
+ isc_nm_httphandler_t *handler;
uint8_t *buf;
size_t bufsize;
size_t bufpos;
int32_t stream_id;
- isc_nm_http2_session_t *session;
+ isc_nm_http_session_t *session;
isc_nmsocket_t *httpserver;
- isc_http2_request_type_t request_type;
- isc_http2_scheme_type_t request_scheme;
+ isc_http_request_type_t request_type;
+ isc_http_scheme_type_t request_scheme;
+
size_t content_length;
+ char clenbuf[128];
+
bool content_type_verified;
bool accept_type_verified;
- isc_nm_http_cb_t handler_cb;
- void *handler_cbarg;
+ isc_nm_recv_cb_t cb;
+ void *cbarg;
LINK(struct isc_nmsocket_h2) link;
- ISC_LIST(isc_nm_http2_server_handler_t) handlers;
- ISC_LIST(isc_nm_http_doh_cbarg_t) handlers_cbargs;
- isc_rwlock_t handlers_lock;
-
- char response_content_length_str[128];
+ ISC_LIST(isc_nm_httphandler_t) handlers;
+ ISC_LIST(isc_nm_httpcbarg_t) handler_cbargs;
+ isc_rwlock_t lock;
- struct isc_nmsocket_h2_connect_data {
+ struct {
char *uri;
bool post;
+ isc_tlsctx_t *tlsctx;
+ isc_nmiface_t local_interface;
+ void *cstream;
} connect;
} isc_nmsocket_h2_t;
+
struct isc_nmsocket {
/*% Unlocked, RO */
int magic;
/*% TLS stuff */
struct tls {
- SSL *ssl;
- SSL_CTX *ctx;
+ isc_tls_t *tls;
+ isc_tlsctx_t *ctx;
BIO *app_rbio;
BIO *app_wbio;
BIO *ssl_rbio;
struct tlsstream {
bool server;
BIO *app_bio;
- SSL *ssl;
- SSL_CTX *ctx;
+ isc_tls_t *tls;
+ isc_tlsctx_t *ctx;
BIO *ssl_bio;
isc_nmsocket_t *tlslistener;
+ isc_nmiface_t server_iface;
+ isc_nmiface_t local_iface;
+ bool connect_from_networker;
+ atomic_bool result_updated;
enum {
TLS_INIT,
TLS_HANDSHAKE,
TLS_IO,
- TLS_ERROR,
TLS_CLOSING,
TLS_CLOSED
- } state;
+ } state; /*%< The order of these is significant */
size_t nsending;
- /* List of active send requests. */
- ISC_LIST(isc__nm_uvreq_t) sends;
} tlsstream;
isc_nmsocket_h2_t h2;
void
isc__nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
isc_result_t eresult);
+
+void
+isc__nm_connectcb_force_async(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult);
+
void
isc__nm_async_connectcb(isc__networker_t *worker, isc__netievent_t *ev0);
/*%<
* Issue a connect callback on the socket, used to call the callback
-
*/
void
void
isc__nm_tls_stoplistening(isc_nmsocket_t *sock);
+void
+isc__nm_tls_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+/*%<
+ * Set the read timeout and reset the timer for the socket
+ * associated with 'handle', and the TCP socket it wraps
+ * around.
+ */
+
void
isc__nm_http_stoplistening(isc_nmsocket_t *sock);
void
-isc__nm_http_clear_handlers(isc_nmsocket_t *sock);
+isc__nm_http_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+/*%<
+ * Set the read timeout and reset the timer for the socket
+ * associated with 'handle', and the TLS/TCP socket it wraps
+ * around.
+ */
+
+void
+isc__nm_http_initsocket(isc_nmsocket_t *sock);
void
-isc__nm_http_clear_session(isc_nmsocket_t *sock);
+isc__nm_http_cleanup_data(isc_nmsocket_t *sock);
+
+isc_result_t
+isc__nm_http_request(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_recv_cb_t reply_cb, void *cbarg);
void
isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg);
+void
+isc__nm_http_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
+
void
isc__nm_http_close(isc_nmsocket_t *sock);
isc__nm_async_httpclose(isc__networker_t *worker, isc__netievent_t *ev0);
bool
-isc__nm_parse_doh_query_string(const char *query_string, const char **start,
- size_t *len);
+isc__nm_parse_httpquery(const char *query_string, const char **start,
+ size_t *len);
char *
isc__nm_base64url_to_base64(isc_mem_t *mem, const char *base64url,
isc__nm_base64_to_base64url(isc_mem_t *mem, const char *base64,
const size_t base64_len, size_t *res_len);
+void
+isc__nm_httpsession_attach(isc_nm_http_session_t *source,
+ isc_nm_http_session_t **targetp);
+void
+isc__nm_httpsession_detach(isc_nm_http_session_t **sessionp);
+
#define isc__nm_uverr2result(x) \
isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__)
isc_result_t
isc_condition_destroy(&sock->cond);
isc_condition_destroy(&sock->scond);
isc__nm_tls_cleanup_data(sock);
-
- if (sock->type == isc_nm_httplistener) {
- isc__nm_http_clear_handlers(sock);
- isc_rwlock_destroy(&sock->h2.handlers_lock);
- }
-
- if (sock->h2.request_path != NULL) {
- isc_mem_free(sock->mgr->mctx, sock->h2.request_path);
- sock->h2.request_path = NULL;
- }
-
- if (sock->h2.query_data != NULL) {
- isc_mem_free(sock->mgr->mctx, sock->h2.query_data);
- sock->h2.query_data = NULL;
- }
-
- if (sock->h2.connect.uri != NULL) {
- isc_mem_free(sock->mgr->mctx, sock->h2.connect.uri);
- sock->h2.query_data = NULL;
- }
-
- if (sock->h2.buf != NULL) {
- isc_mem_free(sock->mgr->mctx, sock->h2.buf);
- sock->h2.buf = NULL;
- }
-
- isc__nm_http_clear_session(sock);
+ isc__nm_http_cleanup_data(sock);
#ifdef NETMGR_TRACE
LOCK(&sock->mgr->lock);
ISC_LIST_UNLINK(sock->mgr->active_sockets, sock, active_link);
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_close(sock);
return;
- case isc_nm_httpstream:
+ case isc_nm_httpsocket:
isc__nm_http_close(sock);
return;
default:
case isc_nm_tcpdnslistener:
case isc_nm_tlsdnssocket:
case isc_nm_tlsdnslistener:
- case isc_nm_httpstream:
+ case isc_nm_httpsocket:
case isc_nm_httplistener:
if (family == AF_INET) {
sock->statsindex = tcp4statsindex;
isc_refcount_init(&sock->references, 1);
memset(&sock->tlsstream, 0, sizeof(sock->tlsstream));
- ISC_LIST_INIT(sock->tlsstream.sends);
NETMGR_TRACE_LOG("isc__nmsocket_init():%p->references = %lu\n", sock,
isc_refcount_current(&sock->references));
atomic_store(&sock->active_child_connections, 0);
- if (type == isc_nm_httplistener) {
- ISC_LIST_INIT(sock->h2.handlers);
- ISC_LIST_INIT(sock->h2.handlers_cbargs);
- isc_rwlock_init(&sock->h2.handlers_lock, 0, 1);
- }
-
- sock->h2.session = NULL;
- sock->h2.httpserver = NULL;
- sock->h2.query_data = NULL;
- sock->h2.query_data_len = 0;
- sock->h2.query_too_large = false;
- sock->h2.request_path = NULL;
- sock->h2.request_type = ISC_HTTP_REQ_UNSUPPORTED;
- sock->h2.request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED;
- sock->h2.content_length = 0;
- sock->h2.content_type_verified = false;
- sock->h2.accept_type_verified = false;
- sock->h2.handler_cb = NULL;
- sock->h2.handler_cbarg = NULL;
- sock->h2.connect.uri = NULL;
- sock->h2.buf = NULL;
+ isc__nm_http_initsocket(sock);
sock->magic = NMSOCK_MAGIC;
}
sock->statichandle = handle;
}
- if (sock->type == isc_nm_httpstream) {
- handle->httpsession = sock->h2.session;
+ if (sock->type == isc_nm_httpsocket && sock->h2.session) {
+ isc__nm_httpsession_attach(sock->h2.session,
+ &handle->httpsession);
}
return (handle);
handle->doreset(handle->opaque);
}
+ if (sock->type == isc_nm_httpsocket && handle->httpsession != NULL) {
+ isc__nm_httpsession_detach(&handle->httpsession);
+ }
+
nmhandle_deactivate(sock, handle);
/*
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_settimeout(handle, timeout);
break;
+ case isc_nm_tlssocket:
+ isc__nm_tls_settimeout(handle, timeout);
+ break;
+ case isc_nm_httpsocket:
+ isc__nm_http_settimeout(handle, timeout);
+ break;
default:
INSIST(0);
ISC_UNREACHABLE();
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_send(handle, region, cb, cbarg);
break;
- case isc_nm_httpstream:
+ case isc_nm_httpsocket:
isc__nm_http_send(handle, region, cb, cbarg);
break;
default:
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_read(handle, cb, cbarg);
break;
+ case isc_nm_httpsocket:
+ isc__nm_http_read(handle, cb, cbarg);
+ break;
default:
INSIST(0);
ISC_UNREACHABLE();
}
}
-void
-isc__nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
- isc_result_t eresult) {
+static void
+nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq, isc_result_t eresult,
+ bool force_async) {
+ isc__netievent_connectcb_t *ievent = NULL;
+
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(VALID_UVREQ(uvreq));
REQUIRE(VALID_NMHANDLE(uvreq->handle));
- if (eresult == ISC_R_SUCCESS) {
- isc__netievent_connectcb_t ievent = { .sock = sock,
- .req = uvreq,
- .result = eresult };
- isc__nm_async_connectcb(NULL, (isc__netievent_t *)&ievent);
- } else {
- isc__netievent_connectcb_t *ievent =
- isc__nm_get_netievent_connectcb(sock->mgr, sock, uvreq,
- eresult);
+ ievent = isc__nm_get_netievent_connectcb(sock->mgr, sock, uvreq,
+ eresult);
+ if (force_async) {
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
+ } else {
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
}
}
+void
+isc__nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult) {
+ nm_connectcb(sock, uvreq, eresult, false);
+}
+
+void
+isc__nm_connectcb_force_async(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult) {
+ nm_connectcb(sock, uvreq, eresult, true);
+}
+
void
isc__nm_async_connectcb(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_connectcb_t *ievent = (isc__netievent_connectcb_t *)ev0;
return ("isc_nm_tlsdnssocket");
case isc_nm_httplistener:
return ("isc_nm_httplistener");
- case isc_nm_httpstream:
- return ("isc_nm_httpstream");
+ case isc_nm_httpsocket:
+ return ("isc_nm_httpsocket");
default:
INSIST(0);
ISC_UNREACHABLE();
REQUIRE(isc__nm_in_netthread());
REQUIRE(sock->tid == isc_nm_tid());
- result = isc__nm_socket(req->peer.type.sa.sa_family, SOCK_STREAM, 0,
- &sock->fd);
- /*
- * The socket() call can fail spuriously on FreeBSD 12, so we need to
- * handle the failure early and gracefully.
- */
- if (result != ISC_R_SUCCESS) {
- atomic_store(&sock->closed, true);
- isc__nm_uvreq_t *cbreq = NULL;
- cbreq = isc__nm_uvreq_get(sock->mgr, sock);
- cbreq->cb.connect = req->cb.connect;
- cbreq->cbarg = req->cbarg;
- isc_nmhandle_attach(req->handle, &cbreq->handle);
- isc__nmsocket_clearcb(sock);
- isc__nm_connectcb(sock, cbreq, result);
- goto error;
- }
result = isc__nm_socket_connectiontimeout(sock->fd,
sock->connect_timeout);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
done:
result = isc__nm_uverr2result(r);
-error:
+
LOCK(&sock->lock);
sock->result = result;
SIGNAL(&sock->cond);
REQUIRE(sock->parent == NULL);
REQUIRE(sock->tid == isc_nm_tid());
- sock->fd = (uv_os_sock_t)(-1);
result = tcp_connect_direct(sock, req);
if (result != ISC_R_SUCCESS) {
atomic_store(&sock->active, false);
isc_nmsocket_t *sock = NULL;
isc__netievent_tcpconnect_t *ievent = NULL;
isc__nm_uvreq_t *req = NULL;
+ sa_family_t sa_family;
+ uv_os_sock_t fd;
REQUIRE(VALID_NM(mgr));
REQUIRE(local != NULL);
REQUIRE(peer != NULL);
+ sa_family = peer->addr.type.sa.sa_family;
+
+ /*
+ * The socket() call can fail spuriously on FreeBSD 12, so we need to
+ * handle the failure early and gracefully.
+ */
+ result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &fd);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
sock = isc_mem_get(mgr->mctx, sizeof(*sock));
isc__nmsocket_init(sock, mgr, isc_nm_tcpsocket, local);
sock->extrahandlesize = extrahandlesize;
sock->connect_timeout = timeout;
sock->result = ISC_R_DEFAULT;
+ sock->fd = fd;
atomic_init(&sock->client, true);
req = isc__nm_uvreq_get(mgr, sock);
}
sock->tls.state = TLS_STATE_NONE;
- sock->tls.ssl = SSL_new(sock->tls.ctx);
- RUNTIME_CHECK(sock->tls.ssl != NULL);
+ sock->tls.tls = isc_tls_create(sock->tls.ctx);
+ RUNTIME_CHECK(sock->tls.tls != NULL);
/*
*
* may be necessary to increment the number of references available
* using BIO_up_ref(3) before calling the set0 functions.
*/
- SSL_set0_rbio(sock->tls.ssl, sock->tls.ssl_rbio);
- SSL_set0_wbio(sock->tls.ssl, sock->tls.ssl_wbio);
+ SSL_set0_rbio(sock->tls.tls, sock->tls.ssl_rbio);
+ SSL_set0_wbio(sock->tls.tls, sock->tls.ssl_wbio);
#else
- SSL_set_bio(sock->tls.ssl, sock->tls.ssl_rbio, sock->tls.ssl_wbio);
+ SSL_set_bio(sock->tls.tls, sock->tls.ssl_rbio, sock->tls.ssl_wbio);
#endif
- SSL_set_connect_state(sock->tls.ssl);
+ SSL_set_connect_state(sock->tls.tls);
result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
return;
}
- rv = SSL_shutdown(sock->tls.ssl);
+ rv = SSL_shutdown(sock->tls.tls);
if (rv == 1) {
sock->tls.state = TLS_STATE_NONE;
return;
}
- err = SSL_get_error(sock->tls.ssl, rv);
+ err = SSL_get_error(sock->tls.tls, rv);
switch (err) {
case SSL_ERROR_WANT_READ:
size_t len;
for (;;) {
- (void)SSL_peek(sock->tls.ssl, &(char){ '\0' }, 0);
+ (void)SSL_peek(sock->tls.tls, &(char){ '\0' }, 0);
- int pending = SSL_pending(sock->tls.ssl);
+ int pending = SSL_pending(sock->tls.tls);
if (pending > TLS_BUF_SIZE) {
pending = TLS_BUF_SIZE;
}
}
len = 0;
- rv = SSL_read_ex(sock->tls.ssl,
+ rv = SSL_read_ex(sock->tls.tls,
sock->buf + sock->buf_len,
sock->buf_size - sock->buf_len, &len);
if (rv != 1) {
process_sock_buffer(sock);
}
- } else if (!SSL_is_init_finished(sock->tls.ssl)) {
- if (SSL_is_server(sock->tls.ssl)) {
- rv = SSL_accept(sock->tls.ssl);
+ } else if (!SSL_is_init_finished(sock->tls.tls)) {
+ if (SSL_is_server(sock->tls.tls)) {
+ rv = SSL_accept(sock->tls.tls);
} else {
- rv = SSL_connect(sock->tls.ssl);
+ rv = SSL_connect(sock->tls.tls);
}
} else {
}
if (rv <= 0) {
- err = SSL_get_error(sock->tls.ssl, rv);
+ err = SSL_get_error(sock->tls.tls, rv);
}
switch (err) {
case SSL_ERROR_WANT_READ:
if (sock->tls.state == TLS_STATE_NONE &&
- !SSL_is_init_finished(sock->tls.ssl)) {
+ !SSL_is_init_finished(sock->tls.tls)) {
sock->tls.state = TLS_STATE_HANDSHAKE;
start_reading(sock);
}
/* Stop state after handshake */
if (sock->tls.state == TLS_STATE_HANDSHAKE &&
- SSL_is_init_finished(sock->tls.ssl))
+ SSL_is_init_finished(sock->tls.tls))
{
sock->tls.state = TLS_STATE_IO;
- if (SSL_is_server(sock->tls.ssl)) {
+ if (SSL_is_server(sock->tls.tls)) {
REQUIRE(sock->recv_handle != NULL);
result = sock->accept_cb(sock->recv_handle,
ISC_R_SUCCESS,
csock->tls.state = TLS_STATE_NONE;
- csock->tls.ssl = SSL_new(ssock->tls.ctx);
-
- if (csock->tls.ssl == NULL) {
- char errbuf[256];
- unsigned long err = ERR_get_error();
-
- ERR_error_string_n(err, errbuf, sizeof(errbuf));
- fprintf(stderr, "%s:SSL_new(%p) -> %s\n", __func__,
- ssock->tls.ctx, errbuf);
- }
-
- RUNTIME_CHECK(csock->tls.ssl != NULL);
+ csock->tls.tls = isc_tls_create(ssock->tls.ctx);
+ RUNTIME_CHECK(csock->tls.tls != NULL);
r = BIO_new_bio_pair(&csock->tls.ssl_wbio, TLS_BUF_SIZE,
&csock->tls.app_rbio, TLS_BUF_SIZE);
* may be necessary to increment the number of references available
* using BIO_up_ref(3) before calling the set0 functions.
*/
- SSL_set0_rbio(csock->tls.ssl, csock->tls.ssl_rbio);
- SSL_set0_wbio(csock->tls.ssl, csock->tls.ssl_wbio);
+ SSL_set0_rbio(csock->tls.tls, csock->tls.ssl_rbio);
+ SSL_set0_wbio(csock->tls.tls, csock->tls.ssl_wbio);
#else
- SSL_set_bio(csock->tls.ssl, csock->tls.ssl_rbio, csock->tls.ssl_wbio);
+ SSL_set_bio(csock->tls.tls, csock->tls.ssl_rbio, csock->tls.ssl_wbio);
#endif
- SSL_set_accept_state(csock->tls.ssl);
+ SSL_set_accept_state(csock->tls.tls);
/* FIXME: Set SSL_MODE_RELEASE_BUFFERS */
}
/* Writes won't succeed until handshake end */
- if (!SSL_is_init_finished(sock->tls.ssl)) {
+ if (!SSL_is_init_finished(sock->tls.tls)) {
goto requeue;
}
memmove(worker->sendbuf + sizeof(uint16_t), req->uvbuf.base,
req->uvbuf.len);
- rv = SSL_write_ex(sock->tls.ssl, worker->sendbuf, sendlen, &bytes);
+ rv = SSL_write_ex(sock->tls.tls, worker->sendbuf, sendlen, &bytes);
if (rv > 0) {
/* SSL_write_ex() doesn't do partial writes */
INSIST(sendlen == bytes);
}
/* Nothing was written, maybe enqueue? */
- err = SSL_get_error(sock->tls.ssl, rv);
+ err = SSL_get_error(sock->tls.tls, rv);
switch (err) {
case SSL_ERROR_WANT_WRITE:
atomic_store(&sock->connected, false);
- if (sock->tls.ssl) {
- SSL_free(sock->tls.ssl);
- sock->tls.ssl = NULL;
+ if (sock->tls.tls != NULL) {
+ isc_tls_free(&sock->tls.tls);
}
BIO_free_all(sock->tls.app_rbio);
switch (tls_err) {
case SSL_ERROR_ZERO_RETURN:
return (ISC_R_EOF);
+ case SSL_ERROR_SSL:
+ return (ISC_R_TLSERROR);
default:
return (ISC_R_UNEXPECTED);
}
}
static void
-tls_do_bio(isc_nmsocket_t *sock);
+tls_do_bio(isc_nmsocket_t *sock, isc__nm_uvreq_t *send_data, bool finish);
static void
tls_close_direct(isc_nmsocket_t *sock);
inactive(isc_nmsocket_t *sock) {
return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
sock->outerhandle == NULL ||
+ !isc__nmsocket_active(sock->outerhandle->sock) ||
+ atomic_load(&sock->outerhandle->sock->closing) ||
(sock->listener != NULL &&
!isc__nmsocket_active(sock->listener)) ||
atomic_load(&sock->mgr->closing));
static void
update_result(isc_nmsocket_t *sock, const isc_result_t result) {
- LOCK(&sock->lock);
- sock->result = result;
- SIGNAL(&sock->cond);
- if (!atomic_load(&sock->active)) {
- WAIT(&sock->scond, &sock->lock);
+ if (!sock->tlsstream.server) {
+ LOCK(&sock->lock);
+ sock->result = result;
+ SIGNAL(&sock->cond);
+ while (!atomic_load(&sock->active)) {
+ WAIT(&sock->scond, &sock->lock);
+ }
+ UNLOCK(&sock->lock);
+ } else {
+ LOCK(&sock->lock);
+ sock->result = result;
+ UNLOCK(&sock->lock);
}
- UNLOCK(&sock->lock);
- if (sock->parent) {
- LOCK(&sock->parent->lock);
- sock->parent->result = result;
- UNLOCK(&sock->parent->lock);
+}
+
+static void
+tls_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
+ const isc_result_t result) {
+ if (sock->connect_cb == NULL) {
+ return;
+ }
+ sock->connect_cb(handle, result, sock->connect_cbarg);
+ update_result(sock, result);
+ if (result != ISC_R_SUCCESS) {
+ isc__nmsocket_clearcb(handle->sock);
}
}
tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_nmsocket_tls_send_req_t *send_req =
(isc_nmsocket_tls_send_req_t *)cbarg;
- isc_nmsocket_t *sock = send_req->tlssock;
+ isc_nmsocket_t *tlssock = NULL;
+ bool finish = send_req->finish;
+
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
- REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_NMSOCK(send_req->tlssock));
+
+ tlssock = send_req->tlssock;
+ send_req->tlssock = NULL;
- /* XXXWPK TODO */
- UNUSED(eresult);
+ if (send_req->cb != NULL) {
+ send_req->cb(send_req->handle, eresult, send_req->cbarg);
+ isc_nmhandle_detach(&send_req->handle);
+ }
isc_mem_put(handle->sock->mgr->mctx, send_req->data.base,
send_req->data.length);
isc_mem_put(handle->sock->mgr->mctx, send_req, sizeof(*send_req));
+ tlssock->tlsstream.nsending--;
- sock->tlsstream.nsending--;
- async_tls_do_bio(sock);
- isc__nmsocket_detach(&sock);
+ if (finish && eresult == ISC_R_SUCCESS) {
+ isc_nm_cancelread(handle);
+ } else if (eresult == ISC_R_SUCCESS) {
+ tls_do_bio(tlssock, NULL, false);
+ }
+
+ isc__nmsocket_detach(&tlssock);
}
static void
tls_failed_read_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
- const isc_result_t result, const bool close) {
+ const isc_result_t result) {
REQUIRE(VALID_NMSOCK(sock));
if (!sock->tlsstream.server &&
{
INSIST(handle == NULL);
handle = isc__nmhandle_get(sock, NULL, NULL);
- sock->connect_cb(handle, result, sock->connect_cbarg);
- update_result(sock, result);
- isc__nmsocket_clearcb(sock);
+ tls_call_connect_cb(sock, handle, result);
isc_nmhandle_detach(&handle);
} else if (sock->recv_cb != NULL) {
isc__nm_uvreq_t *req = NULL;
req->cb.recv = sock->recv_cb;
req->cbarg = sock->recv_cbarg;
req->handle = NULL;
- if (handle) {
+ if (handle != NULL) {
REQUIRE(VALID_NMHANDLE(handle));
isc_nmhandle_attach(handle, &req->handle);
} else {
isc__nmsocket_clearcb(sock);
isc__nm_readcb(sock, req, result);
}
- sock->tlsstream.state = TLS_ERROR;
- if (close) {
- isc__nmsocket_prep_destroy(sock);
- }
+ isc__nmsocket_prep_destroy(sock);
+ isc__nmsocket_detach(&sock);
}
static void
(isc__netievent_t *)ievent);
}
+static int
+tls_send_outgoing(isc_nmsocket_t *sock, bool finish, isc_nmhandle_t *tlshandle,
+ isc_nm_cb_t cb, void *cbarg) {
+ isc_nmsocket_tls_send_req_t *send_req = NULL;
+ int pending;
+ int rv;
+
+ if (inactive(sock)) {
+ if (cb != NULL) {
+ INSIST(VALID_NMHANDLE(tlshandle));
+ cb(tlshandle, ISC_R_CANCELED, cbarg);
+ }
+ return (0);
+ }
+
+ if (finish && (SSL_get_shutdown(sock->tlsstream.tls) &
+ SSL_SENT_SHUTDOWN) != SSL_SENT_SHUTDOWN)
+ {
+ (void)SSL_shutdown(sock->tlsstream.tls);
+ }
+
+ pending = BIO_pending(sock->tlsstream.app_bio);
+ if (pending <= 0) {
+ return (pending);
+ }
+
+ /* TODO Should we keep track of these requests in a list? */
+ if (pending > TLS_BUF_SIZE) {
+ pending = TLS_BUF_SIZE;
+ }
+
+ send_req = isc_mem_get(sock->mgr->mctx, sizeof(*send_req));
+ *send_req = (isc_nmsocket_tls_send_req_t){
+ .finish = finish,
+ .data.base = isc_mem_get(sock->mgr->mctx, pending),
+ .data.length = pending
+ };
+
+ isc__nmsocket_attach(sock, &send_req->tlssock);
+ if (cb != NULL) {
+ send_req->cb = cb;
+ send_req->cbarg = cbarg;
+ isc_nmhandle_attach(tlshandle, &send_req->handle);
+ }
+
+ rv = BIO_read(sock->tlsstream.app_bio, send_req->data.base, pending);
+ /* There's something pending, read must succeed */
+ RUNTIME_CHECK(rv == pending);
+
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+
+ sock->tlsstream.nsending++;
+ isc_nm_send(sock->outerhandle, &send_req->data, tls_senddone, send_req);
+
+ return (pending);
+}
+
static void
-tls_do_bio(isc_nmsocket_t *sock) {
+tls_do_bio(isc_nmsocket_t *sock, isc__nm_uvreq_t *send_data, bool finish) {
isc_result_t result = ISC_R_SUCCESS;
int pending, tls_err = 0;
int rv;
- isc__nm_uvreq_t *req;
+ char buf[1];
+ bool sent_shutdown, received_shutdown;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_nm_tid());
}
if (sock->tlsstream.state == TLS_INIT) {
- (void)SSL_do_handshake(sock->tlsstream.ssl);
+ (void)SSL_do_handshake(sock->tlsstream.tls);
sock->tlsstream.state = TLS_HANDSHAKE;
- } else if (sock->tlsstream.state == TLS_ERROR) {
- result = ISC_R_FAILURE;
- goto low_level_error;
} else if (sock->tlsstream.state == TLS_CLOSED) {
return;
}
+ received_shutdown = (SSL_get_shutdown(sock->tlsstream.tls) &
+ SSL_RECEIVED_SHUTDOWN) == SSL_RECEIVED_SHUTDOWN;
+
/* Data from TLS to client */
- char buf[1];
- if (sock->tlsstream.state == TLS_IO && sock->recv_cb != NULL &&
+ if (sock->tlsstream.state >= TLS_IO && sock->recv_cb != NULL &&
!atomic_load(&sock->readpaused))
{
- (void)SSL_peek(sock->tlsstream.ssl, buf, 1);
- while ((pending = SSL_pending(sock->tlsstream.ssl)) > 0) {
+ (void)SSL_peek(sock->tlsstream.tls, buf, 1);
+ while ((pending = SSL_pending(sock->tlsstream.tls)) > 0) {
+ uint8_t recv_buf[TLS_BUF_SIZE];
+ isc_region_t region, dregion;
+
if (pending > TLS_BUF_SIZE) {
pending = TLS_BUF_SIZE;
}
- isc_region_t region = {
- isc_mem_get(sock->mgr->mctx, pending), pending
- };
- isc_region_t dregion;
- memset(region.base, 0, region.length);
- rv = SSL_read(sock->tlsstream.ssl, region.base,
+ region = (isc_region_t){ .base = &recv_buf[0],
+ .length = pending };
+
+ rv = SSL_read(sock->tlsstream.tls, region.base,
region.length);
/* Pending succeded, so should read */
RUNTIME_CHECK(rv == pending);
+
dregion = (isc_region_t){ region.base, rv };
sock->recv_cb(sock->statichandle, ISC_R_SUCCESS,
&dregion, sock->recv_cbarg);
- isc_mem_put(sock->mgr->mctx, region.base,
- region.length);
}
}
+ if (send_data != NULL) {
+ INSIST(sock->tlsstream.state > TLS_HANDSHAKE);
+ rv = SSL_write(sock->tlsstream.tls, send_data->uvbuf.base,
+ send_data->uvbuf.len);
+ if (rv != (int)send_data->uvbuf.len) {
+ result = received_shutdown ? ISC_R_CANCELED
+ : ISC_R_TLSERROR;
+ send_data->cb.send(send_data->handle, result,
+ send_data->cbarg);
+ send_data = NULL;
+ if (!received_shutdown) {
+ isc__nmsocket_detach(&sock);
+ return;
+ }
+ }
+ }
+
+ sent_shutdown = (SSL_get_shutdown(sock->tlsstream.tls) &
+ SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN;
+
/* Peek to move the session forward */
- (void)SSL_peek(sock->tlsstream.ssl, buf, 1);
+ (void)SSL_peek(sock->tlsstream.tls, buf, 1);
/* Data from TLS to network */
- pending = BIO_pending(sock->tlsstream.app_bio);
- if (pending > 0) {
- /*TODO Should we keep the track of these requests in a list? */
- isc_nmsocket_tls_send_req_t *send_req = NULL;
- if (pending > TLS_BUF_SIZE) {
- pending = TLS_BUF_SIZE;
+ if (send_data != NULL) {
+ pending = tls_send_outgoing(sock, finish, send_data->handle,
+ send_data->cb.send,
+ send_data->cbarg);
+ } else {
+ if (received_shutdown && !sent_shutdown) {
+ finish = true;
+ (void)SSL_shutdown(sock->tlsstream.tls);
}
- send_req = isc_mem_get(sock->mgr->mctx, sizeof(*send_req));
- send_req->data.base = isc_mem_get(sock->mgr->mctx, pending);
- send_req->data.length = pending;
- send_req->tlssock = NULL;
- isc__nmsocket_attach(sock, &send_req->tlssock);
- rv = BIO_read(sock->tlsstream.app_bio, send_req->data.base,
- pending);
- /* There's something pending, read must succeed */
- RUNTIME_CHECK(rv == pending);
- INSIST(VALID_NMHANDLE(sock->outerhandle));
- isc_nm_send(sock->outerhandle, &send_req->data, tls_senddone,
- send_req);
+ pending = tls_send_outgoing(sock, finish, NULL, NULL, NULL);
+ }
+
+ if (pending > 0) {
/* We'll continue in tls_senddone */
return;
}
/* Get the potential error code */
- rv = SSL_peek(sock->tlsstream.ssl, buf, 1);
-
+ rv = SSL_peek(sock->tlsstream.tls, buf, 1);
if (rv < 0) {
- tls_err = SSL_get_error(sock->tlsstream.ssl, rv);
+ tls_err = SSL_get_error(sock->tlsstream.tls, rv);
}
/* Only after doing the IO we can check if SSL handshake is done */
if (sock->tlsstream.state == TLS_HANDSHAKE &&
- SSL_is_init_finished(sock->tlsstream.ssl) == 1)
+ SSL_is_init_finished(sock->tlsstream.tls) == 1)
{
isc_nmhandle_t *tlshandle = isc__nmhandle_get(sock, NULL, NULL);
if (sock->tlsstream.server) {
ISC_R_SUCCESS,
sock->listener->accept_cbarg);
} else {
- sock->connect_cb(tlshandle, ISC_R_SUCCESS,
- sock->connect_cbarg);
- update_result(tlshandle->sock, ISC_R_SUCCESS);
+ tls_call_connect_cb(sock, tlshandle, ISC_R_SUCCESS);
}
isc_nmhandle_detach(&tlshandle);
sock->tlsstream.state = TLS_IO;
}
switch (tls_err) {
- case 0:
+ case SSL_ERROR_NONE:
+ if (sent_shutdown && received_shutdown) {
+ /* clean shutdown */
+ isc_nm_cancelread(sock->outerhandle);
+ isc__nm_tls_close(sock);
+ };
return;
+ break;
case SSL_ERROR_WANT_WRITE:
if (sock->tlsstream.nsending == 0) {
/*
* already the send callback will call it.
*/
async_tls_do_bio(sock);
+ return;
} else {
return;
}
break;
case SSL_ERROR_WANT_READ:
- INSIST(VALID_NMHANDLE(sock->outerhandle));
- isc_nm_resumeread(sock->outerhandle);
+ if (sock->outerhandle != NULL) {
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+ isc_nm_resumeread(sock->outerhandle);
+ }
+ return;
break;
default:
result = tls_error_to_result(tls_err);
goto error;
}
- while ((req = ISC_LIST_HEAD(sock->tlsstream.sends)) != NULL) {
- INSIST(VALID_UVREQ(req));
- rv = SSL_write(sock->tlsstream.ssl, req->uvbuf.base,
- req->uvbuf.len);
- if (rv < 0) {
- if (sock->tlsstream.nsending == 0) {
- async_tls_do_bio(sock);
- }
- return;
- }
- if (rv != (int)req->uvbuf.len) {
- if (!sock->tlsstream.server &&
- (sock->tlsstream.state == TLS_HANDSHAKE ||
- TLS_INIT))
- {
- isc_nmhandle_t *tlshandle =
- isc__nmhandle_get(sock, NULL, NULL);
- sock->connect_cb(tlshandle, result,
- sock->connect_cbarg);
- update_result(tlshandle->sock, result);
- isc_nmhandle_detach(&tlshandle);
- }
- sock->tlsstream.state = TLS_ERROR;
- async_tls_do_bio(sock);
- return;
- }
- ISC_LIST_UNLINK(sock->tlsstream.sends, req, link);
- req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
- isc__nm_uvreq_put(&req, sock);
- }
-
return;
error:
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
ISC_LOG_ERROR, "SSL error in BIO: %d %s", tls_err,
isc_result_totext(result));
-low_level_error:
- if (sock->tlsstream.state == TLS_HANDSHAKE) {
- isc_nmhandle_t *tlshandle = isc__nmhandle_get(sock, NULL, NULL);
- if (!sock->tlsstream.server) {
- sock->connect_cb(tlshandle, result,
- sock->connect_cbarg);
- update_result(tlshandle->sock, result);
- }
- isc_nmhandle_detach(&tlshandle);
- } else if (sock->tlsstream.state == TLS_IO) {
- if (ISC_LIST_HEAD(sock->tlsstream.sends) != NULL) {
- while ((req = ISC_LIST_HEAD(sock->tlsstream.sends)) !=
- NULL) {
- req->cb.send(sock->statichandle, result,
- req->cbarg);
- ISC_LIST_UNLINK(sock->tlsstream.sends, req,
- link);
- isc__nm_uvreq_put(&req, sock);
- }
- } else if (sock->recv_cb != NULL) {
- tls_failed_read_cb(sock, sock->statichandle, result,
- false);
- } else {
- tls_close_direct(sock);
- }
- }
- sock->tlsstream.state = TLS_ERROR;
+ tls_failed_read_cb(sock, sock->statichandle, result);
}
static void
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(tlssock->tid == isc_nm_tid());
if (result != ISC_R_SUCCESS) {
- tls_failed_read_cb(tlssock, tlssock->statichandle, result,
- true);
+ tls_failed_read_cb(tlssock, tlssock->statichandle, result);
return;
}
rv = BIO_write(tlssock->tlsstream.app_bio, region->base,
region->length);
-
if (rv != (int)region->length) {
/* XXXWPK log it? */
- tlssock->tlsstream.state = TLS_ERROR;
+ tls_failed_read_cb(tlssock, tlssock->statichandle,
+ ISC_R_TLSERROR);
+ return;
}
- tls_do_bio(tlssock);
+ tls_do_bio(tlssock, NULL, false);
}
static isc_result_t
if (BIO_new_bio_pair(&(sock->tlsstream.ssl_bio), TLS_BUF_SIZE,
&(sock->tlsstream.app_bio), TLS_BUF_SIZE) != 1)
{
- SSL_free(sock->tlsstream.ssl);
+ isc_tls_free(&sock->tlsstream.tls);
return (ISC_R_TLSERROR);
}
- SSL_set_bio(sock->tlsstream.ssl, sock->tlsstream.ssl_bio,
+ SSL_set_bio(sock->tlsstream.tls, sock->tlsstream.ssl_bio,
sock->tlsstream.ssl_bio);
if (server) {
- SSL_set_accept_state(sock->tlsstream.ssl);
+ SSL_set_accept_state(sock->tlsstream.tls);
} else {
- SSL_set_connect_state(sock->tlsstream.ssl);
+ SSL_set_connect_state(sock->tlsstream.tls);
}
sock->tlsstream.nsending = 0;
isc_nm_read(sock->outerhandle, tls_readcb, sock);
- tls_do_bio(sock);
+ tls_do_bio(sock, NULL, false);
return (ISC_R_SUCCESS);
}
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;
- int r;
/* If accept() was unsuccessful we can't do anything */
if (result != ISC_R_SUCCESS) {
*/
tlssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*tlssock));
isc__nmsocket_init(tlssock, handle->sock->mgr, isc_nm_tlssocket,
- handle->sock->iface);
+ &tlslistensock->tlsstream.server_iface);
/* We need to initialize SSL now to reference SSL_CTX properly */
tlssock->tlsstream.ctx = tlslistensock->tlsstream.ctx;
- tlssock->tlsstream.ssl = SSL_new(tlssock->tlsstream.ctx);
- ISC_LIST_INIT(tlssock->tlsstream.sends);
- if (tlssock->tlsstream.ssl == NULL) {
- update_result(tlssock, ISC_R_TLSERROR);
+ tlssock->tlsstream.tls = isc_tls_create(tlssock->tlsstream.ctx);
+ if (tlssock->tlsstream.tls == NULL) {
atomic_store(&tlssock->closed, true);
isc__nmsocket_detach(&tlssock);
return (ISC_R_TLSERROR);
tlssock->tlsstream.server = true;
tlssock->tlsstream.state = TLS_INIT;
- r = uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
- &tlssock->timer);
- RUNTIME_CHECK(r == 0);
-
- tlssock->timer.data = tlssock;
- tlssock->timer_initialized = true;
tlssock->tlsstream.ctx = tlslistensock->tlsstream.ctx;
result = initialize_tls(tlssock, true);
REQUIRE(VALID_NM(mgr));
isc__nmsocket_init(tlssock, mgr, isc_nm_tlslistener, iface);
+ tlssock->tlsstream.server_iface = *iface;
+ ISC_LINK_INIT(&tlssock->tlsstream.server_iface.addr, link);
+ tlssock->iface = &tlssock->tlsstream.server_iface;
tlssock->result = ISC_R_DEFAULT;
tlssock->accept_cb = accept_cb;
tlssock->accept_cbarg = accept_cbarg;
tlssock->extrahandlesize = extrahandlesize;
tlssock->tlsstream.ctx = sslctx;
- tlssock->tlsstream.ssl = NULL;
+ tlssock->tlsstream.tls = NULL;
/*
* tlssock will be a TLS 'wrapper' around an unencrypted stream.
void
isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0) {
- int rv;
isc__netievent_tlssend_t *ievent = (isc__netievent_tlssend_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
isc__nm_uvreq_t *req = ievent->req;
- ievent->req = NULL;
+
REQUIRE(VALID_UVREQ(req));
REQUIRE(sock->tid == isc_nm_tid());
+
UNUSED(worker);
+ ievent->req = NULL;
+
if (inactive(sock)) {
req->cb.send(req->handle, ISC_R_CANCELED, req->cbarg);
isc__nm_uvreq_put(&req, sock);
return;
}
- if (!ISC_LIST_EMPTY(sock->tlsstream.sends)) {
- /* We're not the first */
- ISC_LIST_APPEND(sock->tlsstream.sends, req, link);
- tls_do_bio(sock);
- return;
- }
- rv = SSL_write(sock->tlsstream.ssl, req->uvbuf.base, req->uvbuf.len);
- if (rv < 0) {
- /*
- * We might need to read, we might need to write, or the
- * TLS socket might be dead - in any case, we need to
- * enqueue the uvreq and let the TLS BIO layer do the rest.
- */
- ISC_LIST_APPEND(sock->tlsstream.sends, req, link);
- tls_do_bio(sock);
- return;
- }
- if (rv != (int)req->uvbuf.len) {
- sock->tlsstream.state = TLS_ERROR;
- async_tls_do_bio(sock);
- return;
- }
- req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
+ tls_do_bio(sock, req, false);
isc__nm_uvreq_put(&req, sock);
- tls_do_bio(sock);
return;
}
isc_nmsocket_t *sock = ievent->sock;
REQUIRE(sock->tid == isc_nm_tid());
+
UNUSED(worker);
- tls_do_bio(sock);
+ tls_do_bio(sock, NULL, false);
}
void
isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ isc__netievent_tlsstartread_t *ievent = NULL;
+ isc_nmsocket_t *sock = NULL;
+
REQUIRE(VALID_NMHANDLE(handle));
- REQUIRE(VALID_NMSOCK(handle->sock));
- REQUIRE(handle->sock->statichandle == handle);
- REQUIRE(handle->sock->tid == isc_nm_tid());
- isc__netievent_tlsstartread_t *ievent = NULL;
- isc_nmsocket_t *sock = handle->sock;
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->statichandle == handle);
+ REQUIRE(sock->tid == isc_nm_tid());
if (inactive(sock)) {
cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg);
isc__nm_tls_pauseread(isc_nmhandle_t *handle) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
- isc_nmsocket_t *sock = handle->sock;
- atomic_store(&sock->readpaused, true);
+ atomic_store(&handle->sock->readpaused, true);
+ if (handle->sock->outerhandle != NULL) {
+ isc_nm_pauseread(handle->sock->outerhandle);
+ }
}
void
isc__nm_tls_resumeread(isc_nmhandle_t *handle) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
- isc_nmsocket_t *sock = handle->sock;
-
- atomic_store(&sock->readpaused, false);
- async_tls_do_bio(sock);
-}
-static void
-timer_close_cb(uv_handle_t *handle) {
- isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
- tls_close_direct(sock);
+ atomic_store(&handle->sock->readpaused, false);
+ async_tls_do_bio(handle->sock);
}
static void
tls_close_direct(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_nm_tid());
-
- /* if (!sock->tlsstream.server) { */
- /* INSIST(sock->tlsstream.state != TLS_HANDSHAKE && */
- /* sock->tlsstream.state != TLS_INIT); */
- /* } */
-
- sock->tlsstream.state = TLS_CLOSING;
-
- if (sock->timer_running) {
- uv_timer_stop(&sock->timer);
- sock->timer_running = false;
+ /*
+ * 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);
}
- /* 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);
- }
- if (sock->tlsstream.ssl != NULL) {
- SSL_free(sock->tlsstream.ssl);
- sock->tlsstream.ssl = NULL;
- /* These are destroyed when we free SSL* */
- sock->tlsstream.ctx = NULL;
- sock->tlsstream.ssl_bio = NULL;
- }
- if (sock->tlsstream.app_bio != NULL) {
- BIO_free(sock->tlsstream.app_bio);
- sock->tlsstream.app_bio = NULL;
- }
- sock->tlsstream.state = TLS_CLOSED;
- atomic_store(&sock->closed, true);
- isc__nmsocket_detach(&sock);
+ if (sock->listener != NULL) {
+ isc__nmsocket_detach(&sock->listener);
}
+
+ /* further cleanup performed in isc__nm_tls_cleanup_data() */
+ atomic_store(&sock->active, false);
+ atomic_store(&sock->closed, true);
+ sock->tlsstream.state = TLS_CLOSED;
}
void
isc__nm_tls_close(isc_nmsocket_t *sock) {
+ isc__netievent_tlsclose_t *ievent = NULL;
+
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlssocket);
return;
}
- if (sock->tid == isc_nm_tid()) {
- tls_close_direct(sock);
- } else {
- isc__netievent_tlsclose_t *ievent =
- isc__nm_get_netievent_tlsclose(sock->mgr, sock);
- isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
- (isc__netievent_t *)ievent);
- }
+ ievent = isc__nm_get_netievent_tlsclose(sock->mgr, sock);
+ isc__nm_maybe_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;
+ isc_nmsocket_t *sock = ievent->sock;
REQUIRE(ievent->sock->tid == isc_nm_tid());
+
UNUSED(worker);
- tls_close_direct(ievent->sock);
+ tls_close_direct(sock);
}
void
atomic_store(&sock->closed, true);
sock->recv_cb = NULL;
sock->recv_cbarg = NULL;
- if (sock->tlsstream.ssl != NULL) {
- SSL_free(sock->tlsstream.ssl);
- sock->tlsstream.ssl = NULL;
+ if (sock->tlsstream.tls != NULL) {
+ isc_tls_free(&sock->tlsstream.tls);
sock->tlsstream.ctx = NULL;
}
nsock = isc_mem_get(mgr->mctx, sizeof(*nsock));
isc__nmsocket_init(nsock, mgr, isc_nm_tlssocket, local);
+ nsock->tlsstream.local_iface = *local;
+ ISC_LINK_INIT(&nsock->tlsstream.local_iface.addr, link);
+ nsock->iface = &nsock->tlsstream.local_iface;
nsock->extrahandlesize = extrahandlesize;
nsock->result = ISC_R_DEFAULT;
nsock->connect_cb = cb;
}
static void
-tls_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+tcp_connected(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
+ isc_nmhandle_t *tlshandle = NULL;
REQUIRE(VALID_NMSOCK(tlssock));
+ REQUIRE(VALID_NMHANDLE(handle));
if (result != ISC_R_SUCCESS) {
- tlssock->connect_cb(handle, result, tlssock->connect_cbarg);
- update_result(tlssock, result);
- tls_close_direct(tlssock);
- return;
+ goto error;
}
- INSIST(VALID_NMHANDLE(handle));
-
tlssock->peer = isc_nmhandle_peeraddr(handle);
isc_nmhandle_attach(handle, &tlssock->outerhandle);
result = initialize_tls(tlssock, false);
if (result != ISC_R_SUCCESS) {
- tlssock->connect_cb(handle, result, tlssock->connect_cbarg);
- update_result(tlssock, result);
- tls_close_direct(tlssock);
- return;
+ goto error;
}
+
+ return;
+error:
+ tlshandle = isc__nmhandle_get(tlssock, NULL, NULL);
+ atomic_store(&tlssock->closed, true);
+ tls_call_connect_cb(tlssock, tlshandle, result);
+ isc_nmhandle_detach(&tlshandle);
+ isc__nmsocket_detach(&tlssock);
}
void
(isc__netievent_tlsconnect_t *)ev0;
isc_nmsocket_t *tlssock = ievent->sock;
isc_result_t result;
- int r;
isc_nmhandle_t *tlshandle = NULL;
UNUSED(worker);
/*
* We need to initialize SSL now to reference SSL_CTX properly.
*/
- tlssock->tlsstream.ssl = SSL_new(tlssock->tlsstream.ctx);
- if (tlssock->tlsstream.ssl == NULL) {
+ tlssock->tlsstream.tls = isc_tls_create(tlssock->tlsstream.ctx);
+ if (tlssock->tlsstream.tls == NULL) {
result = ISC_R_TLSERROR;
+ update_result(tlssock, result);
goto error;
}
tlssock->tid = isc_nm_tid();
- r = uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
- &tlssock->timer);
- RUNTIME_CHECK(r == 0);
-
- tlssock->timer.data = tlssock;
- tlssock->timer_initialized = true;
tlssock->tlsstream.state = TLS_INIT;
- result = isc_nm_tcpconnect(worker->mgr, (isc_nmiface_t *)&ievent->local,
- (isc_nmiface_t *)&ievent->peer,
- tls_connect_cb, tlssock,
- tlssock->connect_timeout, 0);
- if (result != ISC_R_SUCCESS) {
- goto error;
- }
+ (void)isc_nm_tcpconnect(worker->mgr, (isc_nmiface_t *)&ievent->local,
+ (isc_nmiface_t *)&ievent->peer, tcp_connected,
+ tlssock, tlssock->connect_timeout, 0);
return;
+
error:
tlshandle = isc__nmhandle_get(tlssock, NULL, NULL);
atomic_store(&tlssock->closed, true);
- tlssock->connect_cb(tlshandle, result, tlssock->connect_cbarg);
+ tls_call_connect_cb(tlssock, tlshandle, result);
isc_nmhandle_detach(&tlshandle);
- update_result(tlssock, result);
- tls_close_direct(tlssock);
+ isc__nmsocket_detach(&tlssock);
+}
+
+static void
+tls_cancelread(isc_nmsocket_t *sock) {
+ if (!inactive(sock) && sock->tlsstream.state == TLS_IO) {
+ tls_do_bio(sock, NULL, true);
+ } else if (sock->outerhandle != NULL) {
+ isc_nm_cancelread(sock->outerhandle);
+ }
}
void
REQUIRE(sock->type == isc_nm_tlssocket);
- ievent = isc__nm_get_netievent_tlscancel(sock->mgr, sock, handle);
- isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
- (isc__netievent_t *)ievent);
+ if (sock->tid == isc_nm_tid()) {
+ tls_cancelread(sock);
+ } else {
+ ievent = isc__nm_get_netievent_tlscancel(sock->mgr, sock,
+ handle);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
}
void
isc__nm_async_tlscancel(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlscancel_t *ievent = (isc__netievent_tlscancel_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
- isc_nmhandle_t *handle = ievent->handle;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(worker->id == sock->tid);
REQUIRE(sock->tid == isc_nm_tid());
- UNUSED(worker);
-
- tls_failed_read_cb(sock, handle, ISC_R_EOF, false);
- if (sock->outerhandle) {
- isc__nm_tcp_cancelread(sock->outerhandle);
- }
+ UNUSED(worker);
+ tls_cancelread(sock);
}
void
isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0) {
- UNUSED(worker);
isc__netievent_tlsdobio_t *ievent = (isc__netievent_tlsdobio_t *)ev0;
- tls_do_bio(ievent->sock);
+
+ UNUSED(worker);
+
+ tls_do_bio(ievent->sock, NULL, false);
}
void
isc__nm_tls_cleanup_data(isc_nmsocket_t *sock) {
- if (sock->tlsstream.tlslistener) {
+ if (sock->type == isc_nm_tcplistener &&
+ sock->tlsstream.tlslistener != NULL) {
REQUIRE(VALID_NMSOCK(sock->tlsstream.tlslistener));
isc__nmsocket_detach(&sock->tlsstream.tlslistener);
+ } else if (sock->type == isc_nm_tlssocket) {
+ if (sock->tlsstream.tls != NULL) {
+ isc_tls_free(&sock->tlsstream.tls);
+ /* These are destroyed when we free SSL */
+ sock->tlsstream.ctx = NULL;
+ sock->tlsstream.ssl_bio = NULL;
+ }
+ if (sock->tlsstream.app_bio != NULL) {
+ BIO_free(sock->tlsstream.app_bio);
+ sock->tlsstream.app_bio = NULL;
+ }
+ }
+}
+
+void
+isc__nm_tls_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_tlssocket);
+
+ sock = handle->sock;
+ if (sock->outerhandle != NULL) {
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+ isc_nmhandle_settimeout(sock->outerhandle, timeout);
}
}
"default", /*%< 68 */
"IPv4 prefix", /*%< 69 */
"TLS error", /*%< 70 */
+ "ALPN for HTTP/2 failed" /*%< 71 */
};
-static const char *identifier[ISC_R_NRESULTS] = {
- "ISC_R_SUCCESS",
- "ISC_R_NOMEMORY",
- "ISC_R_TIMEDOUT",
- "ISC_R_NOTHREADS",
- "ISC_R_ADDRNOTAVAIL",
- "ISC_R_ADDRINUSE",
- "ISC_R_NOPERM",
- "ISC_R_NOCONN",
- "ISC_R_NETUNREACH",
- "ISC_R_HOSTUNREACH",
- "ISC_R_NETDOWN",
- "ISC_R_HOSTDOWN",
- "ISC_R_CONNREFUSED",
- "ISC_R_NORESOURCES",
- "ISC_R_EOF",
- "ISC_R_BOUND",
- "ISC_R_RELOAD",
- "ISC_R_LOCKBUSY",
- "ISC_R_EXISTS",
- "ISC_R_NOSPACE",
- "ISC_R_CANCELED",
- "ISC_R_NOTBOUND",
- "ISC_R_SHUTTINGDOWN",
- "ISC_R_NOTFOUND",
- "ISC_R_UNEXPECTEDEND",
- "ISC_R_FAILURE",
- "ISC_R_IOERROR",
- "ISC_R_NOTIMPLEMENTED",
- "ISC_R_UNBALANCED",
- "ISC_R_NOMORE",
- "ISC_R_INVALIDFILE",
- "ISC_R_BADBASE64",
- "ISC_R_UNEXPECTEDTOKEN",
- "ISC_R_QUOTA",
- "ISC_R_UNEXPECTED",
- "ISC_R_ALREADYRUNNING",
- "ISC_R_IGNORE",
- "ISC_R_MASKNONCONTIG",
- "ISC_R_FILENOTFOUND",
- "ISC_R_FILEEXISTS",
- "ISC_R_NOTCONNECTED",
- "ISC_R_RANGE",
- "ISC_R_NOENTROPY",
- "ISC_R_MULTICAST",
- "ISC_R_NOTFILE",
- "ISC_R_NOTDIRECTORY",
- "ISC_R_QUEUEFULL",
- "ISC_R_FAMILYMISMATCH",
- "ISC_R_FAMILYNOSUPPORT",
- "ISC_R_BADHEX",
- "ISC_R_TOOMANYOPENFILES",
- "ISC_R_NOTBLOCKING",
- "ISC_R_UNBALANCEDQUOTES",
- "ISC_R_INPROGRESS",
- "ISC_R_CONNECTIONRESET",
- "ISC_R_SOFTQUOTA",
- "ISC_R_BADNUMBER",
- "ISC_R_DISABLED",
- "ISC_R_MAXSIZE",
- "ISC_R_BADADDRESSFORM",
- "ISC_R_BADBASE32",
- "ISC_R_UNSET",
- "ISC_R_MULTIPLE",
- "ISC_R_WOULDBLOCK",
- "ISC_R_COMPLETE",
- "ISC_R_CRYPTOFAILURE",
- "ISC_R_DISCQUOTA",
- "ISC_R_DISCFULL",
- "ISC_R_DEFAULT",
- "ISC_R_IPV4PREFIX",
- "ISC_R_TLSERROR",
-};
+static const char *identifier[ISC_R_NRESULTS] = { "ISC_R_SUCCESS",
+ "ISC_R_NOMEMORY",
+ "ISC_R_TIMEDOUT",
+ "ISC_R_NOTHREADS",
+ "ISC_R_ADDRNOTAVAIL",
+ "ISC_R_ADDRINUSE",
+ "ISC_R_NOPERM",
+ "ISC_R_NOCONN",
+ "ISC_R_NETUNREACH",
+ "ISC_R_HOSTUNREACH",
+ "ISC_R_NETDOWN",
+ "ISC_R_HOSTDOWN",
+ "ISC_R_CONNREFUSED",
+ "ISC_R_NORESOURCES",
+ "ISC_R_EOF",
+ "ISC_R_BOUND",
+ "ISC_R_RELOAD",
+ "ISC_R_LOCKBUSY",
+ "ISC_R_EXISTS",
+ "ISC_R_NOSPACE",
+ "ISC_R_CANCELED",
+ "ISC_R_NOTBOUND",
+ "ISC_R_SHUTTINGDOWN",
+ "ISC_R_NOTFOUND",
+ "ISC_R_UNEXPECTEDEND",
+ "ISC_R_FAILURE",
+ "ISC_R_IOERROR",
+ "ISC_R_NOTIMPLEMENTED",
+ "ISC_R_UNBALANCED",
+ "ISC_R_NOMORE",
+ "ISC_R_INVALIDFILE",
+ "ISC_R_BADBASE64",
+ "ISC_R_UNEXPECTEDTOKEN",
+ "ISC_R_QUOTA",
+ "ISC_R_UNEXPECTED",
+ "ISC_R_ALREADYRUNNING",
+ "ISC_R_IGNORE",
+ "ISC_R_MASKNONCONTIG",
+ "ISC_R_FILENOTFOUND",
+ "ISC_R_FILEEXISTS",
+ "ISC_R_NOTCONNECTED",
+ "ISC_R_RANGE",
+ "ISC_R_NOENTROPY",
+ "ISC_R_MULTICAST",
+ "ISC_R_NOTFILE",
+ "ISC_R_NOTDIRECTORY",
+ "ISC_R_QUEUEFULL",
+ "ISC_R_FAMILYMISMATCH",
+ "ISC_R_FAMILYNOSUPPORT",
+ "ISC_R_BADHEX",
+ "ISC_R_TOOMANYOPENFILES",
+ "ISC_R_NOTBLOCKING",
+ "ISC_R_UNBALANCEDQUOTES",
+ "ISC_R_INPROGRESS",
+ "ISC_R_CONNECTIONRESET",
+ "ISC_R_SOFTQUOTA",
+ "ISC_R_BADNUMBER",
+ "ISC_R_DISABLED",
+ "ISC_R_MAXSIZE",
+ "ISC_R_BADADDRESSFORM",
+ "ISC_R_BADBASE32",
+ "ISC_R_UNSET",
+ "ISC_R_MULTIPLE",
+ "ISC_R_WOULDBLOCK",
+ "ISC_R_COMPLETE",
+ "ISC_R_CRYPTOFAILURE",
+ "ISC_R_DISCQUOTA",
+ "ISC_R_DISCFULL",
+ "ISC_R_DEFAULT",
+ "ISC_R_IPV4PREFIX",
+ "ISC_R_TLSERROR",
+ "ISC_R_HTTP2ALPNERROR" };
#define ISC_RESULT_RESULTSET 2
#define ISC_RESULT_UNAVAILABLESET 3
static atomic_bool POST = ATOMIC_VAR_INIT(true);
static atomic_bool use_TLS = ATOMIC_VAR_INIT(false);
-static SSL_CTX *server_ssl_ctx = NULL;
+static isc_tlsctx_t *server_tlsctx = NULL;
+static isc_tlsctx_t *client_tlsctx = NULL;
#define NSENDS 100
#define NWRITES 10
#define X(v)
#endif
+typedef struct csdata {
+ isc_nm_recv_cb_t reply_cb;
+ void *cb_arg;
+ isc_region_t region;
+} csdata_t;
+
+static void
+connect_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ csdata_t data;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ memmove(&data, arg, sizeof(data));
+ isc_mem_put(handle->sock->mgr->mctx, arg, sizeof(data));
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ result = isc__nm_http_request(handle, &data.region, data.reply_cb,
+ data.cb_arg);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ isc_mem_put(handle->sock->mgr->mctx, data.region.base,
+ data.region.length);
+ return;
+error:
+ data.reply_cb(handle, result, NULL, data.cb_arg);
+ isc_mem_put(handle->sock->mgr->mctx, data.region.base,
+ data.region.length);
+}
+
+static isc_result_t
+connect_send_request(isc_nm_t *mgr, const char *uri, bool post,
+ isc_region_t *region, isc_nm_recv_cb_t cb, void *cbarg,
+ bool tls, unsigned int timeout) {
+ isc_result_t result;
+ isc_region_t copy;
+ csdata_t *data = NULL;
+ isc_tlsctx_t *ctx = NULL;
+
+ copy = (isc_region_t){ .base = isc_mem_get(mgr->mctx, region->length),
+ .length = region->length };
+ memmove(copy.base, region->base, region->length);
+ data = isc_mem_get(mgr->mctx, sizeof(*data));
+ *data = (csdata_t){ .reply_cb = cb, .cb_arg = cbarg, .region = copy };
+ if (tls) {
+ ctx = client_tlsctx;
+ }
+
+ result = isc_nm_httpconnect(
+ mgr, NULL, (isc_nmiface_t *)&tcp_listen_addr, uri, post,
+ connect_send_cb, data, ctx, timeout, 0);
+ return (result);
+}
+
static int
setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
isc_result_t result;
_setup(void **state) {
UNUSED(state);
- /*workers = isc_os_ncpus();*/
+ workers = isc_os_ncpus();
if (isc_test_begin(NULL, false, workers) != ISC_R_SUCCESS) {
return (-1);
assert_non_null(nm[i]);
}
- server_ssl_ctx = NULL;
- isc_tlsctx_createserver(NULL, NULL, &server_ssl_ctx);
+ server_tlsctx = NULL;
+ isc_tlsctx_createserver(NULL, NULL, &server_tlsctx);
+ client_tlsctx = NULL;
+ isc_tlsctx_createclient(&client_tlsctx);
+ isc_tlsctx_enable_http2client_alpn(client_tlsctx);
*state = nm;
}
isc_mem_put(test_mctx, nm, MAX_NM * sizeof(nm[0]));
- if (server_ssl_ctx) {
- isc_tlsctx_free(&server_ssl_ctx);
+ if (server_tlsctx != NULL) {
+ isc_tlsctx_free(&server_tlsctx);
+ }
+ if (client_tlsctx != NULL) {
+ isc_tlsctx_free(&client_tlsctx);
}
return (0);
uint16_t port;
char saddr[INET6_ADDRSTRLEN] = { 0 };
int family;
+
if (sa == NULL || outbuf == NULL || outbuf_len == 0) {
return;
}
}
}
atomic_store(&was_error, true);
- /* Send failed, we need to stop reading too */
- isc_nm_cancelread(handle);
}
}
isc_nm_t *listen_nm = nm[0];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
- isc_sockaddr_t tcp_connect_addr;
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
WILL_RETURN(uv_tcp_bind, UV_EADDRINUSE);
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
- isc_sockaddr_t tcp_connect_addr;
char req_url[256];
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
result = isc_nm_listenhttp(listen_nm, (isc_nmiface_t *)&tcp_listen_addr,
0, NULL, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
- result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
- noop_read_cb, NULL, 0);
+ result = isc_nm_http_endpoint(listen_sock, DOH_PATH, noop_read_cb, NULL,
+ 0);
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
sockaddr_to_url(&tcp_listen_addr, false, req_url, sizeof(req_url),
DOH_PATH);
- (void)isc_nm_http_connect_send_request(
+ (void)connect_send_request(
connect_nm, req_url, atomic_load(&POST),
&(isc_region_t){ .base = (uint8_t *)send_msg.base,
.length = send_msg.len },
- noop_read_cb, NULL, NULL, 30000);
+ noop_read_cb, NULL, atomic_load(&use_TLS), 30000);
isc_nm_closedown(connect_nm);
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
- isc_sockaddr_t tcp_connect_addr;
char req_url[256];
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
result = isc_nm_listenhttp(listen_nm, (isc_nmiface_t *)&tcp_listen_addr,
0, NULL, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
- result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
- noop_read_cb, NULL, 0);
+ result = isc_nm_http_endpoint(listen_sock, DOH_PATH, noop_read_cb, NULL,
+ 0);
assert_int_equal(result, ISC_R_SUCCESS);
sockaddr_to_url(&tcp_listen_addr, false, req_url, sizeof(req_url),
DOH_PATH);
- (void)isc_nm_http_connect_send_request(
+ (void)connect_send_request(
connect_nm, req_url, atomic_load(&POST),
&(isc_region_t){ .base = (uint8_t *)send_msg.base,
.length = send_msg.len },
- noop_read_cb, NULL, NULL, 30000);
+ noop_read_cb, NULL, atomic_load(&use_TLS), 30000);
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
size_t i;
atomic_fetch_sub(&nsends, 1);
for (i = 0; i < NWRITES / 2; i++) {
- eresult = isc_nm_httprequest(
+ eresult = isc__nm_http_request(
handle,
&(isc_region_t){
.base = (uint8_t *)send_msg.base,
static isc_threadresult_t
doh_connect_thread(isc_threadarg_t arg) {
isc_nm_t *connect_nm = (isc_nm_t *)arg;
- isc_sockaddr_t tcp_connect_addr;
char req_url[256];
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
sockaddr_to_url(&tcp_listen_addr, atomic_load(&use_TLS), req_url,
sizeof(req_url), DOH_PATH);
while (atomic_load(&nsends) > 0) {
- (void)isc_nm_http_connect_send_request(
+ (void)connect_send_request(
connect_nm, req_url, atomic_load(&POST),
&(isc_region_t){ .base = (uint8_t *)send_msg.base,
.length = send_msg.len },
- doh_receive_send_reply_cb, NULL, NULL, 5000);
+ doh_receive_send_reply_cb, NULL, atomic_load(&use_TLS),
+ 30000);
}
return ((isc_threadresult_t)0);
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
- isc_sockaddr_t tcp_connect_addr;
char req_url[256];
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
atomic_store(&nsends, 1);
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
result = isc_nm_listenhttp(
listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
- atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ atomic_load(&use_TLS) ? server_tlsctx : NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
- result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
- doh_receive_request_cb, NULL, 0);
+ result = isc_nm_http_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
assert_int_equal(result, ISC_R_SUCCESS);
sockaddr_to_url(&tcp_listen_addr, atomic_load(&use_TLS), req_url,
sizeof(req_url), DOH_PATH);
- result = isc_nm_http_connect_send_request(
+ result = connect_send_request(
connect_nm, req_url, atomic_load(&POST),
&(isc_region_t){ .base = (uint8_t *)send_msg.base,
.length = send_msg.len },
- doh_receive_reply_cb, NULL, NULL, 5000);
+ doh_receive_reply_cb, NULL, atomic_load(&use_TLS), 30000);
assert_int_equal(result, ISC_R_SUCCESS);
goto error;
}
- result = isc_nm_httprequest(
+ result = isc__nm_http_request(
handle,
&(isc_region_t){ .base = (uint8_t *)send_msg.base,
.length = send_msg.len },
goto error;
}
- result = isc_nm_httprequest(
+ result = isc__nm_http_request(
handle,
&(isc_region_t){ .base = (uint8_t *)send_msg.base,
.length = send_msg.len },
if (result != ISC_R_SUCCESS) {
goto error;
}
-
- isc_nm_resumeread(handle);
return;
error:
atomic_store(&was_error, true);
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
- isc_sockaddr_t tcp_connect_addr;
char req_url[256];
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+ isc_tlsctx_t *ctx = NULL;
atomic_store(&nsends, 2);
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
result = isc_nm_listenhttp(
listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
- atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ atomic_load(&use_TLS) ? server_tlsctx : NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
- result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
- doh_receive_request_cb, NULL, 0);
+ result = isc_nm_http_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
assert_int_equal(result, ISC_R_SUCCESS);
sockaddr_to_url(&tcp_listen_addr, atomic_load(&use_TLS), req_url,
sizeof(req_url), DOH_PATH);
+
+ if (atomic_load(&use_TLS)) {
+ ctx = client_tlsctx;
+ }
+
result = isc_nm_httpconnect(
- connect_nm, NULL, NULL, req_url, atomic_load(&POST),
- doh_connect_send_two_requests_cb, NULL, NULL, 5000, 0);
+ connect_nm, NULL, (isc_nmiface_t *)&tcp_listen_addr, req_url,
+ atomic_load(&POST), doh_connect_send_two_requests_cb, NULL, ctx,
+ 5000, 0);
assert_int_equal(result, ISC_R_SUCCESS);
isc_nmsocket_t *listen_sock = NULL;
size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
isc_thread_t threads[32] = { 0 };
- isc_sockaddr_t tcp_connect_addr;
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
result = isc_nm_listenhttp(
listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
- atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ atomic_load(&use_TLS) ? server_tlsctx : NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
- result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
- doh_receive_request_cb, NULL, 0);
+ result = isc_nm_http_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < nthreads; i++) {
isc_nmsocket_t *listen_sock = NULL;
size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
isc_thread_t threads[32] = { 0 };
- isc_sockaddr_t tcp_connect_addr;
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
result = isc_nm_listenhttp(
listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
- atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ atomic_load(&use_TLS) ? server_tlsctx : NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
- result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
- doh_receive_request_cb, NULL, 0);
+ result = isc_nm_http_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < nthreads; i++) {
isc_nmsocket_t *listen_sock = NULL;
size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
isc_thread_t threads[32] = { 0 };
- isc_sockaddr_t tcp_connect_addr;
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
result = isc_nm_listenhttp(
listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
- atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ atomic_load(&use_TLS) ? server_tlsctx : NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
- result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
- doh_receive_request_cb, NULL, 0);
+ result = isc_nm_http_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < nthreads; i++) {
isc_nmsocket_t *listen_sock = NULL;
size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
isc_thread_t threads[32] = { 0 };
- isc_sockaddr_t tcp_connect_addr;
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
-
- tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
- isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
result = isc_nm_listenhttp(
listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
- atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ atomic_load(&use_TLS) ? server_tlsctx : NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
- result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
- doh_receive_request_cb, NULL, 0);
+ result = isc_nm_http_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < nthreads; i++) {
"NjJjaGFyYWN0ZXJsYWJlbC1tYWtlcy1iYXNlNjR1cmwtZGlzdGluY3"
"QtZnJvbS1zdGFuZGFyZC1iYXNlNjQHZXhhbXBsZQNjb20AAAEAAQ";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_true(ret);
assert_non_null(queryp);
assert_true(len > 0);
"NjJjaGFyYWN0ZXJsYWJlbC1tYWtlcy1iYXNlNjR1cmwtZGlzdGluY3"
"QtZnJvbS1zdGFuZGFyZC1iYXNlNjQHZXhhbXBsZQNjb20AAAEAAQ&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_true(ret);
assert_non_null(queryp);
assert_true(len > 0);
size_t len = 0;
char str[] = "?dns=123&dns=567";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_true(ret);
assert_non_null(queryp);
assert_true(len > 0);
size_t len = 0;
char str[] = "?name1=123&dns=567&name2=123&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_true(ret);
assert_non_null(queryp);
assert_true(len > 0);
"BE%D0%B2%D0%B5_%D0%BA%D0%BE%D0%B4%D1%83%D0%B2%D0%B0%"
"D0%BD%D0%BD%D1%8F&dns=123&veaction=edit§ion=0";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_true(ret);
assert_non_null(queryp);
assert_true(len > 0);
"BE%D0%B2%D0%B5_%D0%BA%D0%BE%D0%B4%D1%83%D0%B2%D0%B0%"
"D0%BD%D0%BD%D1%8F&veaction=edit§ion=0";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "?&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "?dns&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "?dns=&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "?dns=123&&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "?dns=123%12&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_true(ret);
assert_non_null(queryp);
assert_true(len > 0);
size_t len = 0;
char str[] = "?dns=123%ZZ&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "?dns=123%%&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "?dns=123%AZ&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_false(ret);
assert_null(queryp);
assert_true(len == 0);
size_t len = 0;
char str[] = "?dns=123%0AZ&";
- ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ ret = isc__nm_parse_httpquery(str, &queryp, &len);
assert_true(ret);
assert_non_null(queryp);
assert_true(len > 0);
}
}
-/*
-static char wikipedia_org_A[] = { 0xae, 0x35, 0x01, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x77,
- 0x69, 0x6b, 0x69, 0x70, 0x65, 0x64, 0x69,
- 0x61, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00,
- 0x01, 0x00, 0x01 };
-
-static void
-doh_print_reply_cb(isc_nmhandle_t *handle, isc_result_t eresult,
- isc_region_t *region, void *cbarg) {
- assert_non_null(handle);
- UNUSED(cbarg);
- UNUSED(region);
-
- puts("Cloudflare DNS query result:");
- if (eresult == ISC_R_SUCCESS) {
- puts("success!");
- printf("Response size: %lu\n", region->length);
- atomic_fetch_add(&creads, 1);
- isc_nm_resumeread(handle);
- } else {
- puts("failure!");
- atomic_store(&was_error, true);
- isc_nm_cancelread(handle);
- }
-}
-
-static void
-doh_cloudflare(void **state) {
- isc_nm_t **nm = (isc_nm_t **)*state;
- isc_result_t result = ISC_R_SUCCESS;
-
- result = isc_nm_http_connect_send_request(
- nm[0], "https://cloudflare-dns.com/dns-query",
- atomic_load(&POST),
- &(isc_region_t){ .base = (uint8_t *)wikipedia_org_A,
- .length = sizeof(wikipedia_org_A) },
- doh_print_reply_cb, NULL, NULL, 5000);
-
- assert_int_equal(result, ISC_R_SUCCESS);
-
- while (atomic_load(&creads) != 1 && atomic_load(&was_error) == false) {
- isc_thread_yield();
- }
-
- isc_nm_closedown(nm[0]);
-}
-
-static void
-doh_cloudflare_POST(void **state) {
- atomic_store(&POST, true);
- doh_cloudflare(state);
-}
-
-static void
-doh_cloudflare_GET(void **state) {
- atomic_store(&POST, false);
- doh_cloudflare(state);
-}
-*/
int
main(void) {
const struct CMUnitTest tests_short[] = {
cmocka_unit_test_setup_teardown(doh_cloudflare_POST, nm_setup,
nm_teardown)*/
};
+ int result = 0;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
UNUSED(tests_long);
- return (cmocka_run_group_tests(tests_short, _setup, _teardown));
+ result = (cmocka_run_group_tests(tests_short, _setup, _teardown));
#else
if (getenv("CI") != NULL || !reuse_supported) {
- return (cmocka_run_group_tests(tests_short, _setup, _teardown));
+ result = (cmocka_run_group_tests(tests_short, _setup,
+ _teardown));
} else {
- return (cmocka_run_group_tests(tests_long, _setup, _teardown));
+ result =
+ (cmocka_run_group_tests(tests_long, _setup, _teardown));
}
#endif
+ return result;
}
#else /* HAVE_CMOCKA */
* information regarding copyright ownership.
*/
+#include <inttypes.h>
+#include <nghttp2/nghttp2.h>
+
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
const SSL_METHOD *method = NULL;
REQUIRE(ctxp != NULL && *ctxp == NULL);
-
- if (ephemeral) {
- INSIST(keyfile == NULL);
- INSIST(certfile == NULL);
- } else {
- INSIST(keyfile != NULL);
- INSIST(certfile != NULL);
- }
+ REQUIRE((keyfile == NULL) == (certfile == NULL));
method = TLS_server_method();
if (method == NULL) {
return (ISC_R_TLSERROR);
}
+
+isc_tls_t *
+isc_tls_create(isc_tlsctx_t *ctx) {
+ isc_tls_t *newctx = NULL;
+
+ REQUIRE(ctx != NULL);
+
+ newctx = SSL_new(ctx);
+ if (newctx == NULL) {
+ char errbuf[256];
+ unsigned long err = ERR_get_error();
+
+ ERR_error_string_n(err, errbuf, sizeof(errbuf));
+ fprintf(stderr, "%s:SSL_new(%p) -> %s\n", __func__, ctx,
+ errbuf);
+ }
+
+ return (newctx);
+}
+
+void
+isc_tls_free(isc_tls_t **tlsp) {
+ REQUIRE(tlsp != NULL && *tlsp != NULL);
+
+ SSL_free(*tlsp);
+ *tlsp = NULL;
+}
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+/*
+ * NPN TLS extension client callback.
+ */
+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) {
+ return (SSL_TLSEXT_ERR_NOACK);
+ }
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
+
+void
+isc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx) {
+ REQUIRE(ctx != NULL);
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ SSL_CTX_set_next_proto_select_cb(ctx, select_next_proto_cb, NULL);
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)NGHTTP2_PROTO_ALPN,
+ NGHTTP2_PROTO_ALPN_LEN);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+}
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+static int
+next_proto_cb(isc_tls_t *ssl, const unsigned char **data, unsigned int *len,
+ void *arg) {
+ UNUSED(ssl);
+ UNUSED(arg);
+
+ *data = (const unsigned char *)NGHTTP2_PROTO_ALPN;
+ *len = (unsigned int)NGHTTP2_PROTO_ALPN_LEN;
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+static int
+alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg) {
+ int ret;
+
+ UNUSED(ssl);
+ UNUSED(arg);
+
+ ret = nghttp2_select_next_protocol((unsigned char **)(uintptr_t)out,
+ outlen, in, inlen);
+
+ if (ret != 1) {
+ return (SSL_TLSEXT_ERR_NOACK);
+ }
+
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+
+void
+isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *tls) {
+ REQUIRE(tls != NULL);
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ SSL_CTX_set_next_protos_advertised_cb(tls, next_proto_cb, NULL);
+#endif // OPENSSL_NO_NEXTPROTONEG
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_select_cb(tls, alpn_select_proto_cb, NULL);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+}
+
+void
+isc_tls_get_http2_alpn(isc_tls_t *tls, const unsigned char **alpn,
+ unsigned int *alpnlen) {
+ REQUIRE(tls != NULL);
+ REQUIRE(alpn != NULL);
+ REQUIRE(alpnlen != NULL);
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ SSL_get0_next_proto_negotiated(tls, alpn, alpnlen);
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (*alpn == NULL) {
+ SSL_get0_alpn_selected(tls, alpn, alpnlen);
+ }
+#endif
+}
isc_nm_closedown
isc_nm_destroy
isc_nm_detach
-isc_nm_http_add_doh_endpoint
-isc_nm_http_add_endpoint
-isc_nm_http_connect_send_request
+isc_nm_http_endpoint
isc_nm_httpconnect
-isc_nm_httprequest
isc_nm_listenhttp
isc_nm_listentcpdns
isc_nm_listentls
isc_timermgr_createinctx
isc_timermgr_destroy
isc_timermgr_poke
-isc__tls_initialize
-isc__tls_shutdown
+isc_tls_get_http2_alpn
+isc_tls_create
+isc_tls_free
isc_tlsctx_createclient
isc_tlsctx_createserver
isc_tlsctx_free
+isc_tlsctx_enable_http2client_alpn
+isc_tlsctx_enable_http2server_alpn
+isc_tm_timegm
+isc_tm_strptime
isc__trampoline_initialize
isc__trampoline_shutdown
isc__trampoline_get
isc__trampoline_run
-isc_tm_timegm
-isc_tm_strptime
isc_url_parse
isc_utf8_bom
isc_utf8_valid
if (result == ISC_R_SUCCESS) {
for (i = 0; i < neps; i++) {
- result = isc_nm_http_add_doh_endpoint(
- sock, eps[i], ns__client_request, ifp,
- sizeof(ns_client_t));
+ result = isc_nm_http_endpoint(sock, eps[i],
+ ns__client_request, ifp,
+ sizeof(ns_client_t));
}
}