mirror of
https://github.com/curl/curl.git
synced 2026-08-24 23:53:44 +03:00
dnsd: add support for DoH
dnsd now opens UDP+TCP sockets and accepts http: DoH requests to obtain the same, configured answers (records, delays, error codes) as over UDP. DoH: use `async->queries_ongoing` like all other resolvers instead of the internal `pending` counter. Fixes waiting for results. Tests: in pytest, parameterize dnsd tests to use both DNS and DoH. Closes #22506
This commit is contained in:
parent
24ff74fa8d
commit
7f6a75664f
9 changed files with 1357 additions and 724 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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}'
|
||||
|
||||
# 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):
|
||||
dnsd.set_answers()
|
||||
def dns_settings(self, dns_method, dnsd):
|
||||
xargs = []
|
||||
run_env = os.environ.copy()
|
||||
run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}'
|
||||
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.parametrize("dns_method", ["DNS", "DoH"])
|
||||
def test_21_06_dnsd_empty(self, env: Env, httpd, dnsd, dns_method):
|
||||
dnsd.set_answers()
|
||||
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():
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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'):
|
||||
|
|
|
|||
1313
tests/server/dnsd.c
1313
tests/server/dnsd.c
File diff suppressed because it is too large
Load diff
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue