1#![allow(dead_code)]
2
3extern crate alloc;
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use embedded_tls::blocking::{TlsConnection, TlsContext};
8use embedded_tls::TlsConfig;
9use rand_core::{CryptoRng, RngCore};
10
11pub struct OsRng {
14 state: u32,
15}
16
17impl Default for OsRng {
18 fn default() -> Self {
19 Self::new()
20 }
21}
22
23static OS_RNG_CALL_COUNTER: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
30
31impl OsRng {
32 pub fn new() -> Self {
33 let t = crate::kernel::timer::get_ticks() as u32;
34 let call_no = OS_RNG_CALL_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
35 let seed = t ^ call_no.wrapping_mul(0x9E37_79B9);
36 Self {
37 state: if seed == 0 { 0xA11C0B0B } else { seed },
38 }
39 }
40}
41
42impl RngCore for OsRng {
43 fn next_u32(&mut self) -> u32 {
44 let mut x = self.state;
45 x ^= x << 13;
46 x ^= x >> 17;
47 x ^= x << 5;
48 self.state = x;
49 x
50 }
51 fn next_u64(&mut self) -> u64 {
52 ((self.next_u32() as u64) << 32) | (self.next_u32() as u64)
53 }
54 fn fill_bytes(&mut self, dest: &mut [u8]) {
55 for chunk in dest.chunks_mut(4) {
56 let val = self.next_u32().to_le_bytes();
57 let len = chunk.len();
58 chunk.copy_from_slice(&val[..len]);
59 }
60 }
61 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
62 self.fill_bytes(dest);
63 Ok(())
64 }
65}
66
67impl CryptoRng for OsRng {}
68
69pub static WRITE_WAIT_TICKS: core::sync::atomic::AtomicU32 =
75 core::sync::atomic::AtomicU32::new(0);
76pub static WRITE_WAIT_CALLS: core::sync::atomic::AtomicU32 =
77 core::sync::atomic::AtomicU32::new(0);
78pub static WRITE_RETX_COUNT: core::sync::atomic::AtomicU32 =
80 core::sync::atomic::AtomicU32::new(0);
81
82pub struct TcpIoWrapper {
85 pub local_port: u16,
86 buffer: Vec<u8>,
87 pos: usize,
103 retry_write: bool,
111}
112
113impl TcpIoWrapper {
114 pub fn new(local_port: u16) -> Self {
115 Self {
116 local_port,
117 buffer: Vec::new(),
118 pos: 0,
119 retry_write: false,
120 }
121 }
122
123 pub fn new_with_retry_write(local_port: u16) -> Self {
124 Self {
125 local_port,
126 buffer: Vec::new(),
127 pos: 0,
128 retry_write: true,
129 }
130 }
131}
132
133impl embedded_io::ErrorType for TcpIoWrapper {
134 type Error = embedded_io::ErrorKind;
135}
136
137impl embedded_io::Read for TcpIoWrapper {
138 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
139 if buf.is_empty() {
140 return Ok(0);
141 }
142 let _pr = crate::os_lib::web_engine::perf::start();
143 let start_tick = crate::kernel::timer::get_ticks();
144 let timeout_ticks = 1500usize; loop {
172 if self.pos < self.buffer.len() {
173 let avail = self.buffer.len() - self.pos;
174 let len = avail.min(buf.len());
175 buf[..len].copy_from_slice(&self.buffer[self.pos..self.pos + len]);
176 self.pos += len;
177 if self.pos == self.buffer.len() {
179 self.buffer.clear();
180 self.pos = 0;
181 }
182 crate::os_lib::web_engine::perf::add(
183 crate::os_lib::web_engine::perf::Slot::TlsReadWait,
184 _pr,
185 );
186 return Ok(len);
187 }
188
189 if let Ok(chunk) = crate::kernel::net::tcp_recv_real(self.local_port, 1) {
194 if !chunk.is_empty() {
195 if self.pos > 0 {
198 self.buffer.drain(0..self.pos);
199 self.pos = 0;
200 }
201 self.buffer.extend_from_slice(&chunk);
202 continue; }
204 }
205
206 if crate::kernel::net::tcp_is_socket_closed(self.local_port)
207 && self.pos >= self.buffer.len()
208 {
209 return Ok(0); }
213
214 let elapsed = crate::kernel::timer::get_ticks().wrapping_sub(start_tick);
216 if elapsed >= timeout_ticks {
217 crate::warn!(
222 "[TLS][TIMEOUT] 受信タイムアウト local_port={} elapsed_ticks={} timeout_ticks={} seg_accepted={} seg_dropped={}",
223 self.local_port,
224 elapsed,
225 timeout_ticks,
226 crate::kernel::net::tcp::SEG_ACCEPTED.load(core::sync::atomic::Ordering::Relaxed),
227 crate::kernel::net::tcp::SEG_DROPPED.load(core::sync::atomic::Ordering::Relaxed)
228 );
229 return Err(embedded_io::ErrorKind::TimedOut);
230 }
231
232 crate::kernel::scheduler::sleep(1);
238 }
239 }
240}
241
242impl embedded_io::Write for TcpIoWrapper {
243 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
244 const MAX_TCP_PAYLOAD: usize = 1400;
261 let buf = if buf.len() > MAX_TCP_PAYLOAD {
262 &buf[..MAX_TCP_PAYLOAD]
263 } else {
264 buf
265 };
266 let seq_at_send = crate::kernel::net::tcp_current_snd_seq(self.local_port);
275 if crate::kernel::net::tcp_send_real(self.local_port, buf).is_err() {
276 return Err(embedded_io::ErrorKind::BrokenPipe);
277 }
278 if self.retry_write {
279 if let Some(seq) = seq_at_send {
280 const POLL_STEP: usize = 3;
297 const WAIT_PER_TRY: usize = 60;
299 let t_w0 = crate::kernel::timer::get_ticks();
300 let _pw = crate::os_lib::web_engine::perf::start();
301 WRITE_WAIT_CALLS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
302 'retry: for _ in 0..4 {
303 let t_wait0 = crate::kernel::timer::get_ticks();
322 while crate::kernel::timer::get_ticks().wrapping_sub(t_wait0)
323 < WAIT_PER_TRY
324 {
325 let _ps = crate::os_lib::web_engine::perf::start();
329 crate::kernel::scheduler::sleep(POLL_STEP);
330 crate::os_lib::web_engine::perf::add(
331 crate::os_lib::web_engine::perf::Slot::SleepActual,
332 _ps,
333 );
334 let _pe = crate::os_lib::web_engine::perf::start();
335 crate::kernel::usb::poll_ethernet_data_plane_only();
336 crate::os_lib::web_engine::perf::add(
337 crate::os_lib::web_engine::perf::Slot::EthPoll,
338 _pe,
339 );
340 let _pk = crate::os_lib::web_engine::perf::start();
341 let done = crate::kernel::net::tcp_recv_peek_nonempty(self.local_port)
342 || crate::kernel::net::tcp_is_socket_closed(self.local_port);
343 crate::os_lib::web_engine::perf::add(
344 crate::os_lib::web_engine::perf::Slot::PeekCheck,
345 _pk,
346 );
347 if done {
348 break 'retry;
349 }
350 }
351 WRITE_RETX_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
353 let _ = crate::kernel::net::tcp_retransmit_real(self.local_port, buf, seq);
354 }
355 WRITE_WAIT_TICKS.fetch_add(
356 crate::kernel::timer::get_ticks().wrapping_sub(t_w0) as u32,
357 core::sync::atomic::Ordering::Relaxed,
358 );
359 crate::os_lib::web_engine::perf::add(
360 crate::os_lib::web_engine::perf::Slot::TlsWriteWait,
361 _pw,
362 );
363 }
364 }
365 Ok(buf.len())
366 }
367
368 fn flush(&mut self) -> Result<(), Self::Error> {
369 Ok(())
370 }
371}
372
373
374pub static FETCH_BYTE_CAP: core::sync::atomic::AtomicUsize =
385 core::sync::atomic::AtomicUsize::new(0);
386
387pub mod stats {
388 use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
389 pub static OK: AtomicU32 = AtomicU32::new(0);
390 pub static DNS_FAIL: AtomicU32 = AtomicU32::new(0);
391 pub static CONNECT_FAIL: AtomicU32 = AtomicU32::new(0);
392 pub static HANDSHAKE_FAIL: AtomicU32 = AtomicU32::new(0);
393 pub static TRUNCATED: AtomicU32 = AtomicU32::new(0);
394
395 pub fn bump(c: &AtomicU32) {
396 c.fetch_add(1, Relaxed);
397 }
398
399 pub fn report(tag: &str) {
401 let ok = OK.load(Relaxed);
402 let dns = DNS_FAIL.load(Relaxed);
403 let conn = CONNECT_FAIL.load(Relaxed);
404 let hs = HANDSHAKE_FAIL.load(Relaxed);
405 let tr = TRUNCATED.load(Relaxed);
406 let total = ok + dns + conn + hs + tr;
407 crate::warn!(
408 "[NET][STATS] {} total={} ok={} dns_fail={} connect_fail={} handshake_fail={} truncated={}",
409 tag, total, ok, dns, conn, hs, tr
410 );
411 }
412}
413
414pub fn https_get(
415 host: &str,
416 path: &str,
417) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
418 let ordinal = crate::kernel::net::next_https_attempt_ordinal();
419 crate::warn!("[NET][DIAG] https_get ordinal={} host={} path={}", ordinal, host, path);
420 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host).inspect_err(|e| {
421 crate::warn!("[NET][DIAG] https_get ordinal={} DNS FAILED err={}", ordinal, e);
422 stats::bump(&stats::DNS_FAIL);
423 stats::report("after-dns-fail");
424 })?;
425 crate::warn!("[NET][DIAG] https_get ordinal={} host={} resolved={}.{}.{}.{}", ordinal, host, remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]);
426 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, 443).inspect_err(|e| {
427 crate::warn!("[NET][DIAG] https_get ordinal={} TCP CONNECT FAILED err={}", ordinal, e);
428 stats::bump(&stats::CONNECT_FAIL);
429 stats::report("after-connect-fail");
430 })?;
431
432 let mut stream = TcpIoWrapper::new(local_port);
433 let mut rng = OsRng::new();
434
435 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
437
438 let config = TlsConfig::new().with_server_name(host);
440
441 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
442 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
443
444 let mut provider =
446 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
447 let _ph = crate::os_lib::web_engine::perf::start();
448 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
449 crate::os_lib::web_engine::perf::add(
450 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
451 _ph,
452 );
453 if let Err(e) = _open {
454 crate::warn!("[NET][DIAG] https_get ordinal={} TLS HANDSHAKE FAILED err={:?}", ordinal, e);
455 stats::bump(&stats::HANDSHAKE_FAIL);
456 stats::report("after-handshake-fail");
457 crate::warn!("[NET][DIAG] https_get ordinal={} about to tcp_close_real", ordinal);
458 let _ = crate::kernel::net::tcp_close_real(local_port);
459 crate::warn!("[NET][DIAG] https_get ordinal={} tcp_close_real returned, returning Err", ordinal);
460 return Err("TLS handshake failed");
461 }
462
463 let request = alloc::format!("GET {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: Mozilla/5.0 (AtmOS Browser)\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Encoding: identity\r\nConnection: close\r\n\r\n", path, host);
465 if embedded_io::Write::write_all(&mut connection, request.as_bytes()).is_err() {
466 crate::kernel::net::tcp_close_real(local_port)?;
467 return Err("HTTPS request failed");
468 }
469
470 embedded_io::Write::flush(&mut connection).ok();
472
473 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
477
478 let mut raw_bytes = Vec::new();
479 let mut chunk = [0u8; 1024];
480 loop {
481 match embedded_io::Read::read(&mut connection, &mut chunk) {
482 Ok(0) => break,
483 Ok(n) => raw_bytes.extend_from_slice(&chunk[..n]),
484 Err(e) => {
485 crate::info!("[NET] TLS read err: {:?}", e);
486 break;
487 }
488 }
489 }
490
491 if raw_bytes.is_empty() {
499 crate::warn!(
500 "[NET][DIAG] https_get ordinal={} EMPTY RESPONSE (0 bytes read) host={}",
501 ordinal,
502 host
503 );
504 let _ = crate::kernel::net::tcp_close_real(local_port);
505 return Err("empty https response");
506 }
507
508 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
509 Ok(s) => s,
510 Err(e) => {
511 let valid = e.into_bytes();
512 alloc::string::String::from_utf8_lossy(&valid).into_owned()
513 }
514 };
515
516 let preview: String = resp_str.chars().take(300).collect();
518 crate::info!("[NET] HTTPS RAW RESP: [{} bytes] {:?}", resp_str.len(), preview);
519 let (status_code, reason, headers, body) = crate::kernel::net::parse_http_response(&resp_str)
520 .unwrap_or((
521 200,
522 String::from("OK"),
523 alloc::collections::BTreeMap::new(),
524 resp_str,
525 ));
526 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
527 .map(|(_, _, _, b)| b)
528 .unwrap_or_default();
529
530 crate::kernel::net::tcp_close_real(local_port)?;
531
532 crate::warn!(
533 "[NET][DIAG] https_get ordinal={} SUCCESS body_bytes={}",
534 ordinal,
535 body_bytes.len()
536 );
537
538 Ok(crate::kernel::net::HttpsGetResult {
539 remote_ip,
540 local_port,
541 tls_version: String::from("TLS1.3"),
542 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
543 cert_subject: alloc::format!("CN={}", host),
544 cert_issuer: String::from("Unknown Root CA"),
545 cert_not_after: String::from("Unknown"),
546 status_code,
547 reason,
548 headers,
549 body,
550 body_bytes,
551 })
552}
553
554pub fn https_request(
558 method: &str,
559 host: &str,
560 path: &str,
561 extra_headers: &[(&str, &str)],
562 content_type: &str,
563 body: Option<&[u8]>,
564) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
565 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host)?;
566 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, 443)?;
567
568 let mut stream = TcpIoWrapper::new(local_port);
569 let mut rng = OsRng::new();
570
571 let mut read_buf = alloc::vec![0u8; 18432];
572 let mut write_buf = alloc::vec![0u8; 16384];
573
574 let config = TlsConfig::new().with_server_name(host);
575 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
576 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
577 let mut provider =
578 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
579 let _ph = crate::os_lib::web_engine::perf::start();
580 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
581 crate::os_lib::web_engine::perf::add(
582 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
583 _ph,
584 );
585 if let Err(e) = _open {
586 crate::warn!("[NET] TLS handshake error: {:?}", e);
587 let _ = crate::kernel::net::tcp_close_real(local_port);
588 return Err("TLS handshake failed");
589 }
590
591 let mut header = alloc::format!(
592 "{} {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: AtmOS/1.0\r\nAccept: application/json\r\n",
593 method,
594 path,
595 host
596 );
597 for (k, v) in extra_headers {
598 header.push_str(k);
599 header.push_str(": ");
600 header.push_str(v);
601 header.push_str("\r\n");
602 }
603 let body = body.unwrap_or(&[]);
604 if !content_type.is_empty() {
605 header.push_str(&alloc::format!("Content-Type: {}\r\n", content_type));
606 }
607 if !body.is_empty() || method == "POST" || method == "PUT" || method == "PATCH" {
608 header.push_str(&alloc::format!("Content-Length: {}\r\n", body.len()));
609 }
610 header.push_str("Accept-Encoding: identity\r\n");
611 header.push_str("Connection: close\r\n\r\n");
612
613 let mut request = header.into_bytes();
614 request.extend_from_slice(body);
615
616 if embedded_io::Write::write_all(&mut connection, &request).is_err() {
617 crate::kernel::net::tcp_close_real(local_port)?;
618 return Err("HTTPS request failed");
619 }
620 embedded_io::Write::flush(&mut connection).ok();
621 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
624
625 let mut raw_bytes = Vec::new();
626 let mut chunk = [0u8; 1024];
627 loop {
628 match embedded_io::Read::read(&mut connection, &mut chunk) {
629 Ok(0) => break,
630 Ok(n) => raw_bytes.extend_from_slice(&chunk[..n]),
631 Err(_) => break,
632 }
633 }
634
635 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
636 Ok(s) => s,
637 Err(e) => alloc::string::String::from_utf8_lossy(&e.into_bytes()).into_owned(),
638 };
639 let (status_code, reason, headers, rbody) = crate::kernel::net::parse_http_response(&resp_str)
640 .unwrap_or((
641 200,
642 String::from("OK"),
643 alloc::collections::BTreeMap::new(),
644 resp_str,
645 ));
646 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
648 .map(|(_, _, _, b)| b)
649 .unwrap_or_default();
650 crate::kernel::net::tcp_close_real(local_port)?;
651
652 Ok(crate::kernel::net::HttpsGetResult {
653 remote_ip,
654 local_port,
655 tls_version: String::from("TLS1.3"),
656 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
657 cert_subject: alloc::format!("CN={}", host),
658 cert_issuer: String::from("Unknown Root CA"),
659 cert_not_after: String::from("Unknown"),
660 status_code,
661 reason,
662 headers,
663 body: rbody,
664 body_bytes,
665 })
666}
667
668pub fn https_post(
672 host: &str,
673 path: &str,
674 content_type: &str,
675 body: &[u8],
676) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
677 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host)?;
678 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, 443)?;
679
680 let mut stream = TcpIoWrapper::new(local_port);
681 let mut rng = OsRng::new();
682
683 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
685
686 let config = TlsConfig::new().with_server_name(host);
687
688 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
689 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
690
691 let mut provider =
692 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
693 let _ph = crate::os_lib::web_engine::perf::start();
694 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
695 crate::os_lib::web_engine::perf::add(
696 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
697 _ph,
698 );
699 if let Err(e) = _open {
700 crate::warn!("[NET] TLS handshake error: {:?}", e);
701 let _ = crate::kernel::net::tcp_close_real(local_port);
702 return Err("TLS handshake failed");
703 }
704
705 let header = alloc::format!(
707 "POST {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: Mozilla/5.0 (AtmOS Browser)\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Encoding: identity\r\nContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
708 path, host, content_type, body.len()
709 );
710 let mut request = header.into_bytes();
711 request.extend_from_slice(body);
712
713 if embedded_io::Write::write_all(&mut connection, &request).is_err() {
714 crate::kernel::net::tcp_close_real(local_port)?;
715 return Err("HTTPS POST request failed");
716 }
717 embedded_io::Write::flush(&mut connection).ok();
718 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
721
722 let mut raw_bytes = Vec::new();
723 let mut chunk = [0u8; 1024];
724 loop {
725 match embedded_io::Read::read(&mut connection, &mut chunk) {
726 Ok(0) => break,
727 Ok(n) => raw_bytes.extend_from_slice(&chunk[..n]),
728 Err(_) => break,
730 }
731 }
732
733 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
734 Ok(s) => s,
735 Err(e) => alloc::string::String::from_utf8_lossy(&e.into_bytes()).into_owned(),
736 };
737
738 crate::info!("[NET] HTTPS POST: {} bytes received", resp_str.len());
739 let (status_code, reason, headers, body) = crate::kernel::net::parse_http_response(&resp_str)
740 .unwrap_or((
741 200,
742 String::from("OK"),
743 alloc::collections::BTreeMap::new(),
744 resp_str,
745 ));
746 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
747 .map(|(_, _, _, b)| b)
748 .unwrap_or_default();
749
750 crate::kernel::net::tcp_close_real(local_port)?;
751
752 Ok(crate::kernel::net::HttpsGetResult {
753 remote_ip,
754 local_port,
755 tls_version: String::from("TLS1.3"),
756 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
757 cert_subject: alloc::format!("CN={}", host),
758 cert_issuer: String::from("Unknown Root CA"),
759 cert_not_after: String::from("Unknown"),
760 status_code,
761 reason,
762 headers,
763 body,
764 body_bytes,
765 })
766}
767
768pub fn https_get_binary_keepalive(
791 host: &str,
792 paths: &[String],
793) -> Result<Vec<Option<Vec<u8>>>, &'static str> {
794 if paths.is_empty() {
795 return Ok(Vec::new());
796 }
797 let mut out: Vec<Option<Vec<u8>>> = Vec::with_capacity(paths.len());
798
799 let remote_ip = crate::kernel::net::dns::dns_query_a_via_udp(host)?;
800 let local_port = crate::kernel::net::tcp::tcp_connect_real(remote_ip, 443)?;
801
802 let mut rng = OsRng::new();
803 let mut stream = TcpIoWrapper::new_with_retry_write(local_port);
804 let mut read_buf = alloc::vec![0u8; 16384];
805 let mut write_buf = alloc::vec![0u8; 16384];
806 let config = TlsConfig::new().with_server_name(host);
807 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
808 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
809 let mut provider =
810 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
811
812 let _ph = crate::os_lib::web_engine::perf::start();
813 let open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
814 crate::os_lib::web_engine::perf::add(
815 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
816 _ph,
817 );
818 if open.is_err() {
819 let _ = crate::kernel::net::tcp_close_real(local_port);
820 return Err("TLS handshake failed");
821 }
822
823 let mut broken = false;
824 for path in paths.iter() {
825 if broken {
826 out.push(None);
827 continue;
828 }
829 let req = alloc::format!(
831 concat!(
832 "GET {} HTTP/1.1
833",
834 "Host: {}
835",
836 "User-Agent: Mozilla/5.0 (AtmOS Browser)
837",
838 "Accept: */*
839",
840 "Accept-Encoding: identity
841",
842 "Connection: keep-alive
845
846"
847 ),
848 path,
849 host
850 );
851 if embedded_io::Write::write_all(&mut connection, req.as_bytes()).is_err() {
852 broken = true;
853 out.push(None);
854 continue;
855 }
856 embedded_io::Write::flush(&mut connection).ok();
857 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
858
859 let mut buf: Vec<u8> = Vec::new();
861 let mut chunk = [0u8; 1024];
862 let mut header_end: Option<usize> = None;
863 let mut want: Option<usize> = None;
864 loop {
865 if header_end.is_none() {
866 if let Some(i) = crate::kernel::http_body::body_start(&buf) {
867 header_end = Some(i);
868 want = crate::kernel::http_body::content_length(&buf);
869 if want.is_none() {
870 broken = true;
872 break;
873 }
874 }
875 }
876 if let (Some(h), Some(w)) = (header_end, want) {
877 if buf.len() >= h + w {
878 break;
879 }
880 }
881 match embedded_io::Read::read(&mut connection, &mut chunk) {
882 Ok(0) => {
883 broken = true;
884 break;
885 }
886 Ok(n) => buf.extend_from_slice(&chunk[..n]),
887 Err(_) => {
888 broken = true;
889 break;
890 }
891 }
892 }
893 match (header_end, want) {
894 (Some(h), Some(w)) if buf.len() >= h + w => {
895 out.push(Some(crate::kernel::http_body::body_range(&buf)
907 .map(|(a, b)| buf[a..b].to_vec())
908 .unwrap_or_default()));
909 stats::bump(&stats::OK);
910 }
911 _ => {
912 out.push(None);
913 broken = true;
914 }
915 }
916 }
917
918 let _ = crate::kernel::net::tcp_close_real(local_port);
919 stats::report("after-resp");
920 Ok(out)
921}
922
923fn response_looks_truncated(raw: &[u8]) -> bool {
936 crate::kernel::http_body::looks_truncated(raw)
938}
939
940pub fn https_get_binary(host: &str, path: &str) -> Result<Vec<u8>, &'static str> {
941 let _pg = crate::os_lib::web_engine::perf::start();
942 let r = https_get_binary_measured(host, path);
943 crate::os_lib::web_engine::perf::add(
944 crate::os_lib::web_engine::perf::Slot::HttpsGet,
945 _pg,
946 );
947 r
948}
949
950fn https_get_binary_measured(host: &str, path: &str) -> Result<Vec<u8>, &'static str> {
951 const MAX_ATTEMPTS: u32 = 3;
952 let ordinal = crate::kernel::net::next_https_attempt_ordinal();
953 crate::warn!("[NET][DIAG] https_get_binary ordinal={} host={} path={}", ordinal, host, path);
954 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host).inspect_err(|e| {
960 crate::warn!("[NET][DIAG] https_get_binary ordinal={} DNS FAILED err={}", ordinal, e);
961 stats::bump(&stats::DNS_FAIL);
962 stats::report("after-dns-fail");
963 })?;
964 crate::warn!("[NET][DIAG] https_get_binary ordinal={} host={} resolved={}.{}.{}.{}", ordinal, host, remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]);
965 let mut best: Option<Vec<u8>> = None;
966 for attempt in 1..=MAX_ATTEMPTS {
967 match https_get_binary_attempt(remote_ip, host, path) {
968 Ok((raw, complete)) => {
969 if complete {
970 crate::warn!(
971 "[NET][DIAG] https_get_binary ordinal={} SUCCESS attempt={} body_bytes={}",
972 ordinal, attempt, raw.len()
973 );
974 return Ok(crate::kernel::net::extract_http_body_binary(&raw));
975 }
976 crate::warn!(
977 "[NET][DIAG] https_get_binary ordinal={}: truncated response on attempt {}/{} ({} bytes), retrying",
978 ordinal, attempt, MAX_ATTEMPTS, raw.len()
979 );
980 if best.as_ref().is_none_or(|b: &Vec<u8>| raw.len() > b.len()) {
981 best = Some(raw);
982 }
983 }
984 Err(e) => {
985 crate::warn!(
986 "[NET][DIAG] https_get_binary ordinal={} attempt {}/{} FAILED err={}",
987 ordinal, attempt, MAX_ATTEMPTS, e
988 );
989 if attempt == MAX_ATTEMPTS {
990 return Err(e);
991 }
992 }
993 }
994 }
995 match best {
998 Some(raw) => Ok(crate::kernel::net::extract_http_body_binary(&raw)),
999 None => Err("HTTPS request failed"),
1000 }
1001}
1002
1003fn https_get_binary_attempt(
1009 remote_ip: [u8; 4],
1010 host: &str,
1011 path: &str,
1012) -> Result<(Vec<u8>, bool), &'static str> {
1013 crate::debug!(
1014 "[NET][DIAG] https_get_binary host={} resolved_ip={}.{}.{}.{}",
1015 host,
1016 remote_ip[0],
1017 remote_ip[1],
1018 remote_ip[2],
1019 remote_ip[3]
1020 );
1021 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, 443)?;
1022 let handshake_start = crate::kernel::timer::get_ticks();
1023 crate::debug!("[NET][DIAG] tcp_connect_real returned local_port={}", local_port);
1024
1025 let mut stream = TcpIoWrapper::new_with_retry_write(local_port);
1033 let mut rng = OsRng::new();
1034
1035 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
1039
1040 let config = TlsConfig::new().with_server_name(host);
1041
1042 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
1043 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
1044
1045 let mut provider =
1046 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
1047 let _ph = crate::os_lib::web_engine::perf::start();
1048 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
1049 crate::os_lib::web_engine::perf::add(
1050 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
1051 _ph,
1052 );
1053 if let Err(e) = _open {
1054 let elapsed = crate::kernel::timer::get_ticks().wrapping_sub(handshake_start);
1055 crate::warn!("[NET] TLS handshake error: {:?} (elapsed_ticks={})", e, elapsed);
1056 let _ = crate::kernel::net::tcp_close_real(local_port);
1057 return Err("TLS handshake failed");
1058 }
1059
1060 let request = alloc::format!("GET {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: Mozilla/5.0 (AtmOS Browser)\r\nAccept: */*\r\nAccept-Encoding: identity\r\nConnection: close\r\n\r\n", path, host);
1061 if embedded_io::Write::write_all(&mut connection, request.as_bytes()).is_err() {
1062 crate::kernel::net::tcp_close_real(local_port)?;
1063 return Err("HTTPS request failed");
1064 }
1065
1066 embedded_io::Write::flush(&mut connection).ok();
1067 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
1070
1071 let mut body_bytes = Vec::new();
1072 let mut chunk = [0u8; 1024];
1073 loop {
1074 match embedded_io::Read::read(&mut connection, &mut chunk) {
1075 Ok(0) => break,
1076 Ok(n) => {
1077 body_bytes.extend_from_slice(&chunk[..n]);
1078 let cap = FETCH_BYTE_CAP.load(core::sync::atomic::Ordering::Relaxed);
1084 if cap > 0 {
1085 let declared = crate::kernel::http_body::content_length(&body_bytes);
1086 if declared.is_some_and(|c| c > cap) || body_bytes.len() > cap {
1087 crate::warn!(
1088 "[TLS] host={} path={} 取得を打ち切り: 上限 {} バイト超過 (declared={:?} got={})",
1089 host, path, cap, declared, body_bytes.len()
1090 );
1091 let _ = crate::kernel::net::tcp_close_real(local_port);
1092 return Err("fetch exceeds size cap");
1093 }
1094 }
1095 }
1096 Err(e) => {
1097 crate::warn!("[NET][DIAG] TLS read err: {:?} body_bytes_so_far={}", e, body_bytes.len());
1098 break;
1099 }
1100 }
1101 }
1102 crate::warn!(
1103 "[NET][DIAG] read loop done, body_bytes.len()={} preview={:?}",
1104 body_bytes.len(),
1105 String::from_utf8_lossy(&body_bytes[..body_bytes.len().min(80)])
1106 );
1107
1108 crate::kernel::net::tcp_close_real(local_port)?;
1109
1110 let complete = !response_looks_truncated(&body_bytes);
1111 if complete {
1114 stats::bump(&stats::OK);
1115 } else {
1116 stats::bump(&stats::TRUNCATED);
1117 }
1118 stats::report("after-resp");
1119 Ok((body_bytes, complete))
1120}