1#![allow(dead_code)]
12
13use alloc::string::String;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum TcpIpLayer {
17 Link,
18 Internet,
19 Transport,
20 Application,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub struct TcpIpError {
25 pub layer: TcpIpLayer,
26 pub message: &'static str,
27}
28
29impl TcpIpError {
30 pub const fn new(layer: TcpIpLayer, message: &'static str) -> Self {
31 Self { layer, message }
32 }
33}
34
35pub trait LinkLayerApi {
36 fn link_up(&self) -> bool;
37}
38
39pub trait InternetLayerApi {
40 fn resolve_a(&self, host: &str) -> Result<[u8; 4], TcpIpError>;
41}
42
43pub trait TransportLayerApi {
44 fn tcp_connect(&self, remote_ip: [u8; 4], remote_port: u16) -> Result<u16, TcpIpError>;
45 fn tcp_send(&self, local_port: u16, data: &[u8]) -> Result<(), TcpIpError>;
46 fn tcp_recv(
47 &self,
48 local_port: u16,
49 timeout_iters: usize,
50 ) -> Result<alloc::vec::Vec<u8>, TcpIpError>;
51 fn tcp_close(&self, local_port: u16) -> Result<(), TcpIpError>;
52}
53
54pub trait ApplicationLayerApi {
55 fn http_get(&self, host: &str, path: &str) -> Result<AppHttpResponse, TcpIpError>;
56 fn https_get(&self, host: &str, path: &str) -> Result<AppHttpResponse, TcpIpError>;
57 fn http_post(
58 &self,
59 host: &str,
60 path: &str,
61 content_type: &str,
62 body: &[u8],
63 ) -> Result<AppHttpResponse, TcpIpError>;
64}
65
66#[derive(Clone)]
67pub struct AppHttpResponse {
68 pub remote_ip: [u8; 4],
69 pub local_port: u16,
70 pub status_code: u16,
71 pub reason: String,
72 pub headers: alloc::collections::BTreeMap<String, String>,
73 pub body: String,
74 pub tls: Option<TlsSessionMeta>,
75}
76
77#[derive(Clone)]
78pub struct TlsSessionMeta {
79 pub version: String,
80 pub cipher_suite: String,
81 pub cert_subject: String,
82 pub cert_issuer: String,
83 pub cert_not_after: String,
84}
85
86pub struct TcpIpStack;
87
88impl Default for TcpIpStack {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94pub fn validate_web_request(host: &str, path: &str) -> Result<(), TcpIpError> {
103 if host.is_empty() || host.len() > 253 {
104 return Err(TcpIpError::new(
105 TcpIpLayer::Application,
106 "invalid host length",
107 ));
108 }
109 if !host
110 .bytes()
111 .all(|b| b.is_ascii_alphanumeric() || b == b'.' || b == b'-')
112 {
113 return Err(TcpIpError::new(
114 TcpIpLayer::Application,
115 "invalid host characters",
116 ));
117 }
118 if !path.starts_with('/') || path.len() > 4096 {
119 return Err(TcpIpError::new(TcpIpLayer::Application, "invalid path"));
120 }
121 if path.bytes().any(|b| b < 0x20 || b == 0x7f) {
122 return Err(TcpIpError::new(
123 TcpIpLayer::Application,
124 "path contains control characters",
125 ));
126 }
127 Ok(())
128}
129
130const MAX_REQUEST_BODY: usize = 8 * 1024 * 1024;
132
133impl TcpIpStack {
134 pub const fn new() -> Self {
135 Self
136 }
137
138 pub fn web_get(
139 &self,
140 is_https: bool,
141 host: &str,
142 path: &str,
143 ) -> Result<AppHttpResponse, TcpIpError> {
144 validate_web_request(host, path)?;
145 if is_https {
146 self.https_get(host, path)
147 } else {
148 self.http_get(host, path)
149 }
150 }
151
152 pub fn web_post(
155 &self,
156 is_https: bool,
157 host: &str,
158 path: &str,
159 content_type: &str,
160 body: &[u8],
161 ) -> Result<AppHttpResponse, TcpIpError> {
162 validate_web_request(host, path)?;
163 if body.len() > MAX_REQUEST_BODY {
164 return Err(TcpIpError::new(
165 TcpIpLayer::Application,
166 "request body too large",
167 ));
168 }
169 if is_https {
170 let resp = crate::kernel::tls::https_post(host, path, content_type, body)
171 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))?;
172 Ok(AppHttpResponse {
173 remote_ip: resp.remote_ip,
174 local_port: resp.local_port,
175 status_code: resp.status_code,
176 reason: resp.reason,
177 headers: resp.headers,
178 body: resp.body,
179 tls: None,
180 })
181 } else {
182 self.http_post(host, path, content_type, body)
183 }
184 }
185
186 pub fn web_get_binary_complete(
189 &self,
190 is_https: bool,
191 host: &str,
192 path: &str,
193 ) -> Result<alloc::vec::Vec<u8>, TcpIpError> {
194 validate_web_request(host, path)?;
195 if is_https {
196 crate::kernel::tls::https_get_binary_complete(host, path)
197 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))
198 } else {
199 crate::kernel::net::http_get_binary(host, path)
200 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))
201 }
202 }
203
204 pub fn web_get_binary(
205 &self,
206 is_https: bool,
207 host: &str,
208 path: &str,
209 ) -> Result<alloc::vec::Vec<u8>, TcpIpError> {
210 validate_web_request(host, path)?;
211 if is_https {
212 crate::kernel::tls::https_get_binary(host, path)
213 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))
214 } else {
215 crate::kernel::net::http_get_binary(host, path)
216 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))
217 }
218 }
219
220 pub fn web_request(
224 &self,
225 is_https: bool,
226 host: &str,
227 path: &str,
228 method: &str,
229 content_type: &str,
230 body: &[u8],
231 ) -> Result<AppHttpResponse, TcpIpError> {
232 validate_web_request(host, path)?;
233 if body.len() > MAX_REQUEST_BODY {
234 return Err(TcpIpError::new(
235 TcpIpLayer::Application,
236 "request body too large",
237 ));
238 }
239 let m = method.to_ascii_uppercase();
241 if m.is_empty() || m.len() > 16 || !m.bytes().all(|b| b.is_ascii_uppercase()) {
242 return Err(TcpIpError::new(
243 TcpIpLayer::Application,
244 "invalid HTTP method",
245 ));
246 }
247 if is_https {
248 let body_opt = if !body.is_empty() || m == "POST" || m == "PUT" || m == "PATCH" {
250 Some(body)
251 } else {
252 None
253 };
254 let resp =
255 crate::kernel::tls::https_request(&m, host, path, &[], content_type, body_opt)
256 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))?;
257 Ok(AppHttpResponse {
258 remote_ip: resp.remote_ip,
259 local_port: resp.local_port,
260 status_code: resp.status_code,
261 reason: resp.reason,
262 headers: resp.headers,
263 body: resp.body,
264 tls: Some(TlsSessionMeta {
265 version: resp.tls_version,
266 cipher_suite: resp.cipher_suite,
267 cert_subject: resp.cert_subject,
268 cert_issuer: resp.cert_issuer,
269 cert_not_after: resp.cert_not_after,
270 }),
271 })
272 } else {
273 let resp = crate::kernel::net::http_request_real(&m, host, path, content_type, body)
274 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))?;
275 Ok(AppHttpResponse {
276 remote_ip: resp.remote_ip,
277 local_port: resp.local_port,
278 status_code: resp.status_code,
279 reason: resp.reason,
280 headers: resp.headers,
281 body: resp.body,
282 tls: None,
283 })
284 }
285 }
286}
287
288impl LinkLayerApi for TcpIpStack {
289 fn link_up(&self) -> bool {
290 crate::kernel::net::status().link_up
291 }
292}
293
294impl InternetLayerApi for TcpIpStack {
295 fn resolve_a(&self, host: &str) -> Result<[u8; 4], TcpIpError> {
296 crate::kernel::net::dns_query_a_via_udp(host)
297 .map_err(|e| TcpIpError::new(TcpIpLayer::Internet, e))
298 }
299}
300
301impl TransportLayerApi for TcpIpStack {
302 fn tcp_connect(&self, remote_ip: [u8; 4], remote_port: u16) -> Result<u16, TcpIpError> {
303 crate::kernel::net::tcp_connect_real(remote_ip, remote_port)
304 .map_err(|e| TcpIpError::new(TcpIpLayer::Transport, e))
305 }
306
307 fn tcp_send(&self, local_port: u16, data: &[u8]) -> Result<(), TcpIpError> {
308 crate::kernel::net::tcp_send_real(local_port, data)
309 .map_err(|e| TcpIpError::new(TcpIpLayer::Transport, e))
310 }
311
312 fn tcp_recv(
313 &self,
314 local_port: u16,
315 timeout_iters: usize,
316 ) -> Result<alloc::vec::Vec<u8>, TcpIpError> {
317 crate::kernel::net::tcp_recv_real(local_port, timeout_iters)
318 .map_err(|e| TcpIpError::new(TcpIpLayer::Transport, e))
319 }
320
321 fn tcp_close(&self, local_port: u16) -> Result<(), TcpIpError> {
322 crate::kernel::net::tcp_close_real(local_port)
323 .map_err(|e| TcpIpError::new(TcpIpLayer::Transport, e))
324 }
325}
326
327impl ApplicationLayerApi for TcpIpStack {
328 fn http_get(&self, host: &str, path: &str) -> Result<AppHttpResponse, TcpIpError> {
329 let resp = crate::kernel::net::http_get_real(host, path)
330 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))?;
331 Ok(AppHttpResponse {
332 remote_ip: resp.remote_ip,
333 local_port: resp.local_port,
334 status_code: resp.status_code,
335 reason: resp.reason,
336 headers: resp.headers,
337 body: resp.body,
338 tls: None,
339 })
340 }
341
342 fn https_get(&self, host: &str, path: &str) -> Result<AppHttpResponse, TcpIpError> {
343 let resp = crate::kernel::tls::https_get(host, path)
344 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))?;
345 Ok(AppHttpResponse {
346 remote_ip: resp.remote_ip,
347 local_port: resp.local_port,
348 status_code: resp.status_code,
349 reason: resp.reason,
350 headers: resp.headers,
351 body: resp.body,
352 tls: Some(TlsSessionMeta {
353 version: resp.tls_version,
354 cipher_suite: resp.cipher_suite,
355 cert_subject: resp.cert_subject,
356 cert_issuer: resp.cert_issuer,
357 cert_not_after: resp.cert_not_after,
358 }),
359 })
360 }
361
362 fn http_post(
363 &self,
364 host: &str,
365 path: &str,
366 content_type: &str,
367 body: &[u8],
368 ) -> Result<AppHttpResponse, TcpIpError> {
369 let resp = crate::kernel::net::http_post_real(host, path, content_type, body)
370 .map_err(|e| TcpIpError::new(TcpIpLayer::Application, e))?;
371 Ok(AppHttpResponse {
372 remote_ip: resp.remote_ip,
373 local_port: resp.local_port,
374 status_code: resp.status_code,
375 reason: resp.reason,
376 headers: resp.headers,
377 body: resp.body,
378 tls: None,
379 })
380 }
381}