Skip to content

Commit 490ca13

Browse files
authored
Expose DNS-over-HTTP options (#434)
* Expose DNS-over-HTTP options Expose options related to DNS-over-HTTP name resolution support. Includes a brief working example. Support for DoH is enabled in curl by default and is available as of 7.62.0. * Fix verify option datatypes * Make rustfmt happy
1 parent 3e35a00 commit 490ca13

File tree

5 files changed

+167
-0
lines changed

5 files changed

+167
-0
lines changed

curl-sys/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ pub const CURLOPT_PROXY_CAPATH: CURLoption = CURLOPTTYPE_OBJECTPOINT + 247;
597597
pub const CURLOPT_PROXY_SSLCERT: CURLoption = CURLOPTTYPE_OBJECTPOINT + 254;
598598
pub const CURLOPT_PROXY_SSLKEY: CURLoption = CURLOPTTYPE_OBJECTPOINT + 256;
599599

600+
pub const CURLOPT_DOH_URL: CURLoption = CURLOPTTYPE_OBJECTPOINT + 279;
600601
pub const CURLOPT_UPLOAD_BUFFERSIZE: CURLoption = CURLOPTTYPE_LONG + 280;
601602

602603
pub const CURLOPT_MAXAGE_CONN: CURLoption = CURLOPTTYPE_LONG + 288;
@@ -609,6 +610,9 @@ pub const CURLOPT_ISSUERCERT_BLOB: CURLoption = CURLOPTTYPE_BLOB + 295;
609610

610611
pub const CURLOPT_AWS_SIGV4: CURLoption = CURLOPTTYPE_OBJECTPOINT + 305;
611612

613+
pub const CURLOPT_DOH_SSL_VERIFYPEER: CURLoption = CURLOPTTYPE_LONG + 306;
614+
pub const CURLOPT_DOH_SSL_VERIFYHOST: CURLoption = CURLOPTTYPE_LONG + 307;
615+
pub const CURLOPT_DOH_SSL_VERIFYSTATUS: CURLoption = CURLOPTTYPE_LONG + 308;
612616
pub const CURLOPT_CAINFO_BLOB: CURLoption = CURLOPTTYPE_BLOB + 309;
613617

614618
pub const CURL_IPRESOLVE_WHATEVER: c_int = 0;

examples/doh.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use curl::easy::Easy;
2+
use std::io::{stdout, Write};
3+
4+
fn main() -> Result<(), curl::Error> {
5+
let mut curl = Easy::new();
6+
7+
curl.url("https://example.com")?;
8+
curl.doh_url(Some("https://cloudflare-dns.com/dns-query"))?;
9+
curl.write_function(|data| {
10+
stdout().write_all(data).unwrap();
11+
Ok(data.len())
12+
})?;
13+
14+
curl.perform()?;
15+
16+
Ok(())
17+
}

src/easy/handle.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,26 @@ impl Easy {
648648
self.inner.dns_cache_timeout(dur)
649649
}
650650

651+
/// Same as [`Easy2::doh_url`](struct.Easy2.html#method.doh_url)
652+
pub fn doh_url(&mut self, url: Option<&str>) -> Result<(), Error> {
653+
self.inner.doh_url(url)
654+
}
655+
656+
/// Same as [`Easy2::doh_ssl_verify_peer`](struct.Easy2.html#method.doh_ssl_verify_peer)
657+
pub fn doh_ssl_verify_peer(&mut self, verify: bool) -> Result<(), Error> {
658+
self.inner.doh_ssl_verify_peer(verify)
659+
}
660+
661+
/// Same as [`Easy2::doh_ssl_verify_host`](struct.Easy2.html#method.doh_ssl_verify_host)
662+
pub fn doh_ssl_verify_host(&mut self, verify: bool) -> Result<(), Error> {
663+
self.inner.doh_ssl_verify_host(verify)
664+
}
665+
666+
/// Same as [`Easy2::doh_ssl_verify_status`](struct.Easy2.html#method.doh_ssl_verify_status)
667+
pub fn doh_ssl_verify_status(&mut self, verify: bool) -> Result<(), Error> {
668+
self.inner.doh_ssl_verify_status(verify)
669+
}
670+
651671
/// Same as [`Easy2::buffer_size`](struct.Easy2.html#method.buffer_size)
652672
pub fn buffer_size(&mut self, size: usize) -> Result<(), Error> {
653673
self.inner.buffer_size(size)

src/easy/handler.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,123 @@ impl<H> Easy2<H> {
10491049
self.setopt_long(curl_sys::CURLOPT_DNS_CACHE_TIMEOUT, dur.as_secs() as c_long)
10501050
}
10511051

1052+
/// Provide the DNS-over-HTTPS URL.
1053+
///
1054+
/// The parameter must be URL-encoded in the following format:
1055+
/// `https://host:port/path`. It **must** specify a HTTPS URL.
1056+
///
1057+
/// libcurl does not validate the syntax or use this variable until the
1058+
/// transfer is issued. Even if you set a crazy value here, this method will
1059+
/// still return [`Ok`].
1060+
///
1061+
/// curl sends `POST` requests to the given DNS-over-HTTPS URL.
1062+
///
1063+
/// To find the DoH server itself, which might be specified using a name,
1064+
/// libcurl will use the default name lookup function. You can bootstrap
1065+
/// that by providing the address for the DoH server with
1066+
/// [`Easy2::resolve`].
1067+
///
1068+
/// Disable DoH use again by setting this option to [`None`].
1069+
///
1070+
/// By default this option is not set and corresponds to `CURLOPT_DOH_URL`.
1071+
pub fn doh_url(&mut self, url: Option<&str>) -> Result<(), Error> {
1072+
if let Some(url) = url {
1073+
let url = CString::new(url)?;
1074+
self.setopt_str(curl_sys::CURLOPT_DOH_URL, &url)
1075+
} else {
1076+
self.setopt_ptr(curl_sys::CURLOPT_DOH_URL, ptr::null())
1077+
}
1078+
}
1079+
1080+
/// This option tells curl to verify the authenticity of the DoH
1081+
/// (DNS-over-HTTPS) server's certificate. A value of `true` means curl
1082+
/// verifies; `false` means it does not.
1083+
///
1084+
/// This option is the DoH equivalent of [`Easy2::ssl_verify_peer`] and only
1085+
/// affects requests to the DoH server.
1086+
///
1087+
/// When negotiating a TLS or SSL connection, the server sends a certificate
1088+
/// indicating its identity. Curl verifies whether the certificate is
1089+
/// authentic, i.e. that you can trust that the server is who the
1090+
/// certificate says it is. This trust is based on a chain of digital
1091+
/// signatures, rooted in certification authority (CA) certificates you
1092+
/// supply. curl uses a default bundle of CA certificates (the path for that
1093+
/// is determined at build time) and you can specify alternate certificates
1094+
/// with the [`Easy2::cainfo`] option or the [`Easy2::capath`] option.
1095+
///
1096+
/// When `doh_ssl_verify_peer` is enabled, and the verification fails to
1097+
/// prove that the certificate is authentic, the connection fails. When the
1098+
/// option is zero, the peer certificate verification succeeds regardless.
1099+
///
1100+
/// Authenticating the certificate is not enough to be sure about the
1101+
/// server. You typically also want to ensure that the server is the server
1102+
/// you mean to be talking to. Use [`Easy2::doh_ssl_verify_host`] for that.
1103+
/// The check that the host name in the certificate is valid for the host
1104+
/// name you are connecting to is done independently of the
1105+
/// `doh_ssl_verify_peer` option.
1106+
///
1107+
/// **WARNING:** disabling verification of the certificate allows bad guys
1108+
/// to man-in-the-middle the communication without you knowing it. Disabling
1109+
/// verification makes the communication insecure. Just having encryption on
1110+
/// a transfer is not enough as you cannot be sure that you are
1111+
/// communicating with the correct end-point.
1112+
///
1113+
/// By default this option is set to `true` and corresponds to
1114+
/// `CURLOPT_DOH_SSL_VERIFYPEER`.
1115+
pub fn doh_ssl_verify_peer(&mut self, verify: bool) -> Result<(), Error> {
1116+
self.setopt_long(curl_sys::CURLOPT_DOH_SSL_VERIFYPEER, verify.into())
1117+
}
1118+
1119+
/// Tells curl to verify the DoH (DNS-over-HTTPS) server's certificate name
1120+
/// fields against the host name.
1121+
///
1122+
/// This option is the DoH equivalent of [`Easy2::ssl_verify_host`] and only
1123+
/// affects requests to the DoH server.
1124+
///
1125+
/// When `doh_ssl_verify_host` is `true`, the SSL certificate provided by
1126+
/// the DoH server must indicate that the server name is the same as the
1127+
/// server name to which you meant to connect to, or the connection fails.
1128+
///
1129+
/// Curl considers the DoH server the intended one when the Common Name
1130+
/// field or a Subject Alternate Name field in the certificate matches the
1131+
/// host name in the DoH URL to which you told Curl to connect.
1132+
///
1133+
/// When the verify value is set to `false`, the connection succeeds
1134+
/// regardless of the names used in the certificate. Use that ability with
1135+
/// caution!
1136+
///
1137+
/// See also [`Easy2::doh_ssl_verify_peer`] to verify the digital signature
1138+
/// of the DoH server certificate. If libcurl is built against NSS and
1139+
/// [`Easy2::doh_ssl_verify_peer`] is `false`, `doh_ssl_verify_host` is also
1140+
/// set to `false` and cannot be overridden.
1141+
///
1142+
/// By default this option is set to `true` and corresponds to
1143+
/// `CURLOPT_DOH_SSL_VERIFYHOST`.
1144+
pub fn doh_ssl_verify_host(&mut self, verify: bool) -> Result<(), Error> {
1145+
self.setopt_long(
1146+
curl_sys::CURLOPT_DOH_SSL_VERIFYHOST,
1147+
if verify { 2 } else { 0 },
1148+
)
1149+
}
1150+
1151+
/// Pass a long as parameter set to 1 to enable or 0 to disable.
1152+
///
1153+
/// This option determines whether libcurl verifies the status of the DoH
1154+
/// (DNS-over-HTTPS) server cert using the "Certificate Status Request" TLS
1155+
/// extension (aka. OCSP stapling).
1156+
///
1157+
/// This option is the DoH equivalent of CURLOPT_SSL_VERIFYSTATUS and only
1158+
/// affects requests to the DoH server.
1159+
///
1160+
/// Note that if this option is enabled but the server does not support the
1161+
/// TLS extension, the verification will fail.
1162+
///
1163+
/// By default this option is set to `false` and corresponds to
1164+
/// `CURLOPT_DOH_SSL_VERIFYSTATUS`.
1165+
pub fn doh_ssl_verify_status(&mut self, verify: bool) -> Result<(), Error> {
1166+
self.setopt_long(curl_sys::CURLOPT_DOH_SSL_VERIFYSTATUS, verify.into())
1167+
}
1168+
10521169
/// Specify the preferred receive buffer size, in bytes.
10531170
///
10541171
/// This is treated as a request, not an order, and the main point of this

systest/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ fn main() {
7676
_ => {}
7777
}
7878
}
79+
if version < 76 {
80+
match s {
81+
"CURLOPT_DOH_SSL_VERIFYHOST" => return true,
82+
"CURLOPT_DOH_SSL_VERIFYPEER" => return true,
83+
"CURLOPT_DOH_SSL_VERIFYSTATUS" => return true,
84+
_ => {}
85+
}
86+
}
7987
if version < 75 {
8088
match s {
8189
"CURLAUTH_AWS_SIGV4" => return true,
@@ -138,6 +146,7 @@ fn main() {
138146
}
139147
if version < 62 {
140148
match s {
149+
"CURLOPT_DOH_URL" => return true,
141150
"CURLOPT_UPLOAD_BUFFERSIZE" => return true,
142151
_ => {}
143152
}

0 commit comments

Comments
 (0)