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