1use std::{net::SocketAddr, ops::Deref, sync::Arc};
17
18use async_trait::async_trait;
19use endhost_api_client::client::CrpcEndhostApiClient;
20use reqwest_connect_rpc::{
21 client::{CrpcClientCreationError, CrpcClientError},
22 token_source::TokenSource,
23};
24use snap_tun::client::SnapTunControlPlaneClient;
25use url::Url;
26use x25519_dalek::PublicKey;
27
28use crate::{
29 api::crpc::{GET_SNAP_DATA_PLANE_ADDRESS, REGISTER_SNAPTUN_IDENTITY, SERVICE_PATH},
30 proto::anapaya::snap::v1 as proto,
31};
32
33pub mod re_export {
35 pub use endhost_api_client::client::{CrpcEndhostApiClient, EndhostApiClient};
36 pub use reqwest_connect_rpc::{
37 client::{CrpcClientCreationError, CrpcClientError},
38 token_source::*,
39 };
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct GetDataPlaneAddressResponse {
45 pub address: SocketAddr,
47 pub snap_tun_control_address: Option<Url>,
50 pub snap_static_x25519: Option<PublicKey>,
53}
54
55#[async_trait]
57pub trait ControlPlaneApi: Send + Sync {
58 async fn get_data_plane_address(&self) -> Result<GetDataPlaneAddressResponse, CrpcClientError>;
60
61 async fn register_snaptun_identity(
63 &self,
64 initiator_identity: PublicKey,
65 psk_share: Option<[u8; 32]>,
66 ) -> Result<Option<[u8; 32]>, CrpcClientError>;
67}
68
69pub struct CrpcSnapControlClient {
71 client: CrpcEndhostApiClient,
72}
73
74impl Deref for CrpcSnapControlClient {
75 type Target = CrpcEndhostApiClient;
76
77 fn deref(&self) -> &Self::Target {
78 &self.client
79 }
80}
81
82impl CrpcSnapControlClient {
83 pub fn new(base_url: &Url) -> Result<Self, CrpcClientCreationError> {
85 let client = CrpcEndhostApiClient::new(base_url)?;
86 Ok(Self { client })
87 }
88
89 pub fn new_with_client(
91 base_url: &Url,
92 client: reqwest::Client,
93 ) -> Result<Self, CrpcClientCreationError> {
94 Ok(Self {
95 client: CrpcEndhostApiClient::new_with_client(base_url, client)?,
96 })
97 }
98
99 pub fn use_token_source(&mut self, token_source: Arc<dyn TokenSource>) -> &mut Self {
101 self.client.use_token_source(token_source);
102 self
103 }
104}
105
106#[async_trait]
107impl ControlPlaneApi for CrpcSnapControlClient {
108 async fn get_data_plane_address(&self) -> Result<GetDataPlaneAddressResponse, CrpcClientError> {
109 let res: proto::GetSnapDataPlaneAddressResponse = self
110 .client
111 .unary_request::<proto::GetSnapDataPlaneAddressRequest, proto::GetSnapDataPlaneAddressResponse>(
112 &format!("{SERVICE_PATH}{GET_SNAP_DATA_PLANE_ADDRESS}"),
113 &proto::GetSnapDataPlaneAddressRequest::default(),
114 )
115 .await?;
116 let address = res.address.parse().map_err(|e: std::net::AddrParseError| {
117 CrpcClientError::DecodeError {
118 context: "parsing data plane address".into(),
119 source: Some(e.into()),
120 body: None,
121 }
122 })?;
123
124 let snap_tun_control_address = res
125 .snap_tun_control_address
126 .map(|address| {
127 if let Ok(url) = Url::parse(&address) {
129 return Ok(url);
130 }
131 match address.parse::<SocketAddr>() {
132 Ok(addr) => {
133 let mut u = Url::parse("http://.").unwrap();
134 let _ = u.set_ip_host(addr.ip());
135 let _ = u.set_port(Some(addr.port()));
136 Ok(u)
137 }
138 Err(e) => {
139 Err(CrpcClientError::DecodeError {
140 context: "parsing server control address".into(),
141 source: Some(e.into()),
142 body: None,
143 })
144 }
145 }
146 })
147 .transpose()?;
148 let snap_static_x25519 = res
149 .snap_static_x25519
150 .map(|key| {
151 let key_bytes: [u8; 32] =
152 key.as_slice()
153 .try_into()
154 .map_err(|e: std::array::TryFromSliceError| {
155 CrpcClientError::DecodeError {
156 context: "server static identity is not 32 bytes".into(),
157 source: Some(e.into()),
158 body: None,
159 }
160 })?;
161 Ok::<_, CrpcClientError>(PublicKey::from(key_bytes))
162 })
163 .transpose()?;
164 Ok(GetDataPlaneAddressResponse {
165 address,
166 snap_tun_control_address,
167 snap_static_x25519,
168 })
169 }
170
171 async fn register_snaptun_identity(
172 &self,
173 initiator_identity: PublicKey,
174 psk_share: Option<[u8; 32]>,
175 ) -> Result<Option<[u8; 32]>, CrpcClientError> {
176 let res = self.client.unary_request::<proto::RegisterSnapTunIdentityRequest, proto::RegisterSnapTunIdentityResponse>(
177 &format!("{SERVICE_PATH}{REGISTER_SNAPTUN_IDENTITY}"),
178 &proto::RegisterSnapTunIdentityRequest { initiator_static_x25519: initiator_identity.to_bytes().to_vec(), psk_share: psk_share.unwrap_or([0u8;32]).to_vec() },
179 ).await?;
180 let psk_share = if res.psk_share.as_slice() == [0u8; 32] {
181 None
182 } else {
183 Some(res.psk_share.as_slice().try_into().map_err(
184 |e: std::array::TryFromSliceError| {
185 CrpcClientError::DecodeError {
186 context: "psk share is not 32 bytes".into(),
187 source: Some(e.into()),
188 body: None,
189 }
190 },
191 )?)
192 };
193 Ok(psk_share)
194 }
195}
196
197#[async_trait]
198impl SnapTunControlPlaneClient for CrpcSnapControlClient {
199 async fn register_identity(
200 &self,
201 initiator_identity: PublicKey,
202 psk_share: Option<[u8; 32]>,
203 ) -> Result<Option<[u8; 32]>, CrpcClientError> {
204 self.register_snaptun_identity(initiator_identity, psk_share)
205 .await
206 }
207}