dig_stun/credential/
signed_client.rs1use std::net::SocketAddr;
6use std::time::Duration;
7
8use tokio::net::UdpSocket;
9
10use crate::codec::{parse_binding_response, StunError, BINDING_SUCCESS};
11use crate::credential::request::encode_identity_request;
12use crate::credential::response::parse_challenge;
13use crate::credential::signature::StunSigner;
14use crate::credential::wire::{
15 is_valid_spki_der, BINDING_ERROR, ERR_STALE_NONCE, ERR_UNAUTHENTICATED, REALM,
16};
17use crate::scope::{scope_of, Scope};
18use crate::transaction_id::new_transaction_id;
19
20use super::request::encode_signed_request;
21
22#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
26pub enum SignedQueryError {
27 #[error("{0}")]
30 Stun(#[from] StunError),
31 #[error("server refused with code {code}")]
35 Refused {
36 code: u16,
38 },
39 #[error("credential exchange cannot proceed")]
42 BadChallenge,
43}
44
45enum Stage {
49 Initial,
51 AfterFirstChallenge,
53 AfterReSign,
56}
57
58pub async fn query_reflexive_address_signed(
69 socket: &UdpSocket,
70 server: SocketAddr,
71 timeout: Duration,
72 signer: &dyn StunSigner,
73) -> Result<SocketAddr, SignedQueryError> {
74 let spki = signer.spki_der();
75 if !is_valid_spki_der(spki) {
76 return Err(SignedQueryError::BadChallenge);
77 }
78
79 let deadline = tokio::time::Instant::now() + timeout;
80 let mut buf = [0u8; 512];
81
82 let mut expected_txid = new_transaction_id();
83 send(
84 socket,
85 server,
86 &encode_identity_request(&expected_txid, spki),
87 )
88 .await?;
89 let mut stage = Stage::Initial;
90
91 loop {
92 let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
93 if remaining.is_zero() {
94 return Err(SignedQueryError::Stun(StunError::Timeout));
95 }
96 let (n, from) = match tokio::time::timeout(remaining, socket.recv_from(&mut buf)).await {
97 Ok(Ok(x)) => x,
98 Ok(Err(e)) => return Err(SignedQueryError::Stun(StunError::Io(e.to_string()))),
99 Err(_) => return Err(SignedQueryError::Stun(StunError::Timeout)),
100 };
101 if from != server {
102 continue; }
104 let msg = &buf[..n];
105 if msg.len() < 2 {
106 return Err(SignedQueryError::Stun(StunError::Truncated));
107 }
108 let msg_type = u16::from_be_bytes([msg[0], msg[1]]);
109
110 if msg_type == BINDING_SUCCESS {
111 match parse_binding_response(msg, Some(&expected_txid)) {
112 Ok(addr) if scope_of(addr) == Scope::NeverDialable => {
113 return Err(SignedQueryError::Stun(StunError::NoMappedAddress));
114 }
115 Ok(addr) => return Ok(addr),
116 Err(StunError::TransactionIdMismatch) => continue, Err(e) => return Err(SignedQueryError::Stun(e)),
118 }
119 } else if msg_type == BINDING_ERROR {
120 let challenge = match parse_challenge(msg, &expected_txid) {
121 Ok(c) => c,
122 Err(StunError::TransactionIdMismatch) => continue, Err(e) => return Err(SignedQueryError::Stun(e)),
124 };
125
126 match stage {
127 Stage::Initial => {
128 let has_dig_realm = challenge.realm.as_deref() == Some(REALM);
129 match (challenge.code, has_dig_realm, &challenge.nonce) {
130 (ERR_UNAUTHENTICATED, true, Some(nonce)) => {
131 expected_txid = new_transaction_id();
132 let req = encode_signed_request(&expected_txid, nonce, signer);
133 send(socket, server, &req).await?;
134 stage = Stage::AfterFirstChallenge;
135 }
136 _ => {
137 return Err(SignedQueryError::Refused {
138 code: challenge.code,
139 })
140 }
141 }
142 }
143 Stage::AfterFirstChallenge => match (challenge.code, &challenge.nonce) {
144 (ERR_STALE_NONCE, Some(nonce)) => {
145 expected_txid = new_transaction_id();
146 let req = encode_signed_request(&expected_txid, nonce, signer);
147 send(socket, server, &req).await?;
148 stage = Stage::AfterReSign;
149 }
150 _ => {
151 return Err(SignedQueryError::Refused {
152 code: challenge.code,
153 })
154 }
155 },
156 Stage::AfterReSign => {
157 return Err(SignedQueryError::Refused {
160 code: challenge.code,
161 });
162 }
163 }
164 } else {
165 return Err(SignedQueryError::Stun(StunError::UnexpectedType(msg_type)));
166 }
167 }
168}
169
170async fn send(
171 socket: &UdpSocket,
172 server: SocketAddr,
173 datagram: &[u8],
174) -> Result<(), SignedQueryError> {
175 socket
176 .send_to(datagram, server)
177 .await
178 .map(|_| ())
179 .map_err(|e| SignedQueryError::Stun(StunError::Io(e.to_string())))
180}