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_acked_through(
352 self.local_port,
353 seq.wrapping_add(buf.len() as u32),
354 ) || crate::kernel::net::tcp_recv_peek_nonempty(self.local_port)
355 || crate::kernel::net::tcp_is_socket_closed(self.local_port);
356 crate::os_lib::web_engine::perf::add(
357 crate::os_lib::web_engine::perf::Slot::PeekCheck,
358 _pk,
359 );
360 if done {
361 break 'retry;
362 }
363 }
364 WRITE_RETX_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
366 let _ = crate::kernel::net::tcp_retransmit_real(self.local_port, buf, seq);
367 }
368 WRITE_WAIT_TICKS.fetch_add(
369 crate::kernel::timer::get_ticks().wrapping_sub(t_w0) as u32,
370 core::sync::atomic::Ordering::Relaxed,
371 );
372 crate::os_lib::web_engine::perf::add(
373 crate::os_lib::web_engine::perf::Slot::TlsWriteWait,
374 _pw,
375 );
376 }
377 }
378 Ok(buf.len())
379 }
380
381 fn flush(&mut self) -> Result<(), Self::Error> {
382 Ok(())
383 }
384}
385
386
387pub static FETCH_BYTE_CAP: core::sync::atomic::AtomicUsize =
398 core::sync::atomic::AtomicUsize::new(0);
399
400pub mod stats {
401 use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
402 pub static OK: AtomicU32 = AtomicU32::new(0);
403 pub static DNS_FAIL: AtomicU32 = AtomicU32::new(0);
404 pub static CONNECT_FAIL: AtomicU32 = AtomicU32::new(0);
405 pub static HANDSHAKE_FAIL: AtomicU32 = AtomicU32::new(0);
406 pub static TRUNCATED: AtomicU32 = AtomicU32::new(0);
407
408 pub fn bump(c: &AtomicU32) {
409 c.fetch_add(1, Relaxed);
410 }
411
412 pub fn report(tag: &str) {
414 let ok = OK.load(Relaxed);
415 let dns = DNS_FAIL.load(Relaxed);
416 let conn = CONNECT_FAIL.load(Relaxed);
417 let hs = HANDSHAKE_FAIL.load(Relaxed);
418 let tr = TRUNCATED.load(Relaxed);
419 let total = ok + dns + conn + hs + tr;
420 crate::warn!(
421 "[NET][STATS] {} total={} ok={} dns_fail={} connect_fail={} handshake_fail={} truncated={}",
422 tag, total, ok, dns, conn, hs, tr
423 );
424 }
425}
426
427pub fn https_get(
428 host: &str,
429 path: &str,
430) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
431 https_get_with_referer(host, path, None)
432}
433
434pub fn https_get_with_referer(
437 host: &str,
438 path: &str,
439 referer: Option<&str>,
440) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
441 let ordinal = crate::kernel::net::next_https_attempt_ordinal();
442 crate::warn!("[NET][DIAG] https_get ordinal={} host={} path={}", ordinal, host, path);
443 let (host_only, port) = crate::kernel::net::parse_host_port_with_default(host, 443);
444 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host_only).inspect_err(|e| {
445 crate::warn!("[NET][DIAG] https_get ordinal={} DNS FAILED err={}", ordinal, e);
446 stats::bump(&stats::DNS_FAIL);
447 stats::report("after-dns-fail");
448 })?;
449 crate::warn!("[NET][DIAG] https_get ordinal={} host={} resolved={}.{}.{}.{}", ordinal, host_only, remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]);
450 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, port).inspect_err(|e| {
451 crate::warn!("[NET][DIAG] https_get ordinal={} TCP CONNECT FAILED port={} err={}", ordinal, port, e);
452 stats::bump(&stats::CONNECT_FAIL);
453 stats::report("after-connect-fail");
454 })?;
455
456 let mut stream = TcpIoWrapper::new(local_port);
457 let mut rng = OsRng::new();
458
459 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
461
462 let config = TlsConfig::new().with_server_name(host_only);
464
465 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
466 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
467
468 let mut provider =
470 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
471 let _ph = crate::os_lib::web_engine::perf::start();
472 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
473 crate::os_lib::web_engine::perf::add(
474 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
475 _ph,
476 );
477 if let Err(e) = _open {
478 crate::warn!("[NET][DIAG] https_get ordinal={} TLS HANDSHAKE FAILED err={:?}", ordinal, e);
479 stats::bump(&stats::HANDSHAKE_FAIL);
480 stats::report("after-handshake-fail");
481 crate::warn!("[NET][DIAG] https_get ordinal={} about to tcp_close_real", ordinal);
482 let _ = crate::kernel::net::tcp_close_real(local_port);
483 crate::warn!("[NET][DIAG] https_get ordinal={} tcp_close_real returned, returning Err", ordinal);
484 return Err("TLS handshake failed");
485 }
486
487 let referer_hdr = referer.map(|r| alloc::format!("Referer: {}\r\n", r)).unwrap_or_default();
489 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\n{}Connection: close\r\n\r\n", path, host, referer_hdr);
490 if embedded_io::Write::write_all(&mut connection, request.as_bytes()).is_err() {
491 crate::kernel::net::tcp_close_real(local_port)?;
492 return Err("HTTPS request failed");
493 }
494
495 embedded_io::Write::flush(&mut connection).ok();
497
498 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
502
503 let mut raw_bytes = Vec::new();
504 let mut chunk = [0u8; 1024];
505 loop {
506 match embedded_io::Read::read(&mut connection, &mut chunk) {
507 Ok(0) => break,
508 Ok(n) => raw_bytes.extend_from_slice(&chunk[..n]),
509 Err(e) => {
510 crate::info!("[NET] TLS read err: {:?}", e);
511 break;
512 }
513 }
514 }
515
516 if raw_bytes.is_empty() {
524 crate::warn!(
525 "[NET][DIAG] https_get ordinal={} EMPTY RESPONSE (0 bytes read) host={}",
526 ordinal,
527 host
528 );
529 let _ = crate::kernel::net::tcp_close_real(local_port);
530 return Err("empty https response");
531 }
532
533 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
534 Ok(s) => s,
535 Err(e) => {
536 let valid = e.into_bytes();
537 alloc::string::String::from_utf8_lossy(&valid).into_owned()
538 }
539 };
540
541 let preview: String = resp_str.chars().take(300).collect();
543 crate::info!("[NET] HTTPS RAW RESP: [{} bytes] {:?}", resp_str.len(), preview);
544 let (status_code, reason, headers, body) = crate::kernel::net::parse_http_response_text(&raw_bytes)
545 .unwrap_or((
546 200,
547 String::from("OK"),
548 alloc::collections::BTreeMap::new(),
549 resp_str,
550 ));
551 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
552 .map(|(_, _, _, b)| b)
553 .unwrap_or_default();
554
555 crate::kernel::net::tcp_close_real(local_port)?;
556
557 crate::warn!(
558 "[NET][DIAG] https_get ordinal={} SUCCESS body_bytes={}",
559 ordinal,
560 body_bytes.len()
561 );
562
563 Ok(crate::kernel::net::HttpsGetResult {
564 remote_ip,
565 local_port,
566 tls_version: String::from("TLS1.3"),
567 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
568 cert_subject: alloc::format!("CN={}", host),
569 cert_issuer: String::from("Unknown Root CA"),
570 cert_not_after: String::from("Unknown"),
571 status_code,
572 reason,
573 headers,
574 body,
575 body_bytes,
576 })
577}
578
579pub fn https_request(
589 method: &str,
590 host: &str,
591 path: &str,
592 extra_headers: &[(&str, &str)],
593 content_type: &str,
594 body: Option<&[u8]>,
595) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
596 const MAX_ATTEMPTS: u32 = 3;
597 let idempotent = matches!(method, "GET" | "HEAD" | "OPTIONS");
598 let mut partial: Option<crate::kernel::net::HttpsGetResult> = None;
599 for attempt in 1..=MAX_ATTEMPTS {
600 let (res, complete) =
601 match https_request_once(method, host, path, extra_headers, content_type, body) {
602 Ok(r) => r,
603 Err(e) => return partial.ok_or(e),
605 };
606 if complete || !idempotent || method == "HEAD" {
607 return Ok(res);
608 }
609 crate::warn!(
610 "[NET][DIAG] https_request 途中切れ {} {}{} attempt={}/{} body={}",
611 method, host, path, attempt, MAX_ATTEMPTS, res.body_bytes.len()
612 );
613 partial = Some(res);
614 }
615 partial.ok_or("HTTPS request failed")
616}
617
618fn https_request_once(
620 method: &str,
621 host: &str,
622 path: &str,
623 extra_headers: &[(&str, &str)],
624 content_type: &str,
625 body: Option<&[u8]>,
626) -> Result<(crate::kernel::net::HttpsGetResult, bool), &'static str> {
627 let (host_only, port) = crate::kernel::net::parse_host_port_with_default(host, 443);
628 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host_only)?;
629 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, port)?;
630
631 let mut stream = TcpIoWrapper::new(local_port);
632 let mut rng = OsRng::new();
633
634 let mut read_buf = alloc::vec![0u8; 18432];
635 let mut write_buf = alloc::vec![0u8; 16384];
636
637 let config = TlsConfig::new().with_server_name(host_only);
638 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
639 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
640 let mut provider =
641 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
642 let _ph = crate::os_lib::web_engine::perf::start();
643 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
644 crate::os_lib::web_engine::perf::add(
645 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
646 _ph,
647 );
648 if let Err(e) = _open {
649 crate::warn!("[NET] TLS handshake error: {:?}", e);
650 let _ = crate::kernel::net::tcp_close_real(local_port);
651 return Err("TLS handshake failed");
652 }
653
654 let mut header = alloc::format!(
655 "{} {} HTTP/1.1\r\nHost: {}\r\nUser-Agent: AtmOS/1.0\r\n",
656 method,
657 path,
658 host
659 );
660 if !extra_headers.iter().any(|(k, _)| k.eq_ignore_ascii_case("accept")) {
662 header.push_str("Accept: application/json\r\n");
663 }
664 for (k, v) in extra_headers {
665 header.push_str(k);
666 header.push_str(": ");
667 header.push_str(v);
668 header.push_str("\r\n");
669 }
670 let body = body.unwrap_or(&[]);
671 if !content_type.is_empty() {
672 header.push_str(&alloc::format!("Content-Type: {}\r\n", content_type));
673 }
674 if !body.is_empty() || method == "POST" || method == "PUT" || method == "PATCH" {
675 header.push_str(&alloc::format!("Content-Length: {}\r\n", body.len()));
676 }
677 header.push_str("Accept-Encoding: identity\r\n");
678 header.push_str("Connection: close\r\n\r\n");
679
680 let mut request = header.into_bytes();
681 request.extend_from_slice(body);
682
683 if embedded_io::Write::write_all(&mut connection, &request).is_err() {
684 crate::kernel::net::tcp_close_real(local_port)?;
685 return Err("HTTPS request failed");
686 }
687 embedded_io::Write::flush(&mut connection).ok();
688 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
691
692 let mut raw_bytes = Vec::new();
693 let mut chunk = [0u8; 1024];
694 loop {
695 match embedded_io::Read::read(&mut connection, &mut chunk) {
696 Ok(0) => break,
697 Ok(n) => {
698 raw_bytes.extend_from_slice(&chunk[..n]);
699 if crate::kernel::http_body::is_framed_complete(&raw_bytes) {
701 break;
702 }
703 }
704 Err(_) => break,
705 }
706 }
707
708 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
709 Ok(s) => s,
710 Err(e) => alloc::string::String::from_utf8_lossy(&e.into_bytes()).into_owned(),
711 };
712 let (status_code, reason, headers, rbody) = crate::kernel::net::parse_http_response_text(&raw_bytes)
713 .unwrap_or((
714 200,
715 String::from("OK"),
716 alloc::collections::BTreeMap::new(),
717 resp_str,
718 ));
719 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
721 .map(|(_, _, _, b)| b)
722 .unwrap_or_default();
723 let complete = crate::kernel::http_body::is_complete_response(&raw_bytes);
724 crate::kernel::net::tcp_close_real(local_port)?;
725
726 Ok((crate::kernel::net::HttpsGetResult {
727 remote_ip,
728 local_port,
729 tls_version: String::from("TLS1.3"),
730 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
731 cert_subject: alloc::format!("CN={}", host),
732 cert_issuer: String::from("Unknown Root CA"),
733 cert_not_after: String::from("Unknown"),
734 status_code,
735 reason,
736 headers,
737 body: rbody,
738 body_bytes,
739 }, complete))
740}
741
742pub fn https_post(
746 host: &str,
747 path: &str,
748 content_type: &str,
749 body: &[u8],
750) -> Result<crate::kernel::net::HttpsGetResult, &'static str> {
751 let (host_only, port) = crate::kernel::net::parse_host_port_with_default(host, 443);
752 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host_only)?;
753 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, port)?;
754
755 let mut stream = TcpIoWrapper::new(local_port);
756 let mut rng = OsRng::new();
757
758 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
760
761 let config = TlsConfig::new().with_server_name(host_only);
762
763 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
764 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
765
766 let mut provider =
767 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
768 let _ph = crate::os_lib::web_engine::perf::start();
769 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
770 crate::os_lib::web_engine::perf::add(
771 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
772 _ph,
773 );
774 if let Err(e) = _open {
775 crate::warn!("[NET] TLS handshake error: {:?}", e);
776 let _ = crate::kernel::net::tcp_close_real(local_port);
777 return Err("TLS handshake failed");
778 }
779
780 let header = alloc::format!(
782 "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",
783 path, host, content_type, body.len()
784 );
785 let mut request = header.into_bytes();
786 request.extend_from_slice(body);
787
788 if embedded_io::Write::write_all(&mut connection, &request).is_err() {
789 crate::kernel::net::tcp_close_real(local_port)?;
790 return Err("HTTPS POST request failed");
791 }
792 embedded_io::Write::flush(&mut connection).ok();
793 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
796
797 let mut raw_bytes = Vec::new();
798 let mut chunk = [0u8; 1024];
799 loop {
800 match embedded_io::Read::read(&mut connection, &mut chunk) {
801 Ok(0) => break,
802 Ok(n) => raw_bytes.extend_from_slice(&chunk[..n]),
803 Err(_) => break,
805 }
806 }
807
808 let resp_str = match alloc::string::String::from_utf8(raw_bytes.clone()) {
809 Ok(s) => s,
810 Err(e) => alloc::string::String::from_utf8_lossy(&e.into_bytes()).into_owned(),
811 };
812
813 crate::info!("[NET] HTTPS POST: {} bytes received", resp_str.len());
814 let (status_code, reason, headers, body) = crate::kernel::net::parse_http_response_text(&raw_bytes)
815 .unwrap_or((
816 200,
817 String::from("OK"),
818 alloc::collections::BTreeMap::new(),
819 resp_str,
820 ));
821 let body_bytes = crate::kernel::net::parse_http_response_bytes(&raw_bytes)
822 .map(|(_, _, _, b)| b)
823 .unwrap_or_default();
824
825 crate::kernel::net::tcp_close_real(local_port)?;
826
827 Ok(crate::kernel::net::HttpsGetResult {
828 remote_ip,
829 local_port,
830 tls_version: String::from("TLS1.3"),
831 cipher_suite: String::from("TLS_AES_128_GCM_SHA256"),
832 cert_subject: alloc::format!("CN={}", host),
833 cert_issuer: String::from("Unknown Root CA"),
834 cert_not_after: String::from("Unknown"),
835 status_code,
836 reason,
837 headers,
838 body,
839 body_bytes,
840 })
841}
842
843pub fn https_get_binary_keepalive(
866 host: &str,
867 paths: &[String],
868) -> Result<Vec<Option<Vec<u8>>>, &'static str> {
869 if paths.is_empty() {
870 return Ok(Vec::new());
871 }
872 let mut out: Vec<Option<Vec<u8>>> = Vec::with_capacity(paths.len());
873
874 let (host_only, port) = crate::kernel::net::parse_host_port_with_default(host, 443);
875 let remote_ip = crate::kernel::net::dns::dns_query_a_via_udp(host_only)?;
876 let local_port = crate::kernel::net::tcp::tcp_connect_real(remote_ip, port)?;
877
878 let mut rng = OsRng::new();
879 let mut stream = TcpIoWrapper::new_with_retry_write(local_port);
880 let mut read_buf = alloc::vec![0u8; 16384];
881 let mut write_buf = alloc::vec![0u8; 16384];
882 let config = TlsConfig::new().with_server_name(host_only);
883 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
884 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
885 let mut provider =
886 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
887
888 let _ph = crate::os_lib::web_engine::perf::start();
889 let open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
890 crate::os_lib::web_engine::perf::add(
891 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
892 _ph,
893 );
894 if open.is_err() {
895 let _ = crate::kernel::net::tcp_close_real(local_port);
896 return Err("TLS handshake failed");
897 }
898
899 let mut broken = false;
900 for path in paths.iter() {
901 if broken {
902 out.push(None);
903 continue;
904 }
905 let req = alloc::format!(
907 concat!(
908 "GET {} HTTP/1.1
909",
910 "Host: {}
911",
912 "User-Agent: Mozilla/5.0 (AtmOS Browser)
913",
914 "Accept: */*
915",
916 "Accept-Encoding: identity
917",
918 "Connection: keep-alive
921
922"
923 ),
924 path,
925 host
926 );
927 if embedded_io::Write::write_all(&mut connection, req.as_bytes()).is_err() {
928 broken = true;
929 out.push(None);
930 continue;
931 }
932 embedded_io::Write::flush(&mut connection).ok();
933 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
934
935 let mut buf: Vec<u8> = Vec::new();
937 let mut chunk = [0u8; 1024];
938 let mut header_end: Option<usize> = None;
939 let mut want: Option<usize> = None;
940 loop {
941 if header_end.is_none() {
942 if let Some(i) = crate::kernel::http_body::body_start(&buf) {
943 header_end = Some(i);
944 want = crate::kernel::http_body::content_length(&buf);
945 if want.is_none() {
946 broken = true;
948 break;
949 }
950 }
951 }
952 if let (Some(h), Some(w)) = (header_end, want) {
953 if buf.len() >= h + w {
954 break;
955 }
956 }
957 match embedded_io::Read::read(&mut connection, &mut chunk) {
958 Ok(0) => {
959 broken = true;
960 break;
961 }
962 Ok(n) => buf.extend_from_slice(&chunk[..n]),
963 Err(_) => {
964 broken = true;
965 break;
966 }
967 }
968 }
969 match (header_end, want) {
970 (Some(h), Some(w)) if buf.len() >= h + w => {
971 out.push(Some(crate::kernel::http_body::body_range(&buf)
983 .map(|(a, b)| buf[a..b].to_vec())
984 .unwrap_or_default()));
985 stats::bump(&stats::OK);
986 }
987 _ => {
988 out.push(None);
989 broken = true;
990 }
991 }
992 }
993
994 let _ = crate::kernel::net::tcp_close_real(local_port);
995 stats::report("after-resp");
996 Ok(out)
997}
998
999fn response_looks_truncated(raw: &[u8]) -> bool {
1012 !crate::kernel::http_body::is_complete_response(raw)
1019}
1020
1021pub fn https_get_binary(host: &str, path: &str) -> Result<Vec<u8>, &'static str> {
1022 https_get_binary_policy(host, path, true)
1023}
1024
1025pub fn https_get_binary_complete(host: &str, path: &str) -> Result<Vec<u8>, &'static str> {
1034 https_get_binary_policy(host, path, false)
1035}
1036
1037fn https_get_binary_policy(
1038 host: &str,
1039 path: &str,
1040 accept_partial: bool,
1041) -> Result<Vec<u8>, &'static str> {
1042 let _pg = crate::os_lib::web_engine::perf::start();
1043 let r = https_get_binary_measured(host, path, accept_partial);
1044 crate::os_lib::web_engine::perf::add(
1045 crate::os_lib::web_engine::perf::Slot::HttpsGet,
1046 _pg,
1047 );
1048 r
1049}
1050
1051fn https_get_binary_measured(
1052 host: &str,
1053 path: &str,
1054 accept_partial: bool,
1055) -> Result<Vec<u8>, &'static str> {
1056 const MAX_ATTEMPTS: u32 = 3;
1057 let ordinal = crate::kernel::net::next_https_attempt_ordinal();
1058 crate::warn!("[NET][DIAG] https_get_binary ordinal={} host={} path={}", ordinal, host, path);
1059 let (host_only, port) = crate::kernel::net::parse_host_port_with_default(host, 443);
1065 let t_dns0 = crate::kernel::timer::get_ticks();
1066 let remote_ip = crate::kernel::net::dns_query_a_via_udp(host_only).inspect_err(|e| {
1067 crate::warn!("[NET][DIAG] https_get_binary ordinal={} DNS FAILED err={}", ordinal, e);
1068 stats::bump(&stats::DNS_FAIL);
1069 stats::report("after-dns-fail");
1070 })?;
1071 crate::warn!("[NET][DIAG] https_get_binary ordinal={} host={} resolved={}.{}.{}.{} dns_ticks={}", ordinal, host, remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3], crate::kernel::timer::get_ticks().wrapping_sub(t_dns0));
1072 let mut best: Option<Vec<u8>> = None;
1073 for attempt in 1..=MAX_ATTEMPTS {
1074 let resume = best.as_deref().and_then(crate::kernel::http_body::resume_offset);
1077 match https_get_binary_attempt(remote_ip, host, host_only, port, path, resume) {
1078 Ok((raw, complete)) => {
1079 let (raw, complete) = match (resume, best.as_deref()) {
1080 (Some(off), Some(prev)) => {
1081 match crate::kernel::http_body::merge_range_response(prev, off, &raw) {
1082 Some(merged) => {
1083 let done = !crate::kernel::http_body::looks_truncated(&merged);
1084 crate::warn!(
1085 "[NET][DIAG] https_get_binary ordinal={} Range 再開 from={} +{} bytes complete={}",
1086 ordinal, off, merged.len() - prev.len(), done
1087 );
1088 (merged, done)
1089 }
1090 None => (raw, complete),
1092 }
1093 }
1094 _ => (raw, complete),
1095 };
1096 if complete {
1097 crate::warn!(
1098 "[NET][DIAG] https_get_binary ordinal={} SUCCESS attempt={} body_bytes={}",
1099 ordinal, attempt, raw.len()
1100 );
1101 return Ok(crate::kernel::net::extract_http_body_binary(&raw));
1102 }
1103 crate::warn!(
1104 "[NET][DIAG] https_get_binary ordinal={}: truncated response on attempt {}/{} ({} bytes), retrying",
1105 ordinal, attempt, MAX_ATTEMPTS, raw.len()
1106 );
1107 if best.as_ref().is_none_or(|b: &Vec<u8>| raw.len() > b.len()) {
1108 best = Some(raw);
1109 }
1110 }
1111 Err(e) => {
1112 crate::warn!(
1113 "[NET][DIAG] https_get_binary ordinal={} attempt {}/{} FAILED err={}",
1114 ordinal, attempt, MAX_ATTEMPTS, e
1115 );
1116 if attempt == MAX_ATTEMPTS {
1117 return Err(e);
1118 }
1119 }
1120 }
1121 }
1122 match best {
1125 Some(raw) if accept_partial => Ok(crate::kernel::net::extract_http_body_binary(&raw)),
1126 Some(raw) => {
1127 crate::warn!(
1128 "[NET][DIAG] https_get_binary ordinal={} 全 {} 回とも途中で切れたため不採用 (best={} bytes)",
1129 ordinal, MAX_ATTEMPTS, raw.len()
1130 );
1131 Err("HTTPS response truncated")
1132 }
1133 None => Err("HTTPS request failed"),
1134 }
1135}
1136
1137fn https_get_binary_attempt(
1143 remote_ip: [u8; 4],
1144 host: &str,
1145 host_only: &str,
1146 port: u16,
1147 path: &str,
1148 range_from: Option<usize>,
1149) -> Result<(Vec<u8>, bool), &'static str> {
1150 crate::debug!(
1151 "[NET][DIAG] https_get_binary host={} resolved_ip={}.{}.{}.{}",
1152 host,
1153 remote_ip[0],
1154 remote_ip[1],
1155 remote_ip[2],
1156 remote_ip[3]
1157 );
1158 if tls12_hosts_contains(host_only) {
1159 return https_get_binary_attempt_tls12(remote_ip, host, host_only, port, path, range_from);
1160 }
1161 let t_conn0 = crate::kernel::timer::get_ticks();
1162 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, port)?;
1163 let handshake_start = crate::kernel::timer::get_ticks();
1164 crate::debug!("[NET][DIAG] tcp_connect_real returned local_port={}", local_port);
1165
1166 let mut stream = TcpIoWrapper::new_with_retry_write(local_port);
1174 let mut rng = OsRng::new();
1175
1176 let mut read_buf = alloc::vec![0u8; 18432]; let mut write_buf = alloc::vec![0u8; 16384];
1180
1181 let config = TlsConfig::new().with_server_name(host_only);
1182
1183 let mut connection: TlsConnection<'_, &mut TcpIoWrapper, embedded_tls::Aes128GcmSha256> =
1184 TlsConnection::new(&mut stream, &mut read_buf[..], &mut write_buf[..]);
1185
1186 let mut provider =
1187 embedded_tls::UnsecureProvider::new::<embedded_tls::Aes128GcmSha256>(&mut rng);
1188 let _ph = crate::os_lib::web_engine::perf::start();
1189 let _open = connection.open(crate::kernel::tls::TlsContext::new(&config, &mut provider));
1190 crate::os_lib::web_engine::perf::add(
1191 crate::os_lib::web_engine::perf::Slot::TlsHandshake,
1192 _ph,
1193 );
1194 if let Err(e) = _open {
1195 let elapsed = crate::kernel::timer::get_ticks().wrapping_sub(handshake_start);
1196 crate::warn!("[NET] TLS handshake error: {:?} (elapsed_ticks={})", e, elapsed);
1197 let _ = crate::kernel::net::tcp_close_real(local_port);
1198 if tls13_hosts_contains(host_only) {
1201 return Err("TLS handshake failed");
1202 }
1203 return https_get_binary_attempt_tls12(remote_ip, host, host_only, port, path, range_from);
1204 }
1205 tls13_hosts_insert(host_only);
1206 let t_http0 = crate::kernel::timer::get_ticks();
1207 let r = read_http_response(&mut connection, local_port, host, path, range_from);
1208 let t_end = crate::kernel::timer::get_ticks();
1209 crate::warn!(
1211 "[NET][TIME] host={} path={} tcp={} tls={} http={} bytes={}",
1212 host,
1213 path.get(..path.len().min(60)).unwrap_or(path),
1214 handshake_start.wrapping_sub(t_conn0),
1215 t_http0.wrapping_sub(handshake_start),
1216 t_end.wrapping_sub(t_http0),
1217 r.as_ref().map(|(b, _)| b.len()).unwrap_or(0)
1218 );
1219 r
1220}
1221
1222static TLS13_HOSTS: spin::Mutex<alloc::collections::BTreeSet<String>> =
1224 spin::Mutex::new(alloc::collections::BTreeSet::new());
1225static TLS12_HOSTS: spin::Mutex<alloc::collections::BTreeSet<String>> =
1227 spin::Mutex::new(alloc::collections::BTreeSet::new());
1228
1229fn tls13_hosts_contains(host: &str) -> bool {
1230 TLS13_HOSTS.lock().contains(host)
1231}
1232
1233fn tls13_hosts_insert(host: &str) {
1234 let mut s = TLS13_HOSTS.lock();
1235 if !s.contains(host) {
1236 s.insert(String::from(host));
1237 }
1238}
1239
1240fn tls12_hosts_contains(host: &str) -> bool {
1241 TLS12_HOSTS.lock().contains(host)
1242}
1243
1244fn https_get_binary_attempt_tls12(
1247 remote_ip: [u8; 4],
1248 host: &str,
1249 host_only: &str,
1250 port: u16,
1251 path: &str,
1252 range_from: Option<usize>,
1253) -> Result<(Vec<u8>, bool), &'static str> {
1254 let local_port = crate::kernel::net::tcp_connect_real(remote_ip, port)?;
1255 let mut stream = TcpIoWrapper::new_with_retry_write(local_port);
1256 let mut rng = OsRng::new();
1257 match crate::kernel::tls12::Tls12::connect(&mut stream, host_only, &mut rng) {
1258 Ok(mut conn) => {
1259 crate::warn!("[NET] TLS 1.2 で接続 host={}", host);
1260 {
1261 let mut s = TLS12_HOSTS.lock();
1262 if !s.contains(host_only) {
1263 s.insert(String::from(host_only));
1264 }
1265 }
1266 let r = read_http_response(&mut conn, local_port, host, path, range_from);
1267 conn.close();
1268 r
1269 }
1270 Err(e) => {
1271 crate::warn!("[NET] TLS 1.2 handshake error: {:?} host={}", e, host);
1272 let _ = crate::kernel::net::tcp_close_real(local_port);
1273 Err("TLS handshake failed")
1274 }
1275 }
1276}
1277
1278fn read_http_response<C: embedded_io::Read + embedded_io::Write>(
1280 connection: &mut C,
1281 local_port: u16,
1282 host: &str,
1283 path: &str,
1284 range_from: Option<usize>,
1285) -> Result<(Vec<u8>, bool), &'static str> {
1286
1287 let range_hdr = range_from
1288 .map(|n| alloc::format!("Range: bytes={}-\r\n", n))
1289 .unwrap_or_default();
1290 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);
1291 if embedded_io::Write::write_all(connection, request.as_bytes()).is_err() {
1292 crate::kernel::net::tcp_close_real(local_port)?;
1293 return Err("HTTPS request failed");
1294 }
1295
1296 embedded_io::Write::flush(connection).ok();
1297 crate::kernel::net::tcp::wait_for_first_byte(local_port, 50);
1300
1301 let mut body_bytes = Vec::new();
1302 let mut chunk = [0u8; 1024];
1303 loop {
1304 match embedded_io::Read::read(connection, &mut chunk) {
1305 Ok(0) => break,
1306 Ok(n) => {
1307 body_bytes.extend_from_slice(&chunk[..n]);
1308 if crate::kernel::http_body::body_range(&body_bytes).is_some() {
1312 break;
1313 }
1314 let cap = FETCH_BYTE_CAP.load(core::sync::atomic::Ordering::Relaxed);
1320 if cap > 0 {
1321 let declared = crate::kernel::http_body::content_length(&body_bytes);
1322 if declared.is_some_and(|c| c > cap) || body_bytes.len() > cap {
1323 crate::warn!(
1324 "[TLS] host={} path={} 取得を打ち切り: 上限 {} バイト超過 (declared={:?} got={})",
1325 host, path, cap, declared, body_bytes.len()
1326 );
1327 let _ = crate::kernel::net::tcp_close_real(local_port);
1328 return Err("fetch exceeds size cap");
1329 }
1330 }
1331 }
1332 Err(e) => {
1333 crate::warn!("[NET][DIAG] TLS read err: {:?} body_bytes_so_far={}", e, body_bytes.len());
1334 break;
1335 }
1336 }
1337 }
1338 crate::warn!(
1339 "[NET][DIAG] read loop done, body_bytes.len()={} preview={:?}",
1340 body_bytes.len(),
1341 String::from_utf8_lossy(&body_bytes[..body_bytes.len().min(80)])
1342 );
1343
1344 crate::kernel::net::tcp_close_real(local_port)?;
1345
1346 let complete = !response_looks_truncated(&body_bytes);
1347 if complete {
1350 stats::bump(&stats::OK);
1351 } else {
1352 stats::bump(&stats::TRUNCATED);
1353 }
1354 stats::report("after-resp");
1355 Ok((body_bytes, complete))
1356}