1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Copyright (C) 2016 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #define WIN32_LEAN_AND_MEAN 1
42 
43 #include "qnetworkinterface.h"
44 #include "qnetworkinterface_p.h"
45 
46 #ifndef QT_NO_NETWORKINTERFACE
47 
48 #include <qhostinfo.h>
49 #include <qhash.h>
50 #include <qurl.h>
51 
52 // Since we need to include winsock2.h, we need to define WIN32_LEAN_AND_MEAN
53 // (above) so windows.h won't include winsock.h.
54 // In addition, we need to include winsock2.h before iphlpapi.h and we need
55 // to include ws2ipdef.h to work around an MinGW-w64 bug
56 // (http://sourceforge.net/p/mingw-w64/mailman/message/32935366/)
57 #include <winsock2.h>
58 #include <ws2ipdef.h>
59 #include <wincrypt.h>
60 #include <iphlpapi.h>
61 #include <ws2tcpip.h>
62 
63 #include <qt_windows.h>
64 
65 // In case these aren't defined
66 #define IF_TYPE_IEEE80216_WMAN  237
67 #define IF_TYPE_IEEE802154      259
68 
69 QT_BEGIN_NAMESPACE
70 
addressFromSockaddr(sockaddr * sa)71 static QHostAddress addressFromSockaddr(sockaddr *sa)
72 {
73     QHostAddress address;
74     if (!sa)
75         return address;
76 
77     if (sa->sa_family == AF_INET) {
78         address.setAddress(htonl(reinterpret_cast<const sockaddr_in *>(sa)->sin_addr.s_addr));
79     } else if (sa->sa_family == AF_INET6) {
80         auto sai6 = reinterpret_cast<const sockaddr_in6 *>(sa);
81         address.setAddress(sai6->sin6_addr.s6_addr);
82         if (sai6->sin6_scope_id)
83             address.setScopeId(QNetworkInterfaceManager::interfaceNameFromIndex(sai6->sin6_scope_id));
84     } else {
85         qWarning("Got unknown socket family %d", sa->sa_family);
86     }
87     return address;
88 
89 }
90 
interfaceIndexFromName(const QString & name)91 uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
92 {
93     NET_IFINDEX id;
94     NET_LUID luid;
95     if (ConvertInterfaceNameToLuidW(reinterpret_cast<const wchar_t *>(name.constData()), &luid) == NO_ERROR
96             && ConvertInterfaceLuidToIndex(&luid, &id) == NO_ERROR)
97         return uint(id);
98     return 0;
99 }
100 
interfaceNameFromIndex(uint index)101 QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
102 {
103     NET_LUID luid;
104     if (ConvertInterfaceIndexToLuid(index, &luid) == NO_ERROR) {
105         WCHAR buf[IF_MAX_STRING_SIZE + 1];
106         if (ConvertInterfaceLuidToNameW(&luid, buf, sizeof(buf)/sizeof(buf[0])) == NO_ERROR)
107             return QString::fromWCharArray(buf);
108     }
109     return QString::number(index);
110 }
111 
interfaceListing()112 static QList<QNetworkInterfacePrivate *> interfaceListing()
113 {
114     QList<QNetworkInterfacePrivate *> interfaces;
115     IP_ADAPTER_ADDRESSES staticBuf[2]; // 2 is arbitrary
116     PIP_ADAPTER_ADDRESSES pAdapter = staticBuf;
117     ULONG bufSize = sizeof staticBuf;
118 
119     ULONG flags = GAA_FLAG_INCLUDE_PREFIX |
120                   GAA_FLAG_SKIP_DNS_SERVER |
121                   GAA_FLAG_SKIP_MULTICAST;
122     ULONG retval = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize);
123     if (retval == ERROR_BUFFER_OVERFLOW) {
124         // need more memory
125         pAdapter = reinterpret_cast<IP_ADAPTER_ADDRESSES *>(malloc(bufSize));
126         if (!pAdapter)
127             return interfaces;
128         // try again
129         if (GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize) != ERROR_SUCCESS) {
130             free(pAdapter);
131             return interfaces;
132         }
133     } else if (retval != ERROR_SUCCESS) {
134         // error
135         return interfaces;
136     }
137 
138     // iterate over the list and add the entries to our listing
139     for (PIP_ADAPTER_ADDRESSES ptr = pAdapter; ptr; ptr = ptr->Next) {
140         // the structure grows over time, so let's make sure the fields
141         // introduced in Windows Vista are present (Luid is the furthest
142         // field we access from IP_ADAPTER_ADDRESSES_LH)
143         Q_ASSERT(ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, Luid));
144         Q_ASSERT(ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, Ipv6IfIndex));
145 
146         QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
147         interfaces << iface;
148 
149         iface->index = 0;
150         if (ptr->Ipv6IfIndex != 0)
151             iface->index = ptr->Ipv6IfIndex;
152         else if (ptr->IfIndex != 0)
153             iface->index = ptr->IfIndex;
154 
155         iface->mtu = qMin<qint64>(ptr->Mtu, INT_MAX);
156         iface->flags = QNetworkInterface::CanBroadcast;
157         if (ptr->OperStatus == IfOperStatusUp)
158             iface->flags |= QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
159         if ((ptr->Flags & IP_ADAPTER_NO_MULTICAST) == 0)
160             iface->flags |= QNetworkInterface::CanMulticast;
161         if (ptr->IfType == IF_TYPE_PPP)
162             iface->flags |= QNetworkInterface::IsPointToPoint;
163 
164         switch (ptr->IfType) {
165         case IF_TYPE_ETHERNET_CSMACD:
166             iface->type = QNetworkInterface::Ethernet;
167             break;
168 
169         case IF_TYPE_FDDI:
170             iface->type = QNetworkInterface::Fddi;
171             break;
172 
173         case IF_TYPE_PPP:
174             iface->type = QNetworkInterface::Ppp;
175             break;
176 
177         case IF_TYPE_SLIP:
178             iface->type = QNetworkInterface::Slip;
179             break;
180 
181         case IF_TYPE_SOFTWARE_LOOPBACK:
182             iface->type = QNetworkInterface::Loopback;
183             iface->flags |= QNetworkInterface::IsLoopBack;
184             break;
185 
186         case IF_TYPE_IEEE80211:
187             iface->type = QNetworkInterface::Ieee80211;
188             break;
189 
190         case IF_TYPE_IEEE1394:
191             iface->type = QNetworkInterface::Ieee1394;
192             break;
193 
194         case IF_TYPE_IEEE80216_WMAN:
195             iface->type = QNetworkInterface::Ieee80216;
196             break;
197 
198         case IF_TYPE_IEEE802154:
199             iface->type = QNetworkInterface::Ieee802154;
200             break;
201         }
202 
203         // use ConvertInterfaceLuidToNameW because that returns a friendlier name, though not
204         // as "friendly" as FriendlyName below
205         WCHAR buf[IF_MAX_STRING_SIZE + 1];
206         if (ConvertInterfaceLuidToNameW(&ptr->Luid, buf, sizeof(buf)/sizeof(buf[0])) == NO_ERROR)
207             iface->name = QString::fromWCharArray(buf);
208         if (iface->name.isEmpty())
209             iface->name = QString::fromLocal8Bit(ptr->AdapterName);
210 
211         iface->friendlyName = QString::fromWCharArray(ptr->FriendlyName);
212         if (ptr->PhysicalAddressLength)
213             iface->hardwareAddress = iface->makeHwAddress(ptr->PhysicalAddressLength,
214                                                           ptr->PhysicalAddress);
215 
216         // parse the IP (unicast) addresses
217         for (PIP_ADAPTER_UNICAST_ADDRESS addr = ptr->FirstUnicastAddress; addr; addr = addr->Next) {
218             Q_ASSERT(addr->Length >= offsetof(IP_ADAPTER_UNICAST_ADDRESS, OnLinkPrefixLength));
219 
220             // skip addresses in invalid state
221             if (addr->DadState == IpDadStateInvalid)
222                 continue;
223 
224             QNetworkAddressEntry entry;
225             entry.setIp(addressFromSockaddr(addr->Address.lpSockaddr));
226             entry.setPrefixLength(addr->OnLinkPrefixLength);
227 
228             auto toDeadline = [](ULONG lifetime) -> QDeadlineTimer {
229                 if (lifetime == 0xffffffffUL)
230                     return QDeadlineTimer::Forever;
231                 return QDeadlineTimer(lifetime * 1000);
232             };
233             entry.setAddressLifetime(toDeadline(addr->ValidLifetime), toDeadline(addr->PreferredLifetime));
234             entry.setDnsEligibility(addr->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE ?
235                                         QNetworkAddressEntry::DnsEligible :
236                                         QNetworkAddressEntry::DnsIneligible);
237 
238             iface->addressEntries << entry;
239         }
240     }
241 
242     if (pAdapter != staticBuf)
243         free(pAdapter);
244 
245     return interfaces;
246 }
247 
scan()248 QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
249 {
250     return interfaceListing();
251 }
252 
localDomainName()253 QString QHostInfo::localDomainName()
254 {
255     FIXED_INFO info, *pinfo;
256     ULONG bufSize = sizeof info;
257     pinfo = &info;
258     if (GetNetworkParams(pinfo, &bufSize) == ERROR_BUFFER_OVERFLOW) {
259         pinfo = reinterpret_cast<FIXED_INFO *>(malloc(bufSize));
260         if (!pinfo)
261             return QString();
262         // try again
263         if (GetNetworkParams(pinfo, &bufSize) != ERROR_SUCCESS) {
264             free(pinfo);
265             return QString();   // error
266         }
267     }
268 
269     QString domainName = QUrl::fromAce(pinfo->DomainName);
270 
271     if (pinfo != &info)
272         free(pinfo);
273 
274     return domainName;
275 }
276 
277 QT_END_NAMESPACE
278 
279 #endif // QT_NO_NETWORKINTERFACE
280