aivpn_server/
passive_distribution.rs1use base64::Engine;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use tracing::{debug, info};
15
16use aivpn_common::error::Result;
17use aivpn_common::mask::MaskProfile;
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct PassiveDistributionConfig {
22 pub enable: bool,
24
25 pub dns_domains: Vec<String>,
27
28 pub image_urls: Vec<String>,
30
31 pub blockchain_networks: Vec<BlockchainNetwork>,
33
34 pub check_interval_secs: u64,
36}
37
38impl Default for PassiveDistributionConfig {
39 fn default() -> Self {
40 Self {
41 enable: false,
42 dns_domains: vec![
43 "mask1.aivpn.network".to_string(),
44 "mask2.aivpn.network".to_string(),
45 ],
46 image_urls: vec![],
47 blockchain_networks: vec![BlockchainNetwork::Bitcoin, BlockchainNetwork::Ethereum],
48 check_interval_secs: 300, }
50 }
51}
52
53#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
55pub enum BlockchainNetwork {
56 Bitcoin,
57 Ethereum,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub enum DeliveryMethod {
63 DnsTxt {
64 domain: String,
65 record_type: String,
66 },
67 ImageLsb {
68 url: String,
69 extraction_key: u32,
70 },
71 BlockchainOpReturn {
72 network: BlockchainNetwork,
73 txid_prefix: String,
74 },
75 Webhook {
76 url: String,
77 secret: String,
78 },
79}
80
81pub struct PassiveMaskReceiver {
83 config: PassiveDistributionConfig,
84
85 cached_masks: HashMap<String, MaskProfile>,
87
88 known_mask_ids: Vec<String>,
90}
91
92impl PassiveMaskReceiver {
93 pub fn new(config: PassiveDistributionConfig) -> Self {
95 Self {
96 config,
97 cached_masks: HashMap::new(),
98 known_mask_ids: Vec::new(),
99 }
100 }
101
102 pub async fn poll_masks(&mut self) -> Result<Vec<MaskProfile>> {
104 if !self.config.enable {
105 return Ok(vec![]);
106 }
107
108 let mut new_masks = Vec::new();
109
110 for domain in &self.config.dns_domains {
112 if let Ok(Some(mask)) = self.check_dns_txt(domain).await {
113 if !self.known_mask_ids.contains(&mask.mask_id) {
114 info!("Discovered new mask via DNS: {}", mask.mask_id);
115 new_masks.push(mask);
116 }
117 }
118 }
119
120 for image_url in &self.config.image_urls {
122 if let Ok(Some(mask)) = self.check_image_lsb(image_url).await {
123 if !self.known_mask_ids.contains(&mask.mask_id) {
124 info!("Discovered new mask via image: {}", mask.mask_id);
125 new_masks.push(mask);
126 }
127 }
128 }
129
130 for mask in &new_masks {
132 self.known_mask_ids.push(mask.mask_id.clone());
133 self.cached_masks.insert(mask.mask_id.clone(), mask.clone());
134 }
135
136 Ok(new_masks)
137 }
138
139 async fn check_dns_txt(&self, domain: &str) -> Result<Option<MaskProfile>> {
141 debug!("Checking DNS TXT for mask: {}", domain);
145
146 Ok(None)
150 }
151
152 async fn check_image_lsb(&self, url: &str) -> Result<Option<MaskProfile>> {
154 debug!("Checking image LSB for mask: {}", url);
163
164 Ok(None)
165 }
166
167 #[allow(dead_code)]
169 fn decode_mask_payload(&self, payload: &[u8]) -> Result<Option<MaskProfile>> {
170 use aivpn_common::error::Error;
171
172 if payload.len() < 96 {
173 return Err(Error::InvalidPacket("Payload too short"));
174 }
175
176 let _mask_latent = &payload[0..64];
178 let _signature = &payload[64..96];
179
180 debug!("Decoded {} bytes of mask payload", payload.len());
185
186 Ok(None) }
188
189 pub fn get_cached_mask(&self, mask_id: &str) -> Option<&MaskProfile> {
191 self.cached_masks.get(mask_id)
192 }
193
194 pub fn get_all_masks(&self) -> Vec<&MaskProfile> {
196 self.cached_masks.values().collect()
197 }
198
199 pub fn clear_cache(&mut self) {
201 self.cached_masks.clear();
202 }
203}
204
205pub struct SteganographicEncoder {
207 _signing_key: [u8; 64],
209}
210
211impl SteganographicEncoder {
212 pub fn new(signing_key: [u8; 64]) -> Self {
214 Self {
215 _signing_key: signing_key,
216 }
217 }
218
219 pub fn encode_for_dns(&self, mask: &MaskProfile) -> Result<String> {
221 let mask_bytes = rmp_serde::to_vec(mask)?;
223
224 let encoded = base64::engine::general_purpose::STANDARD.encode(&mask_bytes);
228
229 Ok(format!("aivpn-mask-v1:{}", encoded))
231 }
232
233 pub fn encode_for_image(&self, _mask: &MaskProfile) -> Result<Vec<u8>> {
235 unimplemented!("image LSB steganography encoding is not yet implemented")
236 }
237
238 pub fn encode_for_blockchain(&self, _mask: &MaskProfile) -> Result<Vec<u8>> {
240 unimplemented!("blockchain OP_RETURN encoding is not yet implemented")
241 }
242}