diff options
author | Stuart Henderson <sthen@cvs.openbsd.org> | 2020-10-28 11:31:09 +0000 |
---|---|---|
committer | Stuart Henderson <sthen@cvs.openbsd.org> | 2020-10-28 11:31:09 +0000 |
commit | ffd360fda41541c22afa48057ba2442cd9df038b (patch) | |
tree | be7385e9723fc55d37debfede78452c07576c854 /usr.sbin/unbound/util | |
parent | a56279be8b3753a421738d4e7125b314c4e562c8 (diff) |
merge Unbound 1.12.0
Diffstat (limited to 'usr.sbin/unbound/util')
-rw-r--r-- | usr.sbin/unbound/util/config_file.c | 45 | ||||
-rw-r--r-- | usr.sbin/unbound/util/config_file.h | 29 | ||||
-rw-r--r-- | usr.sbin/unbound/util/configlexer.lex | 11 | ||||
-rw-r--r-- | usr.sbin/unbound/util/configparser.y | 100 | ||||
-rw-r--r-- | usr.sbin/unbound/util/fptr_wlist.c | 3 | ||||
-rw-r--r-- | usr.sbin/unbound/util/iana_ports.inc | 2 | ||||
-rw-r--r-- | usr.sbin/unbound/util/mini_event.h | 4 | ||||
-rw-r--r-- | usr.sbin/unbound/util/module.h | 2 | ||||
-rw-r--r-- | usr.sbin/unbound/util/net_help.c | 59 | ||||
-rw-r--r-- | usr.sbin/unbound/util/net_help.h | 6 | ||||
-rw-r--r-- | usr.sbin/unbound/util/netevent.c | 610 | ||||
-rw-r--r-- | usr.sbin/unbound/util/netevent.h | 143 |
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). |