1use super::proto;
6use rustls::DistinguishedName;
7use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
8use rustls::crypto::{
9 CryptoProvider, WebPkiSupportedAlgorithms, verify_tls12_signature, verify_tls13_signature,
10};
11use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName, UnixTime};
12use rustls::server::danger::{ClientCertVerified, ClientCertVerifier};
13use rustls::{
14 ClientConfig, ClientConnection, DigitallySignedStruct, ServerConfig, ServerConnection,
15 SignatureScheme, StreamOwned,
16};
17use sha2::{Digest, Sha256};
18use std::io::{self, BufRead, BufReader, Read, Write};
19use std::net::{IpAddr, Shutdown, SocketAddr, TcpStream};
20use std::sync::{Arc, Mutex};
21use std::time::Duration;
22
23pub fn valid_pin(fingerprint: &str) -> bool {
25 fingerprint.len() == 64 && fingerprint.bytes().all(|b| b.is_ascii_hexdigit())
26}
27
28pub fn sha256_hex(bytes: &[u8]) -> String {
29 Sha256::digest(bytes)
30 .iter()
31 .map(|b| format!("{b:02x}"))
32 .collect()
33}
34
35fn other<E: std::fmt::Display>(error: E) -> io::Error {
36 io::Error::other(error.to_string())
37}
38
39fn provider() -> Arc<CryptoProvider> {
40 Arc::new(rustls::crypto::ring::default_provider())
41}
42
43#[derive(Clone)]
50pub struct Identity {
51 pub cert_der: Vec<u8>,
52 pub key_der: Vec<u8>,
53 pub fingerprint: String,
54}
55
56impl std::fmt::Debug for Identity {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("Identity")
59 .field("fingerprint", &self.fingerprint)
60 .finish_non_exhaustive()
61 }
62}
63
64impl Identity {
65 pub fn generate(alias: &str) -> io::Result<Identity> {
71 let key_pair = rcgen::KeyPair::generate().map_err(other)?;
72 let common_name: String = alias.chars().filter(|c| !c.is_control()).take(64).collect();
73 let mut params =
74 rcgen::CertificateParams::new(vec!["localsend".to_string()]).map_err(other)?;
75 params.distinguished_name.push(
76 rcgen::DnType::CommonName,
77 rcgen::DnValue::Utf8String(common_name),
78 );
79 let certificate = params.self_signed(&key_pair).map_err(other)?;
80 Identity::from_der(certificate.der().to_vec(), key_pair.serialize_der())
81 }
82
83 pub fn load(cert_der: Vec<u8>, key_der: Vec<u8>) -> io::Result<Identity> {
87 Identity::from_der(cert_der, key_der)
88 }
89
90 fn from_der(cert_der: Vec<u8>, key_der: Vec<u8>) -> io::Result<Identity> {
91 let identity = Identity {
92 fingerprint: sha256_hex(&cert_der),
93 cert_der,
94 key_der,
95 };
96 server_config(&identity)?;
97 Ok(identity)
98 }
99}
100
101static USER_AGENT: Mutex<String> = Mutex::new(String::new());
103
104pub fn set_user_agent(agent: &str) {
105 *USER_AGENT
106 .lock()
107 .unwrap_or_else(|poisoned| poisoned.into_inner()) = agent.to_string();
108}
109
110fn user_agent() -> String {
111 USER_AGENT
112 .lock()
113 .unwrap_or_else(|poisoned| poisoned.into_inner())
114 .clone()
115}
116
117pub(crate) const MISMATCH_MESSAGE: &str = "the device's certificate does not match its fingerprint";
120
121pub(crate) fn is_identity_mismatch(error: &io::Error) -> bool {
124 error
125 .get_ref()
126 .and_then(|inner| inner.downcast_ref::<rustls::Error>())
127 .is_some_and(|tls| matches!(tls, rustls::Error::General(text) if text == MISMATCH_MESSAGE))
128}
129
130#[derive(Debug)]
136struct OptionalClientCert {
137 algorithms: WebPkiSupportedAlgorithms,
138}
139
140impl ClientCertVerifier for OptionalClientCert {
141 fn offer_client_auth(&self) -> bool {
142 true
143 }
144
145 fn client_auth_mandatory(&self) -> bool {
146 false
147 }
148
149 fn root_hint_subjects(&self) -> &[DistinguishedName] {
150 &[]
151 }
152
153 fn verify_client_cert(
154 &self,
155 _end_entity: &CertificateDer<'_>,
156 _intermediates: &[CertificateDer<'_>],
157 _now: UnixTime,
158 ) -> Result<ClientCertVerified, rustls::Error> {
159 Ok(ClientCertVerified::assertion())
160 }
161
162 fn verify_tls12_signature(
163 &self,
164 message: &[u8],
165 cert: &CertificateDer<'_>,
166 dss: &DigitallySignedStruct,
167 ) -> Result<HandshakeSignatureValid, rustls::Error> {
168 verify_tls12_signature(message, cert, dss, &self.algorithms)
169 }
170
171 fn verify_tls13_signature(
172 &self,
173 message: &[u8],
174 cert: &CertificateDer<'_>,
175 dss: &DigitallySignedStruct,
176 ) -> Result<HandshakeSignatureValid, rustls::Error> {
177 verify_tls13_signature(message, cert, dss, &self.algorithms)
178 }
179
180 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
181 self.algorithms.supported_schemes()
182 }
183}
184
185static CLIENT_IDENTITY: Mutex<Option<(Vec<u8>, Vec<u8>)>> = Mutex::new(None);
188
189pub fn set_client_identity(identity: &Identity) {
190 let mut slot = CLIENT_IDENTITY
191 .lock()
192 .unwrap_or_else(|poisoned| poisoned.into_inner());
193 *slot = Some((identity.cert_der.clone(), identity.key_der.clone()));
194}
195
196pub fn server_config(identity: &Identity) -> io::Result<Arc<ServerConfig>> {
197 let certificate = CertificateDer::from(identity.cert_der.clone());
198 let key = PrivateKeyDer::from(PrivatePkcs8KeyDer::from(identity.key_der.clone()));
199 let provider = provider();
200 let verifier = OptionalClientCert {
201 algorithms: provider.signature_verification_algorithms,
202 };
203 let config = ServerConfig::builder_with_provider(provider)
204 .with_safe_default_protocol_versions()
205 .map_err(other)?
206 .with_client_cert_verifier(Arc::new(verifier))
207 .with_single_cert(vec![certificate], key)
208 .map_err(other)?;
209 Ok(Arc::new(config))
210}
211
212#[derive(Debug)]
223struct PinVerifier {
224 expected: Option<String>,
225 algorithms: WebPkiSupportedAlgorithms,
226}
227
228impl ServerCertVerifier for PinVerifier {
229 fn verify_server_cert(
230 &self,
231 end_entity: &CertificateDer<'_>,
232 _intermediates: &[CertificateDer<'_>],
233 _server_name: &ServerName<'_>,
234 _ocsp_response: &[u8],
235 _now: UnixTime,
236 ) -> Result<ServerCertVerified, rustls::Error> {
237 if let Some(expected) = &self.expected
238 && sha256_hex(end_entity.as_ref()) != *expected
239 {
240 return Err(rustls::Error::General(
241 "the device's certificate does not match its fingerprint".into(),
242 ));
243 }
244 Ok(ServerCertVerified::assertion())
245 }
246
247 fn verify_tls12_signature(
248 &self,
249 message: &[u8],
250 cert: &CertificateDer<'_>,
251 dss: &DigitallySignedStruct,
252 ) -> Result<HandshakeSignatureValid, rustls::Error> {
253 verify_tls12_signature(message, cert, dss, &self.algorithms)
254 }
255
256 fn verify_tls13_signature(
257 &self,
258 message: &[u8],
259 cert: &CertificateDer<'_>,
260 dss: &DigitallySignedStruct,
261 ) -> Result<HandshakeSignatureValid, rustls::Error> {
262 verify_tls13_signature(message, cert, dss, &self.algorithms)
263 }
264
265 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
266 self.algorithms.supported_schemes()
267 }
268}
269
270fn client_config(fingerprint: &str) -> io::Result<Arc<ClientConfig>> {
271 let provider = provider();
272 let expected = {
273 let f = fingerprint.to_ascii_lowercase();
274 (f.len() == 64 && f.bytes().all(|b| b.is_ascii_hexdigit())).then_some(f)
275 };
276 let verifier = PinVerifier {
277 expected,
278 algorithms: provider.signature_verification_algorithms,
279 };
280 let builder = ClientConfig::builder_with_provider(provider)
281 .with_safe_default_protocol_versions()
282 .map_err(other)?
283 .dangerous()
284 .with_custom_certificate_verifier(Arc::new(verifier));
285 let own = CLIENT_IDENTITY
286 .lock()
287 .unwrap_or_else(|poisoned| poisoned.into_inner())
288 .clone();
289 let config = match own {
290 Some((cert, key)) => builder
291 .with_client_auth_cert(
292 vec![CertificateDer::from(cert)],
293 PrivateKeyDer::from(PrivatePkcs8KeyDer::from(key)),
294 )
295 .map_err(other)?,
296 None => builder.with_no_client_auth(),
297 };
298 Ok(Arc::new(config))
299}
300
301pub enum Wire {
304 Plain(TcpStream),
305 Server(Box<StreamOwned<ServerConnection, TcpStream>>),
306 Client(Box<StreamOwned<ClientConnection, TcpStream>>),
307}
308
309impl Read for Wire {
310 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
311 match self {
312 Wire::Plain(s) => s.read(buf),
313 Wire::Server(s) => s.read(buf),
314 Wire::Client(s) => s.read(buf),
315 }
316 }
317}
318
319impl Write for Wire {
320 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
321 match self {
322 Wire::Plain(s) => s.write(buf),
323 Wire::Server(s) => s.write(buf),
324 Wire::Client(s) => s.write(buf),
325 }
326 }
327 fn flush(&mut self) -> io::Result<()> {
328 match self {
329 Wire::Plain(s) => s.flush(),
330 Wire::Server(s) => s.flush(),
331 Wire::Client(s) => s.flush(),
332 }
333 }
334}
335
336impl Wire {
337 pub fn tcp(&self) -> &TcpStream {
338 match self {
339 Wire::Plain(s) => s,
340 Wire::Server(s) => &s.sock,
341 Wire::Client(s) => &s.sock,
342 }
343 }
344
345 pub fn peer_fingerprint(&self) -> Option<String> {
349 match self {
350 Wire::Server(s) => s
351 .conn
352 .peer_certificates()
353 .and_then(|certs| certs.first())
354 .map(|cert| sha256_hex(cert.as_ref())),
355 _ => None,
356 }
357 }
358
359 pub fn finish(&mut self) {
361 match self {
362 Wire::Server(s) => s.conn.send_close_notify(),
363 Wire::Client(s) => s.conn.send_close_notify(),
364 Wire::Plain(_) => {}
365 }
366 let _ = self.flush();
367 let _ = self.tcp().shutdown(Shutdown::Both);
368 }
369}
370
371pub fn accept_tls(config: &Arc<ServerConfig>, socket: TcpStream) -> io::Result<Wire> {
373 let connection = ServerConnection::new(config.clone()).map_err(other)?;
374 Ok(Wire::Server(Box::new(StreamOwned::new(connection, socket))))
375}
376
377pub fn connect(
379 ip: IpAddr,
380 port: u16,
381 https: bool,
382 fingerprint: &str,
383 read_timeout: Duration,
384) -> io::Result<Wire> {
385 connect_within(
386 ip,
387 port,
388 https,
389 fingerprint,
390 Duration::from_secs(5),
391 read_timeout,
392 )
393}
394
395pub fn connect_within(
397 ip: IpAddr,
398 port: u16,
399 https: bool,
400 fingerprint: &str,
401 connect_timeout: Duration,
402 read_timeout: Duration,
403) -> io::Result<Wire> {
404 let socket = TcpStream::connect_timeout(&SocketAddr::new(ip, port), connect_timeout)?;
405 socket.set_read_timeout(Some(read_timeout))?;
406 socket.set_write_timeout(Some(Duration::from_secs(30)))?;
407 let _ = socket.set_nodelay(true);
408 if !https {
409 return Ok(Wire::Plain(socket));
410 }
411 let config = client_config(fingerprint)?;
412 let name = ServerName::IpAddress(ip.into());
413 let connection = ClientConnection::new(config, name).map_err(other)?;
414 Ok(Wire::Client(Box::new(StreamOwned::new(connection, socket))))
415}
416
417pub struct Request {
420 pub method: String,
421 pub path: String,
422 pub query: Vec<(String, String)>,
423 pub headers: Vec<(String, String)>,
424}
425
426impl Request {
427 pub fn header(&self, name: &str) -> Option<&str> {
428 self.headers
429 .iter()
430 .find(|(k, _)| k.eq_ignore_ascii_case(name))
431 .map(|(_, v)| v.as_str())
432 }
433 pub fn param(&self, name: &str) -> Option<&str> {
434 self.query
435 .iter()
436 .find(|(k, _)| k == name)
437 .map(|(_, v)| v.as_str())
438 }
439}
440
441fn header_of<'a>(headers: &'a [(String, String)], name: &str) -> Option<&'a str> {
442 headers
443 .iter()
444 .find(|(k, _)| k.eq_ignore_ascii_case(name))
445 .map(|(_, v)| v.as_str())
446}
447
448fn bad(message: &str) -> io::Error {
449 io::Error::new(io::ErrorKind::InvalidData, message.to_string())
450}
451
452fn read_line_limited<R: BufRead>(reader: &mut R, limit: u64) -> io::Result<String> {
453 let mut line = String::new();
454 let read = reader.by_ref().take(limit).read_line(&mut line)?;
455 if read == 0 {
456 return Err(io::Error::new(
457 io::ErrorKind::UnexpectedEof,
458 "connection closed",
459 ));
460 }
461 if !line.ends_with('\n') {
462 return Err(bad("line too long"));
463 }
464 Ok(line.trim_end_matches(['\r', '\n']).to_string())
465}
466
467fn read_headers<R: BufRead>(reader: &mut R) -> io::Result<Vec<(String, String)>> {
468 let mut headers = Vec::new();
469 loop {
470 let line = read_line_limited(reader, 16 * 1024)?;
471 if line.is_empty() {
472 return Ok(headers);
473 }
474 if headers.len() >= 100 {
475 return Err(bad("too many headers"));
476 }
477 if let Some((name, value)) = line.split_once(':') {
478 headers.push((name.trim().to_string(), value.trim().to_string()));
479 }
480 }
481}
482
483pub fn read_request<R: BufRead>(reader: &mut R) -> io::Result<Request> {
484 let line = read_line_limited(reader, 16 * 1024)?;
485 let mut parts = line.split_whitespace();
486 let method = parts
487 .next()
488 .ok_or_else(|| bad("empty request"))?
489 .to_string();
490 let target = parts.next().ok_or_else(|| bad("no request target"))?;
491 let (path, query_text) = target.split_once('?').unwrap_or((target, ""));
492 let query = query_text
493 .split('&')
494 .filter(|p| !p.is_empty())
495 .map(|pair| {
496 let (k, v) = pair.split_once('=').unwrap_or((pair, ""));
497 (proto::url_decode(k), proto::url_decode(v))
498 })
499 .collect();
500 let headers = read_headers(reader)?;
501 Ok(Request {
502 method,
503 path: path.to_string(),
504 query,
505 headers,
506 })
507}
508
509pub fn read_body<R: BufRead>(
512 reader: &mut R,
513 headers: &[(String, String)],
514 max: u64,
515 sink: &mut dyn FnMut(&[u8]) -> io::Result<()>,
516) -> io::Result<u64> {
517 let find = |name: &str| header_of(headers, name);
518 let mut buffer = vec![0u8; 64 * 1024];
519 let mut total = 0u64;
520 let chunked =
521 find("transfer-encoding").is_some_and(|v| v.to_ascii_lowercase().contains("chunked"));
522 if chunked {
523 loop {
524 let size_line = read_line_limited(reader, 1024)?;
525 let size_text = size_line.split(';').next().unwrap_or("").trim();
526 let mut remaining =
527 u64::from_str_radix(size_text, 16).map_err(|_| bad("bad chunk size"))?;
528 if remaining == 0 {
529 while !read_line_limited(reader, 16 * 1024)?.is_empty() {}
530 return Ok(total);
531 }
532 while remaining > 0 {
533 let want = remaining.min(buffer.len() as u64) as usize;
534 let n = reader.read(&mut buffer[..want])?;
535 if n == 0 {
536 return Err(io::Error::new(
537 io::ErrorKind::UnexpectedEof,
538 "body cut short",
539 ));
540 }
541 total += n as u64;
542 if total > max {
543 return Err(bad("body too large"));
544 }
545 sink(&buffer[..n])?;
546 remaining -= n as u64;
547 }
548 read_line_limited(reader, 16)?; }
550 }
551 let length = match find("content-length") {
552 Some(v) => v.parse::<u64>().map_err(|_| bad("bad content length"))?,
553 None => return Ok(0),
554 };
555 if length > max {
556 return Err(bad("body too large"));
557 }
558 let mut remaining = length;
559 while remaining > 0 {
560 let want = remaining.min(buffer.len() as u64) as usize;
561 let n = reader.read(&mut buffer[..want])?;
562 if n == 0 {
563 return Err(io::Error::new(
564 io::ErrorKind::UnexpectedEof,
565 "body cut short",
566 ));
567 }
568 total += n as u64;
569 sink(&buffer[..n])?;
570 remaining -= n as u64;
571 }
572 Ok(total)
573}
574
575fn reason(status: u16) -> &'static str {
576 match status {
577 200 => "OK",
578 204 => "No Content",
579 400 => "Bad Request",
580 401 => "Unauthorized",
581 403 => "Forbidden",
582 404 => "Not Found",
583 409 => "Conflict",
584 413 => "Payload Too Large",
585 422 => "Unprocessable Entity",
586 429 => "Too Many Requests",
587 _ => "Internal Server Error",
588 }
589}
590
591pub fn write_response<W: Write>(
592 writer: &mut W,
593 status: u16,
594 json: Option<&[u8]>,
595) -> io::Result<()> {
596 let body = json.unwrap_or(&[]);
597 let mut head = format!(
598 "HTTP/1.1 {status} {}\r\nConnection: close\r\n",
599 reason(status)
600 );
601 if json.is_some() {
602 head.push_str("Content-Type: application/json\r\n");
603 }
604 head.push_str(&format!("Content-Length: {}\r\n\r\n", body.len()));
605 writer.write_all(head.as_bytes())?;
606 writer.write_all(body)?;
607 writer.flush()
608}
609
610pub struct Reply {
613 pub status: u16,
614 pub body: Vec<u8>,
615}
616
617pub fn call(
620 wire: &mut Wire,
621 method: &str,
622 host: &str,
623 target: &str,
624 content_type: Option<&str>,
625 length: u64,
626 write_body: &mut dyn FnMut(&mut Wire) -> io::Result<()>,
627) -> io::Result<Reply> {
628 let agent = user_agent();
629 let mut head = format!(
630 "{method} {target} HTTP/1.1\r\nHost: {host}\r\nUser-Agent: {agent}\r\nConnection: close\r\nContent-Length: {length}\r\n"
631 );
632 if let Some(kind) = content_type {
633 head.push_str(&format!("Content-Type: {kind}\r\n"));
634 }
635 head.push_str("\r\n");
636 wire.write_all(head.as_bytes())?;
637 write_body(wire)?;
638 wire.flush()?;
639
640 let mut reader = BufReader::new(&mut *wire);
641 let status_line = read_line_limited(&mut reader, 16 * 1024)?;
642 let status: u16 = status_line
643 .split_whitespace()
644 .nth(1)
645 .and_then(|s| s.parse().ok())
646 .ok_or_else(|| bad("bad status line"))?;
647 let headers = read_headers(&mut reader)?;
648 let mut body = Vec::new();
649 let has_length = headers
650 .iter()
651 .any(|(k, _)| k.eq_ignore_ascii_case("content-length"));
652 let chunked = headers.iter().any(|(k, v)| {
653 k.eq_ignore_ascii_case("transfer-encoding") && v.to_ascii_lowercase().contains("chunked")
654 });
655 const CAP: u64 = 8 * 1024 * 1024;
656 if has_length || chunked {
657 read_body(&mut reader, &headers, CAP, &mut |c| {
658 body.extend_from_slice(c);
659 Ok(())
660 })?;
661 } else {
662 let mut limited = (&mut reader).take(CAP);
665 match limited.read_to_end(&mut body) {
666 Ok(_) => {}
667 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {}
668 Err(e) => return Err(e),
669 }
670 }
671 Ok(Reply { status, body })
672}
673
674#[allow(clippy::too_many_arguments)]
676pub fn request_json(
677 ip: IpAddr,
678 port: u16,
679 https: bool,
680 fingerprint: &str,
681 method: &str,
682 target: &str,
683 json: Option<&[u8]>,
684 timeout: Duration,
685) -> io::Result<Reply> {
686 let mut wire = connect(ip, port, https, fingerprint, timeout)?;
687 let payload = json.unwrap_or(&[]);
688 let host = format!("{ip}:{port}");
689 let result = call(
690 &mut wire,
691 method,
692 &host,
693 target,
694 json.map(|_| "application/json"),
695 payload.len() as u64,
696 &mut |w| w.write_all(payload),
697 );
698 wire.finish();
699 result
700}
701
702pub fn prove_key(ip: IpAddr, port: u16, fingerprint: &str) -> bool {
714 let fingerprint = fingerprint.to_ascii_lowercase();
715 if !valid_pin(&fingerprint) {
716 return false;
717 }
718 let timeout = Duration::from_secs(3);
719 let Ok(mut wire) = connect_within(ip, port, true, &fingerprint, timeout, timeout) else {
720 return false;
721 };
722 let host = format!("{ip}:{port}");
723 let result = call(
724 &mut wire,
725 "GET",
726 &host,
727 &format!("{}/info", proto::API),
728 None,
729 0,
730 &mut |_| Ok(()),
731 );
732 wire.finish();
733 result.is_ok_and(|reply| reply.status == 200)
734}
735
736#[cfg(test)]
737mod tests {
738 use super::*;
739 use std::io::Read;
740
741 #[test]
742 fn a_generated_identity_round_trips_through_load() {
743 let made = Identity::generate("Test Mac").unwrap();
744 assert!(valid_pin(&made.fingerprint));
745 assert_eq!(made.fingerprint, sha256_hex(&made.cert_der));
746 let loaded = Identity::load(made.cert_der.clone(), made.key_der.clone()).unwrap();
747 assert_eq!(loaded.fingerprint, made.fingerprint);
748 assert_eq!(loaded.key_der, made.key_der);
749 }
750
751 #[test]
752 fn two_generated_identities_have_different_fingerprints() {
753 let a = Identity::generate("Same Name").unwrap();
754 let b = Identity::generate("Same Name").unwrap();
755 assert_ne!(a.fingerprint, b.fingerprint);
756 }
757
758 #[test]
759 fn corrupt_identity_bytes_are_refused() {
760 assert!(Identity::load(vec![1, 2, 3], vec![4, 5]).is_err());
761 }
762
763 #[test]
764 fn the_verifier_message_is_the_one_the_classifier_matches() {
765 let server = Identity::generate("Server").unwrap();
766 let other = Identity::generate("Other").unwrap();
767 let verifier = PinVerifier {
768 expected: Some(other.fingerprint.clone()),
769 algorithms: provider().signature_verification_algorithms,
770 };
771 let cert = CertificateDer::from(server.cert_der.clone());
772 let name = ServerName::IpAddress("127.0.0.1".parse::<IpAddr>().unwrap().into());
773 let Err(error) = verifier.verify_server_cert(&cert, &[], &name, &[], UnixTime::now())
774 else {
775 panic!("a different certificate is refused");
776 };
777 assert!(matches!(&error, rustls::Error::General(text) if text == MISMATCH_MESSAGE));
778 }
779
780 #[test]
781 fn a_pinned_handshake_with_another_certificate_is_an_identity_mismatch() {
782 let server = Identity::generate("Server").unwrap();
783 let other = Identity::generate("Other").unwrap();
784 let listener = std::net::TcpListener::bind(("127.0.0.1", 0)).unwrap();
785 let port = listener.local_addr().unwrap().port();
786 let tls = server_config(&server).unwrap();
787 let accepting = std::thread::spawn(move || {
788 if let Ok((socket, _)) = listener.accept() {
789 let _ = socket.set_read_timeout(Some(Duration::from_secs(5)));
790 if let Ok(mut wire) = accept_tls(&tls, socket) {
791 let mut buffer = [0u8; 16];
792 let _ = wire.read(&mut buffer);
793 }
794 }
795 });
796 let ip: IpAddr = "127.0.0.1".parse().unwrap();
797 let mut wire = connect(ip, port, true, &other.fingerprint, Duration::from_secs(5)).unwrap();
798 let error = call(&mut wire, "GET", "127.0.0.1", "/", None, 0, &mut |_| Ok(()))
799 .err()
800 .expect("the handshake must fail");
801 assert!(is_identity_mismatch(&error));
802 drop(wire);
803 accepting.join().unwrap();
804 }
805}