1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "platform/impl/network_interface.h"
6
7 #include <winsock2.h>
8 #include <ws2tcpip.h>
9 #include <iphlpapi.h>
10
11 #include "util/osp_logging.h"
12
13 namespace openscreen {
14
GetAllInterfaces()15 std::vector<InterfaceInfo> GetAllInterfaces() {
16 constexpr size_t INITIAL_BUFFER_SIZE = 15000;
17 ULONG outbuflen = INITIAL_BUFFER_SIZE;
18 std::vector<unsigned char> charbuf(INITIAL_BUFFER_SIZE);
19 DWORD ret = NO_ERROR;
20 constexpr int MAX_RETRIES = 5;
21
22 for (int i = 0; i < MAX_RETRIES; ++i) {
23 // TODO: This does not include the loopback interface. Decide if we need it.
24 ret = GetAdaptersAddresses(AF_UNSPEC /* get both v4/v6 addrs */,
25 GAA_FLAG_INCLUDE_PREFIX,
26 NULL,
27 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(charbuf.data()),
28 &outbuflen);
29 if (ret == ERROR_BUFFER_OVERFLOW) {
30 charbuf.resize(outbuflen);
31 continue;
32 }
33 break;
34 }
35
36 if (ret != NO_ERROR) {
37 OSP_DVLOG << "GetAdapterAddresses failed err=" << ret;
38 return std::vector<InterfaceInfo>();
39 }
40
41 std::vector<InterfaceInfo> infos;
42 auto pcurraddrs = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(charbuf.data());
43 while (pcurraddrs != nullptr) {
44 // TODO: return the interfaces
45 OSP_DVLOG << "\tIfIndex=" << pcurraddrs->IfIndex;
46 OSP_DVLOG << "\tAdapter name=" << pcurraddrs->AdapterName;
47
48 // Ignore interfaces that are down
49 if (pcurraddrs->OperStatus == IfOperStatusDown) {
50 pcurraddrs = pcurraddrs->Next;
51 continue;
52 }
53
54 infos.emplace_back();
55 InterfaceInfo& info = infos.back();
56
57 info.index = pcurraddrs->IfIndex;
58 std::memcpy(info.hardware_address.data(), pcurraddrs->PhysicalAddress,
59 std::min((unsigned long)sizeof(info.hardware_address),
60 pcurraddrs->PhysicalAddressLength));
61 info.name = pcurraddrs->AdapterName;
62
63 // Determine the interface type
64 switch (pcurraddrs->IfType) {
65 case IF_TYPE_ETHERNET_CSMACD:
66 info.type = InterfaceInfo::Type::kEthernet;
67 break;
68 case IF_TYPE_IEEE80211:
69 info.type = InterfaceInfo::Type::kWifi;
70 break;
71 case IF_TYPE_SOFTWARE_LOOPBACK:
72 info.type = InterfaceInfo::Type::kLoopback;
73 break;
74 default:
75 info.type = InterfaceInfo::Type::kOther;
76 break;
77 }
78
79 auto punicast = pcurraddrs->FirstUnicastAddress;
80 if (punicast != nullptr) {
81 for (int i = 0; punicast != nullptr; ++i) {
82 if (punicast->Address.lpSockaddr->sa_family == AF_INET) {
83 sockaddr_in* sa_in = (sockaddr_in*)punicast->Address.lpSockaddr;
84 char buff[100];
85 DWORD bufflen = 100;
86 OSP_DVLOG << "\tIPV4:" << inet_ntop(AF_INET, &(sa_in->sin_addr), buff, bufflen);
87 OSP_DVLOG << "\t prefixsize=" << (unsigned int)punicast->OnLinkPrefixLength;
88 IPAddress ip(IPAddress::Version::kV4,
89 reinterpret_cast<uint8_t*>(&(sa_in->sin_addr.s_addr)));
90 info.addresses.emplace_back(ip, punicast->OnLinkPrefixLength);
91 } else if (punicast->Address.lpSockaddr->sa_family == AF_INET6) {
92 sockaddr_in6* sa_in6 = (sockaddr_in6*)punicast->Address.lpSockaddr;
93 char buff[100];
94 DWORD bufflen = 100;
95 OSP_DVLOG << "\tIPV6:" << inet_ntop(AF_INET6, &(sa_in6->sin6_addr), buff, bufflen);
96 OSP_DVLOG << "\t prefixsize=" << (unsigned int)punicast->OnLinkPrefixLength;
97 IPAddress ip(IPAddress::Version::kV6,
98 reinterpret_cast<uint8_t*>(&(sa_in6->sin6_addr.s6_addr)));
99 info.addresses.emplace_back(ip, punicast->OnLinkPrefixLength);
100 } else {
101 OSP_DVLOG << "\tUNSPEC";
102 }
103 punicast = punicast->Next;
104 }
105 }
106 OSP_DVLOG << "\tIfType=" << pcurraddrs->IfType;
107 // TODO: Convert the wide strings to the proper narrow encoding (e.g.
108 // UTF-8) rather than print the addresses.
109 OSP_DVLOG << "\tDescription=" << static_cast<void*>(pcurraddrs->Description);
110 OSP_DVLOG << "\tFriendlyName=" << static_cast<void*>(pcurraddrs->FriendlyName);
111 pcurraddrs = pcurraddrs->Next;
112 }
113 return infos;
114 }
115
116 } // namespace openscreen
117