← Commit list

7f6a75664f

Stefan Eissing
Stefan Eissing
2026-08-07
SUCCESS Duration: 400s

Subject

dnsd: add support for DoH

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/vdns/doh.c+9 -9
@@ -251,8 +251,8 @@ static void doh_probe_done(struct Curl_easy *doh,
return;
}
- dohp->pending--;
- infof(doh, "a DoH request is completed, %u to go", dohp->pending);
+ async->queries_ongoing--;
+ infof(doh, "a DoH request is completed, %u to go", async->queries_ongoing);
dohp->probe_resp[i].result = result;
/* We expect either the meta data still to exist or the sub request
* to have already failed. */
@@ -267,7 +267,7 @@ static void doh_probe_done(struct Curl_easy *doh,
if(result)
infof(doh, "DoH request %s", curl_easy_strerror(result));
- if(!dohp->pending) {
+ if(!async->queries_ongoing) {
/* DoH completed, run master to act on results */
Curl_multi_mark_dirty(master);
}
@@ -487,7 +487,7 @@ CURLcode Curl_doh(struct Curl_easy *data,
&dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
if(result)
goto error;
- dohp->pending++;
+ async->queries_ongoing++;
}
#ifdef USE_IPV6
@@ -499,7 +499,7 @@ CURLcode Curl_doh(struct Curl_easy *data,
&dohp->probe_resp[DOH_SLOT_IPV6].probe_mid);
if(result)
goto error;
- dohp->pending++;
+ async->queries_ongoing++;
}
#endif
@@ -520,7 +520,7 @@ CURLcode Curl_doh(struct Curl_easy *data,
curlx_free(qname);
if(result)
goto error;
- dohp->pending++;
+ async->queries_ongoing++;
}
#endif
return CURLE_OK;
@@ -1211,7 +1211,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data,
return async->for_proxy ?
CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST;
}
- else if(!dohp->pending) {
+ else if(!async->queries_ongoing) {
struct Curl_dns_entry *dns = NULL;
DOHcode rc[DOH_SLOT_COUNT];
bool negative = TRUE;
@@ -1235,7 +1235,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data,
if(rc[slot] && (rc[slot] != DOH_DNS_NXDOMAIN))
negative = FALSE;
if(rc[slot]) {
- CURL_TRC_DNS(data, "[%s] [DoH] error: %s type %s for %s",
+ CURL_TRC_DNS(data, "[%s] [DoH] error: %s of type %s for %s",
Curl_resolv_query_str(async->dns_queries),
doh_strerror(rc[slot]),
doh_type2name(p->dnstype), async->peer->hostname);
@@ -1306,7 +1306,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data,
if(dns)
result = Curl_dnscache_add(data, dns);
*pdns = dns;
- } /* !dohp->pending */
+ } /* !async->queries_ongoing */
else
/* wait for pending DoH transactions to complete */
return CURLE_AGAIN;
lib/vdns/doh.h+0 -1
@@ -109,7 +109,6 @@ struct doh_response {
* as easy meta for CURL_EZM_DOH_MASTER */
struct doh_probes {
struct doh_response probe_resp[DOH_SLOT_COUNT];
- unsigned int pending; /* still outstanding probes */
};
/*
tests/http/test_21_resolve.py+47 -38
@@ -124,73 +124,84 @@ class TestResolve:
r.check_stats(count=count, http_status=0, exitcode=6)
assert r.duration > timedelta(milliseconds=count * delay_ms), f'{r}'
+ def dns_settings(self, dns_method, dnsd):
+ xargs = []
+ run_env = os.environ.copy()
+ run_env['CURL_DEBUG'] = 'dns,doh'
+ if dns_method == 'DoH':
+ if not Env.curl_can_doh():
+ pytest.skip(reason="curl built without DoH")
+ xargs = ['--doh-insecure', '--doh-url', f'http://127.0.0.1:{dnsd.port}/']
+ else:
+ if not Env.curl_override_dns():
+ pytest.skip(reason="no DNS override")
+ run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
+ run_env['CURL_QUICK_EXIT'] = '1'
+ return run_env, xargs
+
# dnsd with no answers
- @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override")
- def test_21_06_dnsd_empty(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_21_06_dnsd_empty(self, env: Env, httpd, dnsd, dns_method):
dnsd.set_answers()
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = 'https://test-dnsd.http.curl.invalid/'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(6) # could not resolve host
r.check_stats(count=1, http_status=0, exitcode=6)
# dnsd with one answer for A
- @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override")
- def test_21_07_dnsd_a(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_21_07_dnsd_a(self, env: Env, httpd, dnsd, dns_method):
dnsd.set_answers(addr_a=['127.0.0.1'])
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['remote_ip'] == '127.0.0.1'
# dnsd with one answer for AAAA
- @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override")
@pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6")
- def test_21_08_dnsd_aaaa(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_21_08_dnsd_aaaa(self, env: Env, httpd, dnsd, dns_method):
dnsd.set_answers(addr_aaaa=['[::1]'])
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_QUICK_EXIT'] = '1'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['remote_ip'] == '::1'
# dnsd with one answer for A, delayed one for AAAA
- @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override")
- def test_21_09_dnsd_a_delay(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_21_09_dnsd_a_delay(self, env: Env, httpd, dnsd, dns_method):
+ if dns_method == 'DoH':
+ pytest.skip(reason='DoH does not handle partial responses')
dnsd.set_answers(addr_a=['127.0.0.1'], addr_aaaa=['[::1]'],
delay_aaaa_ms=env.test_timeout * 1000)
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_QUICK_EXIT'] = '1'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['remote_ip'] == '127.0.0.1'
# dnsd with one answer for AAAA, delayed one for A
- @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override")
@pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6")
- def test_21_10_dnsd_aaaa_delay(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_21_10_dnsd_aaaa_delay(self, env: Env, httpd, dnsd, dns_method):
+ if dns_method == 'DoH':
+ pytest.skip(reason='DoH does not handle partial responses')
dnsd.set_answers(addr_a=['127.0.0.1'], addr_aaaa=['[::1]'],
delay_a_ms=env.test_timeout * 1000)
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_QUICK_EXIT'] = '1'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['remote_ip'] == '::1'
@@ -234,15 +245,14 @@ class TestResolve:
# dnsd giving NXDOMAIN for all families: the negative answer is
# cached and a second lookup of the same name uses the cache
- @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override")
- def test_21_13_dnsd_nxdomain_cached(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_21_13_dnsd_nxdomain_cached(self, env: Env, httpd, dnsd, dns_method):
count = 2
dnsd.set_answers(rcode_a=3, rcode_aaaa=3)
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
urls = [f'https://test-nx.http.curl.invalid/?id={i}' for i in range(count)]
- r = curl.http_download(urls=urls, with_stats=True)
+ r = curl.http_download(urls=urls, with_stats=True, extra_args=xargs)
r.check_exit_code(6)
r.check_stats(count=count, http_status=0, exitcode=6)
if env.curl_is_verbose():
@@ -250,15 +260,14 @@ class TestResolve:
# dnsd failing one family with SERVFAIL: not an authoritative
# negative answer, a second lookup of the same name tries again
- @pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override")
- def test_21_14_dnsd_servfail_uncached(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_21_14_dnsd_servfail_uncached(self, env: Env, httpd, dnsd, dns_method):
count = 2
dnsd.set_answers(rcode_a=2, rcode_aaaa=3)
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
urls = [f'https://test-sf.http.curl.invalid/?id={i}' for i in range(count)]
- r = curl.http_download(urls=urls, with_stats=True)
+ r = curl.http_download(urls=urls, with_stats=True, extra_args=xargs)
r.check_exit_code(6)
r.check_stats(count=count, http_status=0, exitcode=6)
if env.curl_is_verbose():
tests/http/test_22_httpsrr.py+41 -44
@@ -33,7 +33,6 @@ log = logging.getLogger(__name__)
@pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug")
-@pytest.mark.skipif(condition=not Env.curl_override_dns(), reason="no DNS override")
@pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPSRR'), reason="no HTTPSRR support")
class TestHTTPSRR:
@@ -44,86 +43,86 @@ class TestHTTPSRR:
yield dnsd
dnsd.stop()
- # dnsd a HTTPS-RR that prefers HTTP/1.1.
- def test_22_01_httpsrr_h1(self, env: Env, httpd, dnsd):
- dnsd.set_answers(addr_a=['127.0.0.1'],
- https=['10 . alpn=http/1.1'])
+ def dns_settings(self, dns_method, dnsd):
run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1'
run_env['CURL_QUICK_EXIT'] = '1'
run_env['CURL_DEBUG'] = 'dns,https-connect'
+ run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1'
+ xargs = []
+ if dns_method == 'DoH':
+ if not Env.curl_can_doh():
+ pytest.skip(reason="curl built without DoH")
+ xargs = ['--doh-insecure', '--doh-url', f'http://127.0.0.1:{dnsd.port}/']
+ else:
+ if not Env.curl_override_dns():
+ pytest.skip(reason="no DNS override")
+ run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
+ return run_env, xargs
+
+ # dnsd a HTTPS-RR that prefers HTTP/1.1.
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_22_01_httpsrr_h1(self, env: Env, httpd, dnsd, dns_method):
+ dnsd.set_answers(addr_a=['127.0.0.1'],
+ https=['10 . alpn=http/1.1'])
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['http_version'] == '1.1', f'{r}'
# dnsd a HTTPS-RR that prefers HTTP/2, this overrides the --http3 option.
@pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support")
- def test_22_02_httpsrr_h3(self, env: Env, httpd, dnsd, nghttpx):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_22_02_httpsrr_h3(self, env: Env, httpd, dnsd, nghttpx, dns_method):
dnsd.set_answers(addr_a=['127.0.0.1'],
https=['10 . alpn=h2'])
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1'
- run_env['CURL_QUICK_EXIT'] = '1'
- run_env['CURL_DEBUG'] = 'dns,https-connect'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
+ xargs.append('--http3')
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True, extra_args=[
- '--http3'
- ])
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['http_version'] == '2', f'{r}'
# dnsd a HTTPS-RR that prefers HTTP/3.
@pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support")
- def test_22_03_httpsrr_h3(self, env: Env, httpd, dnsd, nghttpx):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_22_03_httpsrr_h3(self, env: Env, httpd, dnsd, nghttpx, dns_method):
dnsd.set_answers(addr_a=['127.0.0.1'],
https=['10 . alpn=h3,h2'])
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1'
- run_env['CURL_QUICK_EXIT'] = '1'
- run_env['CURL_DEBUG'] = 'dns,https-connect'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['http_version'] == '3', f'{r}'
# dnsd a HTTPS-RR that prefers HTTP/1.1 for another target, so ignored.
- def test_22_04_httpsrr_wrong_target(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_22_04_httpsrr_wrong_target(self, env: Env, httpd, dnsd, dns_method):
dnsd.set_answers(addr_a=['127.0.0.1'],
https=['10 another alpn=http/1.1'])
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1'
- run_env['CURL_QUICK_EXIT'] = '1'
- run_env['CURL_DEBUG'] = 'dns,https-connect'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['http_version'] == '2', f'{r}'
# dnsd a HTTPS-RR with no-default-alpn, ignored by curl for now
- def test_22_05_httpsrr_no_default_alpn(self, env: Env, httpd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_22_05_httpsrr_no_default_alpn(self, env: Env, httpd, dnsd, dns_method):
dnsd.set_answers(addr_a=['127.0.0.1'],
https=['10 . no-default-alpn alpn=http/1.1'])
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1'
- run_env['CURL_QUICK_EXIT'] = '1'
- run_env['CURL_DEBUG'] = 'dns,https-connect'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env, force_resolv=False)
url = f'https://{env.authority_for(env.domain1, "http/1.1")}/data.json'
- r = curl.http_download(urls=[url], with_stats=True)
+ r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs)
r.check_exit_code(0)
r.check_stats(count=1, http_status=200, exitcode=0)
assert r.stats[0]['http_version'] == '2', f'{r}'
@@ -132,16 +131,14 @@ class TestHTTPSRR:
@pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPS-proxy'),
reason='curl lacks HTTPS-proxy support')
@pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available")
- def test_22_06_httpsrr_proxy(self, env: Env, httpd, nghttpx_fwd, dnsd):
+ @pytest.mark.parametrize("dns_method", ["DNS", "DoH"])
+ def test_22_06_httpsrr_proxy(self, env: Env, httpd, nghttpx_fwd, dnsd, dns_method):
dnsd.set_answers(addr_a=['127.0.0.1'],
https=['10 . alpn=http/1.1'])
- run_env = os.environ.copy()
- run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
- run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1'
- run_env['CURL_QUICK_EXIT'] = '1'
+ run_env, xargs = self.dns_settings(dns_method, dnsd)
curl = CurlClient(env=env, run_env=run_env)
url = f'https://localhost:{env.https_port}/data.json'
- xargs = curl.get_proxy_args(tunnel=True)
+ xargs.extend(curl.get_proxy_args(tunnel=True))
r = curl.http_download(urls=[url], alpn_proto='h2', with_stats=True,
extra_args=xargs)
r.check_response(count=1, http_status=200)
tests/http/testenv/env.py+5 -0
@@ -163,6 +163,7 @@ class EnvConfig:
self.curl_can_cert_status = 'cert-status: ON' in p.stdout
self.curl_override_dns = 'override-dns: ON' in p.stdout
self.curl_resolv_threaded = 'resolv-threaded: ON' in p.stdout
+ self.curl_can_doh = 'DoH: ON' in p.stdout
self.ports = {}
@@ -590,6 +591,10 @@ class Env:
def curl_resolv_threaded() -> bool:
return Env.CONFIG.curl_resolv_threaded
+ @staticmethod
+ def curl_can_doh() -> bool:
+ return Env.CONFIG.curl_can_doh
+
@staticmethod
def curl_can_early_data() -> bool:
if Env.curl_uses_lib('gnutls'):
tests/server/dnsd.c+916 -443
@@ -187,7 +187,8 @@ static const char *type2string(uint16_t qtype)
*
* Return query (qname + type + class), type and id.
*/
-static int store_incoming(int qid, const unsigned char *data, size_t size,
+static int store_incoming(const char *source, int query_id,
+ const unsigned char *data, size_t size,
unsigned char *qbuf, size_t qbuflen, size_t *qlen,
uint16_t *qtype, uint16_t *idp)
{
@@ -214,7 +215,7 @@ static int store_incoming(int qid, const unsigned char *data, size_t size,
int error = errno;
logmsg("fopen() failed with error (%d) %s",
error, curlx_strerror(error, errbuf, sizeof(errbuf)));
- logmsg("Error opening file '%s'", dumpfile);
+ logmsg("[%s] Error opening file '%s'", source, dumpfile);
return -1;
}
@@ -239,12 +240,12 @@ static int store_incoming(int qid, const unsigned char *data, size_t size,
data += 2; /* skip the next 16 bits */
size -= 2;
#if 0
- fprintf(server, "QR: %x\n", (*idp & 0x8000) > 15);
- fprintf(server, "OPCODE: %x\n", (*idp & 0x7800) >> 11);
- fprintf(server, "TC: %x\n", (*idp & 0x200) >> 9);
- fprintf(server, "RD: %x\n", (*idp & 0x100) >> 8);
- fprintf(server, "Z: %x\n", (*idp & 0x70) >> 4);
- fprintf(server, "RCODE: %x\n", (*idp & 0x0f));
+ fprintf(server, "[%s] QR: %x\n", source, (*idp & 0x8000) > 15);
+ fprintf(server, "[%s] OPCODE: %x\n", source, (*idp & 0x7800) >> 11);
+ fprintf(server, "[%s] TC: %x\n", source, (*idp & 0x200) >> 9);
+ fprintf(server, "[%s] RD: %x\n", source, (*idp & 0x100) >> 8);
+ fprintf(server, "[%s] Z: %x\n", source, (*idp & 0x70) >> 4);
+ fprintf(server, "[%s] RCODE: %x\n", source, (*idp & 0x0f));
#endif
(void)get16bit(&data, &size);
@@ -259,8 +260,8 @@ static int store_incoming(int qid, const unsigned char *data, size_t size,
qd = get16bit(&data, &size);
fprintf(server, "QNAME %s QTYPE %s\n", name, type2string(qd));
*qtype = qd;
- logmsg("[%d] Question for '%s' type %x / %s",
- qid, name, qd, type2string(qd));
+ logmsg("[%d] [%s] Question for '%s' type %x / %s",
+ query_id, source, name, qd, type2string(qd));
(void)get16bit(&data, &size);
@@ -310,256 +311,26 @@ static int add_answer(struct blob *body,
return blob_addn(body, a, alen);
}
-#ifdef _WIN32
-#define SENDTO3 int
-#else
-#define SENDTO3 size_t
-#endif
-
-#define INSTRUCTIONS "dnsd.cmd"
-
-static curlx_struct_stat finfo_last;
-static unsigned char ipv4_pref[4];
-static unsigned char ipv6_pref[16];
-static unsigned char ancount_a;
-static unsigned char ancount_aaaa;
-
-static timediff_t a_delay_ms;
-static timediff_t aaaa_delay_ms;
-static timediff_t https_delay_ms;
-static unsigned char rcode_a;
-static unsigned char rcode_aaaa;
-
-static int query_id = -1;
-
-static struct blob httpsrr;
-
-struct resp {
- struct resp *next;
- int qid;
- struct curltime send_ts;
- struct sockaddr addr;
- curl_socklen_t addrlen;
- struct blob body;
-};
-
-static struct resp *resp_queue;
-
-static CURLcode send_resp(curl_socket_t sock, struct resp *resp)
-{
- ssize_t rc;
- int sockerr = 0;
-
- do {
- rc = sendto(sock, (const void *)resp->body.data, (SENDTO3)resp->body.dlen,
- 0, &resp->addr, resp->addrlen);
- } while((rc < 0) && ((sockerr = SOCKERRNO) == SOCKEINTR));
- if(rc < 0) {
- char errbuf[STRERROR_LEN];
- logmsg("failed sending %zu bytes, error: (%d) %s", resp->body.dlen,
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- return CURLE_SEND_ERROR;
- }
- else if(rc != (ssize_t)resp->body.dlen) {
- logmsg("failed sending %zu bytes, sent: %zd", resp->body.dlen, rc);
- return CURLE_SEND_ERROR;
- }
- logmsg("[%d] sent response", resp->qid);
- return CURLE_OK;
-}
-
-static void queue_resp(struct resp *resp)
-{
- struct resp **panchor = &resp_queue;
- while(*panchor) {
- timediff_t ms = curlx_ptimediff_ms(&(*panchor)->send_ts, &resp->send_ts);
- if(ms > 0) /* resp is to be sent before *panchor */
- break;
- panchor = &(*panchor)->next;
- }
- resp->next = *panchor;
- *panchor = resp;
-}
-
-static timediff_t send_resp_queue(curl_socket_t sock)
-{
- struct resp **panchor = &resp_queue;
- struct curltime now = curlx_now();
- timediff_t timeout_ms = 0;
-
- while(*panchor) {
- struct resp *resp = *panchor;
- timediff_t ms = curlx_ptimediff_ms(&resp->send_ts, &now);
-
- if(ms > 0) {
- timeout_ms = ms;
- break;
- }
- *panchor = resp->next;
- send_resp(sock, resp);
- curlx_free(resp);
- }
- return timeout_ms;
-}
-
-static void clear_resp_queue(void)
+static void fdset_add_sock(fd_set *fds, curl_socket_t sock, int *pmaxfd)
{
- while(resp_queue) {
- struct resp *resp = resp_queue;
- resp_queue = resp->next;
- curlx_free(resp);
- }
+ FD_SET(sock, fds);
+ if((int)sock > *pmaxfd)
+ *pmaxfd = (int)sock;
}
-/* this is an answer to a question */
-static struct resp *
-create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen,
- const unsigned char *qbuf, size_t qlen,
- uint16_t qtype, uint16_t id)
+static struct curltime now_plus(timediff_t delta_ms)
{
- struct resp *resp;
- int a;
- timediff_t delay_ms = 0;
- char addrbuf[128]; /* IP address buffer */
- uint8_t header[12] = {
- 0x80, 0xea, /* ID, overwrite */
- 0x81, 0x80,
- /* Flags: 0x8180 Standard query response, No error
-
- 1... .... .... .... = Response: Message is a response
- .000 0... .... .... = Opcode: Standard query (0)
- .... .0.. .... .... = Authoritative: Server is not an authority for
- domain
- .... ..0. .... .... = Truncated: Message is not truncated
- .... ...1 .... .... = Recursion desired: Do query recursively
- .... .... 1... .... = Recursion available: Server can do recursive
- queries
- .... .... .0.. .... = Z: reserved (0)
- .... .... ..0. .... = Answer authenticated: Answer/authority portion
- was not authenticated by the server
- .... .... ...0 .... = Non-authenticated data: Unacceptable
- .... .... .... 0000 = Reply code: No error (0)
- */
- 0x0, 0x1, /* QDCOUNT a single question */
- 0x0, 0x0, /* ANCOUNT number of answers */
- 0x0, 0x0, /* NSCOUNT */
- 0x0, 0x0 /* ARCOUNT */
- };
- uint16_t ancount = 0;
- unsigned char rcode = 0;
-
- switch(qtype) {
- case QTYPE_A:
- ancount = ancount_a;
- delay_ms = a_delay_ms;
- rcode = rcode_a;
- break;
- case QTYPE_AAAA:
- ancount = ancount_aaaa;
- delay_ms = aaaa_delay_ms;
- rcode = rcode_aaaa;
- break;
- case QTYPE_HTTPS:
- if(httpsrr.dlen)
- ancount = 1;
- delay_ms = https_delay_ms;
- break;
- }
- if(rcode)
- ancount = 0;
-
- resp = curlx_calloc(1, sizeof(*resp));
- if(!resp)
- goto error;
-
- resp->qid = qid;
- /* on some platforms `curl_socklen_t` is an `int`. Casting might
- * wrap this, but then it still has to fit our record size. */
- if((size_t)addrlen > sizeof(resp->addr)) {
- logmsg("unable to handle addrlen of %zu", (size_t)addrlen);
- goto error;
- }
- memcpy(&resp->addr, CURL_UNCONST(addr), addrlen);
- resp->addrlen = addrlen;
-
- header[0] = (uint8_t)(id >> 8);
- header[1] = (uint8_t)(id & 0xff);
-
- if(rcode) {
- header[3] = (uint8_t)((header[3] & 0xf0) | (rcode & 0x0f));
- logmsg("[%d] response rcode %u", qid, (unsigned int)rcode);
- }
-
- header[6] = (uint8_t)(ancount >> 8);
- header[7] = (uint8_t)(ancount & 0xff);
-
- if(blob_addn(&resp->body, header, sizeof(header)))
- goto error;
-
- if(blob_addn(&resp->body, qbuf, qlen)) {
- logmsg("unable to handle query of length %zu", qlen);
- goto error;
- }
-
- switch(qtype) {
- case QTYPE_A:
- for(a = 0; !rcode && (a < ancount_a); a++) {
- const unsigned char *store = ipv4_pref;
- const char *ip;
- if(add_answer(&resp->body, store, sizeof(ipv4_pref), QTYPE_A))
- goto error;
- ip = curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf));
- logmsg("[%d] response A (%x) '%s'", qid, (unsigned int)QTYPE_A,
- ip ? ip : "(null)");
- }
- if(!ancount_a)
- logmsg("[%d] response A empty", qid);
- break;
- case QTYPE_AAAA:
- for(a = 0; !rcode && (a < ancount_aaaa); a++) {
- const unsigned char *store = ipv6_pref;
- const char *ip;
- if(add_answer(&resp->body, store, sizeof(ipv6_pref), QTYPE_AAAA))
- goto error;
- ip = curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf));
- logmsg("[%d] response AAAA (%x) '%s'", qid, (unsigned int)QTYPE_AAAA,
- ip ? ip : "(null)");
- }
- if(!ancount_aaaa)
- logmsg("[%d] response AAAA empty", qid);
- break;
- case QTYPE_HTTPS:
- if(httpsrr.dlen) {
- if(add_answer(&resp->body, httpsrr.data, httpsrr.dlen, QTYPE_HTTPS)) {
- logmsg("[%d] error adding https %zu response bytes", qid,
- httpsrr.dlen);
- goto error;
- }
- logmsg("[%d] response HTTPS (%x), %zu bytes", qid,
- (unsigned int)QTYPE_HTTPS, httpsrr.dlen);
- }
- else
- logmsg("[%d] response HTTPS, no record", qid);
- break;
- }
-
- resp->send_ts = curlx_now();
- if(delay_ms > 0) {
- int usec = (int)((delay_ms % 1000) * 1000);
- resp->send_ts.tv_sec += (time_t)(delay_ms / 1000);
- resp->send_ts.tv_usec += usec;
- if(resp->send_ts.tv_usec >= 1000000) {
- resp->send_ts.tv_sec++;
- resp->send_ts.tv_usec -= 1000000;
+ struct curltime ts = curlx_now();
+ if(delta_ms > 0) {
+ int usec = (int)((delta_ms % 1000) * 1000);
+ ts.tv_sec += (time_t)(delta_ms / 1000);
+ ts.tv_usec += usec;
+ if(ts.tv_usec >= 1000000) {
+ ts.tv_sec++;
+ ts.tv_usec -= 1000000;
}
- logmsg("[%d] delay response by %" FMT_TIMEDIFF_T "ms", qid, delay_ms);
}
- return resp;
-
-error:
- logmsg("[%d] failed to create response", qid);
- curlx_free(resp);
- return NULL;
+ return ts;
}
static int read_https_alpn_part(struct blob *b, struct Curl_str *str)
@@ -580,6 +351,27 @@ static int read_https_alpn_part(struct blob *b, struct Curl_str *str)
return 0;
}
+#ifdef _WIN32
+#define SENDTO3 int
+#else
+#define SENDTO3 size_t
+#endif
+
+#define INSTRUCTIONS "dnsd.cmd"
+
+static curlx_struct_stat finfo_last;
+static unsigned char ipv4_pref[4];
+static unsigned char ipv6_pref[16];
+static unsigned char ancount_a;
+static unsigned char ancount_aaaa;
+
+static timediff_t a_delay_ms;
+static timediff_t aaaa_delay_ms;
+static timediff_t https_delay_ms;
+static unsigned char rcode_a;
+static unsigned char rcode_aaaa;
+static struct blob httpsrr;
+
static int read_https_alpn(struct blob *b, const char **ps)
{
struct Curl_str word;
@@ -618,7 +410,7 @@ static int read_https(struct blob *b, const char *s)
curlx_str_passblanks(&s);
if(curlx_str_word(&s, &word, UINT16_MAX)) {
- logmsg("https: unable to read target qname, input=%s", s);
+ logmsg("[CONFIG] https: unable to read target qname, input=%s", s);
return 1;
}
if(blob_add_qname(b, &word))
@@ -675,7 +467,7 @@ static void read_instructions(void)
blob_reset(&httpsrr);
finfo_last = finfo;
- logmsg("read instructions from %s", file);
+ logmsg("[CONFIG] reading from %s", file);
f = curlx_fopen(file, FOPEN_READTEXT);
if(f) {
char buf[256];
@@ -757,55 +549,820 @@ static void read_instructions(void)
rc = buf[0] ? 0 : 1;
}
if(rc != 1) {
- logmsg("Bad line in %s: '%s'", file, buf);
+ logmsg("[CONFIG] Bad line in %s: '%s'", file, buf);
}
else if(rtype) {
- logmsg("added %s record via '%s'", rtype, buf);
+ logmsg("[CONFIG] added %s record via '%s'", rtype, buf);
}
}
}
- logmsg("set delays: A=%" FMT_TIMEDIFF_T "ms AAAA=%" FMT_TIMEDIFF_T
- "ms HTTPS=%" FMT_TIMEDIFF_T "ms",
+ logmsg("[CONFIG] set delays: A=%" FMT_TIMEDIFF_T "ms AAAA=%"
+ FMT_TIMEDIFF_T "ms HTTPS=%" FMT_TIMEDIFF_T "ms",
a_delay_ms, aaaa_delay_ms, https_delay_ms);
curlx_fclose(f);
}
else
- logmsg("Error opening file '%s'", file);
+ logmsg("[CONFIG] Error opening file '%s'", file);
}
-static int test_dnsd(int argc, const char **argv)
+static int last_query_id = -1;
+
+static int dnsd_make_answer(struct blob *blob, int query_id,
+ const uint8_t *qbuf, size_t qlen,
+ uint16_t qtype, uint16_t id,
+ timediff_t *pdelay_ms)
{
- srvr_sockaddr_union_t me;
- ssize_t n = 0;
- int arg = 1;
- curl_socket_t sock = CURL_SOCKET_BAD;
- int flag;
- int rc;
- int sockerr;
- char errbuf[STRERROR_LEN];
- int result = 0;
- struct resp *resp;
+ int a;
+ char addrbuf[128]; /* IP address buffer */
+ uint8_t header[12] = {
+ 0x80, 0xea, /* ID, overwrite */
+ 0x81, 0x80,
+ /* Flags: 0x8180 Standard query response, No error
- pidname = ".dnsd.pid";
- serverlogfile = "log/dnsd.log";
- serverlogslocked = 0;
- server_port = 9123; /* UDP */
+ 1... .... .... .... = Response: Message is a response
+ .000 0... .... .... = Opcode: Standard query (0)
+ .... .0.. .... .... = Authoritative: Server is not an authority for
+ domain
+ .... ..0. .... .... = Truncated: Message is not truncated
+ .... ...1 .... .... = Recursion desired: Do query recursively
+ .... .... 1... .... = Recursion available: Server can do recursive
+ queries
+ .... .... .0.. .... = Z: reserved (0)
+ .... .... ..0. .... = Answer authenticated: Answer/authority portion
+ was not authenticated by the server
+ .... .... ...0 .... = Non-authenticated data: Unacceptable
+ .... .... .... 0000 = Reply code: No error (0)
+ */
+ 0x0, 0x1, /* QDCOUNT a single question */
+ 0x0, 0x0, /* ANCOUNT number of answers */
+ 0x0, 0x0, /* NSCOUNT */
+ 0x0, 0x0 /* ARCOUNT */
+ };
+ uint16_t ancount = 0;
+ unsigned char rcode = 0;
- while(argc > arg) {
- const char *opt;
- curl_off_t num;
- if(!strcmp("--verbose", argv[arg])) {
- arg++;
- /* nothing yet */
- }
- else if(!strcmp("--version", argv[arg])) {
- printf("dnsd IPv4%s\n",
-#ifdef USE_IPV6
- "/IPv6"
-#else
- ""
-#endif
- );
+ /* read once per incoming query, which is probably more than one
+ per test case */
+ read_instructions();
+
+ switch(qtype) {
+ case QTYPE_A:
+ ancount = ancount_a;
+ *pdelay_ms = a_delay_ms;
+ rcode = rcode_a;
+ break;
+ case QTYPE_AAAA:
+ ancount = ancount_aaaa;
+ *pdelay_ms = aaaa_delay_ms;
+ rcode = rcode_aaaa;
+ break;
+ case QTYPE_HTTPS:
+ if(httpsrr.dlen)
+ ancount = 1;
+ *pdelay_ms = https_delay_ms;
+ break;
+ default:
+ *pdelay_ms = 0;
+ }
+ if(rcode)
+ ancount = 0;
+
+ header[0] = (uint8_t)(id >> 8);
+ header[1] = (uint8_t)(id & 0xff);
+
+ if(rcode) {
+ header[3] = (uint8_t)((header[3] & 0xf0) | (rcode & 0x0f));
+ logmsg("[%d] response rcode %u", query_id, (unsigned int)rcode);
+ }
+
+ header[6] = (uint8_t)(ancount >> 8);
+ header[7] = (uint8_t)(ancount & 0xff);
+
+ if(blob_addn(blob, header, sizeof(header)))
+ return 1;
+
+ if(blob_addn(blob, qbuf, qlen)) {
+ logmsg("unable to handle query of length %zu", qlen);
+ return 1;
+ }
+
+ switch(qtype) {
+ case QTYPE_A:
+ for(a = 0; !rcode && (a < ancount_a); a++) {
+ const unsigned char *store = ipv4_pref;
+ const char *ip;
+ if(add_answer(blob, store, sizeof(ipv4_pref), QTYPE_A))
+ return 1;
+ ip = curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf));
+ logmsg("[%d] response A (%x) '%s'", query_id, (unsigned int)QTYPE_A,
+ ip ? ip : "(null)");
+ }
+ if(!ancount_a)
+ logmsg("[%d] response A empty", query_id);
+ break;
+ case QTYPE_AAAA:
+ for(a = 0; !rcode && (a < ancount_aaaa); a++) {
+ const unsigned char *store = ipv6_pref;
+ const char *ip;
+ if(add_answer(blob, store, sizeof(ipv6_pref), QTYPE_AAAA))
+ return 1;
+ ip = curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf));
+ logmsg("[%d] response AAAA (%x) '%s'", query_id,
+ (unsigned int)QTYPE_AAAA, ip ? ip : "(null)");
+ }
+ if(!ancount_aaaa)
+ logmsg("[%d] response AAAA empty", query_id);
+ break;
+ case QTYPE_HTTPS:
+ if(httpsrr.dlen) {
+ if(add_answer(blob, httpsrr.data, httpsrr.dlen, QTYPE_HTTPS)) {
+ logmsg("[%d] error adding https %zu response bytes", query_id,
+ httpsrr.dlen);
+ return 1;
+ }
+ logmsg("[%d] response HTTPS (%x), %zu bytes", query_id,
+ (unsigned int)QTYPE_HTTPS, httpsrr.dlen);
+ }
+ else
+ logmsg("[%d] response HTTPS, no record", query_id);
+ break;
+ }
+
+ return 0;
+}
+
+struct udp_resp {
+ struct udp_resp *next;
+ int query_id;
+ struct curltime send_ts;
+ struct sockaddr addr;
+ curl_socklen_t addrlen;
+ struct blob body;
+};
+
+static struct udp_resp *udp_resp_queue;
+
+static CURLcode send_udp_resp(curl_socket_t sock, struct udp_resp *resp)
+{
+ ssize_t rc;
+ int sockerr = 0;
+
+ do {
+ rc = sendto(sock, (const void *)resp->body.data, (SENDTO3)resp->body.dlen,
+ 0, &resp->addr, resp->addrlen);
+ } while((rc < 0) && ((sockerr = SOCKERRNO) == SOCKEINTR));
+ if(rc < 0) {
+ char errbuf[STRERROR_LEN];
+ logmsg("[%d-UDP] failed sending %zu bytes, error: (%d) %s",
+ resp->query_id, resp->body.dlen,
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ return CURLE_SEND_ERROR;
+ }
+ else if(rc != (ssize_t)resp->body.dlen) {
+ logmsg("[%d-UDP] failed sending %zu bytes, sent: %zd",
+ resp->query_id, resp->body.dlen, rc);
+ return CURLE_SEND_ERROR;
+ }
+ logmsg("[%d-UDP] sent response", resp->query_id);
+ return CURLE_OK;
+}
+
+static void queue_udp_resp(struct udp_resp *resp)
+{
+ struct udp_resp **panchor = &udp_resp_queue;
+ while(*panchor) {
+ timediff_t ms = curlx_ptimediff_ms(&(*panchor)->send_ts, &resp->send_ts);
+ if(ms > 0) /* resp is to be sent before *panchor */
+ break;
+ panchor = &(*panchor)->next;
+ }
+ resp->next = *panchor;
+ *panchor = resp;
+}
+
+static timediff_t queue_udp_next_ms(struct curltime *pnow)
+{
+ timediff_t next_ms = 1000;
+ struct udp_resp *r;
+
+ for(r = udp_resp_queue; r; r = r->next) {
+ timediff_t ms = curlx_ptimediff_ms(&r->send_ts, pnow);
+ if((ms > 0) && (ms < next_ms))
+ next_ms = ms;
+ else if(ms <= 0)
+ return 0;
+ }
+ return next_ms;
+}
+
+static void queue_udp_send(curl_socket_t sock, struct curltime *pnow)
+{
+ struct udp_resp **panchor = &udp_resp_queue;
+
+ while(*panchor) {
+ struct udp_resp *resp = *panchor;
+ timediff_t ms = curlx_ptimediff_ms(&resp->send_ts, pnow);
+
+ /* if not due yet, break as response queue is time sorted */
+ if(ms > 0)
+ break;
+ *panchor = resp->next;
+ send_udp_resp(sock, resp);
+ curlx_free(resp);
+ }
+}
+
+static void queue_udp_clear(void)
+{
+ while(udp_resp_queue) {
+ struct udp_resp *resp = udp_resp_queue;
+ udp_resp_queue = resp->next;
+ curlx_free(resp);
+ }
+}
+
+/* this is an answer to a question */
+static struct udp_resp *
+udp_resp_create(int query_id,
+ const struct sockaddr *addr, curl_socklen_t addrlen,
+ const unsigned char *qbuf, size_t qlen,
+ uint16_t qtype, uint16_t id)
+{
+ struct udp_resp *resp;
+ timediff_t delay_ms = 0;
+
+ resp = curlx_calloc(1, sizeof(*resp));
+ if(!resp)
+ goto error;
+
+ resp->query_id = query_id;
+ /* on some platforms `curl_socklen_t` is an `int`. Casting might
+ * wrap this, but then it still has to fit our record size. */
+ if((size_t)addrlen > sizeof(resp->addr)) {
+ logmsg("[%d-UDP] unable to handle addrlen of %zu",
+ query_id, (size_t)addrlen);
+ goto error;
+ }
+ memcpy(&resp->addr, CURL_UNCONST(addr), addrlen);
+ resp->addrlen = addrlen;
+
+ if(dnsd_make_answer(&resp->body, query_id, qbuf, qlen, qtype, id, &delay_ms))
+ goto error;
+
+ resp->send_ts = now_plus(delay_ms);
+ if(delay_ms > 0)
+ logmsg("[%d-UDP] delay response by %" FMT_TIMEDIFF_T "ms",
+ query_id, delay_ms);
+ return resp;
+
+error:
+ logmsg("[%d-UDP] failed to create response", query_id);
+ curlx_free(resp);
+ return NULL;
+}
+
+static int udp_recv_req(curl_socket_t sock)
+{
+ srvr_sockaddr_union_t from;
+ curl_socklen_t fromlen;
+ uint8_t inbuffer[1500];
+ uint8_t qbuf[256]; /* query storage */
+ size_t qlen = 0; /* query size */
+ struct udp_resp *resp;
+ uint16_t qtype = 0, id;
+ ssize_t n;
+ int result = 0;
+
+ fromlen = sizeof(from);
+#ifdef USE_IPV6
+ if(socket_domain == AF_INET6)
+ fromlen = sizeof(from.sa6);
+ else
+#endif
+ fromlen = sizeof(from.sa4);
+
+ n = (ssize_t)recvfrom(sock, (char *)inbuffer, sizeof(inbuffer), 0,
+ &from.sa, &fromlen);
+ if(got_exit_signal)
+ goto out;
+ if(n < 0) {
+ logmsg("UDP, recvfrom error");
+ result = 3;
+ goto out;
+ }
+
+ ++last_query_id;
+ store_incoming("UDP", last_query_id, inbuffer, n,
+ qbuf, sizeof(qbuf), &qlen, &qtype, &id);
+
+ set_advisor_read_lock(loglockfile);
+ serverlogslocked = 1;
+
+ resp = udp_resp_create(last_query_id, &from.sa, fromlen, qbuf,
+ qlen, qtype, id);
+ if(!resp)
+ logmsg("[%d-UDP] error creating response", last_query_id);
+ else
+ queue_udp_resp(resp);
+
+out:
+ return result;
+}
+
+#define MAX_DOH_CONNS 512
+#define MAX_DOH_INBUF_LEN (8 * 1024)
+#define MAX_DOH_OUTBUF_LEN (8 * 1024)
+
+struct doh_conn {
+ curl_socket_t sock;
+ int index;
+ int query_id;
+ char inbuf[MAX_DOH_INBUF_LEN];
+ size_t inblen;
+ size_t inbody_offset;
+ size_t inbody_len;
+ char outbuf[MAX_DOH_OUTBUF_LEN];
+ size_t outblen;
+ struct curltime send_ts;
+ BIT(want_recv);
+ BIT(want_send);
+ BIT(close_pending);
+};
+
+static struct doh_conn doh_conns[MAX_DOH_CONNS];
+
+static void doh_conns_init(void)
+{
+ int i;
+ for(i = 0; i < MAX_DOH_CONNS; ++i) {
+ doh_conns[i].sock = CURL_SOCKET_BAD;
+ doh_conns[i].index = i;
+ }
+}
+
+static int doh_conns_add(curl_socket_t sock)
+{
+ int i;
+ for(i = 0; i < MAX_DOH_CONNS; ++i) {
+ if(doh_conns[i].sock == CURL_SOCKET_BAD) {
+ doh_conns[i].sock = sock;
+ doh_conns[i].want_recv = TRUE;
+ logmsg("[x-%d-DOH] accepted new connection, fd=%ld", i, (long)sock);
+ return 0;
+ }
+ }
+ logmsg("Too many open DoH connections, closing incoming.");
+ sclose(sock);
+ return 1;
+}
+
+static void doh_conn_close(size_t i)
+{
+ if(i >= MAX_DOH_CONNS)
+ return;
+ if(doh_conns[i].sock != CURL_SOCKET_BAD)
+ sclose(doh_conns[i].sock);
+ doh_conns[i].sock = CURL_SOCKET_BAD;
+ doh_conns[i].want_recv = FALSE;
+ doh_conns[i].want_send = FALSE;
+ logmsg("[x-%d-DOH] connection closed", (int)i);
+}
+
+static void doh_conns_close_all(void)
+{
+ size_t i;
+ for(i = 0; i < MAX_DOH_CONNS; ++i) {
+ doh_conn_close(i);
+ }
+}
+
+static void doh_conns_fdsets(fd_set *readfds, fd_set *writefds,
+ struct curltime *pnow,
+ int *pmaxfd,
+ timediff_t *ptimeout_ms)
+{
+ size_t i;
+ for(i = 0; i < MAX_DOH_CONNS; ++i) {
+ struct doh_conn *c = &doh_conns[i];
+ if((c->sock != CURL_SOCKET_BAD)) {
+ if(!c->want_send && c->outblen) { /* check delayed send */
+ timediff_t ms = curlx_ptimediff_ms(&c->send_ts, pnow);
+ if(ms <= 0)
+ c->want_send = TRUE;
+ else if((ms < *ptimeout_ms) || !*ptimeout_ms)
+ *ptimeout_ms = ms;
+ }
+ if(c->want_send)
+ FD_SET(c->sock, writefds);
+ if(c->want_recv)
+ FD_SET(c->sock, readfds);
+
+ if((FD_ISSET(c->sock, readfds) || FD_ISSET(c->sock, writefds)) &&
+ (int)c->sock > *pmaxfd)
+ *pmaxfd = (int)c->sock;
+ }
+ }
+}
+
+static int doh_conn_send(struct doh_conn *c, struct curltime *pnow)
+{
+ char errbuf[STRERROR_LEN];
+ ssize_t rc;
+ int sockerr;
+
+ if(c->outblen) {
+ timediff_t ms = curlx_ptimediff_ms(&c->send_ts, pnow);
+ size_t n;
+
+ if(ms > 0) { /* not due yet */
+ c->want_send = FALSE;
+ goto out;
+ }
+
+ rc = swrite(c->sock, c->outbuf, c->outblen);
+ if(rc < 0) {
+ sockerr = SOCKERRNO;
+ if((sockerr == SOCKEINPROGRESS) || SOCK_EAGAIN(sockerr))
+ return 0;
+ sockerr = SOCKERRNO;
+ logmsg("[%d-%d-DOH] swrite(%ld) failed with error (%d) %s",
+ c->query_id, c->index, (long)c->sock,
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ return 1;
+ }
+ n = (size_t)rc;
+ if(n >= c->outblen) { /* all sent */
+ c->outblen = 0;
+ logmsg("[%d-%d-DOH] last response byte sent", c->query_id, c->index);
+ }
+ else {
+ c->outblen -= n;
+ memmove(c->outbuf, c->outbuf + n, c->outblen);
+ }
+ }
+
+out:
+ if(!c->outblen) {
+ c->want_send = FALSE;
+ if(c->close_pending)
+ return 1;
+ else {
+ logmsg("[x-%d-DOH] switching to recv", c->index);
+ c->want_recv = TRUE;
+ }
+ }
+ return 0;
+}
+
+static const char *http_status_descr(int http_status)
+{
+ switch(http_status) {
+ case 200:
+ return "Ok";
+ case 400:
+ return "Bad Request";
+ case 404:
+ return "Not Found";
+ case 405:
+ return "Method Not Allowed";
+ default:
+ if(http_status < 100)
+ return "This is wrong";
+ if(http_status < 200)
+ return "Just Kidding";
+ if(http_status < 300)
+ return "Lgtm";
+ if(http_status < 400)
+ return "Follow Me For More Requests";
+ if(http_status < 500)
+ return "You were wrong";
+ return "We did something wrong";
+ }
+}
+
+static int doh_conn_send_err(struct doh_conn *c, int http_status)
+{
+ c->inblen = 0;
+ c->close_pending = TRUE;
+ c->want_recv = FALSE;
+ if(c->outblen) {
+ logmsg("[%d-%d-DOH] error sending response with %zu bytes still outgoing",
+ c->query_id, c->index, c->outblen);
+ return 1;
+ }
+
+ memset(c->outbuf, 0, sizeof(c->outbuf));
+ snprintf(c->outbuf, sizeof(c->outbuf) - 1,
+ "HTTP/1.1 %d %s\r\n"
+ "Content-Length: 0\r\n"
+ "Connection: close\r\n"
+ "\r\n",
+ http_status, http_status_descr(http_status));
+ c->outblen = strlen(c->outbuf);
+ c->want_send = TRUE;
+ logmsg("[%d-%d-DOH] sending HTTP response %d",
+ c->query_id, c->index, http_status);
+ return 0;
+}
+
+static int doh_conn_send_answer(struct doh_conn *c, struct blob *body,
+ timediff_t delay_ms)
+{
+ if(c->outblen) { /* Should not happen */
+ logmsg("[%d-%d-DOH] trying to send an answer with outbuf still having "
+ "%zu bytes", c->query_id, c->index, c->outblen);
+ return 1;
+ }
+ memset(c->outbuf, 0, sizeof(c->outbuf));
+ snprintf(c->outbuf, sizeof(c->outbuf) - 1,
+ "HTTP/1.1 200 %s\r\n"
+ "Server: curl/test-dnsd\r\n"
+ "Date: Thu, 06 Aug 2026 08:42:00 GMT\r\n"
+ "Content-Type: application/dns-message\r\n"
+ "Content-Length: %ld\r\n"
+ "\r\n",
+ http_status_descr(200), (long)body->dlen);
+ c->outblen = strlen(c->outbuf);
+ if((c->outblen + body->dlen) > sizeof(c->outbuf)) {
+ logmsg("[%d-%d-DOH] response size of %zu too large for outbuf",
+ c->query_id, c->index, c->outblen + body->dlen);
+ c->outblen = 0;
+ return 1;
+ }
+ memcpy(c->outbuf + c->outblen, body->data, body->dlen);
+ c->outblen += body->dlen;
+ c->send_ts = now_plus(delay_ms);
+ if(delay_ms > 0)
+ logmsg("[%d-%d-DOH] delay response by %" FMT_TIMEDIFF_T "ms",
+ c->query_id, c->index, delay_ms);
+ else
+ c->want_send = TRUE;
+ c->want_recv = FALSE;
+ logmsg("[%d-%d-DOH] sending HTTP response 200",
+ c->query_id, c->index);
+ return 0;
+}
+
+static int doh_req_parse_headers(const char **pstr,
+ size_t *pcontent_length,
+ bool *pcomplete)
+{
+ static const struct Curl_str HD_content_length = {
+ STRCONST("Content-Length:")
+ };
+ static const struct Curl_str HD_content_type = {
+ STRCONST("Content-Type:")
+ };
+ static const struct Curl_str HD_wanted_type = {
+ STRCONST("application/dns-message")
+ };
+ const char *p = *pstr;
+ bool ct_ok = FALSE;
+ bool cl_ok = FALSE;
+ bool eoh = FALSE;
+
+ *pcomplete = FALSE;
+ *pcontent_length = 0;
+ while(p[0]) {
+ const char *nl, *start = p;
+ struct Curl_str hd_name, hd_val;
+
+ if((p[0] == '\r') && (p[1] == '\n')) {
+ p += 2;
+ eoh = TRUE;
+ break;
+ }
+ nl = strchr(p, '\n');
+ if(!nl) /* incomplete */
+ break;
+ if(curlx_str_word(&p, &hd_name, 1024) ||
+ curlx_str_singlespace(&p) ||
+ curlx_str_untilnl(&p, &hd_val, 1024) ||
+ curlx_str_newline(&p) ||
+ curlx_str_newline(&p)) {
+ logmsg("unrecognized request header '%.*s'",
+ (int)(nl - start), start);
+ return 1;
+ }
+ if(curlx_str_case_equal(&HD_content_type, &hd_name)) {
+ if(!curlx_str_case_equal(&HD_wanted_type, &hd_val)) {
+ logmsg("wrong content-type: '%.*s'", (int)hd_val.len, hd_val.str);
+ return 1;
+ }
+ ct_ok = TRUE;
+ }
+ else if(curlx_str_case_equal(&HD_content_length, &hd_name)) {
+ const char *s = hd_val.str;
+ curl_off_t offt;
+ if(curlx_str_number(&s, &offt, 4096)) {
+ logmsg("wrong content-length: '%.*s'", (int)hd_val.len, hd_val.str);
+ return 1;
+ }
+ *pcontent_length = (size_t)offt;
+ cl_ok = TRUE;
+ }
+ else {
+ /* ignore this header */
+ }
+ }
+
+ *pstr = p;
+ if(!eoh)
+ return 0; /* need more */
+ if(!ct_ok) {
+ logmsg("request missing Content-Type");
+ return 1;
+ }
+ if(!cl_ok) {
+ logmsg("request missing Content-Length");
+ return 1;
+ }
+ *pcomplete = TRUE;
+ return 0;
+}
+
+static int doh_conn_do_req(struct doh_conn *c, bool eos)
+{
+ static const struct Curl_str DOH_PROTO = { STRCONST("HTTP/1.1") };
+ static const struct Curl_str DOH_METHOD = { STRCONST("POST") };
+ static const struct Curl_str DOH_PATH = { STRCONST("/") };
+ const char *first_nl;
+ bool complete = FALSE;
+
+ if(!c->inblen && eos)
+ return 1;
+
+ if(!c->inbody_offset) {
+ first_nl = strchr(c->inbuf, '\n');
+ if(first_nl) {
+ /* This is a poor man's HTTP/1.1 parser and we should rather have
+ * one in curlx that we can share. */
+ const char *p = c->inbuf;
+ struct Curl_str method, path, proto;
+
+ if(curlx_str_word(&p, &method, 1024) || curlx_str_singlespace(&p)) {
+ logmsg("[x-%d-DOH] unrecognized first request line method '%.*s'",
+ c->index, (int)(first_nl - c->inbuf), c->inbuf);
+ return 1;
+ }
+ if(curlx_str_word(&p, &path, 1024) || curlx_str_singlespace(&p)) {
+ logmsg("[x-%d-DOH] unrecognized first request line path '%.*s'",
+ c->index, (int)(first_nl - c->inbuf), c->inbuf);
+ return 1;
+ }
+ if(curlx_str_untilnl(&p, &proto, 1024) ||
+ curlx_str_newline(&p) ||
+ curlx_str_newline(&p)) {
+ logmsg("[x-%d-DOH] unrecognized first request line proto '%.*s'",
+ c->index, (int)(first_nl - c->inbuf), c->inbuf);
+ return 1;
+ }
+ if(!curlx_str_case_equal(&DOH_PROTO, &proto)) {
+ logmsg("[x-%d-DOH] unrecognized request protocol '%.*s'",
+ c->index, (int)proto.len, proto.str);
+ return 1;
+ }
+ if(!curlx_str_case_equal(&DOH_METHOD, &method)) {
+ logmsg("[x-%d-DOH] unsupported request method '%.*s'",
+ c->index, (int)method.len, method.str);
+ return doh_conn_send_err(c, 405);
+ }
+ if(!curlx_str_case_equal(&DOH_PATH, &path)) {
+ logmsg("[x-%d-DOH] request path not fond '%.*s'",
+ c->index, (int)path.len, path.str);
+ return doh_conn_send_err(c, 404);
+ }
+ if(doh_req_parse_headers(&p, &c->inbody_len, &complete)) {
+ return doh_conn_send_err(c, 400);
+ }
+ /* Looks ok, remember the start of the body bytes */
+ c->inbody_offset = (p - c->inbuf);
+ }
+ if(!complete)
+ return eos ? 1 : 0; /* want more, error if client close */
+ }
+
+ if(c->inblen >= (c->inbody_offset + c->inbody_len)) {
+ /* We have all bytes for processing the request */
+ uint8_t qbuf[256]; /* query storage */
+ size_t qlen = 0; /* query size */
+ size_t rlen = c->inbody_offset + c->inbody_len; /* request size */
+ uint16_t qtype = 0, id;
+ struct blob blob;
+ timediff_t delay_ms;
+
+ c->query_id = ++last_query_id;
+ if(store_incoming("DoH", c->query_id, (const uint8_t *)c->inbuf +
+ c->inbody_offset, c->inbody_len,
+ qbuf, sizeof(qbuf), &qlen, &qtype, &id)) {
+ logmsg("[%d-%d-DOH] error storing incoming request",
+ c->query_id, c->index);
+ return doh_conn_send_err(c, 400);
+ }
+
+ /* remove handled request from inbuf */
+ if(rlen >= c->inblen)
+ c->inblen = 0;
+ else {
+ memmove(c->inbuf, c->inbuf + rlen, c->inblen - rlen);
+ c->inblen -= rlen;
+ }
+ c->inbody_len = c->inbody_offset = 0;
+
+ memset(&blob, 0, sizeof(blob));
+ if(dnsd_make_answer(&blob, c->query_id, qbuf, qlen, qtype, id,
+ &delay_ms)) {
+ return doh_conn_send_err(c, 500);
+ }
+ return doh_conn_send_answer(c, &blob, delay_ms);
+ }
+ return 0;
+}
+
+static int doh_conn_recv(struct doh_conn *c)
+{
+ char errbuf[STRERROR_LEN];
+ ssize_t n;
+ int sockerr;
+
+ n = sread(c->sock, c->inbuf + c->inblen, sizeof(c->inbuf) - 1 - c->inblen);
+ if(n < 0) {
+ sockerr = SOCKERRNO;
+ if((sockerr == SOCKEINPROGRESS) || SOCK_EAGAIN(sockerr))
+ return 0;
+ sockerr = SOCKERRNO;
+ logmsg("[x-%d-DOH] sread() failed with error (%d) %s", c->index,
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ return 1;
+ }
+ else if(n == 0) {
+ logmsg("[x-%d-DOH] sread() == 0, client closed connection", c->index);
+ return doh_conn_do_req(c, TRUE);
+ }
+ else {
+ c->inblen += (size_t)n;
+ c->inbuf[c->inblen] = 0;
+ logmsg("[x-%d-DOH] sread() == %zu, processing", c->index, (size_t)n);
+ return doh_conn_do_req(c, FALSE);
+ }
+}
+
+static int doh_conns_serve(fd_set *readfds, fd_set *writefds)
+{
+ struct curltime now;
+ size_t i;
+
+ for(i = 0; i < MAX_DOH_CONNS; ++i) {
+ struct doh_conn *c = &doh_conns[i];
+ if(c->sock != CURL_SOCKET_BAD) {
+ if(c->want_send && FD_ISSET(c->sock, writefds)) {
+ now = curlx_now();
+ if(doh_conn_send(c, &now)) {
+ doh_conn_close(i);
+ continue;
+ }
+ }
+ if(c->want_recv && FD_ISSET(c->sock, readfds)) {
+ if(doh_conn_recv(c)) {
+ doh_conn_close(i);
+ continue;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int test_dnsd(int argc, const char **argv)
+{
+ int arg = 1;
+ curl_socket_t sock_udp = CURL_SOCKET_BAD;
+ curl_socket_t sock_tcp_listen = CURL_SOCKET_BAD;
+ char errbuf[STRERROR_LEN];
+ int rc, sockerr;
+ int result = 0;
+
+ pidname = ".dnsd.pid";
+ serverlogfile = "log/dnsd.log";
+ serverlogslocked = 0;
+ server_port = 9123; /* UDP */
+ socket_domain = AF_INET;
+
+ while(argc > arg) {
+ const char *opt;
+ curl_off_t num;
+ if(!strcmp("--verbose", argv[arg])) {
+ arg++;
+ /* nothing yet */
+ }
+ else if(!strcmp("--version", argv[arg])) {
+ printf("dnsd IPv4%s\n",
+#ifdef USE_IPV6
+ "/IPv6"
+#else
+ ""
+#endif
+ );
return 0;
}
else if(!strcmp("--pidfile", argv[arg])) {
@@ -870,97 +1427,14 @@ static int test_dnsd(int argc, const char **argv)
install_signal_handlers(FALSE);
-#ifdef USE_IPV6
- if(socket_domain == AF_INET6)
- sock = socket(AF_INET6, SOCK_DGRAM, 0);
- else
-#endif
- sock = socket(AF_INET, SOCK_DGRAM, 0);
-
- if(sock == CURL_SOCKET_BAD) {
- sockerr = SOCKERRNO;
- logmsg("Error creating socket (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- result = 1;
+ result = open_stream_sock(&sock_tcp_listen, &server_port);
+ if(result)
goto dnsd_cleanup;
- }
+ doh_conns_init();
- flag = 1;
- if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) {
- sockerr = SOCKERRNO;
- logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- result = 1;
+ result = open_udp_sock(&sock_udp, &server_port);
+ if(result)
goto dnsd_cleanup;
- }
-
-#ifdef USE_IPV6
- if(socket_domain == AF_INET6) {
- memset(&me.sa6, 0, sizeof(me.sa6));
- me.sa6.sin6_family = AF_INET6;
- me.sa6.sin6_addr = in6addr_any;
- me.sa6.sin6_port = htons(server_port);
- rc = bind(sock, &me.sa, sizeof(me.sa6));
- }
- else
-#endif
- {
- memset(&me.sa4, 0, sizeof(me.sa4));
- me.sa4.sin_family = AF_INET;
- me.sa4.sin_addr.s_addr = INADDR_ANY;
- me.sa4.sin_port = htons(server_port);
- rc = bind(sock, &me.sa, sizeof(me.sa4));
- }
- if(rc) {
- sockerr = SOCKERRNO;
- logmsg("Error binding socket on port %hu (%d) %s", server_port,
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- result = 1;
- goto dnsd_cleanup;
- }
-
- if(!server_port) {
- /* The system was supposed to choose a port number, figure out which
- port we actually got and update the listener port value with it. */
- curl_socklen_t la_size;
- srvr_sockaddr_union_t localaddr;
- memset(&localaddr, 0, sizeof(localaddr));
-#ifdef USE_IPV6
- if(socket_domain == AF_INET6)
- la_size = sizeof(localaddr.sa6);
- else
-#endif
- la_size = sizeof(localaddr.sa4);
-
- if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
- sockerr = SOCKERRNO;
- logmsg("getsockname() failed with error (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- sclose(sock);
- goto dnsd_cleanup;
- }
- switch(localaddr.sa.sa_family) {
- case AF_INET:
- server_port = ntohs(localaddr.sa4.sin_port);
- break;
-#ifdef USE_IPV6
- case AF_INET6:
- server_port = ntohs(localaddr.sa6.sin6_port);
- break;
-#endif
- default:
- break;
- }
- if(!server_port) {
- /* Real failure, listener port shall not be zero beyond this point. */
- logmsg("Apparently getsockname() succeeded, with listener port zero.");
- logmsg("A valid reason for this failure is a binary built without");
- logmsg("proper network library linkage. This might not be the only");
- logmsg("reason, but double check it before anything else.");
- result = 2;
- goto dnsd_cleanup;
- }
- }
dnsd_wrotepidfile = write_pidfile(pidname);
if(!dnsd_wrotepidfile) {
@@ -976,75 +1450,76 @@ static int test_dnsd(int argc, const char **argv)
}
}
- logmsg("Running %s version on port UDP/%d", socket_type, (int)server_port);
- curlx_nonblock(sock, TRUE);
+ /* start accepting connections */
+ if(listen(sock_tcp_listen, 50)) {
+ sockerr = SOCKERRNO;
+ logmsg("listen() failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto dnsd_cleanup;
+ }
+
+ logmsg("Running %s on port UDP+TCP/%u", socket_type, server_port);
+ curlx_nonblock(sock_udp, TRUE);
+ curlx_nonblock(sock_tcp_listen, TRUE);
for(;;) {
- uint16_t id = 0;
- uint8_t inbuffer[1500];
- srvr_sockaddr_union_t from;
- curl_socklen_t fromlen;
- uint8_t qbuf[256]; /* query storage */
- size_t qlen = 0; /* query size */
- uint16_t qtype = 0;
timediff_t timeout_ms = 0;
- fromlen = sizeof(from);
-#ifdef USE_IPV6
- if(socket_domain == AF_INET6)
- fromlen = sizeof(from.sa6);
- else
-#endif
- fromlen = sizeof(from.sa4);
+ fd_set readfds, writefds;
+ struct timeval tv;
+ int maxfd = 0;
+ struct curltime now = curlx_now();
- timeout_ms = send_resp_queue(sock);
+ FD_ZERO(&readfds);
+ fdset_add_sock(&readfds, sock_udp, &maxfd);
+ fdset_add_sock(&readfds, sock_tcp_listen, &maxfd);
- {
- fd_set readfds;
- struct timeval tv;
- int maxfd = (int)sock;
+ FD_ZERO(&writefds);
+ timeout_ms = queue_udp_next_ms(&now);
+ if(!timeout_ms)
+ fdset_add_sock(&writefds, sock_udp, &maxfd);
- FD_ZERO(&readfds);
- FD_SET(sock, &readfds);
- if(!timeout_ms || (timeout_ms > 100))
- timeout_ms = 100;
+ doh_conns_fdsets(&readfds, &writefds, &now, &maxfd, &timeout_ms);
- rc = select(maxfd + 1, &readfds, NULL, NULL,
- curlx_mstotv(&tv, timeout_ms));
+ if(!timeout_ms || (timeout_ms > 100))
+ timeout_ms = 100;
- if(rc == -1) {
- logmsg("error %d returned by select()", SOCKERRNO);
- }
- else if(!rc) { /* timeout */
- continue;
- }
+ rc = select(maxfd + 1, &readfds, &writefds, NULL,
+ curlx_mstotv(&tv, timeout_ms));
+
+ if(rc == -1) {
+ logmsg("error %d returned by select()", SOCKERRNO);
}
- n = (ssize_t)recvfrom(sock, (char *)inbuffer, sizeof(inbuffer), 0,
- &from.sa, &fromlen);
- if(got_exit_signal)
- break;
- if(n < 0) {
- logmsg("recvfrom");
- result = 3;
- break;
+ else if(!rc) { /* timeout */
+ continue;
}
- /* read once per incoming query, which is probably more than one
- per test case */
- read_instructions();
-
- ++query_id;
- store_incoming(query_id, inbuffer, n,
- qbuf, sizeof(qbuf), &qlen, &qtype, &id);
+ if(FD_ISSET(sock_udp, &writefds)) {
+ now = curlx_now();
+ queue_udp_send(sock_udp, &now);
+ }
+ if(FD_ISSET(sock_udp, &readfds)) {
+ result = udp_recv_req(sock_udp);
+ if(result)
+ break;
+ }
- set_advisor_read_lock(loglockfile);
- serverlogslocked = 1;
+ result = doh_conns_serve(&readfds, &writefds);
+ if(result)
+ break;
- resp = create_resp(query_id, &from.sa, fromlen, qbuf,
- qlen, qtype, id);
- if(!resp)
- logmsg("error creating response");
- else
- queue_resp(resp);
+ if(FD_ISSET(sock_tcp_listen, &readfds)) {
+ /* Service all queued connections */
+ curl_socket_t sock_conn;
+ while(TRUE) {
+ sock_conn = accept_connection(sock_tcp_listen);
+ if(!sock_conn) /* no more connections to accept */
+ break;
+ if(sock_conn == CURL_SOCKET_BAD)
+ goto dnsd_cleanup;
+ doh_conns_add(sock_conn);
+ }
+ }
if(got_exit_signal)
break;
@@ -1056,14 +1531,12 @@ static int test_dnsd(int argc, const char **argv)
}
dnsd_cleanup:
+ if(sock_udp != CURL_SOCKET_BAD)
+ sclose(sock_udp);
+ if(sock_tcp_listen != CURL_SOCKET_BAD)
+ sclose(sock_tcp_listen);
-#if 0
- if((peer != sock) && (peer != CURL_SOCKET_BAD))
- sclose(peer);
-#endif
-
- if(sock != CURL_SOCKET_BAD)
- sclose(sock);
+ doh_conns_close_all();
if(got_exit_signal)
logmsg("signalled to die");
@@ -1078,7 +1551,7 @@ dnsd_cleanup:
clear_advisor_read_lock(loglockfile);
}
- clear_resp_queue();
+ queue_udp_clear();
restore_signal_handlers(FALSE);
tests/server/first.h+8 -0
@@ -159,6 +159,14 @@ extern curl_socket_t sockdaemon(curl_socket_t sock,
uint16_t *listenport,
const char *unix_socket,
bool bind_only);
+extern int open_udp_sock(curl_socket_t *psock, uint16_t *pport);
+extern int open_stream_sock(curl_socket_t *psock, uint16_t *pport);
+extern curl_socket_t accept_connection(curl_socket_t listen_sock);
+extern bool curlx_str_case_equal(const struct Curl_str *s1,
+ const struct Curl_str *s2);
+
+/* returns true if the current socket is an IP one */
+extern bool socket_domain_is_ip(void);
/* global variables */
static const char *srcpath = "."; /* pointing to the test directory */
tests/server/sws.c+14 -211
@@ -189,23 +189,6 @@ static char *data_to_hex(const char *data, size_t len)
/* work around for handling trailing headers */
static int already_recv_zeroed_chunk = FALSE;
-#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED)
-/* returns true if the current socket is an IP one */
-static bool socket_domain_is_ip(void)
-{
- switch(socket_domain) {
- case AF_INET:
-#ifdef USE_IPV6
- case AF_INET6:
-#endif
- return TRUE;
- default:
- /* case AF_UNIX: */
- return FALSE;
- }
-}
-#endif
-
/* parse the file on disk that might have a test number for us */
static int parse_cmdfile(struct sws_httprequest *req)
{
@@ -1749,92 +1732,6 @@ static void http_upgrade(struct sws_httprequest *req)
/* left to implement */
}
-/* returns a socket handle, or 0 if there are no more waiting sockets,
- or < 0 if there was an error */
-static curl_socket_t accept_connection(curl_socket_t sock)
-{
- curl_socket_t msgsock = CURL_SOCKET_BAD;
- int sockerr;
- char errbuf[STRERROR_LEN];
- int flag = 1;
-
- if(MAX_SOCKETS == num_sockets) {
- logmsg("Too many open sockets!");
- return CURL_SOCKET_BAD;
- }
-
- msgsock = accept(sock, NULL, NULL);
-
- if(got_exit_signal) {
- if(msgsock != CURL_SOCKET_BAD)
- sclose(msgsock);
- return CURL_SOCKET_BAD;
- }
-
- if(msgsock == CURL_SOCKET_BAD) {
- sockerr = SOCKERRNO;
- if(SOCK_EAGAIN(sockerr)) {
- /* nothing to accept */
- return 0;
- }
- logmsg("MAJOR ERROR, accept() failed with error (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- return CURL_SOCKET_BAD;
- }
-
- if(curlx_nonblock(msgsock, TRUE)) {
- sockerr = SOCKERRNO;
- logmsg("curlx_nonblock failed with error (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- sclose(msgsock);
- return CURL_SOCKET_BAD;
- }
-
-#if defined(_WIN32) && defined(USE_UNIX_SOCKETS)
- if(socket_domain != AF_UNIX) {
-#endif
- if(setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
- (void *)&flag, sizeof(flag))) {
- sockerr = SOCKERRNO;
- logmsg("setsockopt(SO_KEEPALIVE) failed with error (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- sclose(msgsock);
- return CURL_SOCKET_BAD;
- }
-#if defined(_WIN32) && defined(USE_UNIX_SOCKETS)
- }
-#endif
-
- /*
- * As soon as this server accepts a connection from the test harness it
- * must set the server logs advisor read lock to indicate that server
- * logs should not be read until this lock is removed by this server.
- */
-
- if(!serverlogslocked)
- set_advisor_read_lock(loglockfile);
- serverlogslocked += 1;
-
- logmsg("====> Client connect");
-
- all_sockets[num_sockets] = msgsock;
- num_sockets += 1;
-
-#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED)
- if(socket_domain_is_ip()) {
- /*
- * Disable the Nagle algorithm to make it easier to send out a large
- * response in many small segments to torture the clients more.
- */
- if(setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
- (void *)&flag, sizeof(flag)))
- logmsg("====> TCP_NODELAY failed");
- }
-#endif
-
- return msgsock;
-}
-
/* returns 1 if the connection should be serviced again immediately, 0 if there
is no data waiting, or < 0 if it should be closed */
static int service_connection(curl_socket_t *msgsock,
@@ -1914,11 +1811,9 @@ static int service_connection(curl_socket_t *msgsock,
static int test_sws(int argc, const char *argv[])
{
- srvr_sockaddr_union_t me;
curl_socket_t sock = CURL_SOCKET_BAD;
int wrotepidfile = 0;
int wroteportfile = 0;
- int flag;
#ifdef USE_UNIX_SOCKETS
bool unlink_socket = FALSE;
#endif
@@ -1932,6 +1827,7 @@ static int test_sws(int argc, const char *argv[])
const char *location_str = port_str;
int keepalive_secs = 5;
const char *protocol_type = "HTTP";
+ int result = 0;
/* a default CONNECT port is pointless, but still ... */
size_t socket_idx;
@@ -2005,6 +1901,7 @@ static int test_sws(int argc, const char *argv[])
arg++;
if(argc > arg) {
#ifdef USE_UNIX_SOCKETS
+ srvr_sockaddr_union_t me;
server_unix_socket = argv[arg];
if(strlen(server_unix_socket) >= sizeof(me.sau.sun_path)) {
fprintf(stderr,
@@ -2091,115 +1988,13 @@ static int test_sws(int argc, const char *argv[])
if(!req)
goto sws_cleanup;
- sock = socket(socket_domain, SOCK_STREAM, 0);
+ result = open_stream_sock(&sock, &server_port);
+ if(result)
+ goto sws_cleanup;
all_sockets[0] = sock;
num_sockets = 1;
- if(sock == CURL_SOCKET_BAD) {
- sockerr = SOCKERRNO;
- logmsg("Error creating socket (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- goto sws_cleanup;
- }
-
-#if defined(_WIN32) && defined(USE_UNIX_SOCKETS)
- if(socket_domain != AF_UNIX) {
-#endif
- flag = 1;
- if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
- (void *)&flag, sizeof(flag))) {
- sockerr = SOCKERRNO;
- logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- goto sws_cleanup;
- }
-#if defined(_WIN32) && defined(USE_UNIX_SOCKETS)
- }
-#endif
- if(curlx_nonblock(sock, TRUE)) {
- sockerr = SOCKERRNO;
- logmsg("curlx_nonblock failed with error (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- goto sws_cleanup;
- }
-
- switch(socket_domain) {
- case AF_INET:
- memset(&me.sa4, 0, sizeof(me.sa4));
- me.sa4.sin_family = AF_INET;
- me.sa4.sin_addr.s_addr = INADDR_ANY;
- me.sa4.sin_port = htons(server_port);
- rc = bind(sock, &me.sa, sizeof(me.sa4));
- break;
-#ifdef USE_IPV6
- case AF_INET6:
- memset(&me.sa6, 0, sizeof(me.sa6));
- me.sa6.sin6_family = AF_INET6;
- me.sa6.sin6_addr = in6addr_any;
- me.sa6.sin6_port = htons(server_port);
- rc = bind(sock, &me.sa, sizeof(me.sa6));
- break;
-#endif /* USE_IPV6 */
-#ifdef USE_UNIX_SOCKETS
- case AF_UNIX:
- rc = bind_unix_socket(sock, server_unix_socket, &me.sau);
-#endif /* USE_UNIX_SOCKETS */
- }
- if(rc) {
- sockerr = SOCKERRNO;
-#ifdef USE_UNIX_SOCKETS
- if(socket_domain == AF_UNIX)
- logmsg("Error binding socket on path %s (%d) %s", server_unix_socket,
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- else
-#endif
- logmsg("Error binding socket on port %hu (%d) %s", server_port,
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- goto sws_cleanup;
- }
-
- if(!server_port) {
- /* The system was supposed to choose a port number, figure out which
- port we actually got and update the listener port value with it. */
- curl_socklen_t la_size;
- srvr_sockaddr_union_t localaddr;
- memset(&localaddr, 0, sizeof(localaddr));
-#ifdef USE_IPV6
- if(socket_domain == AF_INET6)
- la_size = sizeof(localaddr.sa6);
- else
-#endif
- la_size = sizeof(localaddr.sa4);
- if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
- sockerr = SOCKERRNO;
- logmsg("getsockname() failed with error (%d) %s",
- sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
- sclose(sock);
- goto sws_cleanup;
- }
- switch(localaddr.sa.sa_family) {
- case AF_INET:
- server_port = ntohs(localaddr.sa4.sin_port);
- break;
-#ifdef USE_IPV6
- case AF_INET6:
- server_port = ntohs(localaddr.sa6.sin6_port);
- break;
-#endif
- default:
- break;
- }
- if(!server_port) {
- /* Real failure, listener port shall not be zero beyond this point. */
- logmsg("Apparently getsockname() succeeded, with listener port zero.");
- logmsg("A valid reason for this failure is a binary built without");
- logmsg("proper network library linkage. This might not be the only");
- logmsg("reason, but double check it before anything else.");
- sclose(sock);
- goto sws_cleanup;
- }
- }
#ifdef USE_UNIX_SOCKETS
if(socket_domain != AF_UNIX)
#endif
@@ -2302,11 +2097,19 @@ static int test_sws(int argc, const char *argv[])
/* Service all queued connections */
curl_socket_t msgsock;
do {
+ if(MAX_SOCKETS == num_sockets) {
+ logmsg("Too many open sockets!");
+ goto sws_cleanup;
+ }
msgsock = accept_connection(sock);
+ if(!msgsock)
+ break;
logmsg("accept_connection %ld returned %ld",
(long)sock, (long)msgsock);
if(msgsock == CURL_SOCKET_BAD)
goto sws_cleanup;
+ all_sockets[num_sockets] = msgsock;
+ num_sockets += 1;
if(req->delay)
curlx_wait_ms(req->delay);
} while(msgsock > 0);
@@ -2414,5 +2217,5 @@ sws_cleanup:
restore_signal_handlers(FALSE);
- return 0;
+ return result;
}
tests/server/util.c+339 -0
@@ -886,3 +886,342 @@ curl_socket_t sockdaemon(curl_socket_t sock,
return sock;
}
+
+int open_udp_sock(curl_socket_t *psock, uint16_t *pport)
+{
+ srvr_sockaddr_union_t me;
+ curl_socket_t sock = CURL_SOCKET_BAD;
+ uint16_t port = *pport;
+ int sockerr, flag, rc;
+ char errbuf[STRERROR_LEN];
+ int result = 0;
+
+ sock = socket(socket_domain, SOCK_DGRAM, 0);
+
+ if(sock == CURL_SOCKET_BAD) {
+ sockerr = SOCKERRNO;
+ logmsg("Error creating socket (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+
+ flag = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) {
+ sockerr = SOCKERRNO;
+ logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+
+#ifdef USE_IPV6
+ if(socket_domain == AF_INET6) {
+ memset(&me.sa6, 0, sizeof(me.sa6));
+ me.sa6.sin6_family = AF_INET6;
+ me.sa6.sin6_addr = in6addr_any;
+ me.sa6.sin6_port = htons(port);
+ rc = bind(sock, &me.sa, sizeof(me.sa6));
+ }
+ else
+#endif
+ {
+ memset(&me.sa4, 0, sizeof(me.sa4));
+ me.sa4.sin_family = AF_INET;
+ me.sa4.sin_addr.s_addr = INADDR_ANY;
+ me.sa4.sin_port = htons(port);
+ rc = bind(sock, &me.sa, sizeof(me.sa4));
+ }
+ if(rc) {
+ sockerr = SOCKERRNO;
+ logmsg("Error binding socket on port %hu (%d) %s", port,
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+
+ if(!port) {
+ /* The system was supposed to choose a port number, figure out which
+ port we actually got and update the listener port value with it. */
+ curl_socklen_t la_size;
+ srvr_sockaddr_union_t localaddr;
+ memset(&localaddr, 0, sizeof(localaddr));
+#ifdef USE_IPV6
+ if(socket_domain == AF_INET6)
+ la_size = sizeof(localaddr.sa6);
+ else
+#endif
+ la_size = sizeof(localaddr.sa4);
+ if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
+ sockerr = SOCKERRNO;
+ logmsg("getsockname() failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+ switch(localaddr.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(localaddr.sa4.sin_port);
+ break;
+#ifdef USE_IPV6
+ case AF_INET6:
+ port = ntohs(localaddr.sa6.sin6_port);
+ break;
+#endif
+ default:
+ break;
+ }
+ if(!port) {
+ /* Real failure, listener port shall not be zero beyond this point. */
+ logmsg("Apparently getsockname() succeeded, with listener port zero.");
+ logmsg("A valid reason for this failure is a binary built without");
+ logmsg("proper network library linkage. This might not be the only");
+ logmsg("reason, but double check it before anything else.");
+ result = 2;
+ goto out;
+ }
+ }
+
+out:
+ if(result) {
+ if(sock != CURL_SOCKET_BAD)
+ sclose(sock);
+ sock = CURL_SOCKET_BAD;
+ port = 0;
+ }
+ *psock = sock;
+ *pport = port;
+ return result;
+}
+
+bool socket_domain_is_ip(void)
+{
+ switch(socket_domain) {
+ case AF_INET:
+#ifdef USE_IPV6
+ case AF_INET6:
+#endif
+ return TRUE;
+ default:
+ /* case AF_UNIX: */
+ return FALSE;
+ }
+}
+
+int open_stream_sock(curl_socket_t *psock, uint16_t *pport)
+{
+ srvr_sockaddr_union_t me;
+ char errbuf[STRERROR_LEN];
+ int flag, sockerr, rc = 0;
+ curl_socket_t sock = CURL_SOCKET_BAD;
+ uint16_t port = *pport;
+ int result = 0;
+
+ sock = socket(socket_domain, SOCK_STREAM, 0);
+
+ if(sock == CURL_SOCKET_BAD) {
+ sockerr = SOCKERRNO;
+ logmsg("Error creating socket (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+
+#if defined(_WIN32) && defined(USE_UNIX_SOCKETS)
+ if(socket_domain != AF_UNIX) {
+#endif
+ flag = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&flag, sizeof(flag))) {
+ sockerr = SOCKERRNO;
+ logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+#if defined(_WIN32) && defined(USE_UNIX_SOCKETS)
+ }
+#endif
+ if(curlx_nonblock(sock, TRUE)) {
+ sockerr = SOCKERRNO;
+ logmsg("curlx_nonblock failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+
+ switch(socket_domain) {
+ case AF_INET:
+ memset(&me.sa4, 0, sizeof(me.sa4));
+ me.sa4.sin_family = AF_INET;
+ me.sa4.sin_addr.s_addr = INADDR_ANY;
+ me.sa4.sin_port = htons(port);
+ rc = bind(sock, &me.sa, sizeof(me.sa4));
+ break;
+#ifdef USE_IPV6
+ case AF_INET6:
+ memset(&me.sa6, 0, sizeof(me.sa6));
+ me.sa6.sin6_family = AF_INET6;
+ me.sa6.sin6_addr = in6addr_any;
+ me.sa6.sin6_port = htons(port);
+ rc = bind(sock, &me.sa, sizeof(me.sa6));
+ break;
+#endif /* USE_IPV6 */
+#ifdef USE_UNIX_SOCKETS
+ case AF_UNIX:
+ rc = bind_unix_socket(sock, server_unix_socket, &me.sau);
+#endif /* USE_UNIX_SOCKETS */
+ }
+ if(rc) {
+ sockerr = SOCKERRNO;
+#ifdef USE_UNIX_SOCKETS
+ if(socket_domain == AF_UNIX)
+ logmsg("Error binding socket on path %s (%d) %s", server_unix_socket,
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ else
+#endif
+ logmsg("Error binding socket on port %hu (%d) %s", port,
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+
+ if(!port) {
+ /* The system was supposed to choose a port number, figure out which
+ port we actually got and update the listener port value with it. */
+ curl_socklen_t la_size;
+ srvr_sockaddr_union_t localaddr;
+ memset(&localaddr, 0, sizeof(localaddr));
+#ifdef USE_IPV6
+ if(socket_domain != AF_INET6)
+#endif
+ la_size = sizeof(localaddr.sa4);
+#ifdef USE_IPV6
+ else
+ la_size = sizeof(localaddr.sa6);
+#endif
+ if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
+ sockerr = SOCKERRNO;
+ logmsg("getsockname() failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto out;
+ }
+ switch(localaddr.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(localaddr.sa4.sin_port);
+ break;
+#ifdef USE_IPV6
+ case AF_INET6:
+ port = ntohs(localaddr.sa6.sin6_port);
+ break;
+#endif
+ default:
+ break;
+ }
+ if(!port) {
+ /* Real failure, listener port shall not be zero beyond this point. */
+ logmsg("Apparently getsockname() succeeded, with listener port zero.");
+ logmsg("A valid reason for this failure is a binary built without");
+ logmsg("proper network library linkage. This might not be the only");
+ logmsg("reason, but double check it before anything else.");
+ result = 1;
+ goto out;
+ }
+ }
+out:
+ if(result) {
+ if(sock != CURL_SOCKET_BAD)
+ sclose(sock);
+ sock = CURL_SOCKET_BAD;
+ port = 0;
+ }
+ *psock = sock;
+ *pport = port;
+ return result;
+}
+
+/* returns a socket handle, or 0 if there are no more waiting sockets,
+ or < 0 if there was an error */
+curl_socket_t accept_connection(curl_socket_t listen_sock)
+{
+ curl_socket_t msgsock = CURL_SOCKET_BAD;
+ int sockerr;
+ char errbuf[STRERROR_LEN];
+ int flag = 1;
+
+ msgsock = accept(listen_sock, NULL, NULL);
+
+ if(got_exit_signal) {
+ if(msgsock != CURL_SOCKET_BAD)
+ sclose(msgsock);
+ return CURL_SOCKET_BAD;
+ }
+
+ if(msgsock == CURL_SOCKET_BAD) {
+ sockerr = SOCKERRNO;
+ if(SOCK_EAGAIN(sockerr)) {
+ /* nothing to accept */
+ return 0;
+ }
+ logmsg("MAJOR ERROR, accept() failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ return CURL_SOCKET_BAD;
+ }
+
+ if(curlx_nonblock(msgsock, TRUE)) {
+ sockerr = SOCKERRNO;
+ logmsg("curlx_nonblock failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ sclose(msgsock);
+ return CURL_SOCKET_BAD;
+ }
+
+#if defined(_WIN32) && defined(USE_UNIX_SOCKETS)
+ if(socket_domain != AF_UNIX) {
+#endif
+ if(setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
+ (void *)&flag, sizeof(flag))) {
+ sockerr = SOCKERRNO;
+ logmsg("setsockopt(SO_KEEPALIVE) failed with error (%d) %s",
+ sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf)));
+ sclose(msgsock);
+ return CURL_SOCKET_BAD;
+ }
+#if defined(_WIN32) && defined(USE_UNIX_SOCKETS)
+ }
+#endif
+
+ /*
+ * As soon as this server accepts a connection from the test harness it
+ * must set the server logs advisor read lock to indicate that server
+ * logs should not be read until this lock is removed by this server.
+ */
+
+ if(!serverlogslocked)
+ set_advisor_read_lock(loglockfile);
+ serverlogslocked += 1;
+
+ logmsg("====> Client connect");
+
+#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED)
+ if(socket_domain_is_ip()) {
+ /*
+ * Disable the Nagle algorithm to make it easier to send out a large
+ * response in many small segments to torture the clients more.
+ */
+ if(setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
+ (void *)&flag, sizeof(flag)))
+ logmsg("====> TCP_NODELAY failed");
+ }
+#endif
+
+ return msgsock;
+}
+
+bool curlx_str_case_equal(const struct Curl_str *s1,
+ const struct Curl_str *s2)
+{
+ return ((s1->len == s2->len) &&
+ !CURL_STRNICMP(s1->str, s2->str, s1->len));
+}