1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![cfg_attr(docsrs, allow(unused_attributes))]
5#![deny(missing_docs)]
6
7use std::sync::OnceLock;
8
9use rustix::net::{bind, ipproto, socket, sockopt::set_ipv6_v6only, AddressFamily, SocketType};
10
11static INIT: OnceLock<Probe> = OnceLock::new();
12
13const V6_PROBES: [(bool, bool); 2] = [
14 (true, true), (false, false), ];
17
18pub fn ipv4() -> bool {
20 probe().ipv4
21}
22
23pub fn ipv6() -> bool {
25 probe().ipv6
26}
27
28pub fn ipv4_mapped_ipv6() -> bool {
31 probe().ipv4_mapped_ipv6
32}
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq)]
36pub struct Probe {
37 ipv4: bool,
38 ipv6: bool,
39 ipv4_mapped_ipv6: bool,
40}
41
42impl Probe {
43 #[inline]
45 pub const fn ipv4(&self) -> bool {
46 self.ipv4
47 }
48
49 #[inline]
51 pub const fn ipv6(&self) -> bool {
52 self.ipv6
53 }
54
55 #[inline]
58 pub const fn ipv4_mapped_ipv6(&self) -> bool {
59 self.ipv4_mapped_ipv6
60 }
61}
62
63pub fn probe() -> Probe {
74 *INIT.get_or_init(probe_in)
75}
76
77fn probe_in() -> Probe {
78 use std::net::{Ipv6Addr, SocketAddrV6};
79
80 let mut caps = Probe {
81 ipv4: false,
82 ipv6: false,
83 ipv4_mapped_ipv6: false,
84 };
85
86 #[cfg(windows)]
87 let _ = rustix::net::wsa_startup();
88
89 {
91 let ipv4_sock = socket(AddressFamily::INET, SocketType::STREAM, Some(ipproto::TCP));
92
93 if ipv4_sock.is_ok() {
94 caps.ipv4 = true;
95 }
96 }
97
98 for (is_ipv6, v6_only) in V6_PROBES {
100 let sock = socket(AddressFamily::INET6, SocketType::STREAM, Some(ipproto::TCP));
101
102 if let Ok(sock) = sock {
103 let _ = set_ipv6_v6only(&sock, v6_only);
105
106 let addr = if is_ipv6 {
108 SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)
109 } else {
110 SocketAddrV6::new(
111 Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x01),
113 0,
114 0,
115 0,
116 )
117 };
118
119 let bind_result = bind(sock, &addr);
121
122 if bind_result.is_ok() {
123 if is_ipv6 {
124 caps.ipv6 = true;
125 } else {
126 caps.ipv4_mapped_ipv6 = true;
127 }
128 }
129 }
130 }
131
132 #[cfg(windows)]
133 let _ = rustix::net::wsa_cleanup();
134
135 caps
136}
137
138#[test]
139fn test() {
140 let caps = probe();
141 println!("IPv4 enabled: {}", caps.ipv4());
142 println!("IPv6 enabled: {}", caps.ipv6());
143 println!("IPv4-mapped IPv6 enabled: {}", caps.ipv4_mapped_ipv6());
144}