1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
#![allow(clippy::cast_ptr_alignment)]

use std;
use std::convert::TryFrom;
use std::ffi::CStr;
use std::net::IpAddr;

use crate::error::*;
use socket2;
use widestring::WideCString;
use windows_sys::Win32::Foundation::ERROR_BUFFER_OVERFLOW;
use windows_sys::Win32::Foundation::ERROR_SUCCESS;
use windows_sys::Win32::Networking::WinSock::AF_UNSPEC;

use crate::bindings::*;

/// Represent an operational status of the adapter
/// See IP_ADAPTER_ADDRESSES docs for more details
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OperStatus {
    IfOperStatusUp = 1,
    IfOperStatusDown = 2,
    IfOperStatusTesting = 3,
    IfOperStatusUnknown = 4,
    IfOperStatusDormant = 5,
    IfOperStatusNotPresent = 6,
    IfOperStatusLowerLayerDown = 7,
}

/// Represent an interface type
/// See IANA docs on iftype for more details
/// <https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib>
/// Note that we only support a subset of the IANA interface
/// types and in case the adapter has an unsupported type,
/// `IfType::Unsupported` is used. `IfType::Other`
/// is different from `IfType::Unsupported`, as the former
/// one is defined by the IANA itself.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum IfType {
    Other = 1,
    EthernetCsmacd = 6,
    Iso88025Tokenring = 9,
    Ppp = 23,
    SoftwareLoopback = 24,
    Atm = 37,
    Ieee80211 = 71,
    Tunnel = 131,
    Ieee1394 = 144,
    Unsupported,
    /// This enum may grow additional variants, so this makes sure clients
    /// don't count on exhaustive matching. (Otherwise, adding a new variant
    /// could break existing code.)
    #[doc(hidden)]
    __Nonexhaustive,
}

/// Represent an adapter.
#[derive(Debug)]
pub struct Adapter {
    adapter_name: String,
    ip_addresses: Vec<IpAddr>,
    prefixes: Vec<(IpAddr, u32)>,
    gateways: Vec<IpAddr>,
    dns_servers: Vec<IpAddr>,
    description: String,
    friendly_name: String,
    physical_address: Option<Vec<u8>>,
    receive_link_speed: u64,
    transmit_link_speed: u64,
    oper_status: OperStatus,
    if_type: IfType,
    ipv6_if_index: u32,
    ipv4_metric: u32,
    ipv6_metric: u32,
}

impl Adapter {
    /// Get the adapter's name
    pub fn adapter_name(&self) -> &str {
        &self.adapter_name
    }
    /// Get the adapter's ip addresses (unicast ip addresses)
    pub fn ip_addresses(&self) -> &[IpAddr] {
        &self.ip_addresses
    }
    /// Get the adapter's prefixes. Returns a list of tuples (IpAddr, u32),
    /// where first element is a subnet address, e.g. 192.168.1.0
    /// and second element is prefix length, e.g. 24
    pub fn prefixes(&self) -> &[(IpAddr, u32)] {
        &self.prefixes
    }
    /// Get the adapter's gateways
    pub fn gateways(&self) -> &[IpAddr] {
        &self.gateways
    }
    /// Get the adapter's dns servers (the preferred dns server is first)
    pub fn dns_servers(&self) -> &[IpAddr] {
        &self.dns_servers
    }
    /// Get the adapter's description
    pub fn description(&self) -> &str {
        &self.description
    }
    /// Get the adapter's friendly name
    pub fn friendly_name(&self) -> &str {
        &self.friendly_name
    }
    /// Get the adapter's physical (MAC) address
    pub fn physical_address(&self) -> Option<&[u8]> {
        self.physical_address.as_ref().map(std::vec::Vec::as_slice)
    }

    /// Get the adapter Recieve Link Speed (bits per second)
    pub fn receive_link_speed(&self) -> u64 {
        self.receive_link_speed
    }

    /// Get the Trasnmit Link Speed (bits per second)
    pub fn transmit_link_speed(&self) -> u64 {
        self.transmit_link_speed
    }

    /// Check if the adapter is up (OperStatus is IfOperStatusUp)
    pub fn oper_status(&self) -> OperStatus {
        self.oper_status
    }

    /// Get the interface type
    pub fn if_type(&self) -> IfType {
        self.if_type
    }

    /// Get the IPv6 interface index.
    ///
    /// The return value can be used as an IPv6 scope id for link-local
    /// addresses.
    pub fn ipv6_if_index(&self) -> u32 {
        self.ipv6_if_index
    }

    /// Returns the metric used to compute route preference for IPv4
    pub fn ipv4_metric(&self) -> u32 {
        self.ipv4_metric
    }
    /// Returns the metric used to compute route preference for IPv6
    pub fn ipv6_metric(&self) -> u32 {
        self.ipv6_metric
    }
}

/// Get all the network adapters on this machine.
pub fn get_adapters() -> Result<Vec<Adapter>> {
    unsafe {
        // Preallocate 16K per Microsoft recommendation, see Remarks section
        // https://docs.microsoft.com/en-us/windows/desktop/api/iphlpapi/nf-iphlpapi-getadaptersaddresses
        let mut buf_len: ULONG = 16384;
        let mut adapters_addresses_buffer = Vec::new();

        let mut result = ERROR_BUFFER_OVERFLOW;
        while result == ERROR_BUFFER_OVERFLOW {
            adapters_addresses_buffer.resize(buf_len as usize, 0);

            result = GetAdaptersAddresses(
                AF_UNSPEC as u32,
                0x0080 | 0x0010, //GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_PREFIX,
                std::ptr::null_mut(),
                adapters_addresses_buffer.as_mut_ptr() as PIP_ADAPTER_ADDRESSES,
                &mut buf_len as *mut ULONG,
            );
        }

        if result != ERROR_SUCCESS {
            return Err(Error {
                kind: ErrorKind::Os(result),
            });
        }

        let mut adapters = vec![];
        let mut adapter_addresses_ptr =
            adapters_addresses_buffer.as_mut_ptr() as PIP_ADAPTER_ADDRESSES;

        while !adapter_addresses_ptr.is_null() {
            adapters.push(get_adapter(adapter_addresses_ptr)?);
            adapter_addresses_ptr = adapter_addresses_ptr.read_unaligned().Next;
        }

        Ok(adapters)
    }
}

unsafe fn get_adapter(adapter_addresses_ptr: PIP_ADAPTER_ADDRESSES) -> Result<Adapter> {
    let adapter_addresses = adapter_addresses_ptr.read_unaligned();
    let adapter_name = CStr::from_ptr(adapter_addresses.AdapterName)
        .to_str()?
        .to_owned();
    let dns_servers = get_dns_servers(adapter_addresses.FirstDnsServerAddress)?;
    let gateways = get_gateways(adapter_addresses.FirstGatewayAddress)?;
    let prefixes = get_prefixes(adapter_addresses.FirstPrefix)?;
    let unicast_addresses = get_unicast_addresses(adapter_addresses.FirstUnicastAddress)?;
    let receive_link_speed: u64 = adapter_addresses.ReceiveLinkSpeed;
    let transmit_link_speed: u64 = adapter_addresses.TransmitLinkSpeed;
    let ipv4_metric = adapter_addresses.Ipv4Metric;
    let ipv6_metric = adapter_addresses.Ipv6Metric;
    let oper_status = match adapter_addresses.OperStatus {
        1 => OperStatus::IfOperStatusUp,
        2 => OperStatus::IfOperStatusDown,
        3 => OperStatus::IfOperStatusTesting,
        4 => OperStatus::IfOperStatusUnknown,
        5 => OperStatus::IfOperStatusDormant,
        6 => OperStatus::IfOperStatusNotPresent,
        7 => OperStatus::IfOperStatusLowerLayerDown,
        v => {
            panic!("unexpected OperStatus value: {}", v);
        }
    };
    let if_type = match adapter_addresses.IfType {
        1 => IfType::Other,
        6 => IfType::EthernetCsmacd,
        9 => IfType::Iso88025Tokenring,
        23 => IfType::Ppp,
        24 => IfType::SoftwareLoopback,
        37 => IfType::Atm,
        71 => IfType::Ieee80211,
        131 => IfType::Tunnel,
        144 => IfType::Ieee1394,
        _ => IfType::Unsupported,
    };
    let ipv6_if_index = adapter_addresses.Ipv6IfIndex;

    let description = WideCString::from_ptr_str(adapter_addresses.Description).to_string()?;
    let friendly_name = WideCString::from_ptr_str(adapter_addresses.FriendlyName).to_string()?;
    let physical_address = if adapter_addresses.PhysicalAddressLength == 0 {
        None
    } else {
        Some(
            adapter_addresses.PhysicalAddress[..adapter_addresses.PhysicalAddressLength as usize]
                .to_vec(),
        )
    };
    Ok(Adapter {
        adapter_name,
        ip_addresses: unicast_addresses,
        prefixes,
        gateways,
        dns_servers,
        description,
        friendly_name,
        physical_address,
        receive_link_speed,
        transmit_link_speed,
        oper_status,
        if_type,
        ipv6_if_index,
        ipv4_metric,
        ipv6_metric,
    })
}

unsafe fn socket_address_to_ipaddr(socket_address: &SOCKET_ADDRESS) -> IpAddr {
    let (_, sockaddr) = socket2::SockAddr::try_init(|storage, length| {
        let sockaddr_length = usize::try_from(socket_address.iSockaddrLength).unwrap();
        assert!(sockaddr_length <= std::mem::size_of_val(&storage.read_unaligned()));
        let dst: *mut u8 = storage.cast();
        let src: *const u8 = socket_address.lpSockaddr.cast();
        dst.copy_from_nonoverlapping(src, sockaddr_length);
        std::ptr::write_unaligned(length, socket_address.iSockaddrLength);
        Ok(())
    })
    .unwrap();

    sockaddr.as_socket().map(|s| s.ip()).unwrap()
}

unsafe fn get_dns_servers(
    mut dns_server_ptr: PIP_ADAPTER_DNS_SERVER_ADDRESS_XP,
) -> Result<Vec<IpAddr>> {
    let mut dns_servers = vec![];

    while !dns_server_ptr.is_null() {
        let dns_server = dns_server_ptr.read_unaligned();
        let ipaddr = socket_address_to_ipaddr(&dns_server.Address);
        dns_servers.push(ipaddr);

        dns_server_ptr = dns_server.Next;
    }

    Ok(dns_servers)
}

unsafe fn get_gateways(mut gateway_ptr: PIP_ADAPTER_GATEWAY_ADDRESS_LH) -> Result<Vec<IpAddr>> {
    let mut gateways = vec![];

    while !gateway_ptr.is_null() {
        let gateway = gateway_ptr.read_unaligned();
        let ipaddr = socket_address_to_ipaddr(&gateway.Address);
        gateways.push(ipaddr);

        gateway_ptr = gateway.Next;
    }

    Ok(gateways)
}

unsafe fn get_unicast_addresses(
    mut unicast_addresses_ptr: PIP_ADAPTER_UNICAST_ADDRESS_LH,
) -> Result<Vec<IpAddr>> {
    let mut unicast_addresses = vec![];

    while !unicast_addresses_ptr.is_null() {
        let unicast_address = unicast_addresses_ptr.read_unaligned();
        let ipaddr = socket_address_to_ipaddr(&unicast_address.Address);
        unicast_addresses.push(ipaddr);

        unicast_addresses_ptr = unicast_address.Next;
    }

    Ok(unicast_addresses)
}

unsafe fn get_prefixes(mut prefixes_ptr: PIP_ADAPTER_PREFIX_XP) -> Result<Vec<(IpAddr, u32)>> {
    let mut prefixes = vec![];

    while !prefixes_ptr.is_null() {
        let prefix = prefixes_ptr.read_unaligned();
        let ipaddr = socket_address_to_ipaddr(&prefix.Address);
        prefixes.push((ipaddr, prefix.PrefixLength));

        prefixes_ptr = prefix.Next;
    }

    Ok(prefixes)
}