summaryrefslogtreecommitdiff
path: root/usr.sbin/unbound/util
diff options
context:
space:
mode:
authorStuart Henderson <sthen@cvs.openbsd.org>2020-10-28 11:31:09 +0000
committerStuart Henderson <sthen@cvs.openbsd.org>2020-10-28 11:31:09 +0000
commitffd360fda41541c22afa48057ba2442cd9df038b (patch)
treebe7385e9723fc55d37debfede78452c07576c854 /usr.sbin/unbound/util
parenta56279be8b3753a421738d4e7125b314c4e562c8 (diff)
merge Unbound 1.12.0
Diffstat (limited to 'usr.sbin/unbound/util')
-rw-r--r--usr.sbin/unbound/util/config_file.c45
-rw-r--r--usr.sbin/unbound/util/config_file.h29
-rw-r--r--usr.sbin/unbound/util/configlexer.lex11
-rw-r--r--usr.sbin/unbound/util/configparser.y100
-rw-r--r--usr.sbin/unbound/util/fptr_wlist.c3
-rw-r--r--usr.sbin/unbound/util/iana_ports.inc2
-rw-r--r--usr.sbin/unbound/util/mini_event.h4
-rw-r--r--usr.sbin/unbound/util/module.h2
-rw-r--r--usr.sbin/unbound/util/net_help.c59
-rw-r--r--usr.sbin/unbound/util/net_help.h6
-rw-r--r--usr.sbin/unbound/util/netevent.c610
-rw-r--r--usr.sbin/unbound/util/netevent.h143
12 files changed, 939 insertions, 75 deletions
diff --git a/usr.sbin/unbound/util/config_file.c b/usr.sbin/unbound/util/config_file.c
index 6139dfc2610..fb3a8bc5683 100644
--- a/usr.sbin/unbound/util/config_file.c
+++ b/usr.sbin/unbound/util/config_file.c
@@ -78,6 +78,8 @@ gid_t cfg_gid = (gid_t)-1;
int autr_permit_small_holddown = 0;
/** size (in bytes) of stream wait buffers max */
size_t stream_wait_max = 4 * 1024 * 1024;
+size_t http2_query_buffer_max = 4 * 1024 * 1024;
+size_t http2_response_buffer_max = 4 * 1024 * 1024;
/** global config during parsing */
struct config_parser_state* cfg_parser = 0;
@@ -117,6 +119,12 @@ config_create(void)
cfg->tls_cert_bundle = NULL;
cfg->tls_win_cert = 0;
cfg->tls_use_sni = 1;
+ cfg->https_port = UNBOUND_DNS_OVER_HTTPS_PORT;
+ if(!(cfg->http_endpoint = strdup("/dns-query"))) goto error_exit;
+ cfg->http_max_streams = 100;
+ cfg->http_query_buffer_size = 4*1024*1024;
+ cfg->http_response_buffer_size = 4*1024*1024;
+ cfg->http_nodelay = 1;
cfg->use_syslog = 1;
cfg->log_identity = NULL; /* changed later with argv[0] */
cfg->log_time_ascii = 0;
@@ -144,7 +152,7 @@ config_create(void)
cfg->incoming_num_tcp = 2;
#endif
cfg->stream_wait_size = 4 * 1024 * 1024;
- cfg->edns_buffer_size = 4096; /* 4k from rfc recommendation */
+ cfg->edns_buffer_size = 1232; /* from DNS flagday recommendation */
cfg->msg_buffer_size = 65552; /* 64 k + a small margin */
cfg->msg_cache_size = 4 * 1024 * 1024;
cfg->msg_cache_slabs = 4;
@@ -233,8 +241,6 @@ config_create(void)
cfg->trusted_keys_file_list = NULL;
cfg->trust_anchor_signaling = 0;
cfg->root_key_sentinel = 1;
- cfg->dlv_anchor_file = NULL;
- cfg->dlv_anchor_list = NULL;
cfg->domain_insecure = NULL;
cfg->val_date_override = 0;
cfg->val_sig_skew_min = 3600; /* at least daylight savings trouble */
@@ -315,6 +321,8 @@ config_create(void)
cfg->qname_minimisation_strict = 0;
cfg->shm_enable = 0;
cfg->shm_key = 11777;
+ cfg->edns_client_tags = NULL;
+ cfg->edns_client_tag_opcode = LDNS_EDNS_CLIENT_TAG;
cfg->dnscrypt = 0;
cfg->dnscrypt_port = 0;
cfg->dnscrypt_provider = NULL;
@@ -490,6 +498,8 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_YNO("do-ip6:", do_ip6)
else S_YNO("do-udp:", do_udp)
else S_YNO("do-tcp:", do_tcp)
+ else S_YNO("prefer-ip4:", prefer_ip4)
+ else S_YNO("prefer-ip6:", prefer_ip6)
else S_YNO("tcp-upstream:", tcp_upstream)
else S_YNO("udp-upstream-without-downstream:",
udp_upstream_without_downstream)
@@ -511,6 +521,12 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_STR("tls-ciphers:", tls_ciphers)
else S_STR("tls-ciphersuites:", tls_ciphersuites)
else S_YNO("tls-use-sni:", tls_use_sni)
+ else S_NUMBER_NONZERO("https-port:", https_port)
+ else S_STR("http-endpoint", http_endpoint)
+ else S_NUMBER_NONZERO("http-max-streams", http_max_streams)
+ else S_MEMSIZE("http-query-buffer-size", http_query_buffer_size)
+ else S_MEMSIZE("http-response-buffer-size", http_response_buffer_size)
+ else S_YNO("http-nodelay", http_nodelay)
else S_YNO("interface-automatic:", if_automatic)
else S_YNO("use-systemd:", use_systemd)
else S_YNO("do-daemonize:", do_daemonize)
@@ -581,8 +597,6 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_STRLIST("trusted-keys-file:", trusted_keys_file_list)
else S_YNO("trust-anchor-signaling:", trust_anchor_signaling)
else S_YNO("root-key-sentinel:", root_key_sentinel)
- else S_STR("dlv-anchor-file:", dlv_anchor_file)
- else S_STRLIST("dlv-anchor:", dlv_anchor_list)
else S_STRLIST("domain-insecure:", domain_insecure)
else S_NUMBER_OR_ZERO("val-bogus-ttl:", bogus_ttl)
else S_YNO("val-clean-additional:", val_clean_additional)
@@ -950,6 +964,8 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_YNO(opt, "do-ip6", do_ip6)
else O_YNO(opt, "do-udp", do_udp)
else O_YNO(opt, "do-tcp", do_tcp)
+ else O_YNO(opt, "prefer-ip4", prefer_ip4)
+ else O_YNO(opt, "prefer-ip6", prefer_ip6)
else O_YNO(opt, "tcp-upstream", tcp_upstream)
else O_YNO(opt, "udp-upstream-without-downstream", udp_upstream_without_downstream)
else O_DEC(opt, "tcp-mss", tcp_mss)
@@ -968,6 +984,12 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_STR(opt, "tls-ciphers", tls_ciphers)
else O_STR(opt, "tls-ciphersuites", tls_ciphersuites)
else O_YNO(opt, "tls-use-sni", tls_use_sni)
+ else O_DEC(opt, "https-port", https_port)
+ else O_STR(opt, "http-endpoint", http_endpoint)
+ else O_UNS(opt, "http-max-streams", http_max_streams)
+ else O_MEM(opt, "http-query-buffer-size", http_query_buffer_size)
+ else O_MEM(opt, "http-response-buffer-size", http_response_buffer_size)
+ else O_YNO(opt, "http-nodelay", http_nodelay)
else O_YNO(opt, "use-systemd", use_systemd)
else O_YNO(opt, "do-daemonize", do_daemonize)
else O_STR(opt, "chroot", chrootdir)
@@ -998,7 +1020,6 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_DEC(opt, "unwanted-reply-threshold", unwanted_threshold)
else O_YNO(opt, "do-not-query-localhost", donotquery_localhost)
else O_STR(opt, "module-config", module_conf)
- else O_STR(opt, "dlv-anchor-file", dlv_anchor_file)
else O_DEC(opt, "val-bogus-ttl", bogus_ttl)
else O_YNO(opt, "val-clean-additional", val_clean_additional)
else O_DEC(opt, "val-log-level", val_log_level)
@@ -1036,7 +1057,6 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_LST(opt, "trusted-keys-file", trusted_keys_file_list)
else O_YNO(opt, "trust-anchor-signaling", trust_anchor_signaling)
else O_YNO(opt, "root-key-sentinel", root_key_sentinel)
- else O_LST(opt, "dlv-anchor", dlv_anchor_list)
else O_LST(opt, "control-interface", control_ifs.first)
else O_LST(opt, "domain-insecure", domain_insecure)
else O_UNS(opt, "val-override-date", val_date_override)
@@ -1130,6 +1150,7 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_LS3(opt, "access-control-tag-action", acl_tag_actions)
else O_LS3(opt, "access-control-tag-data", acl_tag_datas)
else O_LS2(opt, "access-control-view", acl_view)
+ else O_LS2(opt, "edns-client-tags", edns_client_tags)
#ifdef USE_IPSECMOD
else O_YNO(opt, "ipsecmod-enabled", ipsecmod_enabled)
else O_YNO(opt, "ipsecmod-ignore-bogus", ipsecmod_ignore_bogus)
@@ -1391,8 +1412,8 @@ config_delviews(struct config_view* p)
p = np;
}
}
-/** delete string array */
-static void
+
+void
config_del_strarray(char** array, int num)
{
int i;
@@ -1434,6 +1455,7 @@ config_delete(struct config_file* cfg)
config_delstrlist(cfg->tls_session_ticket_keys.first);
free(cfg->tls_ciphers);
free(cfg->tls_ciphersuites);
+ free(cfg->http_endpoint);
if(cfg->log_identity) {
log_ident_revert_to_default();
free(cfg->log_identity);
@@ -1462,8 +1484,6 @@ config_delete(struct config_file* cfg)
config_delstrlist(cfg->trusted_keys_file_list);
config_delstrlist(cfg->trust_anchor_list);
config_delstrlist(cfg->domain_insecure);
- free(cfg->dlv_anchor_file);
- config_delstrlist(cfg->dlv_anchor_list);
config_deldblstrlist(cfg->acls);
config_deldblstrlist(cfg->tcp_connection_limits);
free(cfg->val_nsec3_key_iterations);
@@ -1499,6 +1519,7 @@ config_delete(struct config_file* cfg)
config_deldblstrlist(cfg->ratelimit_below_domain);
config_delstrlist(cfg->python_script);
config_delstrlist(cfg->dynlib_file);
+ config_deldblstrlist(cfg->edns_client_tags);
#ifdef USE_IPSECMOD
free(cfg->ipsecmod_hook);
config_delstrlist(cfg->ipsecmod_whitelist);
@@ -2043,6 +2064,8 @@ config_apply(struct config_file* config)
log_set_time_asc(config->log_time_ascii);
autr_permit_small_holddown = config->permit_small_holddown;
stream_wait_max = config->stream_wait_size;
+ http2_query_buffer_max = config->http_query_buffer_size;
+ http2_response_buffer_max = config->http_response_buffer_size;
}
void config_lookup_uid(struct config_file* cfg)
diff --git a/usr.sbin/unbound/util/config_file.h b/usr.sbin/unbound/util/config_file.h
index 66e5025d05b..7750eaa0e6b 100644
--- a/usr.sbin/unbound/util/config_file.h
+++ b/usr.sbin/unbound/util/config_file.h
@@ -131,6 +131,19 @@ struct config_file {
/** if SNI is to be used */
int tls_use_sni;
+ /** port on which to provide DNS over HTTPS service */
+ int https_port;
+ /** endpoint for HTTP service */
+ char* http_endpoint;
+ /** MAX_CONCURRENT_STREAMS HTTP/2 setting */
+ uint32_t http_max_streams;
+ /** maximum size of all HTTP2 query buffers combined. */
+ size_t http_query_buffer_size;
+ /** maximum size of all HTTP2 response buffers combined. */
+ size_t http_response_buffer_size;
+ /** set TCP_NODELAY option for http sockets */
+ int http_nodelay;
+
/** outgoing port range number of ports (per thread) */
int outgoing_num_ports;
/** number of outgoing tcp buffers per (per thread) */
@@ -331,10 +344,6 @@ struct config_file {
struct config_strlist* auto_trust_anchor_file_list;
/** files with trusted DNSKEYs in named.conf format, list */
struct config_strlist* trusted_keys_file_list;
- /** DLV anchor file */
- char* dlv_anchor_file;
- /** DLV anchor inline */
- struct config_strlist* dlv_anchor_list;
/** insecure domain list */
struct config_strlist* domain_insecure;
/** send key tag query */
@@ -553,6 +562,11 @@ struct config_file {
/** SHM data - key for the shm */
int shm_key;
+ /** list of EDNS client tag entries, linked list */
+ struct config_str2list* edns_client_tags;
+ /** EDNS opcode to use for EDNS client tags */
+ uint16_t edns_client_tag_opcode;
+
/** DNSCrypt */
/** true to enable dnscrypt */
int dnscrypt;
@@ -625,6 +639,10 @@ extern gid_t cfg_gid;
extern int autr_permit_small_holddown;
/** size (in bytes) of stream wait buffers max */
extern size_t stream_wait_max;
+/** size (in bytes) of all total HTTP2 query buffers max */
+extern size_t http2_query_buffer_max;
+/** size (in bytes) of all total HTTP2 response buffers max */
+extern size_t http2_response_buffer_max;
/**
* Stub config options
@@ -970,6 +988,9 @@ void config_deldblstrlist(struct config_str2list* list);
*/
void config_deltrplstrlist(struct config_str3list* list);
+/** delete string array */
+void config_del_strarray(char** array, int num);
+
/** delete stringbytelist */
void config_del_strbytelist(struct config_strbytelist* list);
diff --git a/usr.sbin/unbound/util/configlexer.lex b/usr.sbin/unbound/util/configlexer.lex
index 83cea4b992f..ea7c1cf7273 100644
--- a/usr.sbin/unbound/util/configlexer.lex
+++ b/usr.sbin/unbound/util/configlexer.lex
@@ -256,6 +256,12 @@ tls-session-ticket-keys{COLON} { YDVAR(1, VAR_TLS_SESSION_TICKET_KEYS) }
tls-ciphers{COLON} { YDVAR(1, VAR_TLS_CIPHERS) }
tls-ciphersuites{COLON} { YDVAR(1, VAR_TLS_CIPHERSUITES) }
tls-use-sni{COLON} { YDVAR(1, VAR_TLS_USE_SNI) }
+https-port{COLON} { YDVAR(1, VAR_HTTPS_PORT) }
+http-endpoint{COLON} { YDVAR(1, VAR_HTTP_ENDPOINT) }
+http-max-streams{COLON} { YDVAR(1, VAR_HTTP_MAX_STREAMS) }
+http-query-buffer-size{COLON} { YDVAR(1, VAR_HTTP_QUERY_BUFFER_SIZE) }
+http-response-buffer-size{COLON} { YDVAR(1, VAR_HTTP_RESPONSE_BUFFER_SIZE) }
+http-nodelay{COLON} { YDVAR(1, VAR_HTTP_NODELAY) }
use-systemd{COLON} { YDVAR(1, VAR_USE_SYSTEMD) }
do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) }
interface{COLON} { YDVAR(1, VAR_INTERFACE) }
@@ -303,6 +309,7 @@ harden-referral-path{COLON} { YDVAR(1, VAR_HARDEN_REFERRAL_PATH) }
harden-algo-downgrade{COLON} { YDVAR(1, VAR_HARDEN_ALGO_DOWNGRADE) }
use-caps-for-id{COLON} { YDVAR(1, VAR_USE_CAPS_FOR_ID) }
caps-whitelist{COLON} { YDVAR(1, VAR_CAPS_WHITELIST) }
+caps-exempt{COLON} { YDVAR(1, VAR_CAPS_WHITELIST) }
unwanted-reply-threshold{COLON} { YDVAR(1, VAR_UNWANTED_REPLY_THRESHOLD) }
private-address{COLON} { YDVAR(1, VAR_PRIVATE_ADDRESS) }
private-domain{COLON} { YDVAR(1, VAR_PRIVATE_DOMAIN) }
@@ -334,6 +341,7 @@ rpz-log{COLON} { YDVAR(1, VAR_RPZ_LOG) }
rpz-log-name{COLON} { YDVAR(1, VAR_RPZ_LOG_NAME) }
zonefile{COLON} { YDVAR(1, VAR_ZONEFILE) }
master{COLON} { YDVAR(1, VAR_MASTER) }
+primary{COLON} { YDVAR(1, VAR_MASTER) }
url{COLON} { YDVAR(1, VAR_URL) }
allow-notify{COLON} { YDVAR(1, VAR_ALLOW_NOTIFY) }
for-downstream{COLON} { YDVAR(1, VAR_FOR_DOWNSTREAM) }
@@ -504,6 +512,7 @@ ipsecmod-ignore-bogus{COLON} { YDVAR(1, VAR_IPSECMOD_IGNORE_BOGUS) }
ipsecmod-hook{COLON} { YDVAR(1, VAR_IPSECMOD_HOOK) }
ipsecmod-max-ttl{COLON} { YDVAR(1, VAR_IPSECMOD_MAX_TTL) }
ipsecmod-whitelist{COLON} { YDVAR(1, VAR_IPSECMOD_WHITELIST) }
+ipsecmod-allow{COLON} { YDVAR(1, VAR_IPSECMOD_WHITELIST) }
ipsecmod-strict{COLON} { YDVAR(1, VAR_IPSECMOD_STRICT) }
cachedb{COLON} { YDVAR(0, VAR_CACHEDB) }
backend{COLON} { YDVAR(1, VAR_CACHEDB_BACKEND) }
@@ -517,6 +526,8 @@ name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) }
name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) }
udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) }
tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) }
+edns-client-tag{COLON} { YDVAR(2, VAR_EDNS_CLIENT_TAG) }
+edns-client-tag-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_TAG_OPCODE) }
<INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; }
/* Quoted strings. Strip leading and ending quotes */
diff --git a/usr.sbin/unbound/util/configparser.y b/usr.sbin/unbound/util/configparser.y
index fe600a999d4..43a886f2403 100644
--- a/usr.sbin/unbound/util/configparser.y
+++ b/usr.sbin/unbound/util/configparser.y
@@ -112,6 +112,9 @@ extern struct config_parser_state* cfg_parser;
%token VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM
%token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM VAR_SSL_PORT VAR_FORWARD_FIRST
%token VAR_STUB_SSL_UPSTREAM VAR_FORWARD_SSL_UPSTREAM VAR_TLS_CERT_BUNDLE
+%token VAR_HTTPS_PORT VAR_HTTP_ENDPOINT VAR_HTTP_MAX_STREAMS
+%token VAR_HTTP_QUERY_BUFFER_SIZE VAR_HTTP_RESPONSE_BUFFER_SIZE
+%token VAR_HTTP_NODELAY
%token VAR_STUB_FIRST VAR_MINIMAL_RESPONSES VAR_RRSET_ROUNDROBIN
%token VAR_MAX_UDP_SIZE VAR_DELAY_CLOSE
%token VAR_UNBLOCK_LAN_ZONES VAR_INSECURE_LAN_ZONES
@@ -175,7 +178,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6
%token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE
%token VAR_RPZ_CNAME_OVERRIDE VAR_RPZ_LOG VAR_RPZ_LOG_NAME
-%token VAR_DYNLIB VAR_DYNLIB_FILE
+%token VAR_DYNLIB VAR_DYNLIB_FILE VAR_EDNS_CLIENT_TAG VAR_EDNS_CLIENT_TAG_OPCODE
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -244,6 +247,9 @@ content_server: server_num_threads | server_verbosity | server_port |
server_log_queries | server_log_replies | server_tcp_upstream | server_ssl_upstream |
server_log_local_actions |
server_ssl_service_key | server_ssl_service_pem | server_ssl_port |
+ server_https_port | server_http_endpoint | server_http_max_streams |
+ server_http_query_buffer_size | server_http_response_buffer_size |
+ server_http_nodelay |
server_minimal_responses | server_rrset_roundrobin | server_max_udp_size |
server_so_reuseport | server_delay_close |
server_unblock_lan_zones | server_insecure_lan_zones |
@@ -285,7 +291,8 @@ content_server: server_num_threads | server_verbosity | server_port |
server_unknown_server_time_limit | server_log_tag_queryreply |
server_stream_wait_size | server_tls_ciphers |
server_tls_ciphersuites | server_tls_session_ticket_keys |
- server_tls_use_sni
+ server_tls_use_sni | server_edns_client_tag |
+ server_edns_client_tag_opcode
;
stubstart: VAR_STUB_ZONE
{
@@ -969,6 +976,61 @@ server_tls_use_sni: VAR_TLS_USE_SNI STRING_ARG
free($2);
}
;
+server_https_port: VAR_HTTPS_PORT STRING_ARG
+ {
+ OUTYY(("P(server_https_port:%s)\n", $2));
+ if(atoi($2) == 0)
+ yyerror("port number expected");
+ else cfg_parser->cfg->https_port = atoi($2);
+ };
+server_http_endpoint: VAR_HTTP_ENDPOINT STRING_ARG
+ {
+ OUTYY(("P(server_http_endpoint:%s)\n", $2));
+ free(cfg_parser->cfg->http_endpoint);
+ if($2 && $2[0] != '/') {
+ cfg_parser->cfg->http_endpoint = malloc(strlen($2)+2);
+ if(!cfg_parser->cfg->http_endpoint)
+ yyerror("out of memory");
+ cfg_parser->cfg->http_endpoint[0] = '/';
+ memmove(cfg_parser->cfg->http_endpoint+1, $2,
+ strlen($2)+1);
+ free($2);
+ } else {
+ cfg_parser->cfg->http_endpoint = $2;
+ }
+ };
+server_http_max_streams: VAR_HTTP_MAX_STREAMS STRING_ARG
+ {
+ OUTYY(("P(server_http_max_streams:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else cfg_parser->cfg->http_max_streams = atoi($2);
+ free($2);
+ };
+server_http_query_buffer_size: VAR_HTTP_QUERY_BUFFER_SIZE STRING_ARG
+ {
+ OUTYY(("P(server_http_query_buffer_size:%s)\n", $2));
+ if(!cfg_parse_memsize($2,
+ &cfg_parser->cfg->http_query_buffer_size))
+ yyerror("memory size expected");
+ free($2);
+ };
+server_http_response_buffer_size: VAR_HTTP_RESPONSE_BUFFER_SIZE STRING_ARG
+ {
+ OUTYY(("P(server_http_response_buffer_size:%s)\n", $2));
+ if(!cfg_parse_memsize($2,
+ &cfg_parser->cfg->http_response_buffer_size))
+ yyerror("memory size expected");
+ free($2);
+ };
+server_http_nodelay: VAR_HTTP_NODELAY STRING_ARG
+ {
+ OUTYY(("P(server_http_nodelay:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->http_nodelay = (strcmp($2, "yes")==0);
+ free($2);
+ };
server_use_systemd: VAR_USE_SYSTEMD STRING_ARG
{
OUTYY(("P(server_use_systemd:%s)\n", $2));
@@ -1120,15 +1182,15 @@ server_root_hints: VAR_ROOT_HINTS STRING_ARG
server_dlv_anchor_file: VAR_DLV_ANCHOR_FILE STRING_ARG
{
OUTYY(("P(server_dlv_anchor_file:%s)\n", $2));
- free(cfg_parser->cfg->dlv_anchor_file);
- cfg_parser->cfg->dlv_anchor_file = $2;
+ log_warn("option dlv-anchor-file ignored: DLV is decommissioned");
+ free($2);
}
;
server_dlv_anchor: VAR_DLV_ANCHOR STRING_ARG
{
OUTYY(("P(server_dlv_anchor:%s)\n", $2));
- if(!cfg_strlist_insert(&cfg_parser->cfg->dlv_anchor_list, $2))
- yyerror("out of memory");
+ log_warn("option dlv-anchor ignored: DLV is decommissioned");
+ free($2);
}
;
server_auto_trust_anchor_file: VAR_AUTO_TRUST_ANCHOR_FILE STRING_ARG
@@ -2403,6 +2465,32 @@ server_ipsecmod_strict: VAR_IPSECMOD_STRICT STRING_ARG
#endif
}
;
+server_edns_client_tag: VAR_EDNS_CLIENT_TAG STRING_ARG STRING_ARG
+ {
+ int tag_data;
+ OUTYY(("P(server_edns_client_tag:%s %s)\n", $2, $3));
+ tag_data = atoi($3);
+ if(tag_data > 65535 || tag_data < 0 ||
+ (tag_data == 0 && (strlen($3) != 1 || $3[0] != '0')))
+ yyerror("edns-client-tag data invalid, needs to be a "
+ "number from 0 to 65535");
+ if(!cfg_str2list_insert(
+ &cfg_parser->cfg->edns_client_tags, $2, $3))
+ fatal_exit("out of memory adding "
+ "edns-client-tag");
+ }
+ ;
+server_edns_client_tag_opcode: VAR_EDNS_CLIENT_TAG_OPCODE STRING_ARG
+ {
+ OUTYY(("P(edns_client_tag_opcode:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("option code expected");
+ else if(atoi($2) > 65535 || atoi($2) < 0)
+ yyerror("option code must be in interval [0, 65535]");
+ else cfg_parser->cfg->edns_client_tag_opcode = atoi($2);
+
+ }
+ ;
stub_name: VAR_NAME STRING_ARG
{
OUTYY(("P(name:%s)\n", $2));
diff --git a/usr.sbin/unbound/util/fptr_wlist.c b/usr.sbin/unbound/util/fptr_wlist.c
index aa275ed534b..7d15d107561 100644
--- a/usr.sbin/unbound/util/fptr_wlist.c
+++ b/usr.sbin/unbound/util/fptr_wlist.c
@@ -138,6 +138,9 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
else if(fptr == &auth_xfer_probe_timer_callback) return 1;
else if(fptr == &auth_xfer_transfer_timer_callback) return 1;
else if(fptr == &mesh_serve_expired_callback) return 1;
+#ifdef USE_DNSTAP
+ else if(fptr == &mq_wakeup_cb) return 1;
+#endif
return 0;
}
diff --git a/usr.sbin/unbound/util/iana_ports.inc b/usr.sbin/unbound/util/iana_ports.inc
index 79488f49a2c..fa25869d3b5 100644
--- a/usr.sbin/unbound/util/iana_ports.inc
+++ b/usr.sbin/unbound/util/iana_ports.inc
@@ -4516,6 +4516,7 @@
6679,
6689,
6696,
+6699,
6701,
6702,
6703,
@@ -4744,6 +4745,7 @@
8023,
8025,
8026,
+8027,
8032,
8033,
8034,
diff --git a/usr.sbin/unbound/util/mini_event.h b/usr.sbin/unbound/util/mini_event.h
index 1734ca574c6..fa71ca3d123 100644
--- a/usr.sbin/unbound/util/mini_event.h
+++ b/usr.sbin/unbound/util/mini_event.h
@@ -54,6 +54,10 @@
#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
+#ifdef HAVE_SYS_SELECT_H
+/* for fd_set on OpenBSD */
+#include <sys/select.h>
+#endif
#include <sys/time.h>
#ifndef HAVE_EVENT_BASE_FREE
diff --git a/usr.sbin/unbound/util/module.h b/usr.sbin/unbound/util/module.h
index fa89c647e37..1eed213008c 100644
--- a/usr.sbin/unbound/util/module.h
+++ b/usr.sbin/unbound/util/module.h
@@ -520,6 +520,8 @@ struct module_env {
struct edns_known_option* edns_known_options;
/* Number of known edns options */
size_t edns_known_options_num;
+ /** EDNS client tag information */
+ struct edns_tags* edns_tags;
/* Make every mesh state unique, do not aggregate mesh states. */
int unique_mesh;
diff --git a/usr.sbin/unbound/util/net_help.c b/usr.sbin/unbound/util/net_help.c
index f59a4d65370..c5216bc2d8c 100644
--- a/usr.sbin/unbound/util/net_help.c
+++ b/usr.sbin/unbound/util/net_help.c
@@ -61,6 +61,9 @@
#ifdef USE_WINSOCK
#include <wincrypt.h>
#endif
+#ifdef HAVE_NGHTTP2_NGHTTP2_H
+#include <nghttp2/nghttp2.h>
+#endif
/** max length of an IP address (the address portion) that we allow */
#define MAX_ADDR_STRLEN 128 /* characters */
@@ -82,6 +85,7 @@ static struct tls_session_ticket_key {
unsigned char *hmac_key;
} *ticket_keys;
+#ifdef HAVE_SSL
/**
* callback TLS session ticket encrypt and decrypt
* For use with SSL_CTX_set_tlsext_ticket_key_cb or
@@ -97,7 +101,6 @@ static struct tls_session_ticket_key {
* @return 0 on no ticket, 1 for okay, and 2 for okay but renew the ticket
* (the ticket is decrypt only). and <0 for failures.
*/
-#ifdef HAVE_SSL
int tls_session_ticket_key_cb(SSL *s, unsigned char* key_name,
unsigned char* iv, EVP_CIPHER_CTX *evp_ctx,
#ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
@@ -884,6 +887,21 @@ log_cert(unsigned level, const char* str, void* cert)
}
#endif /* HAVE_SSL */
+#if defined(HAVE_SSL) && defined(HAVE_NGHTTP2)
+static int alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out,
+ unsigned char* outlen, const unsigned char* in, unsigned int inlen,
+ void* ATTR_UNUSED(arg))
+{
+ int rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in,
+ inlen);
+ if(rv == -1) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ /* either http/1.1 or h2 selected */
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
int
listen_sslctx_setup(void* ctxt)
{
@@ -942,6 +960,9 @@ listen_sslctx_setup(void* ctxt)
#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL
SSL_CTX_set_security_level(ctx, 0);
#endif
+#if defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB) && defined(HAVE_NGHTTP2)
+ SSL_CTX_set_alpn_select_cb(ctx, alpn_select_cb, NULL);
+#endif
#else
(void)ctxt;
#endif /* HAVE_SSL */
@@ -1478,7 +1499,11 @@ int tls_session_ticket_key_cb(SSL *ATTR_UNUSED(sslctx), unsigned char* key_name,
params[1] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
"sha256", 0);
params[2] = OSSL_PARAM_construct_end();
+#ifdef HAVE_EVP_MAC_CTX_SET_PARAMS
+ EVP_MAC_CTX_set_params(hmac_ctx, params);
+#else
EVP_MAC_set_ctx_params(hmac_ctx, params);
+#endif
#elif !defined(HMAC_INIT_EX_RETURNS_VOID)
if (HMAC_Init_ex(hmac_ctx, ticket_keys->hmac_key, 32, digest, NULL) != 1) {
verbose(VERB_CLIENT, "HMAC_Init_ex failed");
@@ -1509,7 +1534,11 @@ int tls_session_ticket_key_cb(SSL *ATTR_UNUSED(sslctx), unsigned char* key_name,
params[1] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
"sha256", 0);
params[2] = OSSL_PARAM_construct_end();
+#ifdef HAVE_EVP_MAC_CTX_SET_PARAMS
+ EVP_MAC_CTX_set_params(hmac_ctx, params);
+#else
EVP_MAC_set_ctx_params(hmac_ctx, params);
+#endif
#elif !defined(HMAC_INIT_EX_RETURNS_VOID)
if (HMAC_Init_ex(hmac_ctx, key->hmac_key, 32, digest, NULL) != 1) {
verbose(VERB_CLIENT, "HMAC_Init_ex failed");
@@ -1554,3 +1583,31 @@ listen_sslctx_delete_ticket_keys(void)
free(ticket_keys);
ticket_keys = NULL;
}
+
+# ifndef USE_WINSOCK
+char*
+sock_strerror(int errn)
+{
+ return strerror(errn);
+}
+
+void
+sock_close(int socket)
+{
+ close(socket);
+}
+
+# else
+char*
+sock_strerror(int ATTR_UNUSED(errn))
+{
+ return wsa_strerror(WSAGetLastError());
+}
+
+void
+sock_close(int socket)
+{
+ closesocket(socket);
+}
+
+# endif /* USE_WINSOCK */
diff --git a/usr.sbin/unbound/util/net_help.h b/usr.sbin/unbound/util/net_help.h
index 29943ada090..45b607a430d 100644
--- a/usr.sbin/unbound/util/net_help.h
+++ b/usr.sbin/unbound/util/net_help.h
@@ -496,4 +496,10 @@ void listen_sslctx_delete_ticket_keys(void);
*/
int netblockdnametoaddr(uint8_t* dname, size_t dnamelen,
struct sockaddr_storage* addr, socklen_t* addrlen, int* net, int* af);
+
+/** Return strerror or wsastrerror for socket error printout */
+char* sock_strerror(int errn);
+/** close the socket with close, or wsa closesocket */
+void sock_close(int socket);
+
#endif /* NET_HELP_H */
diff --git a/usr.sbin/unbound/util/netevent.c b/usr.sbin/unbound/util/netevent.c
index 3e7a433e502..545f09742c7 100644
--- a/usr.sbin/unbound/util/netevent.c
+++ b/usr.sbin/unbound/util/netevent.c
@@ -373,12 +373,7 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet,
if(sent == -1) {
if(!udp_send_errno_needs_log(addr, addrlen))
return 0;
-#ifndef USE_WINSOCK
- verbose(VERB_OPS, "sendto failed: %s", strerror(errno));
-#else
- verbose(VERB_OPS, "sendto failed: %s",
- wsa_strerror(WSAGetLastError()));
-#endif
+ verbose(VERB_OPS, "sendto failed: %s", sock_strerror(errno));
log_addr(VERB_OPS, "remote address is",
(struct sockaddr_storage*)addr, addrlen);
return 0;
@@ -739,7 +734,7 @@ static void
setup_tcp_handler(struct comm_point* c, int fd, int cur, int max)
{
int handler_usage;
- log_assert(c->type == comm_tcp);
+ log_assert(c->type == comm_tcp || c->type == comm_http);
log_assert(c->fd == -1);
sldns_buffer_clear(c->buffer);
#ifdef USE_DNSCRYPT
@@ -845,7 +840,6 @@ int comm_point_perform_accept(struct comm_point* c,
return -1;
}
#endif
- log_err_addr("accept failed", strerror(errno), addr, *addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAECONNRESET)
@@ -854,9 +848,9 @@ int comm_point_perform_accept(struct comm_point* c,
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
return -1;
}
- log_err_addr("accept failed", wsa_strerror(WSAGetLastError()),
- addr, *addrlen);
#endif
+ log_err_addr("accept failed", sock_strerror(errno), addr,
+ *addrlen);
return -1;
}
if(c->tcp_conn_limit && c->type == comm_tcp_accept) {
@@ -914,6 +908,42 @@ comm_point_tcp_win_bio_cb(struct comm_point* c, void* thessl)
}
#endif
+#ifdef HAVE_NGHTTP2
+/** Create http2 session server. Per connection, after TCP accepted.*/
+static int http2_session_server_create(struct http2_session* h2_session)
+{
+ log_assert(h2_session->callbacks);
+ h2_session->is_drop = 0;
+ if(nghttp2_session_server_new(&h2_session->session,
+ h2_session->callbacks,
+ h2_session) == NGHTTP2_ERR_NOMEM) {
+ log_err("failed to create nghttp2 session server");
+ return 0;
+ }
+
+ return 1;
+}
+
+/** Submit http2 setting to session. Once per session. */
+static int http2_submit_settings(struct http2_session* h2_session)
+{
+ int ret;
+ nghttp2_settings_entry settings[1] = {
+ {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
+ h2_session->c->http2_max_streams}};
+
+ ret = nghttp2_submit_settings(h2_session->session, NGHTTP2_FLAG_NONE,
+ settings, 1);
+ if(ret) {
+ verbose(VERB_QUERY, "http2: submit_settings failed, "
+ "error: %s", nghttp2_strerror(ret));
+ return 0;
+ }
+ return 1;
+}
+#endif /* HAVE_NGHTTP2 */
+
+
void
comm_point_tcp_accept_callback(int fd, short event, void* arg)
{
@@ -935,7 +965,28 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
/* clear leftover flags from previous use, and then set the
* correct event base for the event structure for libevent */
ub_event_free(c_hdl->ev->ev);
- c_hdl->ev->ev = ub_event_new(c_hdl->ev->base->eb->base, -1, UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT, comm_point_tcp_handle_callback, c_hdl);
+
+ if(c_hdl->type == comm_http) {
+#ifdef HAVE_NGHTTP2
+ if(!c_hdl->h2_session ||
+ !http2_session_server_create(c_hdl->h2_session)) {
+ log_warn("failed to create nghttp2");
+ return;
+ }
+ if(!c_hdl->h2_session ||
+ !http2_submit_settings(c_hdl->h2_session)) {
+ log_warn("failed to submit http2 settings");
+ return;
+ }
+#endif
+ c_hdl->ev->ev = ub_event_new(c_hdl->ev->base->eb->base, -1,
+ UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT,
+ comm_point_http_handle_callback, c_hdl);
+ } else {
+ c_hdl->ev->ev = ub_event_new(c_hdl->ev->base->eb->base, -1,
+ UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT,
+ comm_point_tcp_handle_callback, c_hdl);
+ }
if(!c_hdl->ev->ev) {
log_warn("could not ub_event_new, dropped tcp");
return;
@@ -1169,6 +1220,18 @@ ssl_handshake(struct comm_point* c)
c->repinfo.addrlen);
}
+ /* check if http2 use is negotiated */
+ if(c->type == comm_http && c->h2_session) {
+ const unsigned char *alpn;
+ unsigned int alpnlen = 0;
+ SSL_get0_alpn_selected(c->ssl, &alpn, &alpnlen);
+ if(alpnlen == 2 && memcmp("h2", alpn, 2) == 0) {
+ /* connection upgraded to HTTP2 */
+ c->tcp_do_toggle_rw = 0;
+ c->use_h2 = 1;
+ }
+ }
+
/* setup listen rw correctly */
if(c->tcp_is_reading) {
if(c->ssl_shake_state != comm_ssl_shake_read)
@@ -1435,8 +1498,6 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
- log_err_addr("read (in tcp s)", strerror(errno),
- &c->repinfo.addr, c->repinfo.addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAECONNRESET)
return 0;
@@ -1447,10 +1508,9 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
UB_EV_READ);
return 1;
}
- log_err_addr("read (in tcp s)",
- wsa_strerror(WSAGetLastError()),
- &c->repinfo.addr, c->repinfo.addrlen);
#endif
+ log_err_addr("read (in tcp s)", sock_strerror(errno),
+ &c->repinfo.addr, c->repinfo.addrlen);
return 0;
}
c->tcp_byte_count += r;
@@ -1483,8 +1543,6 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
- log_err_addr("read (in tcp r)", strerror(errno),
- &c->repinfo.addr, c->repinfo.addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAECONNRESET)
return 0;
@@ -1494,10 +1552,9 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
return 1;
}
- log_err_addr("read (in tcp r)",
- wsa_strerror(WSAGetLastError()),
- &c->repinfo.addr, c->repinfo.addrlen);
#endif
+ log_err_addr("read (in tcp r)", sock_strerror(errno),
+ &c->repinfo.addr, c->repinfo.addrlen);
return 0;
}
sldns_buffer_skip(c->buffer, r);
@@ -1716,8 +1773,6 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
- log_err_addr("tcp send r", strerror(errno),
- &c->repinfo.addr, c->repinfo.addrlen);
#else
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
@@ -1727,9 +1782,9 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
}
if(WSAGetLastError() == WSAECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
- log_err_addr("tcp send r", wsa_strerror(WSAGetLastError()),
- &c->repinfo.addr, c->repinfo.addrlen);
#endif
+ log_err_addr("tcp send r", sock_strerror(errno),
+ &c->repinfo.addr, c->repinfo.addrlen);
return 0;
}
sldns_buffer_skip(buffer, r);
@@ -1914,8 +1969,6 @@ http_read_more(int fd, struct comm_point* c)
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
- log_err_addr("read (in http r)", strerror(errno),
- &c->repinfo.addr, c->repinfo.addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAECONNRESET)
return 0;
@@ -1925,10 +1978,9 @@ http_read_more(int fd, struct comm_point* c)
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
return 1;
}
- log_err_addr("read (in http r)",
- wsa_strerror(WSAGetLastError()),
- &c->repinfo.addr, c->repinfo.addrlen);
#endif
+ log_err_addr("read (in http r)", sock_strerror(errno),
+ &c->repinfo.addr, c->repinfo.addrlen);
return 0;
}
sldns_buffer_skip(c->buffer, r);
@@ -2186,11 +2238,209 @@ http_chunked_segment(struct comm_point* c)
return 1;
}
+#ifdef HAVE_NGHTTP2
+/** Create new http2 session. Called when creating handling comm point. */
+struct http2_session* http2_session_create(struct comm_point* c)
+{
+ struct http2_session* session = calloc(1, sizeof(*session));
+ if(!session) {
+ log_err("malloc failure while creating http2 session");
+ return NULL;
+ }
+ session->c = c;
+
+ return session;
+}
+#endif
+
+/** Delete http2 session. After closing connection or on error */
+void http2_session_delete(struct http2_session* h2_session)
+{
+#ifdef HAVE_NGHTTP2
+ if(h2_session->callbacks)
+ nghttp2_session_callbacks_del(h2_session->callbacks);
+ free(h2_session);
+#else
+ (void)h2_session;
+#endif
+}
+
+#ifdef HAVE_NGHTTP2
+struct http2_stream* http2_stream_create(int32_t stream_id)
+{
+ struct http2_stream* h2_stream = calloc(1, sizeof(*h2_stream));
+ if(!h2_stream) {
+ log_err("malloc failure while creating http2 stream");
+ return NULL;
+ }
+ h2_stream->stream_id = stream_id;
+ return h2_stream;
+}
+
+/** Delete http2 stream. After session delete or stream close callback */
+static void http2_stream_delete(struct http2_session* h2_session,
+ struct http2_stream* h2_stream)
+{
+ if(h2_stream->mesh_state) {
+ mesh_state_remove_reply(h2_stream->mesh, h2_stream->mesh_state,
+ h2_session->c);
+ h2_stream->mesh_state = NULL;
+ }
+ http2_req_stream_clear(h2_stream);
+ free(h2_stream);
+}
+#endif
+
+void http2_stream_add_meshstate(struct http2_stream* h2_stream,
+ struct mesh_area* mesh, struct mesh_state* m)
+{
+ h2_stream->mesh = mesh;
+ h2_stream->mesh_state = m;
+}
+
+/** delete http2 session server. After closing connection. */
+static void http2_session_server_delete(struct http2_session* h2_session)
+{
+#ifdef HAVE_NGHTTP2
+ struct http2_stream* h2_stream, *next;
+ nghttp2_session_del(h2_session->session); /* NULL input is fine */
+ h2_session->session = NULL;
+ for(h2_stream = h2_session->first_stream; h2_stream;) {
+ next = h2_stream->next;
+ http2_stream_delete(h2_session, h2_stream);
+ h2_stream = next;
+ }
+ h2_session->first_stream = NULL;
+ h2_session->is_drop = 0;
+ h2_session->postpone_drop = 0;
+ h2_session->c->h2_stream = NULL;
+#endif
+ (void)h2_session;
+}
+
+#ifdef HAVE_NGHTTP2
+void http2_session_add_stream(struct http2_session* h2_session,
+ struct http2_stream* h2_stream)
+{
+ if(h2_session->first_stream)
+ h2_session->first_stream->prev = h2_stream;
+ h2_stream->next = h2_session->first_stream;
+ h2_session->first_stream = h2_stream;
+}
+
+/** remove stream from session linked list. After stream close callback or
+ * closing connection */
+void http2_session_remove_stream(struct http2_session* h2_session,
+ struct http2_stream* h2_stream)
+{
+ if(h2_stream->prev)
+ h2_stream->prev->next = h2_stream->next;
+ else
+ h2_session->first_stream = h2_stream->next;
+ if(h2_stream->next)
+ h2_stream->next->prev = h2_stream->prev;
+
+}
+
+int http2_stream_close_cb(nghttp2_session* ATTR_UNUSED(session),
+ int32_t stream_id, uint32_t ATTR_UNUSED(error_code), void* cb_arg)
+{
+ struct http2_stream* h2_stream;
+ struct http2_session* h2_session = (struct http2_session*)cb_arg;
+ if(!(h2_stream = nghttp2_session_get_stream_user_data(
+ h2_session->session, stream_id))) {
+ return 0;
+ }
+ http2_session_remove_stream(h2_session, h2_stream);
+ http2_stream_delete(h2_session, h2_stream);
+ return 0;
+}
+
+ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session), uint8_t* buf,
+ size_t len, int ATTR_UNUSED(flags), void* cb_arg)
+{
+#ifdef HAVE_SSL
+ struct http2_session* h2_session = (struct http2_session*)cb_arg;
+ int r;
+
+ log_assert(h2_session->c->type == comm_http);
+ log_assert(h2_session->c->h2_session);
+
+ if(!h2_session->c->ssl)
+ return 0;
+
+ ERR_clear_error();
+ r = SSL_read(h2_session->c->ssl, buf, len);
+ if(r <= 0) {
+ int want = SSL_get_error(h2_session->c->ssl, r);
+ if(want == SSL_ERROR_ZERO_RETURN) {
+ return NGHTTP2_ERR_EOF;
+ } else if(want == SSL_ERROR_WANT_READ) {
+ return NGHTTP2_ERR_WOULDBLOCK;
+ } else if(want == SSL_ERROR_WANT_WRITE) {
+ h2_session->c->ssl_shake_state = comm_ssl_shake_hs_write;
+ comm_point_listen_for_rw(h2_session->c, 0, 1);
+ return NGHTTP2_ERR_WOULDBLOCK;
+ } else if(want == SSL_ERROR_SYSCALL) {
+#ifdef ECONNRESET
+ if(errno == ECONNRESET && verbosity < 2)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+#endif
+ if(errno != 0)
+ log_err("SSL_read syscall: %s",
+ strerror(errno));
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ log_crypto_err("could not SSL_read");
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ return r;
+#else
+ (void)buf;
+ (void)len;
+ (void)cb_arg;
+ return -1;
+#endif
+}
+#endif /* HAVE_NGHTTP2 */
+
+/** Handle http2 read */
+static int
+comm_point_http2_handle_read(int ATTR_UNUSED(fd), struct comm_point* c)
+{
+#ifdef HAVE_NGHTTP2
+ int ret;
+ log_assert(c->h2_session);
+ log_assert(c->ssl);
+
+ /* reading until recv cb returns NGHTTP2_ERR_WOULDBLOCK */
+ ret = nghttp2_session_recv(c->h2_session->session);
+ if(ret) {
+ if(ret != NGHTTP2_ERR_EOF &&
+ ret != NGHTTP2_ERR_CALLBACK_FAILURE) {
+ verbose(VERB_QUERY, "http2: session_recv failed, "
+ "error: %s", nghttp2_strerror(ret));
+ }
+ return 0;
+ }
+ if(nghttp2_session_want_write(c->h2_session->session)) {
+ c->tcp_is_reading = 0;
+ comm_point_stop_listening(c);
+ comm_point_start_listening(c, -1, c->tcp_timeout_msec);
+ } else if(!nghttp2_session_want_read(c->h2_session->session))
+ return 0; /* connection can be closed */
+ return 1;
+#else
+ (void)c;
+ return 0;
+#endif
+}
+
/**
- * Handle http reading callback.
+ * Handle http reading callback.
* @param fd: file descriptor of socket.
* @param c: comm point to read from into buffer.
- * @return: 0 on error
+ * @return: 0 on error
*/
static int
comm_point_http_handle_read(int fd, struct comm_point* c)
@@ -2210,6 +2460,18 @@ comm_point_http_handle_read(int fd, struct comm_point* c)
if(!c->tcp_is_reading)
return 1;
+
+ if(c->use_h2) {
+ return comm_point_http2_handle_read(fd, c);
+ }
+
+ /* http version is <= http/1.1 */
+
+ if(c->http_min_version >= http_version_2) {
+ /* HTTP/2 failed, not allowed to use lower version. */
+ return 0;
+ }
+
/* read more data */
if(c->ssl) {
if(!ssl_http_read_more(c))
@@ -2220,7 +2482,9 @@ comm_point_http_handle_read(int fd, struct comm_point* c)
}
sldns_buffer_flip(c->buffer);
+
while(sldns_buffer_remaining(c->buffer) > 0) {
+ /* Handle HTTP/1.x data */
/* if we are reading headers, read more headers */
if(c->http_in_headers || c->http_in_chunk_headers) {
/* if header is done, process the header */
@@ -2364,8 +2628,6 @@ http_write_more(int fd, struct comm_point* c)
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
- log_err_addr("http send r", strerror(errno),
- &c->repinfo.addr, c->repinfo.addrlen);
#else
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
@@ -2373,15 +2635,92 @@ http_write_more(int fd, struct comm_point* c)
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
return 1;
}
- log_err_addr("http send r", wsa_strerror(WSAGetLastError()),
- &c->repinfo.addr, c->repinfo.addrlen);
#endif
+ log_err_addr("http send r", sock_strerror(errno),
+ &c->repinfo.addr, c->repinfo.addrlen);
return 0;
}
sldns_buffer_skip(c->buffer, r);
return 1;
}
+#ifdef HAVE_NGHTTP2
+ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session), const uint8_t* buf,
+ size_t len, int ATTR_UNUSED(flags), void* cb_arg)
+{
+#ifdef HAVE_SSL
+ int r;
+ struct http2_session* h2_session = (struct http2_session*)cb_arg;
+ log_assert(h2_session->c->type == comm_http);
+ log_assert(h2_session->c->h2_session);
+
+ if(!h2_session->c->ssl)
+ return 0;
+
+ ERR_clear_error();
+ r = SSL_write(h2_session->c->ssl, buf, len);
+ if(r <= 0) {
+ int want = SSL_get_error(h2_session->c->ssl, r);
+ if(want == SSL_ERROR_ZERO_RETURN) {
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ } else if(want == SSL_ERROR_WANT_READ) {
+ h2_session->c->ssl_shake_state = comm_ssl_shake_hs_read;
+ comm_point_listen_for_rw(h2_session->c, 1, 0);
+ return NGHTTP2_ERR_WOULDBLOCK;
+ } else if(want == SSL_ERROR_WANT_WRITE) {
+ return NGHTTP2_ERR_WOULDBLOCK;
+ } else if(want == SSL_ERROR_SYSCALL) {
+#ifdef EPIPE
+ if(errno == EPIPE && verbosity < 2)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+#endif
+ if(errno != 0)
+ log_err("SSL_write syscall: %s",
+ strerror(errno));
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ log_crypto_err("could not SSL_write");
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ return r;
+#else
+ (void)buf;
+ (void)len;
+ (void)cb_arg;
+ return -1;
+#endif
+}
+#endif /* HAVE_NGHTTP2 */
+
+/** Handle http2 writing */
+static int
+comm_point_http2_handle_write(int ATTR_UNUSED(fd), struct comm_point* c)
+{
+#ifdef HAVE_NGHTTP2
+ int ret;
+ log_assert(c->h2_session);
+ log_assert(c->ssl);
+
+ ret = nghttp2_session_send(c->h2_session->session);
+ if(ret) {
+ verbose(VERB_QUERY, "http2: session_send failed, "
+ "error: %s", nghttp2_strerror(ret));
+ return 0;
+ }
+
+ if(nghttp2_session_want_read(c->h2_session->session)) {
+ c->tcp_is_reading = 1;
+ comm_point_stop_listening(c);
+ comm_point_start_listening(c, -1, c->tcp_timeout_msec);
+ } else if(!nghttp2_session_want_write(c->h2_session->session))
+ return 0; /* connection can be closed */
+ return 1;
+#else
+ (void)c;
+ return 0;
+#endif
+}
+
/**
* Handle http writing callback.
* @param fd: file descriptor of socket.
@@ -2413,6 +2752,18 @@ comm_point_http_handle_write(int fd, struct comm_point* c)
#endif /* HAVE_SSL */
if(c->tcp_is_reading)
return 1;
+
+ if(c->use_h2) {
+ return comm_point_http2_handle_write(fd, c);
+ }
+
+ /* http version is <= http/1.1 */
+
+ if(c->http_min_version >= http_version_2) {
+ /* HTTP/2 failed, not allowed to use lower version. */
+ return 0;
+ }
+
/* if we are writing, write more */
if(c->ssl) {
if(!ssl_http_write_more(c))
@@ -2724,11 +3075,129 @@ comm_point_create_tcp_handler(struct comm_base *base,
return c;
}
+static struct comm_point*
+comm_point_create_http_handler(struct comm_base *base,
+ struct comm_point* parent, size_t bufsize, int harden_large_queries,
+ uint32_t http_max_streams, char* http_endpoint,
+ comm_point_callback_type* callback, void* callback_arg)
+{
+ struct comm_point* c = (struct comm_point*)calloc(1,
+ sizeof(struct comm_point));
+ short evbits;
+ if(!c)
+ return NULL;
+ c->ev = (struct internal_event*)calloc(1,
+ sizeof(struct internal_event));
+ if(!c->ev) {
+ free(c);
+ return NULL;
+ }
+ c->ev->base = base;
+ c->fd = -1;
+ c->buffer = sldns_buffer_new(bufsize);
+ if(!c->buffer) {
+ free(c->ev);
+ free(c);
+ return NULL;
+ }
+ c->timeout = (struct timeval*)malloc(sizeof(struct timeval));
+ if(!c->timeout) {
+ sldns_buffer_free(c->buffer);
+ free(c->ev);
+ free(c);
+ return NULL;
+ }
+ c->tcp_is_reading = 0;
+ c->tcp_byte_count = 0;
+ c->tcp_parent = parent;
+ c->tcp_timeout_msec = parent->tcp_timeout_msec;
+ c->tcp_conn_limit = parent->tcp_conn_limit;
+ c->tcl_addr = NULL;
+ c->tcp_keepalive = 0;
+ c->max_tcp_count = 0;
+ c->cur_tcp_count = 0;
+ c->tcp_handlers = NULL;
+ c->tcp_free = NULL;
+ c->type = comm_http;
+ c->tcp_do_close = 1;
+ c->do_not_close = 0;
+ c->tcp_do_toggle_rw = 1; /* will be set to 0 after http2 upgrade */
+ c->tcp_check_nb_connect = 0;
+#ifdef USE_MSG_FASTOPEN
+ c->tcp_do_fastopen = 0;
+#endif
+#ifdef USE_DNSCRYPT
+ c->dnscrypt = 0;
+ c->dnscrypt_buffer = NULL;
+#endif
+ c->repinfo.c = c;
+ c->callback = callback;
+ c->cb_arg = callback_arg;
+
+ c->http_min_version = http_version_2;
+ c->http2_stream_max_qbuffer_size = bufsize;
+ if(harden_large_queries && bufsize > 512)
+ c->http2_stream_max_qbuffer_size = 512;
+ c->http2_max_streams = http_max_streams;
+ if(!(c->http_endpoint = strdup(http_endpoint))) {
+ log_err("could not strdup http_endpoint");
+ sldns_buffer_free(c->buffer);
+ free(c->timeout);
+ free(c->ev);
+ free(c);
+ return NULL;
+ }
+ c->use_h2 = 0;
+#ifdef HAVE_NGHTTP2
+ if(!(c->h2_session = http2_session_create(c))) {
+ log_err("could not create http2 session");
+ free(c->http_endpoint);
+ sldns_buffer_free(c->buffer);
+ free(c->timeout);
+ free(c->ev);
+ free(c);
+ return NULL;
+ }
+ if(!(c->h2_session->callbacks = http2_req_callbacks_create())) {
+ log_err("could not create http2 callbacks");
+ http2_session_delete(c->h2_session);
+ free(c->http_endpoint);
+ sldns_buffer_free(c->buffer);
+ free(c->timeout);
+ free(c->ev);
+ free(c);
+ return NULL;
+ }
+#endif
+
+ /* add to parent free list */
+ c->tcp_free = parent->tcp_free;
+ parent->tcp_free = c;
+ /* ub_event stuff */
+ evbits = UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT;
+ c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
+ comm_point_http_handle_callback, c);
+ if(c->ev->ev == NULL)
+ {
+ log_err("could not set http handler event");
+ parent->tcp_free = c->tcp_free;
+ http2_session_delete(c->h2_session);
+ sldns_buffer_free(c->buffer);
+ free(c->timeout);
+ free(c->ev);
+ free(c);
+ return NULL;
+ }
+ return c;
+}
+
struct comm_point*
comm_point_create_tcp(struct comm_base *base, int fd, int num,
- int idle_timeout, struct tcl_list* tcp_conn_limit, size_t bufsize,
- struct sldns_buffer* spoolbuf, comm_point_callback_type* callback,
- void* callback_arg)
+ int idle_timeout, int harden_large_queries,
+ uint32_t http_max_streams, char* http_endpoint,
+ struct tcl_list* tcp_conn_limit, size_t bufsize,
+ struct sldns_buffer* spoolbuf, enum listen_type port_type,
+ comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
@@ -2792,10 +3261,24 @@ comm_point_create_tcp(struct comm_base *base, int fd, int num,
comm_point_delete(c);
return NULL;
}
- /* now prealloc the tcp handlers */
+ /* now prealloc the handlers */
for(i=0; i<num; i++) {
- c->tcp_handlers[i] = comm_point_create_tcp_handler(base,
- c, bufsize, spoolbuf, callback, callback_arg);
+ if(port_type == listen_type_tcp ||
+ port_type == listen_type_ssl ||
+ port_type == listen_type_tcp_dnscrypt) {
+ c->tcp_handlers[i] = comm_point_create_tcp_handler(base,
+ c, bufsize, spoolbuf, callback, callback_arg);
+ } else if(port_type == listen_type_http) {
+ c->tcp_handlers[i] = comm_point_create_http_handler(
+ base, c, bufsize, harden_large_queries,
+ http_max_streams, http_endpoint,
+ callback, callback_arg);
+ }
+ else {
+ log_err("could not create tcp handler, unknown listen "
+ "type");
+ return NULL;
+ }
if(!c->tcp_handlers[i]) {
comm_point_delete(c);
return NULL;
@@ -3079,6 +3562,9 @@ comm_point_close(struct comm_point* c)
tcl_close_connection(c->tcl_addr);
if(c->tcp_req_info)
tcp_req_info_clear(c->tcp_req_info);
+ if(c->h2_session)
+ http2_session_server_delete(c->h2_session);
+
/* close fd after removing from event lists, or epoll.. is messed up */
if(c->fd != -1 && !c->do_not_close) {
if(c->type == comm_tcp || c->type == comm_http) {
@@ -3087,11 +3573,7 @@ comm_point_close(struct comm_point* c)
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
}
verbose(VERB_ALGO, "close fd %d", c->fd);
-#ifndef USE_WINSOCK
- close(c->fd);
-#else
- closesocket(c->fd);
-#endif
+ sock_close(c->fd);
}
c->fd = -1;
}
@@ -3107,6 +3589,10 @@ comm_point_delete(struct comm_point* c)
SSL_free(c->ssl);
#endif
}
+ if(c->type == comm_http && c->http_endpoint) {
+ free(c->http_endpoint);
+ c->http_endpoint = NULL;
+ }
comm_point_close(c);
if(c->tcp_handlers) {
int i;
@@ -3125,6 +3611,9 @@ comm_point_delete(struct comm_point* c)
if(c->tcp_req_info) {
tcp_req_info_delete(c->tcp_req_info);
}
+ if(c->h2_session) {
+ http2_session_delete(c->h2_session);
+ }
}
ub_event_free(c->ev->ev);
free(c->ev);
@@ -3170,6 +3659,17 @@ comm_point_send_reply(struct comm_reply *repinfo)
#endif
if(repinfo->c->tcp_req_info) {
tcp_req_info_send_reply(repinfo->c->tcp_req_info);
+ } else if(repinfo->c->use_h2) {
+ if(!http2_submit_dns_response(repinfo->c->h2_session)) {
+ comm_point_drop_reply(repinfo);
+ return;
+ }
+ repinfo->c->h2_stream = NULL;
+ repinfo->c->tcp_is_reading = 0;
+ comm_point_stop_listening(repinfo->c);
+ comm_point_start_listening(repinfo->c, -1,
+ repinfo->c->tcp_timeout_msec);
+ return;
} else {
comm_point_start_listening(repinfo->c, -1,
repinfo->c->tcp_timeout_msec);
@@ -3188,6 +3688,16 @@ comm_point_drop_reply(struct comm_reply* repinfo)
return;
if(repinfo->c->tcp_req_info)
repinfo->c->tcp_req_info->is_drop = 1;
+ if(repinfo->c->type == comm_http) {
+ if(repinfo->c->h2_session) {
+ repinfo->c->h2_session->is_drop = 1;
+ if(!repinfo->c->h2_session->postpone_drop)
+ reclaim_http_handler(repinfo->c);
+ return;
+ }
+ reclaim_http_handler(repinfo->c);
+ return;
+ }
reclaim_tcp_handler(repinfo->c);
}
@@ -3232,11 +3742,7 @@ comm_point_start_listening(struct comm_point* c, int newfd, int msec)
}
if(newfd != -1) {
if(c->fd != -1) {
-#ifndef USE_WINSOCK
- close(c->fd);
-#else
- closesocket(c->fd);
-#endif
+ sock_close(c->fd);
}
c->fd = newfd;
ub_event_set_fd(c->ev->ev, c->fd);
diff --git a/usr.sbin/unbound/util/netevent.h b/usr.sbin/unbound/util/netevent.h
index bb2cd1e5373..6986f881b38 100644
--- a/usr.sbin/unbound/util/netevent.h
+++ b/usr.sbin/unbound/util/netevent.h
@@ -61,6 +61,9 @@
#define NET_EVENT_H
#include "dnscrypt/dnscrypt.h"
+#ifdef HAVE_NGHTTP2_NGHTTP2_H
+#include <nghttp2/nghttp2.h>
+#endif
struct sldns_buffer;
struct comm_point;
@@ -68,11 +71,16 @@ struct comm_reply;
struct tcl_list;
struct ub_event_base;
+struct mesh_state;
+struct mesh_area;
+
/* internal event notification data storage structure. */
struct internal_event;
struct internal_base;
struct internal_timer; /* A sub struct of the comm_timer super struct */
+enum listen_type;
+
/** callback from communication point function type */
typedef int comm_point_callback_type(struct comm_point*, void*, int,
struct comm_reply*);
@@ -205,6 +213,15 @@ struct comm_point {
} ssl_shake_state;
/* -------- HTTP ------- */
+ /** Do not allow connection to use HTTP version lower than this. 0=no
+ * minimum. */
+ enum {
+ http_version_none = 0,
+ http_version_2 = 2
+ } http_min_version;
+ /** http endpoint */
+ char* http_endpoint;
+ /* -------- HTTP/1.1 ------- */
/** Currently reading in http headers */
int http_in_headers;
/** Currently reading in chunk headers, 0=not, 1=firstline, 2=unused
@@ -216,6 +233,18 @@ struct comm_point {
struct sldns_buffer* http_temp;
/** http stored content in buffer */
size_t http_stored;
+ /* -------- HTTP/2 ------- */
+ /** http2 session */
+ struct http2_session* h2_session;
+ /** set to 1 if h2 is negotiated to be used (using alpn) */
+ int use_h2;
+ /** stream currently being handled */
+ struct http2_stream* h2_stream;
+ /** maximum allowed query buffer size, per stream */
+ size_t http2_stream_max_qbuffer_size;
+ /** maximum number of HTTP/2 streams per connection. Send in HTTP/2
+ * SETTINGS frame. */
+ uint32_t http2_max_streams;
/* -------- dnstap ------- */
/** the dnstap environment */
@@ -456,10 +485,15 @@ struct comm_point* comm_point_create_udp_ancil(struct comm_base* base,
* @param num: becomes max_tcp_count, the routine allocates that
* many tcp handler commpoints.
* @param idle_timeout: TCP idle timeout in ms.
+ * @param harden_large_queries: whether query size should be limited.
+ * @param http_max_streams: maximum number of HTTP/2 streams per connection.
+ * @param http_endpoint: HTTP endpoint to service queries on
* @param tcp_conn_limit: TCP connection limit info.
* @param bufsize: size of buffer to create for handlers.
* @param spoolbuf: shared spool buffer for tcp_req_info structures.
* or NULL to not create those structures in the tcp handlers.
+ * @param port_type: the type of port we are creating a TCP listener for. Used
+ * to select handler type to use.
* @param callback: callback function pointer for TCP handlers.
* @param callback_arg: will be passed to your callback function.
* @return: returns the TCP listener commpoint. You can find the
@@ -468,8 +502,11 @@ struct comm_point* comm_point_create_udp_ancil(struct comm_base* base,
* Inits timeout to NULL. All handlers are on the free list.
*/
struct comm_point* comm_point_create_tcp(struct comm_base* base,
- int fd, int num, int idle_timeout, struct tcl_list* tcp_conn_limit,
+ int fd, int num, int idle_timeout, int harden_large_queries,
+ uint32_t http_max_streams, char* http_endpoint,
+ struct tcl_list* tcp_conn_limit,
size_t bufsize, struct sldns_buffer* spoolbuf,
+ enum listen_type port_type,
comm_point_callback_type* callback, void* callback_arg);
/**
@@ -724,6 +761,110 @@ void comm_point_tcp_handle_callback(int fd, short event, void* arg);
void comm_point_http_handle_callback(int fd, short event, void* arg);
/**
+ * HTTP2 session. HTTP2 related info per comm point.
+ */
+struct http2_session {
+ /** first item in list of streams */
+ struct http2_stream* first_stream;
+#ifdef HAVE_NGHTTP2
+ /** nghttp2 session */
+ nghttp2_session *session;
+ /** store nghttp2 callbacks for easy reuse */
+ nghttp2_session_callbacks* callbacks;
+#endif
+ /** comm point containing buffer used to build answer in worker or
+ * module */
+ struct comm_point* c;
+ /** session is instructed to get dropped (comm port will be closed) */
+ int is_drop;
+ /** postpone dropping the session, can be used to prevent dropping
+ * while being in a callback */
+ int postpone_drop;
+};
+
+/** enum of HTTP status */
+enum http_status {
+ HTTP_STATUS_OK = 200,
+ HTTP_STATUS_BAD_REQUEST = 400,
+ HTTP_STATUS_NOT_FOUND = 404,
+ HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
+ HTTP_STATUS_URI_TOO_LONG = 414,
+ HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
+ HTTP_STATUS_NOT_IMPLEMENTED = 501
+};
+
+/**
+ * HTTP stream. Part of list of HTTP2 streams per session.
+ */
+struct http2_stream {
+ /** next stream in list per session */
+ struct http2_stream* next;
+ /** previous stream in list per session */
+ struct http2_stream* prev;
+ /** HTTP2 stream ID is an unsigned 31-bit integer */
+ int32_t stream_id;
+ /** HTTP method used for this stream */
+ enum {
+ HTTP_METHOD_POST = 1,
+ HTTP_METHOD_GET,
+ HTTP_METHOD_UNSUPPORTED
+ } http_method;
+ /** message contains invalid content type */
+ int invalid_content_type;
+ /** message body content type */
+ size_t content_length;
+ /** HTTP response status */
+ enum http_status status;
+ /** request for non existing endpoint */
+ int invalid_endpoint;
+ /** query in request is too large */
+ int query_too_large;
+ /** buffer to store query into. Can't use session shared buffer as query
+ * can arrive in parts, intertwined with frames for other queries. */
+ struct sldns_buffer* qbuffer;
+ /** buffer to store response into. Can't use shared buffer as a next
+ * query read callback can overwrite it before it is send out. */
+ struct sldns_buffer* rbuffer;
+ /** mesh area containing mesh state */
+ struct mesh_area* mesh;
+ /** mesh state for query. Used to remove mesh reply before closing
+ * stream. */
+ struct mesh_state* mesh_state;
+};
+
+#ifdef HAVE_NGHTTP2
+/** nghttp2 receive cb. Read from SSL connection into nghttp2 buffer */
+ssize_t http2_recv_cb(nghttp2_session* session, uint8_t* buf,
+ size_t len, int flags, void* cb_arg);
+/** nghttp2 send callback. Send from nghttp2 buffer to ssl socket */
+ssize_t http2_send_cb(nghttp2_session* session, const uint8_t* buf,
+ size_t len, int flags, void* cb_arg);
+/** nghttp2 callback on closing stream */
+int http2_stream_close_cb(nghttp2_session* session, int32_t stream_id,
+ uint32_t error_code, void* cb_arg);
+#endif
+
+/**
+ * Create new http2 stream
+ * @param stream_id: ID for stream to create.
+ * @return malloc'ed stream, NULL on error
+ */
+struct http2_stream* http2_stream_create(int32_t stream_id);
+
+/**
+ * Add new stream to session linked list
+ * @param h2_session: http2 session to add stream to
+ * @param h2_stream: stream to add to session list
+ */
+void http2_session_add_stream(struct http2_session* h2_session,
+ struct http2_stream* h2_stream);
+
+/** Add mesh state to stream. To be able to remove mesh reply on stream closure
+ */
+void http2_stream_add_meshstate(struct http2_stream* h2_stream,
+ struct mesh_area* mesh, struct mesh_state* m);
+
+/**
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for timer comm.
* @param fd: file descriptor (always -1).