1use std::fmt::Formatter;
2
3use rand::prelude::*;
4use serde::{Deserialize, Serialize};
5use sha2::Digest;
6
7#[derive(Serialize, Deserialize, Debug, Clone)]
8#[serde(transparent)]
9pub struct SecretKey(pub String);
10impl SecretKey {
11 pub fn generate() -> Self {
12 let mut rng = rand::thread_rng();
13 Self(
14 std::iter::repeat(())
15 .map(|_| rng.sample(rand::distributions::Alphanumeric))
16 .take(22)
17 .collect::<String>(),
18 )
19 }
20
21 pub fn client_id(&self) -> ClientId {
22 ClientId(base64::encode(
23 &sha2::Sha256::digest(self.0.as_bytes()).to_vec(),
24 ))
25 }
26}
27
28#[derive(Serialize, Deserialize, Debug, Clone)]
29#[serde(transparent)]
30pub struct ReconnectToken(pub String);
31
32#[derive(Serialize, Deserialize, Clone)]
33pub struct ClientQuotas {
34 #[serde(rename = "requestCount")]
35 pub request_count: i32,
36 #[serde(rename = "requestLimit")]
37 pub request_limit: i32,
38}
39
40impl std::fmt::Debug for ClientQuotas {
41 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42 f.debug_struct("ClientQuotas")
43 .field("count", &self.request_count)
44 .field("limit", &self.request_limit)
45 .finish()
46 }
47}
48
49#[derive(Serialize, Deserialize, Debug, Clone)]
50#[serde(rename_all = "snake_case")]
51pub enum ServerHello {
52 Success {
53 sub_domain: String,
54 hostname: String,
55 client_id: ClientId,
56 quotas: ClientQuotas,
57 },
58 SubDomainInUse,
59 InvalidSubDomain,
60 AuthFailed,
61 Error(String),
62}
63
64impl ServerHello {
65 #[allow(unused)]
66 pub fn random_domain() -> String {
67 let mut rng = rand::thread_rng();
68 std::iter::repeat(())
69 .map(|_| rng.sample(rand::distributions::Alphanumeric))
70 .take(8)
71 .collect::<String>()
72 .to_lowercase()
73 }
74
75 #[allow(unused)]
76 pub fn prefixed_random_domain(prefix: &str) -> String {
77 format!("{}-{}", prefix, Self::random_domain())
78 }
79}
80
81#[derive(Serialize, Deserialize, Debug, Clone)]
82pub struct ClientHello {
83 pub device_id: DeviceId,
84 pub sub_domain: Option<String>,
85 pub client_type: ClientType,
86 pub reconnect_token: Option<ReconnectToken>,
87}
88
89impl ClientHello {
90 pub fn generate(sub_domain: Option<String>, typ: ClientType) -> Self {
91 ClientHello {
92 device_id: DeviceId::generate(),
93 client_type: typ,
94 sub_domain,
95 reconnect_token: None,
96 }
97 }
98
99 pub fn reconnect(reconnect_token: ReconnectToken) -> Self {
100 ClientHello {
101 device_id: DeviceId::generate(),
102 sub_domain: None,
103 client_type: ClientType::Anonymous,
104 reconnect_token: Some(reconnect_token),
105 }
106 }
107}
108
109#[derive(Serialize, Deserialize, Debug, Clone)]
110pub enum ClientType {
111 Auth { key: SecretKey },
112 Anonymous,
113}
114
115#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
116#[serde(transparent)]
117pub struct ClientId(String);
118
119impl std::fmt::Display for ClientId {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 self.0.fmt(f)
122 }
123}
124impl ClientId {
125 pub fn generate() -> Self {
126 let mut id = [0u8; 32];
127 rand::thread_rng().fill_bytes(&mut id);
128 ClientId(base64::encode_config(&id, base64::URL_SAFE_NO_PAD))
129 }
130
131 pub fn safe_id(self) -> ClientId {
132 ClientId(base64::encode(
133 &sha2::Sha256::digest(self.0.as_bytes()).to_vec(),
134 ))
135 }
136}
137
138#[derive(Debug, Clone, PartialEq, Eq, Hash)]
139pub struct StreamId([u8; 8]);
140
141impl StreamId {
142 pub fn generate() -> StreamId {
143 let mut id = [0u8; 8];
144 rand::thread_rng().fill_bytes(&mut id);
145 StreamId(id)
146 }
147
148 pub fn to_string(&self) -> String {
149 format!(
150 "stream_{}",
151 base64::encode_config(&self.0, base64::URL_SAFE_NO_PAD)
152 )
153 }
154}
155
156#[derive(Debug, Clone)]
157pub enum ControlPacket {
158 Init(StreamId),
159 Data(StreamId, Vec<u8>),
160 Refused(StreamId),
161 End(StreamId),
162 Ping(Option<ReconnectToken>),
163}
164
165pub const PING_INTERVAL: u64 = 30;
166
167const EMPTY_STREAM: StreamId = StreamId([0xF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
168const TOKEN_STREAM: StreamId = StreamId([0xF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
169
170impl ControlPacket {
171 pub fn serialize(self) -> Vec<u8> {
172 match self {
173 ControlPacket::Init(sid) => [vec![0x01], sid.0.to_vec()].concat(),
174 ControlPacket::Data(sid, data) => [vec![0x02], sid.0.to_vec(), data].concat(),
175 ControlPacket::Refused(sid) => [vec![0x03], sid.0.to_vec()].concat(),
176 ControlPacket::End(sid) => [vec![0x04], sid.0.to_vec()].concat(),
177 ControlPacket::Ping(tok) => {
178 let data = tok.map_or(EMPTY_STREAM.0.to_vec(), |t| {
179 vec![TOKEN_STREAM.0.to_vec(), t.0.into_bytes()].concat()
180 });
181 [vec![0x05], data].concat()
182 }
183 }
184 }
185
186 pub fn packet_type(&self) -> &str {
187 match &self {
188 ControlPacket::Ping(_) => "PING",
189 ControlPacket::Init(_) => "INIT STREAM",
190 ControlPacket::Data(_, _) => "STREAM DATA",
191 ControlPacket::Refused(_) => "REFUSED",
192 ControlPacket::End(_) => "END STREAM",
193 }
194 }
195
196 pub fn deserialize(data: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
197 if data.len() < 9 {
198 return Err("invalid DataPacket, missing stream id".into());
199 }
200
201 let mut stream_id = [0u8; 8];
202 stream_id.clone_from_slice(&data[1..9]);
203 let stream_id = StreamId(stream_id);
204
205 let packet = match data[0] {
206 0x01 => ControlPacket::Init(stream_id),
207 0x02 => ControlPacket::Data(stream_id, data[9..].to_vec()),
208 0x03 => ControlPacket::Refused(stream_id),
209 0x04 => ControlPacket::End(stream_id),
210 0x05 => {
211 if stream_id == EMPTY_STREAM {
212 ControlPacket::Ping(None)
213 } else {
214 ControlPacket::Ping(Some(ReconnectToken(
215 String::from_utf8_lossy(&data[9..]).to_string(),
216 )))
217 }
218 }
219 _ => return Err("invalid control byte in DataPacket".into()),
220 };
221
222 Ok(packet)
223 }
224}
225
226#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
227#[serde(transparent)]
228pub struct DeviceId(String);
229
230impl std::fmt::Display for DeviceId {
231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232 self.0.fmt(f)
233 }
234}
235impl DeviceId {
236 pub fn generate() -> Self {
237 let mut builder = machineid_rs::IdBuilder::new(machineid_rs::Encryption::SHA256);
238 builder.add_component(machineid_rs::HWIDComponent::SystemID);
239 builder.add_component(machineid_rs::HWIDComponent::Username);
240 let key = std::env::var("ACTNEL_DEVICE_KEY").unwrap();
241 let hwid = builder.build(&key).unwrap();
242 DeviceId(hwid)
243 }
244}