/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! FFI glue layer between happy-eyeballs Rust crate and Firefox's C++ networking stack.
mod metrics; mod profiler;
use nserror::{nsresult, NS_ERROR_INVALID_ARG, NS_ERROR_UNEXPECTED, NS_OK}; use nsstring::{nsACString, nsCString}; use std::net::{Ipv4Addr, Ipv6Addr}; use std::ptr; use std::time::{Duration, Instant}; use thin_vec::ThinVec; use xpcom::{AtomicRefcnt, RefCounted};
#[cfg(not(windows))] use libc::{AF_INET, AF_INET6}; #[cfg(windows)] use winapi::shared::ws2def::{AF_INET, AF_INET6};
impl From<IpPreference> for happy_eyeballs::IpPreference { fn from(v: IpPreference) -> Self { match v {
IpPreference::DualStackPreferV6 => Self::DualStackPreferV6,
IpPreference::DualStackPreferV4 => Self::DualStackPreferV4,
IpPreference::Ipv6Only => Self::Ipv6Only,
IpPreference::Ipv4Only => Self::Ipv4Only,
}
}
}
/// Which HTTP versions Firefox is willing to use, derived from prefs such as /// `network.http.http2.enabled` and `network.http.http3.enable`. Mirrors /// `happy_eyeballs::HttpVersions` across the FFI boundary. #[repr(C)] #[derive(Clone, Copy)] pubstruct HttpVersions { pub h1: bool, pub h2: bool, pub h3: bool,
}
impl HappyEyeballs { fn process_dns_response_a(&mutself, id: u64, net_addrs: &ThinVec<NetAddr>) -> nsresult { let id: happy_eyeballs::Id = id.into(); letmut addrs = Vec::with_capacity(net_addrs.len()); for na in net_addrs.iter() { let family =
i32::from(unsafe { moz_netaddr_get_family((na as *const NetAddr).cast()) }); if family != AF_INET {
debug_assert!(false, "got {} instead of AF_INET in A record", family); return NS_ERROR_UNEXPECTED;
} let ip_be = unsafe { moz_netaddr_get_network_order_ip((na as *const NetAddr).cast()) }; let ipv4 = Ipv4Addr::from(u32::from_be(ip_be));
addrs.push(ipv4);
}
let result = happy_eyeballs::DnsResult::A(Ok(addrs)); let input = happy_eyeballs::Input::DnsResult { id, result }; self.inner.process_input(input, Instant::now());
NS_OK
}
fn process_dns_response_aaaa(&mutself, id: u64, net_addrs: &ThinVec<NetAddr>) -> nsresult { let id: happy_eyeballs::Id = id.into(); letmut addrs = Vec::with_capacity(net_addrs.len()); for na in net_addrs.iter() { let family =
i32::from(unsafe { moz_netaddr_get_family((na as *const NetAddr).cast()) }); if family != AF_INET6 {
debug_assert!(false, "got {} instead of AF_INET6 in AAAA record", family); return NS_ERROR_UNEXPECTED;
} let p = unsafe { moz_netaddr_get_ipv6((na as *const NetAddr).cast()) }; let octs: [u8; 16] = unsafe { std::slice::from_raw_parts(p, 16).try_into().unwrap() }; let ipv6 = Ipv6Addr::from(octs);
addrs.push(ipv6);
}
let result = happy_eyeballs::DnsResult::Aaaa(Ok(addrs)); let input = happy_eyeballs::Input::DnsResult { id, result }; self.inner.process_input(input, Instant::now());
for svc_info in service_infos { let target_str = svc_info.target_name.to_utf8(); let target = if target_str.is_empty() {
todo!()
} else {
happy_eyeballs::TargetName::from(target_str.as_ref())
};
letmut alpn_set = std::collections::HashSet::new(); for http_version in &svc_info.alpn_http_versions {
alpn_set.insert((*http_version).into());
}
let ech = if svc_info.ech_config.is_empty() {
None
} else {
Some(happy_eyeballs::EchConfig::new(svc_info.ech_config.to_vec()))
};
letmut ipv4_vec = Vec::new(); for na in &svc_info.ipv4_hints { let family =
i32::from(unsafe { moz_netaddr_get_family((na as *const NetAddr).cast()) });
debug_assert_eq!(family, AF_INET, "Expected IPv4 address in IPv4 hints"); if family != AF_INET { return NS_ERROR_UNEXPECTED;
} let ip_be = unsafe { moz_netaddr_get_network_order_ip((na as *const NetAddr).cast()) }; let ipv4 = Ipv4Addr::from(u32::from_be(ip_be));
ipv4_vec.push(ipv4);
}
letmut ipv6_vec = Vec::new(); for na in &svc_info.ipv6_hints { let family =
i32::from(unsafe { moz_netaddr_get_family((na as *const NetAddr).cast()) });
debug_assert_eq!(family, AF_INET6, "Expected IPv6 address in IPv6 hints"); if family != AF_INET6 { return NS_ERROR_UNEXPECTED;
} let p = unsafe { moz_netaddr_get_ipv6((na as *const NetAddr).cast()) }; let octs: [u8; 16] = unsafe { std::slice::from_raw_parts(p, 16).try_into().unwrap() }; let ipv6 = Ipv6Addr::from(octs);
ipv6_vec.push(ipv6);
}
let port = if svc_info.port == 0 {
None
} else {
Some(svc_info.port)
};
let result = happy_eyeballs::DnsResult::Https(Ok(infos)); let input = happy_eyeballs::Input::DnsResult { id, result }; self.inner.process_input(input, Instant::now());
NS_OK
}
fn process_connection_result(&mutself, id: u64, status: nsresult) -> nsresult { let id: happy_eyeballs::Id = id.into(); self.profiler.connection_result(id, status == NS_OK); if status == NS_OK { self.metrics.connection_succeeded(id);
}
let result = if status == NS_OK {
happy_eyeballs::ConnectionResult::Success
} else {
happy_eyeballs::ConnectionResult::Failure(format!( "connection failed: 0x{:08x}",
status.0
))
};
let input = happy_eyeballs::Input::ConnectionResult { id, result }; self.inner.process_input(input, Instant::now());
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.15Bemerkung:
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.