1 #define H2O_USE_EPOLL 1
7 #include <boost/algorithm/string.hpp>
9 //#include <h2o/http1.h>
10 #include <h2o/http2.h>
12 #include <openssl/err.h>
13 #include <openssl/ssl.h>
22 #include "dnsdist-ecs.hh"
23 #include "dnsdist-rules.hh"
24 #include "dnsdist-xpf.hh"
26 #include "threadname.hh"
30 /* So, how does this work. We use h2o for our http2 and TLS needs.
31 If the operator has configured multiple IP addresses to listen on,
32 we launch multiple h2o listener threads. We can hook in to multiple
33 URLs though on the same IP. There is no SNI yet (I think).
35 h2o is event driven, so we get callbacks if a new DNS query arrived.
36 When it does, we do some minimal parsing on it, and send it on to the
37 dnsdist worker thread which we also launched.
39 This dnsdist worker thread injects the query into the normal dnsdist flow
40 (as a datagram over a socketpair). The response also goes back over a
41 (different) socketpair, where we pick it up and deliver it back to h2o.
43 For coordination, we use the h2o socket multiplexer, which is sensitive to our
48 Paths and parameters etc just *happen* to be null-terminated in HTTP2.
49 They are not in HTTP1. So you MUST use the length field!
52 /* 'Intermediate' compatibility from https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29 */
53 #define DOH_DEFAULT_CIPHERS "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"
55 class DOHAcceptContext
60 memset(&d_h2o_accept_ctx
, 0, sizeof(d_h2o_accept_ctx
));
62 DOHAcceptContext(const DOHAcceptContext
&) = delete;
63 DOHAcceptContext
& operator=(const DOHAcceptContext
&) = delete;
65 h2o_accept_ctx_t
* get()
68 return &d_h2o_accept_ctx
;
75 SSL_CTX_free(d_h2o_accept_ctx
.ssl_ctx
);
76 d_h2o_accept_ctx
.ssl_ctx
= nullptr;
82 h2o_accept_ctx_t d_h2o_accept_ctx
;
83 std::atomic
<uint64_t> d_refcnt
{1};
86 // we create one of these per thread, and pass around a pointer to it
87 // through the bowels of h2o
88 struct DOHServerConfig
90 DOHServerConfig(uint32_t idleTimeout
): accept_ctx(new DOHAcceptContext
)
92 if(socketpair(AF_LOCAL
, SOCK_DGRAM
, 0, dohquerypair
) < 0) {
93 unixDie("Creating a socket pair for DNS over HTTPS");
96 if (socketpair(AF_LOCAL
, SOCK_DGRAM
, 0, dohresponsepair
) < 0) {
97 close(dohquerypair
[0]);
98 close(dohquerypair
[1]);
99 unixDie("Creating a socket pair for DNS over HTTPS");
102 h2o_config_init(&h2o_config
);
103 h2o_config
.http2
.idle_timeout
= idleTimeout
* 1000;
105 DOHServerConfig(const DOHServerConfig
&) = delete;
106 DOHServerConfig
& operator=(const DOHServerConfig
&) = delete;
111 accept_ctx
->release();
115 LocalHolders holders
;
116 h2o_globalconf_t h2o_config
;
117 h2o_context_t h2o_ctx
;
118 DOHAcceptContext
* accept_ctx
{nullptr};
119 ClientState
* cs
{nullptr};
120 std::shared_ptr
<DOHFrontend
> df
{nullptr};
121 int dohquerypair
[2]{-1,-1};
122 int dohresponsepair
[2]{-1,-1};
125 static void on_socketclose(void *data
)
127 DOHAcceptContext
* ctx
= reinterpret_cast<DOHAcceptContext
*>(data
);
131 /* this duplicates way too much from the UDP handler. Sorry.
132 this function calls 'return -1' to drop a query without sending it
133 caller should make sure HTTPS thread hears of that
136 static int processDOHQuery(DOHUnit
* du
)
138 uint16_t queryId
= 0;
142 // we got closed meanwhile. XXX small race condition here
146 DOHServerConfig
* dsc
= reinterpret_cast<DOHServerConfig
*>(du
->req
->conn
->ctx
->storage
.entries
[0].data
);
147 auto& holders
= dsc
->holders
;
148 ClientState
& cs
= *dsc
->cs
;
150 if (du
->query
.size() < sizeof(dnsheader
)) {
151 ++g_stats
.nonCompliantQueries
;
155 if(!holders
.acl
->match(du
->remote
)) {
156 vinfolog("Query from %s (DoH) dropped because of ACL", du
->remote
.toStringWithPort());
164 /* we need an accurate ("real") value for the response and
165 to store into the IDS, but not for insertion into the
167 struct timespec queryRealTime
;
168 gettime(&queryRealTime
, true);
169 uint16_t len
= du
->query
.length();
170 /* allocate a bit more memory to be able to spoof the content,
171 or to add ECS without allocating a new buffer */
172 du
->query
.resize(du
->query
.size() + 512);
173 size_t bufferSize
= du
->query
.size();
174 auto query
= const_cast<char*>(du
->query
.c_str());
175 struct dnsheader
* dh
= reinterpret_cast<struct dnsheader
*>(query
);
177 if (!checkQueryHeaders(dh
)) {
181 uint16_t qtype
, qclass
;
182 unsigned int consumed
= 0;
183 DNSName
qname(query
, len
, sizeof(dnsheader
), false, &qtype
, &qclass
, &consumed
);
184 DNSQuestion
dq(&qname
, qtype
, qclass
, consumed
, &du
->dest
, &du
->remote
, dh
, bufferSize
, len
, false, &queryRealTime
);
185 dq
.ednsAdded
= du
->ednsAdded
;
187 queryId
= ntohs(dh
->id
);
189 std::shared_ptr
<DownstreamState
> ss
{nullptr};
190 auto result
= processQuery(dq
, cs
, holders
, ss
);
192 if (result
== ProcessQueryResult::Drop
) {
196 if (result
== ProcessQueryResult::SendAnswer
) {
197 du
->query
= std::string(reinterpret_cast<char*>(dq
.dh
), dq
.len
);
198 send(du
->rsock
, &du
, sizeof(du
), 0);
202 if (result
!= ProcessQueryResult::PassToBackend
|| ss
== nullptr) {
206 unsigned int idOffset
= (ss
->idOffset
++) % ss
->idStates
.size();
207 IDState
* ids
= &ss
->idStates
[idOffset
];
211 int oldFD
= ids
->origFD
.exchange(cs
.udpFD
);
213 // if we are reusing, no change in outstanding
218 ++g_stats
.downstreamTimeouts
;
222 ids
->origID
= dh
->id
;
223 setIDStateFromDNSQuestion(*ids
, dq
, std::move(qname
));
225 /* If we couldn't harvest the real dest addr, still
226 write down the listening addr since it will be useful
227 (especially if it's not an 'any' one).
228 We need to keep track of which one it is since we may
229 want to use the real but not the listening addr to reply.
231 if (du
->dest
.sin4
.sin_family
!= 0) {
232 ids
->origDest
= du
->dest
;
233 ids
->destHarvested
= true;
236 ids
->origDest
= cs
.local
;
237 ids
->destHarvested
= false;
242 int fd
= pickBackendSocketForSending(ss
);
243 /* you can't touch du after this line, because it might already have been freed */
244 ssize_t ret
= udpClientSendRequestToBackend(ss
, fd
, query
, dq
.len
);
246 vinfolog("Got query for %s|%s from %s (https), relayed to %s", ids
->qname
.toString(), QType(ids
->qtype
).getName(), remote
.toStringWithPort(), ss
->getName());
250 ++g_stats
.downstreamSendErrors
;
253 catch(const std::exception
& e
) {
254 vinfolog("Got an error in DOH question thread while parsing a query from %s, id %d: %s", remote
.toStringWithPort(), queryId
, e
.what());
260 static h2o_pathconf_t
*register_handler(h2o_hostconf_t
*hostconf
, const char *path
, int (*on_req
)(h2o_handler_t
*, h2o_req_t
*))
262 h2o_pathconf_t
*pathconf
= h2o_config_register_path(hostconf
, path
, 0);
263 h2o_handler_t
*handler
= h2o_create_handler(pathconf
, sizeof(*handler
));
264 handler
->on_req
= on_req
;
268 /* this is called by h2o when our request dies.
269 We use this to signal to the 'du' that this req is no longer alive */
270 static void on_generator_dispose(void *_self
)
272 DOHUnit
** du
= (DOHUnit
**)_self
;
273 if(*du
) { // if 0, on_dnsdist cleaned up du already
274 // cout << "du "<<(void*)*du<<" containing req "<<(*du)->req<<" got killed"<<endl;
275 (*du
)->req
= nullptr;
279 static void doh_dispatch_query(DOHServerConfig
* dsc
, h2o_handler_t
* self
, h2o_req_t
* req
, std::string
&& query
, const ComboAddress
& local
, const ComboAddress
& remote
)
283 DNSName
qname(query
.c_str(), query
.size(), sizeof(dnsheader
), false, &qtype
);
285 auto du
= std::unique_ptr
<DOHUnit
>(new DOHUnit
);
289 du
->rsock
= dsc
->dohresponsepair
[0];
290 du
->query
= std::move(query
);
292 du
->self
= reinterpret_cast<DOHUnit
**>(h2o_mem_alloc_shared(&req
->pool
, sizeof(*self
), on_generator_dispose
));
293 auto ptr
= du
.release();
296 if(send(dsc
->dohquerypair
[0], &ptr
, sizeof(ptr
), 0) != sizeof(ptr
)) {
297 delete ptr
; // XXX but now what - will h2o time this out for us?
305 catch(const std::exception
& e
) {
306 vinfolog("Had error parsing DoH DNS packet from %s: %s", remote
.toStringWithPort(), e
.what());
307 h2o_send_error_400(req
, "Bad Request", "The DNS query could not be parsed", 0);
312 For GET, the base64url-encoded payload is in the 'dns' parameter, which might be the first parameter, or not.
313 For POST, the payload is the payload.
315 static int doh_handler(h2o_handler_t
*self
, h2o_req_t
*req
)
318 // g_logstream<<(void*)req<<" doh_handler"<<endl;
319 if(!req
->conn
->ctx
->storage
.size
) {
320 return 0; // although we might was well crash on this
322 h2o_socket_t
* sock
= req
->conn
->callbacks
->get_socket(req
->conn
);
325 h2o_socket_getpeername(sock
, reinterpret_cast<struct sockaddr
*>(&remote
));
326 h2o_socket_getsockname(sock
, reinterpret_cast<struct sockaddr
*>(&local
));
327 DOHServerConfig
* dsc
= reinterpret_cast<DOHServerConfig
*>(req
->conn
->ctx
->storage
.entries
[0].data
);
329 /* looks like we can't delete the Server: header with most versions of h2o */
330 h2o_set_header(&req
->pool
, &req
->res
.headers
, H2O_TOKEN_SERVER
, dsc
->df
->d_serverTokens
.c_str(), dsc
->df
->d_serverTokens
.size(), 1);
332 if(auto tlsversion
= h2o_socket_get_ssl_protocol_version(sock
)) {
333 if(!strcmp(tlsversion
, "TLSv1.0"))
334 ++dsc
->df
->d_tls10queries
;
335 else if(!strcmp(tlsversion
, "TLSv1.1"))
336 ++dsc
->df
->d_tls11queries
;
337 else if(!strcmp(tlsversion
, "TLSv1.2"))
338 ++dsc
->df
->d_tls12queries
;
339 else if(!strcmp(tlsversion
, "TLSv1.3"))
340 ++dsc
->df
->d_tls13queries
;
342 ++dsc
->df
->d_tlsUnknownqueries
;
345 string
path(req
->path
.base
, req
->path
.len
);
347 if (h2o_memis(req
->method
.base
, req
->method
.len
, H2O_STRLIT("POST"))) {
348 ++dsc
->df
->d_postqueries
;
349 if(req
->version
>= 0x0200)
350 ++dsc
->df
->d_http2queries
;
352 ++dsc
->df
->d_http1queries
;
355 query
.reserve(req
->entity
.len
+ 512);
356 query
.assign(req
->entity
.base
, req
->entity
.len
);
357 doh_dispatch_query(dsc
, self
, req
, std::move(query
), local
, remote
);
359 else if(req
->query_at
!= SIZE_MAX
&& (req
->path
.len
- req
->query_at
> 5)) {
360 auto pos
= path
.find("?dns=");
361 if(pos
== string::npos
)
362 pos
= path
.find("&dns=");
363 if(pos
!= string::npos
) {
364 // need to base64url decode this
365 string
sdns(path
.substr(pos
+5));
366 boost::replace_all(sdns
,"-", "+");
367 boost::replace_all(sdns
,"_", "/");
368 // re-add padding that may have been missing
369 switch (sdns
.size() % 4) {
379 /* rough estimate so we hopefully don't need a need allocation later */
380 decoded
.reserve(((sdns
.size() * 3) / 4) + 512);
381 if(B64Decode(sdns
, decoded
) < 0) {
382 h2o_send_error_400(req
, "Bad Request", "Unable to decode BASE64-URL", 0);
383 ++dsc
->df
->d_badrequests
;
387 ++dsc
->df
->d_getqueries
;
388 if(req
->version
>= 0x0200)
389 ++dsc
->df
->d_http2queries
;
391 ++dsc
->df
->d_http1queries
;
393 doh_dispatch_query(dsc
, self
, req
, std::move(decoded
), local
, remote
);
398 vinfolog("HTTP request without DNS parameter: %s", req
->path
.base
);
399 h2o_send_error_400(req
, "Bad Request", "Unable to find the DNS parameter", 0);
400 ++dsc
->df
->d_badrequests
;
405 h2o_send_error_400(req
, "Bad Request", "Unable to parse the request", 0);
406 ++dsc
->df
->d_badrequests
;
410 catch(const exception
& e
)
412 errlog("DOH Handler function failed with error %s", e
.what());
416 HTTPHeaderRule::HTTPHeaderRule(const std::string
& header
, const std::string
& regex
)
419 d_header
= toLower(header
);
420 d_visual
= "http[" + header
+ "] ~ " + regex
;
423 bool HTTPHeaderRule::matches(const DNSQuestion
* dq
) const
429 for (unsigned int i
= 0; i
!= dq
->du
->req
->headers
.size
; ++i
) {
430 if(std::string(dq
->du
->req
->headers
.entries
[i
].name
->base
, dq
->du
->req
->headers
.entries
[i
].name
->len
) == d_header
&&
431 d_regex
.match(std::string(dq
->du
->req
->headers
.entries
[i
].value
.base
, dq
->du
->req
->headers
.entries
[i
].value
.len
))) {
438 string
HTTPHeaderRule::toString() const
443 HTTPPathRule::HTTPPathRule(const std::string
& path
)
449 bool HTTPPathRule::matches(const DNSQuestion
* dq
) const
455 if(dq
->du
->req
->query_at
== SIZE_MAX
) {
456 return dq
->du
->req
->path
.base
== d_path
;
459 return d_path
.compare(0, d_path
.size(), dq
->du
->req
->path
.base
, dq
->du
->req
->query_at
) == 0;
463 string
HTTPPathRule::toString() const
465 return "url path == " + d_path
;
468 void dnsdistclient(int qsock
, int rsock
)
470 setThreadName("dnsdist/doh-cli");
474 DOHUnit
* du
= nullptr;
475 ssize_t got
= recv(qsock
, &du
, sizeof(du
), 0);
477 warnlog("Error receving internal DoH query: %s", strerror(errno
));
480 else if (static_cast<size_t>(got
) < sizeof(du
)) {
484 // if there was no EDNS, we add it with a large buffer size
485 // so we can use UDP to talk to the backend.
486 auto dh
= const_cast<struct dnsheader
*>(reinterpret_cast<const struct dnsheader
*>(du
->query
.c_str()));
490 generateOptRR(std::string(), res
, 4096, 0, false);
493 dh
= const_cast<struct dnsheader
*>(reinterpret_cast<const struct dnsheader
*>(du
->query
.c_str())); // may have reallocated
494 dh
->arcount
= htons(1);
495 du
->ednsAdded
= true;
498 // we leave existing EDNS in place
501 if(processDOHQuery(du
) < 0) {
502 du
->error
= true; // turns our drop into a 500
503 if(send(du
->rsock
, &du
, sizeof(du
), 0) != sizeof(du
))
504 delete du
; // XXX but now what - will h2o time this out for us?
507 catch(const std::exception
& e
) {
508 errlog("Error while processing query received over DoH: %s", e
.what());
511 errlog("Unspecified error while processing query received over DoH");
516 // called if h2o finds that dnsdist gave us an answer
517 static void on_dnsdist(h2o_socket_t
*listener
, const char *err
)
519 DOHUnit
*du
= nullptr;
520 DOHServerConfig
* dsc
= reinterpret_cast<DOHServerConfig
*>(listener
->data
);
521 ssize_t got
= recv(dsc
->dohresponsepair
[1], &du
, sizeof(du
), 0);
524 warnlog("Error reading a DOH internal response: %s", strerror(errno
));
527 else if (static_cast<size_t>(got
) != sizeof(du
)) {
531 if(!du
->req
) { // it got killed in flight
532 // cout << "du "<<(void*)du<<" came back from dnsdist, but it was killed"<<endl;
537 *du
->self
= nullptr; // so we don't clean up again in on_generator_dispose
539 ++dsc
->df
->d_validresponses
;
540 du
->req
->res
.status
= 200;
541 du
->req
->res
.reason
= "OK";
543 h2o_add_header(&du
->req
->pool
, &du
->req
->res
.headers
, H2O_TOKEN_CONTENT_TYPE
, nullptr, H2O_STRLIT("application/dns-message"));
545 // struct dnsheader* dh = (struct dnsheader*)du->query.c_str();
546 // cout<<"Attempt to send out "<<du->query.size()<<" bytes over https, TC="<<dh->tc<<", RCODE="<<dh->rcode<<", qtype="<<du->qtype<<", req="<<(void*)du->req<<endl;
548 du
->req
->res
.content_length
= du
->query
.size();
549 h2o_send_inline(du
->req
, du
->query
.c_str(), du
->query
.size());
552 h2o_send_error_500(du
->req
, "Internal Server Error", "Internal Server Error", 0);
553 ++dsc
->df
->d_errorresponses
;
558 static void on_accept(h2o_socket_t
*listener
, const char *err
)
560 DOHServerConfig
* dsc
= reinterpret_cast<DOHServerConfig
*>(listener
->data
);
561 h2o_socket_t
*sock
= nullptr;
563 if (err
!= nullptr) {
566 // do some dnsdist rules here to filter based on IP address
567 if ((sock
= h2o_evloop_socket_accept(listener
)) == nullptr) {
573 h2o_socket_getpeername(sock
, reinterpret_cast<struct sockaddr
*>(&remote
));
574 // cout<<"New HTTP accept for client "<<remote.toStringWithPort()<<": "<< listener->data << endl;
577 sock
->on_close
.cb
= on_socketclose
;
578 auto accept_ctx
= dsc
->accept_ctx
->get();
579 sock
->on_close
.data
= dsc
->accept_ctx
;
580 ++dsc
->df
->d_httpconnects
;
581 h2o_accept(accept_ctx
, sock
);
584 static int create_listener(const ComboAddress
& addr
, std::shared_ptr
<DOHServerConfig
>& dsc
, int fd
)
586 auto sock
= h2o_evloop_socket_create(dsc
->h2o_ctx
.loop
, fd
, H2O_SOCKET_FLAG_DONT_READ
);
587 sock
->data
= dsc
.get();
588 h2o_socket_read_start(sock
, on_accept
);
593 static std::unique_ptr
<SSL_CTX
, void(*)(SSL_CTX
*)> getTLSContext(const std::vector
<std::pair
<std::string
, std::string
>>& pairs
, const std::string
& ciphers
, const std::string
& ciphers13
)
595 auto ctx
= std::unique_ptr
<SSL_CTX
, void(*)(SSL_CTX
*)>(SSL_CTX_new(SSLv23_server_method()), SSL_CTX_free
);
600 SSL_OP_NO_COMPRESSION
|
601 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
|
602 SSL_OP_SINGLE_DH_USE
|
603 SSL_OP_SINGLE_ECDH_USE
;
605 SSL_CTX_set_options(ctx
.get(), sslOptions
);
607 #ifdef SSL_CTX_set_ecdh_auto
608 SSL_CTX_set_ecdh_auto(ctx
.get(), 1);
611 /* load certificate and private key */
612 for (const auto& pair
: pairs
) {
613 if (SSL_CTX_use_certificate_chain_file(ctx
.get(), pair
.first
.c_str()) != 1) {
614 ERR_print_errors_fp(stderr
);
615 throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, an error occurred while trying to load the DOH server certificate file: " + pair
.first
);
617 if (SSL_CTX_use_PrivateKey_file(ctx
.get(), pair
.second
.c_str(), SSL_FILETYPE_PEM
) != 1) {
618 ERR_print_errors_fp(stderr
);
619 throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, an error occurred while trying to load the DOH server private key file: " + pair
.second
);
623 if (SSL_CTX_set_cipher_list(ctx
.get(), ciphers
.empty() == false ? ciphers
.c_str() : DOH_DEFAULT_CIPHERS
) != 1) {
624 throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, DOH ciphers could not be set: " + ciphers
);
627 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
628 if (!ciphers13
.empty() && SSL_CTX_set_ciphersuites(ctx
.get(), ciphers13
.c_str()) != 1) {
629 throw std::runtime_error("Failed to setup SSL/TLS for DoH listener, DOH TLS 1.3 ciphers could not be set: " + ciphers13
);
631 #endif /* HAVE_SSL_CTX_SET_CIPHERSUITES */
633 h2o_ssl_register_alpn_protocols(ctx
.get(), h2o_http2_alpn_protocols
);
638 static void setupAcceptContext(DOHAcceptContext
& ctx
, DOHServerConfig
& dsc
, bool setupTLS
)
640 auto nativeCtx
= ctx
.get();
641 nativeCtx
->ctx
= &dsc
.h2o_ctx
;
642 nativeCtx
->hosts
= dsc
.h2o_config
.hosts
;
644 auto tlsCtx
= getTLSContext(dsc
.df
->d_certKeyPairs
,
646 dsc
.df
->d_ciphers13
);
648 nativeCtx
->ssl_ctx
= tlsCtx
.release();
654 void DOHFrontend::reloadCertificates()
656 auto newAcceptContext
= std::unique_ptr
<DOHAcceptContext
>(new DOHAcceptContext());
657 setupAcceptContext(*newAcceptContext
, *d_dsc
, true);
658 DOHAcceptContext
* oldCtx
= d_dsc
->accept_ctx
;
659 d_dsc
->accept_ctx
= newAcceptContext
.release();
663 void DOHFrontend::setup()
665 registerOpenSSLUser();
667 d_dsc
= std::make_shared
<DOHServerConfig
>(d_idleTimeout
);
669 auto tlsCtx
= getTLSContext(d_certKeyPairs
,
673 auto accept_ctx
= d_dsc
->accept_ctx
->get();
674 accept_ctx
->ssl_ctx
= tlsCtx
.release();
675 d_dsc
->accept_ctx
->release();
678 // this is the entrypoint from dnsdist.cc
679 void dohThread(ClientState
* cs
)
682 std::shared_ptr
<DOHFrontend
>& df
= cs
->dohFrontend
;
683 auto& dsc
= df
->d_dsc
;
685 dsc
->df
= cs
->dohFrontend
;
687 std::thread
dnsdistThread(dnsdistclient
, dsc
->dohquerypair
[1], dsc
->dohresponsepair
[0]);
688 dnsdistThread
.detach(); // gets us better error reporting
690 setThreadName("dnsdist/doh");
691 // I wonder if this registers an IP address.. I think it does
692 // this may mean we need to actually register a site "name" here and not the IP address
693 h2o_hostconf_t
*hostconf
= h2o_config_register_host(&dsc
->h2o_config
, h2o_iovec_init(df
->d_local
.toString().c_str(), df
->d_local
.toString().size()), 65535);
695 for(const auto& url
: df
->d_urls
) {
696 register_handler(hostconf
, url
.c_str(), doh_handler
);
699 h2o_context_init(&dsc
->h2o_ctx
, h2o_evloop_create(), &dsc
->h2o_config
);
701 // in this complicated way we insert the DOHServerConfig pointer in there
702 h2o_vector_reserve(nullptr, &dsc
->h2o_ctx
.storage
, 1);
703 dsc
->h2o_ctx
.storage
.entries
[0].data
= dsc
.get();
704 ++dsc
->h2o_ctx
.storage
.size
;
706 auto sock
= h2o_evloop_socket_create(dsc
->h2o_ctx
.loop
, dsc
->dohresponsepair
[1], H2O_SOCKET_FLAG_DONT_READ
);
707 sock
->data
= dsc
.get();
709 // this listens to responses from dnsdist to turn into http responses
710 h2o_socket_read_start(sock
, on_dnsdist
);
712 setupAcceptContext(*dsc
->accept_ctx
, *dsc
, false);
714 if (create_listener(df
->d_local
, dsc
, cs
->tcpFD
) != 0) {
715 throw std::runtime_error("DOH server failed to listen on " + df
->d_local
.toStringWithPort() + ": " + strerror(errno
));
720 int result
= h2o_evloop_run(dsc
->h2o_ctx
.loop
, INT32_MAX
);
722 if (errno
!= EINTR
) {
723 errlog("Error in the DoH event loop: %s", strerror(errno
));
728 while (stop
== false);
731 catch(const std::exception
& e
) {
732 throw runtime_error("DOH thread failed to launch: " + std::string(e
.what()));
735 throw runtime_error("DOH thread failed to launch");