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 crate::warn!(
213 "[TLS][EOF] local_port={} {}",
214 self.local_port,
215 crate::kernel::net::tcp::tcp_close_diag(self.local_port)
216 );
217 return Ok(0); }
219
220 let elapsed = crate::kernel::timer::get_ticks().wrapping_sub(start_tick);
222 if elapsed >= timeout_ticks {
223 crate::warn!(
228 "[TLS][TIMEOUT] 受信タイムアウト local_port={} elapsed_ticks={} timeout_ticks={} seg_accepted={} seg_dropped={}",
229 self.local_port,
230 elapsed,
231 timeout_ticks,
232 crate::kernel::net::tcp::SEG_ACCEPTED.load(core::sync::atomic::Ordering::Relaxed),
233 crate::kernel::net::tcp::SEG_DROPPED.load(core::sync::atomic::Ordering::Relaxed)
234 );
235 return Err(embedded_io::ErrorKind::TimedOut);
236 }
237
238 crate::kernel::scheduler::sleep(1);
244 }
245 }
246}
247
248impl embedded_io::Write for TcpIoWrapper {
249 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
250 const MAX_TCP_PAYLOAD: usize = 1400;
267 let buf = if buf.len() > MAX_TCP_PAYLOAD {
268 &buf[..MAX_TCP_PAYLOAD]
269 } else {
270 buf
271 };
272 let seq_at_send = crate::kernel::net::tcp_current_snd_seq(self.local_port);
281 if crate::kernel::net::tcp_send_real(self.local_port, buf).is_err() {
282 return Err(embedded_io::ErrorKind::BrokenPipe);
283 }
284 if self.retry_write {
285 if let Some(seq) = seq_at_send {
286 const POLL_STEP: usize = 3;
303 const WAIT_PER_TRY: usize = 60;
305 let t_w0 = crate::kernel::timer::get_ticks();
306 let _pw = crate::os_lib::web_engine::perf::start();
307 WRITE_WAIT_CALLS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
308 'retry: for _ in 0..4 {
309 let t_wait0 = crate::kernel::timer::get_ticks();
328 while crate::kernel::timer::get_ticks().wrapping_sub(t_wait0)
329 < WAIT_PER_TRY
330 {
331 let _ps = crate::os_lib::web_engine::perf::start();
335 crate::kernel::scheduler::sleep(POLL_STEP);
336 crate::os_lib::web_engine::perf::add(
337 crate::os_lib::web_engine::perf::Slot::SleepActual,
338 _ps,
339 );
340 let _pe = crate::os_lib::web_engine::perf::start();
341 crate::kernel::usb::poll_ethernet_data_plane_only();
342 crate::os_lib::web_engine::perf::add(
343 crate::os_lib::web_engine::perf::Slot::EthPoll,
344 _pe,
345 );
346 let _pk = crate::os_lib::web_engine::perf::start();
347 let done = crate::kernel::net::tcp_recv_peek_nonempty(self.local_port)
348 || crate::kernel::net::tcp_is_socket_closed(self.local_port);
349 crate::os_lib::web_engine::perf::add(
350 crate::os_lib::web_engine::perf::Slot::PeekCheck,
351 _pk,
352 );
353 if done {
354 break 'retry;
355 }
356 }
357 WRITE_RETX_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
359 let _ = crate::kernel::net::tcp_retransmit_real(self.local_port, buf, seq);
360 }
361 WRITE_WAIT_TICKS.fetch_add(
362 crate::kernel::timer::get_ticks().wrapping_sub(t_w0) as u32,
363 core::sync::atomic::Ordering::Relaxed,
364 );
365 crate::os_lib::web_engine::perf::add(
366 crate::os_lib::web_engine::perf::Slot::TlsWriteWait,
367 _pw,
368 );
369 }
370 }
371 Ok(buf.len())
372 }
373
374 fn flush(&mut self) -> Result<(), Self::Error> {
375 Ok(())
376 }
377}
378
379
380pub static FETCH_BYTE_CAP: core::sync::atomic::AtomicUsize =
391 core::sync::atomic::AtomicUsize::new(0);
392
393pub mod stats {
394 use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
395 pub static OK: AtomicU32 = AtomicU32::new(0);
396 pub static DNS_FAIL: AtomicU32 = AtomicU32::new(0);
397 pub static CONNECT_FAIL: AtomicU32 = AtomicU32::new(0);
398 pub static HANDSHAKE_FAIL: AtomicU32 = AtomicU32::new(0);
399 pub static TRUNCATED: AtomicU32 = AtomicU32::new(0);
400
401 pub fn bump(c: &AtomicU32) {
402 c.fetch_add(1, Relaxed);
403 }
404
405 pub fn report(tag: &str) {
407 let ok = OK.load(Relaxed);
408 let dns = DNS_FAIL.load(Relaxed);
409 let conn = CONNECT_FAIL.load(Relaxed);
410 let hs = HANDSHAKE_FAIL.load(Relaxed);
411 let tr = TRUNCATED.load(Relaxed);
412 let total = ok + dns + conn + hs + tr;
413 crate::warn!(
414 "[NET][STATS] {} total={} ok={} dns_fail={} connect_fail={} handshake_fail={} truncated={}",
415 tag, total, ok, dns, conn, hs, tr
416 );
417 }
418}
419
420pub fn https_get(
421 host: &str,
422 path: &str,
423) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
424 let ordinal = crate::kernel::net::next_https_attempt_ordinal();
425 crate::warn!("[NET][DIAG] https_get ordinal={} host={} path={}", ordinal, host, path);
426 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host).inspect_err(|e| {
427 crate::warn!("[NET][DIAG] https_get ordinal={} DNS FAILED err={}", ordinal, e);
428 stats::bump(&stats::DNS_FAIL);
429 stats::report("after-dns-fail");
430 })?;
431 crate::warn!("[NET][DIAG] https_get ordinal={} host={} resolved={}.{}.{}.{}", ordinal, host, remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]);
432 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, 443).inspect_err(|e| {
433 crate::warn!("[NET][DIAG] https_get ordinal={} TCP CONNECT FAILED err={}", ordinal, e);
434 stats::bump(&stats::CONNECT_FAIL);
435 stats::report("after-connect-fail");
436 })?;
437
438 let mut stream = TcpIoWrapper::new(local_port);
439 let mut rng = OsRng::new();
440
441 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
443
444 let config = TlsConfig::new().with_server_name(host);
446
447 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
448 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
449
450 let mut provider =
452 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
453 let _ph = crate::os_lib::web_engine::perf::start();
454 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
455 crate::os_lib::web_engine::perf::add(
456 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
457 _ph,
458 );
459 if let Err(e) = _open {
460 crate::warn!("[NET][DIAG] https_get ordinal={} TLS HANDSHAKE FAILED err={:?}", ordinal, e);
461 stats::bump(&stats::HANDSHAKE_FAIL);
462 stats::report("after-handshake-fail");
463 crate::warn!("[NET][DIAG] https_get ordinal={} about to tcp_close_real", ordinal);
464 let _ = crate::kernel::net::tcp_close_real(local_port);
465 crate::warn!("[NET][DIAG] https_get ordinal={} tcp_close_real returned, returning Err", ordinal);
466 return Err("TLS handshake failed");
467 }
468
469 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);
471 if embedded_io::Write::write_all(&mut connection, request.as_bytes()).is_err() {
472 crate::kernel::net::tcp_close_real(local_port)?;
473 return Err("HTTPS request failed");
474 }
475
476 embedded_io::Write::flush(&mut connection).ok();
478
479 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
483
484 let mut raw_bytes = Vec::new();
485 let mut chunk = [0u8; 1024];
486 loop {
487 match embedded_io::Read::read(&mut connection, &mut chunk) {
488 Ok(0) => break,
489 Ok(n) => raw_bytes.extend_from_slice(&chunk[..n]),
490 Err(e) => {
491 crate::info!("[NET] TLS read err: {:?}", e);
492 break;
493 }
494 }
495 }
496
497 if raw_bytes.is_empty() {
505 crate::warn!(
506 "[NET][DIAG] https_get ordinal={} EMPTY RESPONSE (0 bytes read) host={}",
507 ordinal,
508 host
509 );
510 let _ = crate::kernel::net::tcp_close_real(local_port);
511 return Err("empty https response");
512 }
513
514 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
515 Ok(s) => s,
516 Err(e) => {
517 let valid = e.into_bytes();
518 alloc::string::String::from_utf8_lossy(&valid).into_owned()
519 }
520 };
521
522 let preview: String = resp_str.chars().take(300).collect();
524 crate::info!("[NET] HTTPS RAW RESP: [{} bytes] {:?}", resp_str.len(), preview);
525 let (status_code, reason, headers, body) = crate::kernel::net::parse_http_response(&resp_str)
526 .unwrap_or((
527 200,
528 String::from("OK"),
529 alloc::collections::BTreeMap::new(),
530 resp_str,
531 ));
532 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
533 .map(|(_, _, _, b)| b)
534 .unwrap_or_default();
535
536 crate::kernel::net::tcp_close_real(local_port)?;
537
538 crate::warn!(
539 "[NET][DIAG] https_get ordinal={} SUCCESS body_bytes={}",
540 ordinal,
541 body_bytes.len()
542 );
543
544 Ok(crate::kernel::net::HttpsGetResult {
545 remote_ip,
546 local_port,
547 tls_version: String::from("TLS1.3"),
548 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
549 cert_subject: alloc::format!("CN={}", host),
550 cert_issuer: String::from("Unknown Root CA"),
551 cert_not_after: String::from("Unknown"),
552 status_code,
553 reason,
554 headers,
555 body,
556 body_bytes,
557 })
558}
559
560pub fn https_request(
564 method: &str,
565 host: &str,
566 path: &str,
567 extra_headers: &[(&str, &str)],
568 content_type: &str,
569 body: Option<&[u8]>,
570) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
571 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host)?;
572 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, 443)?;
573
574 let mut stream = TcpIoWrapper::new(local_port);
575 let mut rng = OsRng::new();
576
577 let mut read_buf = alloc::vec![0u8; 18432];
578 let mut write_buf = alloc::vec![0u8; 16384];
579
580 let config = TlsConfig::new().with_server_name(host);
581 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
582 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
583 let mut provider =
584 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
585 let _ph = crate::os_lib::web_engine::perf::start();
586 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
587 crate::os_lib::web_engine::perf::add(
588 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
589 _ph,
590 );
591 if let Err(e) = _open {
592 crate::warn!("[NET] TLS handshake error: {:?}", e);
593 let _ = crate::kernel::net::tcp_close_real(local_port);
594 return Err("TLS handshake failed");
595 }
596
597 let mut header = alloc::format!(
598 "{} {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: AtmOS/1.0\r\nAccept: application/json\r\n",
599 method,
600 path,
601 host
602 );
603 for (k, v) in extra_headers {
604 header.push_str(k);
605 header.push_str(": ");
606 header.push_str(v);
607 header.push_str("\r\n");
608 }
609 let body = body.unwrap_or(&[]);
610 if !content_type.is_empty() {
611 header.push_str(&alloc::format!("Content-Type: {}\r\n", content_type));
612 }
613 if !body.is_empty() || method == "POST" || method == "PUT" || method == "PATCH" {
614 header.push_str(&alloc::format!("Content-Length: {}\r\n", body.len()));
615 }
616 header.push_str("Accept-Encoding: identity\r\n");
617 header.push_str("Connection: close\r\n\r\n");
618
619 let mut request = header.into_bytes();
620 request.extend_from_slice(body);
621
622 if embedded_io::Write::write_all(&mut connection, &request).is_err() {
623 crate::kernel::net::tcp_close_real(local_port)?;
624 return Err("HTTPS request failed");
625 }
626 embedded_io::Write::flush(&mut connection).ok();
627 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
630
631 let mut raw_bytes = Vec::new();
632 let mut chunk = [0u8; 1024];
633 loop {
634 match embedded_io::Read::read(&mut connection, &mut chunk) {
635 Ok(0) => break,
636 Ok(n) => raw_bytes.extend_from_slice(&chunk[..n]),
637 Err(_) => break,
638 }
639 }
640
641 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
642 Ok(s) => s,
643 Err(e) => alloc::string::String::from_utf8_lossy(&e.into_bytes()).into_owned(),
644 };
645 let (status_code, reason, headers, rbody) = crate::kernel::net::parse_http_response(&resp_str)
646 .unwrap_or((
647 200,
648 String::from("OK"),
649 alloc::collections::BTreeMap::new(),
650 resp_str,
651 ));
652 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
654 .map(|(_, _, _, b)| b)
655 .unwrap_or_default();
656 crate::kernel::net::tcp_close_real(local_port)?;
657
658 Ok(crate::kernel::net::HttpsGetResult {
659 remote_ip,
660 local_port,
661 tls_version: String::from("TLS1.3"),
662 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
663 cert_subject: alloc::format!("CN={}", host),
664 cert_issuer: String::from("Unknown Root CA"),
665 cert_not_after: String::from("Unknown"),
666 status_code,
667 reason,
668 headers,
669 body: rbody,
670 body_bytes,
671 })
672}
673
674pub fn https_post(
678 host: &str,
679 path: &str,
680 content_type: &str,
681 body: &[u8],
682) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
683 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host)?;
684 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, 443)?;
685
686 let mut stream = TcpIoWrapper::new(local_port);
687 let mut rng = OsRng::new();
688
689 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
691
692 let config = TlsConfig::new().with_server_name(host);
693
694 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
695 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
696
697 let mut provider =
698 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
699 let _ph = crate::os_lib::web_engine::perf::start();
700 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
701 crate::os_lib::web_engine::perf::add(
702 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
703 _ph,
704 );
705 if let Err(e) = _open {
706 crate::warn!("[NET] TLS handshake error: {:?}", e);
707 let _ = crate::kernel::net::tcp_close_real(local_port);
708 return Err("TLS handshake failed");
709 }
710
711 let header = alloc::format!(
713 "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",
714 path, host, content_type, body.len()
715 );
716 let mut request = header.into_bytes();
717 request.extend_from_slice(body);
718
719 if embedded_io::Write::write_all(&mut connection, &request).is_err() {
720 crate::kernel::net::tcp_close_real(local_port)?;
721 return Err("HTTPS POST request failed");
722 }
723 embedded_io::Write::flush(&mut connection).ok();
724 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
727
728 let mut raw_bytes = Vec::new();
729 let mut chunk = [0u8; 1024];
730 loop {
731 match embedded_io::Read::read(&mut connection, &mut chunk) {
732 Ok(0) => break,
733 Ok(n) => raw_bytes.extend_from_slice(&chunk[..n]),
734 Err(_) => break,
736 }
737 }
738
739 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
740 Ok(s) => s,
741 Err(e) => alloc::string::String::from_utf8_lossy(&e.into_bytes()).into_owned(),
742 };
743
744 crate::info!("[NET] HTTPS POST: {} bytes received", resp_str.len());
745 let (status_code, reason, headers, body) = crate::kernel::net::parse_http_response(&resp_str)
746 .unwrap_or((
747 200,
748 String::from("OK"),
749 alloc::collections::BTreeMap::new(),
750 resp_str,
751 ));
752 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
753 .map(|(_, _, _, b)| b)
754 .unwrap_or_default();
755
756 crate::kernel::net::tcp_close_real(local_port)?;
757
758 Ok(crate::kernel::net::HttpsGetResult {
759 remote_ip,
760 local_port,
761 tls_version: String::from("TLS1.3"),
762 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
763 cert_subject: alloc::format!("CN={}", host),
764 cert_issuer: String::from("Unknown Root CA"),
765 cert_not_after: String::from("Unknown"),
766 status_code,
767 reason,
768 headers,
769 body,
770 body_bytes,
771 })
772}
773
774pub fn https_get_binary_keepalive(
797 host: &str,
798 paths: &[String],
799) -> Result<Vec<Option<Vec<u8>>>, &'static str> {
800 if paths.is_empty() {
801 return Ok(Vec::new());
802 }
803 let mut out: Vec<Option<Vec<u8>>> = Vec::with_capacity(paths.len());
804
805 let remote_ip = crate::kernel::net::dns::dns_query_a_via_udp(host)?;
806 let local_port = crate::kernel::net::tcp::tcp_connect_real(remote_ip, 443)?;
807
808 let mut rng = OsRng::new();
809 let mut stream = TcpIoWrapper::new_with_retry_write(local_port);
810 let mut read_buf = alloc::vec![0u8; 16384];
811 let mut write_buf = alloc::vec![0u8; 16384];
812 let config = TlsConfig::new().with_server_name(host);
813 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
814 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
815 let mut provider =
816 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
817
818 let _ph = crate::os_lib::web_engine::perf::start();
819 let open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
820 crate::os_lib::web_engine::perf::add(
821 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
822 _ph,
823 );
824 if open.is_err() {
825 let _ = crate::kernel::net::tcp_close_real(local_port);
826 return Err("TLS handshake failed");
827 }
828
829 let mut broken = false;
830 for path in paths.iter() {
831 if broken {
832 out.push(None);
833 continue;
834 }
835 let req = alloc::format!(
837 concat!(
838 "GET {} HTTP/1.1
839",
840 "Host: {}
841",
842 "User-Agent: Mozilla/5.0 (AtmOS Browser)
843",
844 "Accept: */*
845",
846 "Accept-Encoding: identity
847",
848 "Connection: keep-alive
851
852"
853 ),
854 path,
855 host
856 );
857 if embedded_io::Write::write_all(&mut connection, req.as_bytes()).is_err() {
858 broken = true;
859 out.push(None);
860 continue;
861 }
862 embedded_io::Write::flush(&mut connection).ok();
863 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
864
865 let mut buf: Vec<u8> = Vec::new();
867 let mut chunk = [0u8; 1024];
868 let mut header_end: Option<usize> = None;
869 let mut want: Option<usize> = None;
870 loop {
871 if header_end.is_none() {
872 if let Some(i) = crate::kernel::http_body::body_start(&buf) {
873 header_end = Some(i);
874 want = crate::kernel::http_body::content_length(&buf);
875 if want.is_none() {
876 broken = true;
878 break;
879 }
880 }
881 }
882 if let (Some(h), Some(w)) = (header_end, want) {
883 if buf.len() >= h + w {
884 break;
885 }
886 }
887 match embedded_io::Read::read(&mut connection, &mut chunk) {
888 Ok(0) => {
889 broken = true;
890 break;
891 }
892 Ok(n) => buf.extend_from_slice(&chunk[..n]),
893 Err(_) => {
894 broken = true;
895 break;
896 }
897 }
898 }
899 match (header_end, want) {
900 (Some(h), Some(w)) if buf.len() >= h + w => {
901 out.push(Some(crate::kernel::http_body::body_range(&buf)
913 .map(|(a, b)| buf[a..b].to_vec())
914 .unwrap_or_default()));
915 stats::bump(&stats::OK);
916 }
917 _ => {
918 out.push(None);
919 broken = true;
920 }
921 }
922 }
923
924 let _ = crate::kernel::net::tcp_close_real(local_port);
925 stats::report("after-resp");
926 Ok(out)
927}
928
929fn response_looks_truncated(raw: &[u8]) -> bool {
942 crate::kernel::http_body::looks_truncated(raw)
944}
945
946pub fn https_get_binary(host: &str, path: &str) -> Result<Vec<u8>, &'static str> {
947 https_get_binary_policy(host, path, true)
948}
949
950pub fn https_get_binary_complete(host: &str, path: &str) -> Result<Vec<u8>, &'static str> {
959 https_get_binary_policy(host, path, false)
960}
961
962fn https_get_binary_policy(
963 host: &str,
964 path: &str,
965 accept_partial: bool,
966) -> Result<Vec<u8>, &'static str> {
967 let _pg = crate::os_lib::web_engine::perf::start();
968 let r = https_get_binary_measured(host, path, accept_partial);
969 crate::os_lib::web_engine::perf::add(
970 crate::os_lib::web_engine::perf::Slot::HttpsGet,
971 _pg,
972 );
973 r
974}
975
976fn https_get_binary_measured(
977 host: &str,
978 path: &str,
979 accept_partial: bool,
980) -> Result<Vec<u8>, &'static str> {
981 const MAX_ATTEMPTS: u32 = 3;
982 let ordinal = crate::kernel::net::next_https_attempt_ordinal();
983 crate::warn!("[NET][DIAG] https_get_binary ordinal={} host={} path={}", ordinal, host, path);
984 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host).inspect_err(|e| {
990 crate::warn!("[NET][DIAG] https_get_binary ordinal={} DNS FAILED err={}", ordinal, e);
991 stats::bump(&stats::DNS_FAIL);
992 stats::report("after-dns-fail");
993 })?;
994 crate::warn!("[NET][DIAG] https_get_binary ordinal={} host={} resolved={}.{}.{}.{}", ordinal, host, remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]);
995 let mut best: Option<Vec<u8>> = None;
996 for attempt in 1..=MAX_ATTEMPTS {
997 let resume = best.as_deref().and_then(crate::kernel::http_body::resume_offset);
1000 match https_get_binary_attempt(remote_ip, host, path, resume) {
1001 Ok((raw, complete)) => {
1002 let (raw, complete) = match (resume, best.as_deref()) {
1003 (Some(off), Some(prev)) => {
1004 match crate::kernel::http_body::merge_range_response(prev, off, &raw) {
1005 Some(merged) => {
1006 let done = !crate::kernel::http_body::looks_truncated(&merged);
1007 crate::warn!(
1008 "[NET][DIAG] https_get_binary ordinal={} Range 再開 from={} +{} bytes complete={}",
1009 ordinal, off, merged.len() - prev.len(), done
1010 );
1011 (merged, done)
1012 }
1013 None => (raw, complete),
1015 }
1016 }
1017 _ => (raw, complete),
1018 };
1019 if complete {
1020 crate::warn!(
1021 "[NET][DIAG] https_get_binary ordinal={} SUCCESS attempt={} body_bytes={}",
1022 ordinal, attempt, raw.len()
1023 );
1024 return Ok(crate::kernel::net::extract_http_body_binary(&raw));
1025 }
1026 crate::warn!(
1027 "[NET][DIAG] https_get_binary ordinal={}: truncated response on attempt {}/{} ({} bytes), retrying",
1028 ordinal, attempt, MAX_ATTEMPTS, raw.len()
1029 );
1030 if best.as_ref().is_none_or(|b: &Vec<u8>| raw.len() > b.len()) {
1031 best = Some(raw);
1032 }
1033 }
1034 Err(e) => {
1035 crate::warn!(
1036 "[NET][DIAG] https_get_binary ordinal={} attempt {}/{} FAILED err={}",
1037 ordinal, attempt, MAX_ATTEMPTS, e
1038 );
1039 if attempt == MAX_ATTEMPTS {
1040 return Err(e);
1041 }
1042 }
1043 }
1044 }
1045 match best {
1048 Some(raw) if accept_partial => Ok(crate::kernel::net::extract_http_body_binary(&raw)),
1049 Some(raw) => {
1050 crate::warn!(
1051 "[NET][DIAG] https_get_binary ordinal={} 全 {} 回とも途中で切れたため不採用 (best={} bytes)",
1052 ordinal, MAX_ATTEMPTS, raw.len()
1053 );
1054 Err("HTTPS response truncated")
1055 }
1056 None => Err("HTTPS request failed"),
1057 }
1058}
1059
1060fn https_get_binary_attempt(
1066 remote_ip: [u8; 4],
1067 host: &str,
1068 path: &str,
1069 range_from: Option<usize>,
1070) -> Result<(Vec<u8>, bool), &'static str> {
1071 crate::debug!(
1072 "[NET][DIAG] https_get_binary host={} resolved_ip={}.{}.{}.{}",
1073 host,
1074 remote_ip[0],
1075 remote_ip[1],
1076 remote_ip[2],
1077 remote_ip[3]
1078 );
1079 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, 443)?;
1080 let handshake_start = crate::kernel::timer::get_ticks();
1081 crate::debug!("[NET][DIAG] tcp_connect_real returned local_port={}", local_port);
1082
1083 let mut stream = TcpIoWrapper::new_with_retry_write(local_port);
1091 let mut rng = OsRng::new();
1092
1093 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
1097
1098 let config = TlsConfig::new().with_server_name(host);
1099
1100 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
1101 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
1102
1103 let mut provider =
1104 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
1105 let _ph = crate::os_lib::web_engine::perf::start();
1106 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
1107 crate::os_lib::web_engine::perf::add(
1108 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
1109 _ph,
1110 );
1111 if let Err(e) = _open {
1112 let elapsed = crate::kernel::timer::get_ticks().wrapping_sub(handshake_start);
1113 crate::warn!("[NET] TLS handshake error: {:?} (elapsed_ticks={})", e, elapsed);
1114 let _ = crate::kernel::net::tcp_close_real(local_port);
1115 return Err("TLS handshake failed");
1116 }
1117
1118 let range_hdr = range_from
1119 .map(|n| alloc::format!("Range: bytes={}-\r\n", n))
1120 .unwrap_or_default();
1121 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\n{}Connection: close\r\n\r\n", path, host, range_hdr);
1122 if embedded_io::Write::write_all(&mut connection, request.as_bytes()).is_err() {
1123 crate::kernel::net::tcp_close_real(local_port)?;
1124 return Err("HTTPS request failed");
1125 }
1126
1127 embedded_io::Write::flush(&mut connection).ok();
1128 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
1131
1132 let mut body_bytes = Vec::new();
1133 let mut chunk = [0u8; 1024];
1134 loop {
1135 match embedded_io::Read::read(&mut connection, &mut chunk) {
1136 Ok(0) => break,
1137 Ok(n) => {
1138 body_bytes.extend_from_slice(&chunk[..n]);
1139 if crate::kernel::http_body::body_range(&body_bytes).is_some() {
1143 break;
1144 }
1145 let cap = FETCH_BYTE_CAP.load(core::sync::atomic::Ordering::Relaxed);
1151 if cap > 0 {
1152 let declared = crate::kernel::http_body::content_length(&body_bytes);
1153 if declared.is_some_and(|c| c > cap) || body_bytes.len() > cap {
1154 crate::warn!(
1155 "[TLS] host={} path={} 取得を打ち切り: 上限 {} バイト超過 (declared={:?} got={})",
1156 host, path, cap, declared, body_bytes.len()
1157 );
1158 let _ = crate::kernel::net::tcp_close_real(local_port);
1159 return Err("fetch exceeds size cap");
1160 }
1161 }
1162 }
1163 Err(e) => {
1164 crate::warn!("[NET][DIAG] TLS read err: {:?} body_bytes_so_far={}", e, body_bytes.len());
1165 break;
1166 }
1167 }
1168 }
1169 crate::warn!(
1170 "[NET][DIAG] read loop done, body_bytes.len()={} preview={:?}",
1171 body_bytes.len(),
1172 String::from_utf8_lossy(&body_bytes[..body_bytes.len().min(80)])
1173 );
1174
1175 crate::kernel::net::tcp_close_real(local_port)?;
1176
1177 let complete = !response_looks_truncated(&body_bytes);
1178 if complete {
1181 stats::bump(&stats::OK);
1182 } else {
1183 stats::bump(&stats::TRUNCATED);
1184 }
1185 stats::report("after-resp");
1186 Ok((body_bytes, complete))
1187}