dnsdist worker thread which we also launched.
This dnsdist worker thread injects the query into the normal dnsdist flow
- (as a datagram over a socketpair). The response also goes back over a
- (different) socketpair, where we pick it up and deliver it back to h2o.
+ (over a pipe). The response also goes back over a (different) pipe,
+ where we pick it up and deliver it back to h2o.
For coordination, we use the h2o socket multiplexer, which is sensitive to our
- socketpair too.
+ pipe too.
*/
/* h2o notes.
// through the bowels of h2o
struct DOHServerConfig
{
- DOHServerConfig(uint32_t idleTimeout): accept_ctx(new DOHAcceptContext)
+ DOHServerConfig(uint32_t idleTimeout, uint32_t internalPipeBufferSize): accept_ctx(new DOHAcceptContext)
{
- if(socketpair(AF_LOCAL, SOCK_DGRAM, 0, dohquerypair) < 0) {
- unixDie("Creating a socket pair for DNS over HTTPS");
+ int fd[2];
+ if (pipe(fd) < 0) {
+ unixDie("Creating a pipe for DNS over HTTPS");
}
+ dohquerypair[0] = fd[1];
+ dohquerypair[1] = fd[0];
- if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, dohresponsepair) < 0) {
+ if (pipe(fd) < 0) {
close(dohquerypair[0]);
close(dohquerypair[1]);
- unixDie("Creating a socket pair for DNS over HTTPS");
+ unixDie("Creating a pipe for DNS over HTTPS");
}
+ dohresponsepair[0] = fd[1];
+ dohresponsepair[1] = fd[0];
+
+ setNonBlocking(dohquerypair[0]);
+ if (internalPipeBufferSize > 0) {
+ setPipeBufferSize(dohquerypair[0], internalPipeBufferSize);
+ }
+
+ setNonBlocking(dohresponsepair[0]);
+ if (internalPipeBufferSize > 0) {
+ setPipeBufferSize(dohresponsepair[0], internalPipeBufferSize);
+ }
+
+ setNonBlocking(dohresponsepair[1]);
+
h2o_config_init(&h2o_config);
h2o_config.http2.idle_timeout = idleTimeout * 1000;
}
/* increase the ref counter before sending the pointer */
oldDU->get();
- if (send(oldDU->rsock, &oldDU, sizeof(oldDU), 0) != sizeof(oldDU)) {
+
+ ssize_t sent = write(oldDU->rsock, &oldDU, sizeof(oldDU));
+ if (sent != sizeof(oldDU)) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ ++g_stats.dohResponsePipeFull;
+ }
+
oldDU->release();
}
+
oldDU->release();
oldDU = nullptr;
}
}
/* increase the ref counter before sending the pointer */
du->get();
- if (send(du->rsock, &du, sizeof(du), 0) != sizeof(du)) {
+
+ ssize_t sent = write(du->rsock, &du, sizeof(du));
+ if (sent != sizeof(du)) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ ++g_stats.dohResponsePipeFull;
+ }
+
du->release();
}
return 0;
auto ptr = du.release();
*(ptr->self) = ptr;
try {
- if(send(dsc->dohquerypair[0], &ptr, sizeof(ptr), 0) != sizeof(ptr)) {
+ ssize_t sent = write(dsc->dohquerypair[0], &ptr, sizeof(ptr));
+ if (sent != sizeof(ptr)) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ ++g_stats.dohQueryPipeFull;
+ }
ptr->release();
ptr = nullptr;
h2o_send_error_500(req, "Internal Server Error", "Internal Server Error", 0);
h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn);
ComboAddress remote;
ComboAddress local;
- h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&remote));
+
+ if (h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&remote)) == 0) {
+ /* getpeername failed, likely because the connection has already been closed,
+ but anyway that means we can't get the remote address, which could allow an ACL bypass */
+ h2o_send_error_500(req, getReasonFromStatusCode(500).c_str(), "Internal Server Error - Unable to get remote address", 0);
+ return 0;
+ }
+
h2o_socket_getsockname(sock, reinterpret_cast<struct sockaddr*>(&local));
DOHServerConfig* dsc = reinterpret_cast<DOHServerConfig*>(req->conn->ctx->storage.entries[0].data);
/* query has been parsed by h2o, which called doh_handler() in the main DoH thread.
In order not to blockfor long, doh_handler() called doh_dispatch_query() which allocated
a DOHUnit object and passed it to us */
-static void dnsdistclient(int qsock, int rsock)
+static void dnsdistclient(int qsock)
{
setThreadName("dnsdist/doh-cli");
for(;;) {
try {
DOHUnit* du = nullptr;
- ssize_t got = recv(qsock, &du, sizeof(du), 0);
+ ssize_t got = read(qsock, &du, sizeof(du));
if (got < 0) {
warnlog("Error receiving internal DoH query: %s", strerror(errno));
continue;
// so we can use UDP to talk to the backend.
auto dh = const_cast<struct dnsheader*>(reinterpret_cast<const struct dnsheader*>(du->query.c_str()));
- if(!dh->arcount) {
+ if (!dh->arcount) {
std::string res;
generateOptRR(std::string(), res, 4096, 0, false);
// we leave existing EDNS in place
}
- if(processDOHQuery(du) < 0) {
+ if (processDOHQuery(du) < 0) {
du->status_code = 500;
/* increase the ref count before sending the pointer */
du->get();
- if(send(du->rsock, &du, sizeof(du), 0) != sizeof(du)) {
- du->release(); // XXX but now what - will h2o time this out for us?
+ ssize_t sent = write(du->rsock, &du, sizeof(du));
+ if (sent != sizeof(du)) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ ++g_stats.dohResponsePipeFull;
+ }
+
+ // XXX but now what - will h2o time this out for us?
+ du->release();
}
}
du->release();
{
DOHUnit *du = nullptr;
DOHServerConfig* dsc = reinterpret_cast<DOHServerConfig*>(listener->data);
- ssize_t got = recv(dsc->dohresponsepair[1], &du, sizeof(du), 0);
+ ssize_t got = read(dsc->dohresponsepair[1], &du, sizeof(du));
if (got < 0) {
warnlog("Error reading a DOH internal response: %s", strerror(errno));
return;
}
- if(!du->req) { // it got killed in flight
+ if (!du->req) { // it got killed in flight
// cout << "du "<<(void*)du<<" came back from dnsdist, but it was killed"<<endl;
du->release();
return;
{
registerOpenSSLUser();
- d_dsc = std::make_shared<DOHServerConfig>(d_idleTimeout);
+ d_dsc = std::make_shared<DOHServerConfig>(d_idleTimeout, d_internalPipeBufferSize);
if (!d_tlsConfig.d_certKeyPairs.empty()) {
try {
dsc->h2o_config.server_name = h2o_iovec_init(df->d_serverTokens.c_str(), df->d_serverTokens.size());
- std::thread dnsdistThread(dnsdistclient, dsc->dohquerypair[1], dsc->dohresponsepair[0]);
+ std::thread dnsdistThread(dnsdistclient, dsc->dohquerypair[1]);
dnsdistThread.detach(); // gets us better error reporting
setThreadName("dnsdist/doh");