Skip to main content

netget/client/dot/
mod.rs

1//! DoT (DNS over TLS) client implementation
2pub mod actions;
3
4pub use actions::DotClientProtocol;
5
6use anyhow::{Context, Result};
7use hickory_proto::op::{Message as DnsMessage, MessageType, OpCode, Query};
8use hickory_proto::rr::{Name, RecordType};
9use std::net::SocketAddr;
10use std::str::FromStr;
11use std::sync::Arc;
12use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf};
13use tokio::net::TcpStream;
14use tokio::sync::{mpsc, Mutex};
15use tokio_rustls::client::TlsStream;
16use tokio_rustls::rustls::pki_types::ServerName;
17use tokio_rustls::rustls::{ClientConfig, RootCertStore};
18use tokio_rustls::TlsConnector;
19use tracing::{debug, error, info, trace};
20
21use crate::client::dot::actions::{DOT_CLIENT_CONNECTED_EVENT, DOT_CLIENT_RESPONSE_RECEIVED_EVENT};
22use crate::client::llm_budget::call_llm_for_client;
23use crate::llm::actions::client_trait::{Client as ClientTrait, ClientActionResult};
24use crate::llm::ollama_client::OllamaClient;
25use crate::llm::ClientLlmResult;
26use crate::protocol::Event;
27use crate::state::app_state::AppState;
28use crate::state::{ClientId, ClientStatus};
29
30/// Connection state for LLM processing
31#[derive(Debug, Clone, PartialEq)]
32enum ConnectionState {
33    Idle,
34    Processing,
35    Accumulating,
36}
37
38/// Per-client data for LLM handling
39struct ClientData {
40    state: ConnectionState,
41    queued_responses: Vec<Vec<u8>>,
42    memory: String,
43}
44
45/// DoT client that makes DNS queries over TLS
46pub struct DotClient;
47
48impl DotClient {
49    /// Connect to a DoT server with integrated LLM actions
50    pub async fn connect_with_llm_actions(
51        remote_addr: String,
52        llm_client: OllamaClient,
53        app_state: Arc<AppState>,
54        status_tx: mpsc::UnboundedSender<String>,
55        client_id: ClientId,
56        startup_params: Option<crate::protocol::StartupParams>,
57    ) -> Result<SocketAddr> {
58        info!("DoT client {} connecting to {}", client_id, remote_addr);
59        let _ = status_tx.send(format!(
60            "[CLIENT] DoT client {} connecting to {}",
61            client_id, remote_addr
62        ));
63
64        // Both parameters are declared in `get_startup_parameters()`; until this pass neither
65        // was read, so `verify_tls: false` silently still verified against the Mozilla roots
66        // and a NetGet DoT server (self-signed) could never be reached.
67        let verify_tls = startup_params
68            .as_ref()
69            .map(|p| p.get_optional_bool("verify_tls"))
70            .transpose()?
71            .flatten()
72            .unwrap_or(true);
73        let server_name_override = startup_params
74            .as_ref()
75            .map(|p| p.get_optional_string("server_name"))
76            .transpose()?
77            .flatten();
78
79        // Parse remote address
80        let remote_socket_addr: SocketAddr = remote_addr
81            .parse()
82            .context("Invalid remote address format")?;
83
84        // Extract hostname for SNI (or use IP as fallback)
85        let server_name = server_name_override
86            .as_deref()
87            .unwrap_or_else(|| remote_addr.split(':').next().unwrap_or("dns.server"));
88
89        // Install a rustls CryptoProvider before building the config. Without one,
90        // `ClientConfig::builder()` panics rather than returning an error whenever the
91        // build has more than one provider feature live, which `all-protocols` does. See
92        // the fuller note in `src/client/tls/mod.rs`.
93        let _ = tokio_rustls::rustls::crypto::ring::default_provider().install_default();
94
95        // Create TLS config with root certificates
96        let root_store = RootCertStore {
97            roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
98        };
99
100        let config = if verify_tls {
101            ClientConfig::builder()
102                .with_root_certificates(root_store)
103                .with_no_client_auth()
104        } else {
105            debug!("DoT client {} accepting invalid certificates", client_id);
106            ClientConfig::builder()
107                .dangerous()
108                .with_custom_certificate_verifier(Arc::new(NoVerification))
109                .with_no_client_auth()
110        };
111
112        let connector = TlsConnector::from(Arc::new(config));
113
114        // Connect TCP stream
115        let tcp_stream = TcpStream::connect(&remote_socket_addr)
116            .await
117            .context("Failed to connect to DoT server")?;
118
119        let local_addr = tcp_stream.local_addr()?;
120
121        // Perform TLS handshake
122        let server_name = match ServerName::try_from(server_name.to_string()) {
123            Ok(name) => name,
124            Err(_) => {
125                debug!("Failed to parse server name, using IP");
126                ServerName::try_from(remote_socket_addr.ip().to_string())
127                    .map_err(|e| anyhow::anyhow!("Invalid server name: {}", e))?
128            }
129        };
130
131        let tls_stream = connector
132            .connect(server_name, tcp_stream)
133            .await
134            .context("TLS handshake failed")?;
135
136        info!("DoT client {} connected to {}", client_id, remote_addr);
137        let _ = status_tx.send(format!("[CLIENT] DoT client {} connected", client_id));
138
139        // Update status
140        app_state
141            .update_client_status(client_id, ClientStatus::Connected)
142            .await;
143        let _ = status_tx.send("__UPDATE_UI__".to_string());
144
145        // Split stream for bidirectional communication
146        let (read_half, write_half) = tokio::io::split(tls_stream);
147        let write_half_arc = Arc::new(Mutex::new(write_half));
148
149        // Initialize client data
150        let client_data = Arc::new(Mutex::new(ClientData {
151            state: ConnectionState::Idle,
152            queued_responses: Vec::new(),
153            memory: String::new(),
154        }));
155
156        // Command channel for injected actions (the dashboard's [ send_dns_query ]).
157        // Registered BEFORE the connected-event LLM call, which a manual `*` rule can park
158        // for minutes - the operator must be able to reach the client while it waits.
159        let command_rx =
160            crate::client::command_support::register_command_channel(&app_state, client_id).await;
161
162        // The read loop does framed `read_exact` reads, which are not cancellation-safe, so
163        // commands are drained by their own task rather than a `select!` arm. Both tasks
164        // share the write half.
165        let cmd_state = app_state.clone();
166        let cmd_tx = status_tx.clone();
167        let cmd_write = write_half_arc.clone();
168        let cmd_task = tokio::spawn(async move {
169            Self::command_loop(command_rx, cmd_write, client_id, cmd_state, cmd_tx).await;
170        });
171        app_state.register_client_task(client_id, cmd_task).await;
172
173        // Spawn read loop for handling responses
174        let write_for_read = write_half_arc.clone();
175        let client_data_for_read = client_data.clone();
176        let read_app_state = app_state.clone();
177        let read_llm_client = llm_client.clone();
178        let read_status_tx = status_tx.clone();
179
180        // Registered with AppState so stop_client can abort this task —
181        // dropping a JoinHandle only detaches it in Tokio.
182        let task_registrar = app_state.clone();
183        let task_handle = tokio::spawn(async move {
184            if let Err(e) = Self::read_loop(
185                read_half,
186                write_for_read,
187                client_id,
188                read_app_state.clone(),
189                read_llm_client,
190                read_status_tx,
191                client_data_for_read,
192            )
193            .await
194            {
195                error!("DoT client {} read loop error: {}", client_id, e);
196            }
197            // Every exit of the read loop (EOF, read error, LLM or injected disconnect) ends
198            // here: drop the command handle so the rail stops offering [ send ] on a dead
199            // client.
200            read_app_state.remove_client_handle(client_id).await;
201        });
202        task_registrar
203            .register_client_task(client_id, task_handle)
204            .await;
205
206        // Call LLM with connected event
207        if let Some(instruction) = app_state.get_instruction_for_client(client_id).await {
208            let protocol = Arc::new(DotClientProtocol::new());
209            let event = Event::new(
210                &DOT_CLIENT_CONNECTED_EVENT,
211                serde_json::json!({
212                    "remote_addr": remote_addr,
213                }),
214            );
215
216            match call_llm_for_client(
217                &llm_client,
218                &app_state,
219                client_id.to_string(),
220                &instruction,
221                &client_data.lock().await.memory,
222                Some(&event),
223                protocol.as_ref(),
224                &status_tx,
225            )
226            .await
227            {
228                Ok(ClientLlmResult {
229                    actions,
230                    memory_updates,
231                }) => {
232                    // Update memory
233                    if let Some(mem) = memory_updates {
234                        client_data.lock().await.memory = mem;
235                    }
236
237                    // Execute initial actions
238                    for action_json in actions {
239                        if let Err(e) = Self::execute_client_action(
240                            client_id,
241                            action_json,
242                            &write_half_arc,
243                            &status_tx,
244                            &app_state,
245                        )
246                        .await
247                        {
248                            error!("DoT client {} action execution failed: {}", client_id, e);
249                        }
250                    }
251                }
252                Err(e) => {
253                    error!("LLM error for DoT client {}: {}", client_id, e);
254                }
255            }
256        }
257
258        Ok(local_addr)
259    }
260
261    /// Read loop for handling DNS responses
262    async fn read_loop(
263        mut read_half: ReadHalf<TlsStream<TcpStream>>,
264        write_half: Arc<Mutex<WriteHalf<TlsStream<TcpStream>>>>,
265        client_id: ClientId,
266        app_state: Arc<AppState>,
267        llm_client: OllamaClient,
268        status_tx: mpsc::UnboundedSender<String>,
269        client_data: Arc<Mutex<ClientData>>,
270    ) -> Result<()> {
271        loop {
272            // Check client status
273            if app_state.get_client(client_id).await.is_none() {
274                info!("DoT client {} stopped", client_id);
275                break;
276            }
277
278            // Read length-prefixed DNS message (2-byte big-endian length)
279            let mut len_buf = [0u8; 2];
280            match read_half.read_exact(&mut len_buf).await {
281                Ok(_) => {}
282                Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
283                    debug!("DoT client {} connection closed by server", client_id);
284                    let _ =
285                        status_tx.send(format!("[CLIENT] DoT client {} disconnected", client_id));
286                    app_state
287                        .update_client_status(client_id, ClientStatus::Disconnected)
288                        .await;
289                    break;
290                }
291                Err(e) => {
292                    error!(
293                        "DoT client {} failed to read length prefix: {}",
294                        client_id, e
295                    );
296                    break;
297                }
298            }
299
300            let dns_len = u16::from_be_bytes(len_buf) as usize;
301
302            if dns_len == 0 || dns_len > 65535 {
303                error!(
304                    "DoT client {} invalid DNS message length: {}",
305                    client_id, dns_len
306                );
307                break;
308            }
309
310            // Read DNS message
311            let mut dns_buf = vec![0u8; dns_len];
312            if let Err(e) = read_half.read_exact(&mut dns_buf).await {
313                error!("DoT client {} failed to read DNS message: {}", client_id, e);
314                break;
315            }
316
317            debug!("DoT client {} received {} bytes", client_id, dns_len);
318            trace!("DoT response hex: {}", hex::encode(&dns_buf));
319
320            // Parse DNS response
321            let dns_message = match DnsMessage::from_vec(&dns_buf) {
322                Ok(msg) => msg,
323                Err(e) => {
324                    error!(
325                        "DoT client {} failed to parse DNS message: {}",
326                        client_id, e
327                    );
328                    continue;
329                }
330            };
331
332            // Extract response information
333            let query_id = dns_message.id();
334            let response_code = format!("{:?}", dns_message.response_code());
335
336            let answers: Vec<serde_json::Value> = dns_message
337                .answers()
338                .iter()
339                .map(|record| {
340                    let data_str = match record.data() {
341                        Some(data) => format!("{}", data),
342                        None => "NULL".to_string(),
343                    };
344                    serde_json::json!({
345                        "name": record.name().to_utf8(),
346                        "type": format!("{:?}", record.record_type()),
347                        "ttl": record.ttl(),
348                        "data": data_str,
349                    })
350                })
351                .collect();
352
353            let authorities: Vec<serde_json::Value> = dns_message
354                .name_servers()
355                .iter()
356                .map(|record| {
357                    let data_str = match record.data() {
358                        Some(data) => format!("{}", data),
359                        None => "NULL".to_string(),
360                    };
361                    serde_json::json!({
362                        "name": record.name().to_utf8(),
363                        "type": format!("{:?}", record.record_type()),
364                        "ttl": record.ttl(),
365                        "data": data_str,
366                    })
367                })
368                .collect();
369
370            let additionals: Vec<serde_json::Value> = dns_message
371                .additionals()
372                .iter()
373                .map(|record| {
374                    let data_str = match record.data() {
375                        Some(data) => format!("{}", data),
376                        None => "NULL".to_string(),
377                    };
378                    serde_json::json!({
379                        "name": record.name().to_utf8(),
380                        "type": format!("{:?}", record.record_type()),
381                        "ttl": record.ttl(),
382                        "data": data_str,
383                    })
384                })
385                .collect();
386
387            info!(
388                "DoT client {} received response: ID={}, Code={}, Answers={}",
389                client_id,
390                query_id,
391                response_code,
392                answers.len()
393            );
394
395            // Check state machine
396            let mut client_data_lock = client_data.lock().await;
397
398            match client_data_lock.state {
399                ConnectionState::Processing => {
400                    // Queue this data for later
401                    debug!("DoT client {} queuing response (LLM processing)", client_id);
402                    client_data_lock.state = ConnectionState::Accumulating;
403                    client_data_lock.queued_responses.push(dns_buf);
404                    continue;
405                }
406                ConnectionState::Accumulating => {
407                    // Already accumulating, just continue queuing
408                    debug!("DoT client {} already accumulating", client_id);
409                    client_data_lock.queued_responses.push(dns_buf);
410                    continue;
411                }
412                ConnectionState::Idle => {
413                    // Normal processing
414                    client_data_lock.state = ConnectionState::Processing;
415                    drop(client_data_lock);
416                }
417            }
418
419            // Call LLM with response
420            if let Some(instruction) = app_state.get_instruction_for_client(client_id).await {
421                let protocol = Arc::new(DotClientProtocol::new());
422                let event = Event::new(
423                    &DOT_CLIENT_RESPONSE_RECEIVED_EVENT,
424                    serde_json::json!({
425                        "query_id": query_id,
426                        "response_code": response_code,
427                        "answers": answers,
428                        "authorities": authorities,
429                        "additionals": additionals,
430                    }),
431                );
432
433                match call_llm_for_client(
434                    &llm_client,
435                    &app_state,
436                    client_id.to_string(),
437                    &instruction,
438                    &client_data.lock().await.memory,
439                    Some(&event),
440                    protocol.as_ref(),
441                    &status_tx,
442                )
443                .await
444                {
445                    Ok(ClientLlmResult {
446                        actions,
447                        memory_updates,
448                    }) => {
449                        // Update memory
450                        if let Some(mem) = memory_updates {
451                            client_data.lock().await.memory = mem;
452                        }
453
454                        // Execute actions
455                        for action_json in actions {
456                            if let Err(e) = Self::execute_client_action(
457                                client_id,
458                                action_json,
459                                &write_half,
460                                &status_tx,
461                                &app_state,
462                            )
463                            .await
464                            {
465                                error!("DoT client {} action execution failed: {}", client_id, e);
466                            }
467                        }
468                    }
469                    Err(e) => {
470                        error!("LLM error for DoT client {}: {}", client_id, e);
471                    }
472                }
473            }
474
475            // Set back to Idle and process queued responses
476            let mut client_data_lock = client_data.lock().await;
477            if !client_data_lock.queued_responses.is_empty() {
478                client_data_lock.queued_responses.clear();
479            }
480            client_data_lock.state = ConnectionState::Idle;
481        }
482
483        Ok(())
484    }
485
486    /// Drain injected commands until the channel closes (client removed) or an injected
487    /// `disconnect` ends the session.
488    ///
489    /// The generic `command_support::handle_stream_client_command` cannot run this client's
490    /// vocabulary because `send_dns_query` yields `ClientActionResult::Custom`, so the action
491    /// goes through [`Self::apply_action`] - the same function the LLM path uses, so the DNS
492    /// encoding exists exactly once - and the outcome is recorded and replied the way the
493    /// generic arm does it.
494    async fn command_loop(
495        mut command_rx: mpsc::Receiver<crate::state::client_handles::ClientCommand>,
496        write_half: Arc<Mutex<WriteHalf<TlsStream<TcpStream>>>>,
497        client_id: ClientId,
498        app_state: Arc<AppState>,
499        status_tx: mpsc::UnboundedSender<String>,
500    ) {
501        use crate::llm::actions::protocol_trait::Protocol;
502        use crate::state::client_handles::ClientSendOutcome;
503        use crate::state::AccessLogOwner;
504
505        let protocol = DotClientProtocol::new();
506        while let Some(command) = command_rx.recv().await {
507            let action = command.action.clone();
508            let outcome = match protocol.execute_action(action.clone()) {
509                Err(e) => Ok(ClientSendOutcome::Rejected {
510                    error: e.to_string(),
511                }),
512                Ok(result) => Self::apply_action(result, &write_half, client_id, &status_tx)
513                    .await
514                    .map(|applied| match applied {
515                        Applied::Disconnect => ClientSendOutcome::Disconnected,
516                        Applied::Sent(0) => ClientSendOutcome::Executed {
517                            detail: "executed (nothing to write)".to_string(),
518                        },
519                        Applied::Sent(bytes_sent) => ClientSendOutcome::Sent { bytes_sent },
520                    }),
521            };
522
523            let outcome_json = match &outcome {
524                Ok(outcome) => serde_json::to_value(outcome).unwrap_or(serde_json::Value::Null),
525                Err(e) => serde_json::json!({"error": e.to_string()}),
526            };
527            app_state
528                .record_access_log(
529                    AccessLogOwner::Client(client_id.as_u32()),
530                    protocol.protocol_name(),
531                    None,
532                    "injected_action",
533                    action,
534                    vec![outcome_json],
535                )
536                .await;
537
538            let disconnect = matches!(outcome, Ok(ClientSendOutcome::Disconnected));
539            if let Err(e) = &outcome {
540                error!("DoT client {} injected action failed: {}", client_id, e);
541                let _ = status_tx.send(format!(
542                    "[WARN] Client {} injected action failed: {}",
543                    client_id, e
544                ));
545            }
546            let _ = status_tx.send("__UPDATE_UI__".to_string());
547            crate::client::command_support::reply(command, outcome);
548
549            if disconnect {
550                // Half-close: the server reads EOF and closes; the read loop then sees
551                // UnexpectedEof and runs its normal disconnect path.
552                let _ = write_half.lock().await.shutdown().await;
553                break;
554            }
555        }
556    }
557
558    /// Execute a client action produced by the LLM
559    async fn execute_client_action(
560        client_id: ClientId,
561        action_json: serde_json::Value,
562        write_half: &Arc<Mutex<WriteHalf<TlsStream<TcpStream>>>>,
563        status_tx: &mpsc::UnboundedSender<String>,
564        app_state: &Arc<AppState>,
565    ) -> Result<()> {
566        let protocol = DotClientProtocol::new();
567        let action_result = protocol.execute_action(action_json)?;
568
569        if let Applied::Disconnect =
570            Self::apply_action(action_result, write_half, client_id, status_tx).await?
571        {
572            info!("DoT client {} disconnecting", client_id);
573            // Remove client from app state, which will cause the read loop to exit
574            app_state.remove_client(client_id).await;
575            let _ = status_tx.send(format!("[CLIENT] DoT client {} disconnected", client_id));
576            let _ = status_tx.send("__UPDATE_UI__".to_string());
577        }
578
579        Ok(())
580    }
581
582    /// Put one executed action on the wire. Shared by the LLM path and injected commands.
583    async fn apply_action(
584        action_result: ClientActionResult,
585        write_half: &Arc<Mutex<WriteHalf<TlsStream<TcpStream>>>>,
586        client_id: ClientId,
587        status_tx: &mpsc::UnboundedSender<String>,
588    ) -> Result<Applied> {
589        match action_result {
590            ClientActionResult::Custom { name, data } if name == "dns_query" => {
591                let bytes_sent = Self::send_dns_query(
592                    client_id,
593                    data.get("domain")
594                        .and_then(|v| v.as_str())
595                        .context("Missing domain")?
596                        .to_string(),
597                    data.get("query_type")
598                        .and_then(|v| v.as_str())
599                        .context("Missing query_type")?
600                        .to_string(),
601                    data.get("recursive")
602                        .and_then(|v| v.as_bool())
603                        .unwrap_or(true),
604                    write_half,
605                    status_tx,
606                )
607                .await?;
608                Ok(Applied::Sent(bytes_sent))
609            }
610            ClientActionResult::Disconnect => Ok(Applied::Disconnect),
611            ClientActionResult::WaitForMore => {
612                debug!("DoT client {} waiting for more data", client_id);
613                Ok(Applied::Sent(0))
614            }
615            // NoAction, SendData (unused by this vocabulary), other Custom, Multiple.
616            _ => Ok(Applied::Sent(0)),
617        }
618    }
619
620    /// Send a DNS query over TLS
621    async fn send_dns_query(
622        client_id: ClientId,
623        domain: String,
624        query_type: String,
625        recursive: bool,
626        write_half: &Arc<Mutex<WriteHalf<TlsStream<TcpStream>>>>,
627        status_tx: &mpsc::UnboundedSender<String>,
628    ) -> Result<usize> {
629        info!(
630            "DoT client {} querying {} {}",
631            client_id, domain, query_type
632        );
633        let _ = status_tx.send(format!("[CLIENT] DoT query: {} {}", domain, query_type));
634
635        // Parse record type
636        let record_type = RecordType::from_str(&query_type)
637            .context(format!("Invalid query type: {}", query_type))?;
638
639        // Parse domain name
640        let name = Name::from_str(&domain).context(format!("Invalid domain name: {}", domain))?;
641
642        // Create DNS query message
643        let mut message = DnsMessage::new();
644        message.set_id(rand::random());
645        message.set_message_type(MessageType::Query);
646        message.set_op_code(OpCode::Query);
647        message.set_recursion_desired(recursive);
648
649        let query = Query::query(name, record_type);
650        message.add_query(query);
651
652        // Encode message
653        let dns_bytes = message.to_vec().context("Failed to encode DNS message")?;
654
655        trace!("DoT query hex: {}", hex::encode(&dns_bytes));
656
657        // Length-prefix the message
658        let len = u16::try_from(dns_bytes.len()).context("DoT request exceeds u16 frame length")?;
659        let mut prefixed_message = len.to_be_bytes().to_vec();
660        prefixed_message.extend_from_slice(&dns_bytes);
661
662        // Send via TLS stream
663        {
664            let mut writer = write_half.lock().await;
665            writer
666                .write_all(&prefixed_message)
667                .await
668                .context("Failed to send DNS query over TLS")?;
669            writer
670                .flush()
671                .await
672                .context("Failed to flush DNS query over TLS")?;
673        }
674
675        debug!(
676            "DoT client {} sent DNS query ({} bytes)",
677            client_id,
678            prefixed_message.len()
679        );
680
681        Ok(prefixed_message.len())
682    }
683}
684
685/// What [`DotClient::apply_action`] did with one action.
686enum Applied {
687    /// Bytes written (0 when the action produced no wire output).
688    Sent(usize),
689    /// The action asked to end the session.
690    Disconnect,
691}
692
693/// Certificate verifier that accepts any certificate. Used only when the declared
694/// `verify_tls` startup parameter is `false` (self-signed NetGet DoT servers, tests).
695#[derive(Debug)]
696struct NoVerification;
697
698impl tokio_rustls::rustls::client::danger::ServerCertVerifier for NoVerification {
699    fn verify_server_cert(
700        &self,
701        _end_entity: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
702        _intermediates: &[tokio_rustls::rustls::pki_types::CertificateDer<'_>],
703        _server_name: &ServerName<'_>,
704        _ocsp_response: &[u8],
705        _now: tokio_rustls::rustls::pki_types::UnixTime,
706    ) -> Result<tokio_rustls::rustls::client::danger::ServerCertVerified, tokio_rustls::rustls::Error>
707    {
708        Ok(tokio_rustls::rustls::client::danger::ServerCertVerified::assertion())
709    }
710
711    fn verify_tls12_signature(
712        &self,
713        _message: &[u8],
714        _cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
715        _dss: &tokio_rustls::rustls::DigitallySignedStruct,
716    ) -> Result<
717        tokio_rustls::rustls::client::danger::HandshakeSignatureValid,
718        tokio_rustls::rustls::Error,
719    > {
720        Ok(tokio_rustls::rustls::client::danger::HandshakeSignatureValid::assertion())
721    }
722
723    fn verify_tls13_signature(
724        &self,
725        _message: &[u8],
726        _cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
727        _dss: &tokio_rustls::rustls::DigitallySignedStruct,
728    ) -> Result<
729        tokio_rustls::rustls::client::danger::HandshakeSignatureValid,
730        tokio_rustls::rustls::Error,
731    > {
732        Ok(tokio_rustls::rustls::client::danger::HandshakeSignatureValid::assertion())
733    }
734
735    fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
736        tokio_rustls::rustls::crypto::ring::default_provider()
737            .signature_verification_algorithms
738            .supported_schemes()
739    }
740}