1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 //#define QNETWORKINTERFACE_DEBUG
43
44 #include "qnetworkinterface.h"
45 #include "qnetworkinterface_p.h"
46 #include <private/qcore_symbian_p.h>
47
48 #ifndef QT_NO_NETWORKINTERFACE
49
50 #include <in_sock.h>
51 #include <in_iface.h>
52 #include <es_sock.h>
53
54 QT_BEGIN_NAMESPACE
55
56
convertFlags(const TSoInetInterfaceInfo & aInfo)57 static QNetworkInterface::InterfaceFlags convertFlags(const TSoInetInterfaceInfo& aInfo)
58 {
59 QNetworkInterface::InterfaceFlags flags = 0;
60 flags |= (aInfo.iState == EIfUp) ? QNetworkInterface::IsUp : QNetworkInterface::InterfaceFlag(0);
61 // We do not have separate flag for running in Symbian OS
62 flags |= (aInfo.iState == EIfUp) ? QNetworkInterface::IsRunning : QNetworkInterface::InterfaceFlag(0);
63 flags |= (aInfo.iFeatures & KIfCanBroadcast) ? QNetworkInterface::CanBroadcast : QNetworkInterface::InterfaceFlag(0);
64 flags |= (aInfo.iFeatures & KIfIsLoopback) ? QNetworkInterface::IsLoopBack : QNetworkInterface::InterfaceFlag(0);
65 flags |= (aInfo.iFeatures & KIfIsPointToPoint) ? QNetworkInterface::IsPointToPoint : QNetworkInterface::InterfaceFlag(0);
66 flags |= (aInfo.iFeatures & KIfCanMulticast) ? QNetworkInterface::CanMulticast : QNetworkInterface::InterfaceFlag(0);
67 return flags;
68 }
69
qt_QHostAddressFromTInetAddr(const TInetAddr & addr)70 QHostAddress qt_QHostAddressFromTInetAddr(const TInetAddr& addr)
71 {
72 if (addr.IsV4Mapped() || addr.Family() == KAfInet) {
73 //convert v4 host address
74 return QHostAddress(addr.Address());
75 } else {
76 //convert v6 host address
77 return QHostAddress((quint8 *)(addr.Ip6Address().u.iAddr8));
78 }
79 }
80
interfaceListing()81 static QList<QNetworkInterfacePrivate *> interfaceListing()
82 {
83 TInt err(KErrNone);
84 QList<QNetworkInterfacePrivate *> interfaces;
85 QList<QHostAddress> addressesWithEstimatedNetmasks;
86
87 // Open dummy socket for interface queries
88 RSocket socket;
89 err = socket.Open(qt_symbianGetSocketServer(), _L("udp"));
90 if (err) {
91 return interfaces;
92 }
93
94 // Ask socket to start enumerating interfaces
95 err = socket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl);
96 if (err) {
97 socket.Close();
98 return interfaces;
99 }
100
101 int ifindex = 0;
102 TPckgBuf<TSoInetInterfaceInfo> infoPckg;
103 TSoInetInterfaceInfo &info = infoPckg();
104 while (socket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, infoPckg) == KErrNone) {
105 if (info.iName != KNullDesC) {
106 TName address;
107 QNetworkAddressEntry entry;
108 QNetworkInterfacePrivate *iface = 0;
109
110 iface = new QNetworkInterfacePrivate;
111 iface->index = ifindex++;
112 interfaces << iface;
113 iface->name = qt_TDesC2QString(info.iName);
114 iface->flags = convertFlags(info);
115
116 if (/*info.iFeatures&KIfHasHardwareAddr &&*/ info.iHwAddr.Family() != KAFUnspec) {
117 for (TInt i = sizeof(SSockAddr); i < sizeof(SSockAddr) + info.iHwAddr.GetUserLen(); i++) {
118 address.AppendNumFixedWidth(info.iHwAddr[i], EHex, 2);
119 if ((i + 1) < sizeof(SSockAddr) + info.iHwAddr.GetUserLen())
120 address.Append(_L(":"));
121 }
122 address.UpperCase();
123 iface->hardwareAddress = qt_TDesC2QString(address);
124 }
125
126 // Get the address of the interface
127 entry.setIp(qt_QHostAddressFromTInetAddr(info.iAddress));
128
129 #if defined(QNETWORKINTERFACE_DEBUG)
130 qDebug() << "address is" << info.iAddress.Family() << entry.ip();
131 qDebug() << "netmask is" << info.iNetMask.Family() << qt_QHostAddressFromTInetAddr( info.iNetMask );
132 #endif
133
134 // Get the interface netmask
135 if (info.iNetMask.IsUnspecified()) {
136 // For some reason netmask is always 0.0.0.0 for IPv4 interfaces
137 // and loopback interfaces (which we statically know)
138 if (info.iAddress.IsV4Mapped()) {
139 if (info.iFeatures & KIfIsLoopback) {
140 entry.setPrefixLength(32);
141 } else {
142 // Workaround: Let Symbian determine netmask based on IP address class (IPv4 only API)
143 TInetAddr netmask;
144 netmask.NetMask(info.iAddress);
145 entry.setNetmask(QHostAddress(netmask.Address())); //binary convert v4 address
146 addressesWithEstimatedNetmasks << entry.ip();
147 #if defined(QNETWORKINTERFACE_DEBUG)
148 qDebug() << "address class determined netmask" << entry.netmask();
149 #endif
150 }
151 } else {
152 // For IPv6 interfaces
153 if (info.iFeatures & KIfIsLoopback) {
154 entry.setPrefixLength(128);
155 } else if (info.iNetMask.IsUnspecified()) {
156 //Don't see this error for IPv6, but try to handle it if it happens
157 entry.setPrefixLength(64); //most common
158 #if defined(QNETWORKINTERFACE_DEBUG)
159 qDebug() << "total guess netmask" << entry.netmask();
160 #endif
161 addressesWithEstimatedNetmasks << entry.ip();
162 }
163 }
164 } else {
165 //Expected code path for IPv6 non loopback interfaces (IPv4 could come here if symbian is fixed)
166 entry.setNetmask(qt_QHostAddressFromTInetAddr(info.iNetMask));
167 #if defined(QNETWORKINTERFACE_DEBUG)
168 qDebug() << "reported netmask" << entry.netmask();
169 #endif
170 }
171
172 // broadcast address is determined from the netmask in postProcess()
173
174 // Add new entry to interface address entries
175 iface->addressEntries << entry;
176
177 #if defined(QNETWORKINTERFACE_DEBUG)
178 qDebug("\n Found network interface %s, interface flags:\n\
179 IsUp = %d, IsRunning = %d, CanBroadcast = %d,\n\
180 IsLoopBack = %d, IsPointToPoint = %d, CanMulticast = %d, \n\
181 ip = %s, netmask = %s, broadcast = %s,\n\
182 hwaddress = %s",
183 iface->name.toLatin1().constData(),
184 iface->flags & QNetworkInterface::IsUp, iface->flags & QNetworkInterface::IsRunning, iface->flags & QNetworkInterface::CanBroadcast,
185 iface->flags & QNetworkInterface::IsLoopBack, iface->flags & QNetworkInterface::IsPointToPoint, iface->flags & QNetworkInterface::CanMulticast,
186 entry.ip().toString().toLatin1().constData(), entry.netmask().toString().toLatin1().constData(), entry.broadcast().toString().toLatin1().constData(),
187 iface->hardwareAddress.toLatin1().constData());
188 #endif
189 }
190 }
191
192 // if we didn't have to guess any netmasks, then we're done.
193 if (addressesWithEstimatedNetmasks.isEmpty()) {
194 socket.Close();
195 return interfaces;
196 }
197
198 // we will try to use routing info to detect more precisely
199 // estimated netmasks and then ::postProcess() should calculate
200 // broadcast addresses
201
202 // use dummy socket to start enumerating routes
203 err = socket.SetOpt(KSoInetEnumRoutes, KSolInetRtCtrl);
204 if (err) {
205 socket.Close();
206 // return what we have
207 // up to this moment
208 return interfaces;
209 }
210
211 TSoInetRouteInfo routeInfo;
212 TPckg<TSoInetRouteInfo> routeInfoPkg(routeInfo);
213 while (socket.GetOpt(KSoInetNextRoute, KSolInetRtCtrl, routeInfoPkg) == KErrNone) {
214 // get interface address
215 QHostAddress ifAddr(qt_QHostAddressFromTInetAddr(routeInfo.iIfAddr));
216 if (ifAddr.isNull())
217 continue;
218 if (!addressesWithEstimatedNetmasks.contains(ifAddr)) {
219 #if defined(QNETWORKINTERFACE_DEBUG)
220 qDebug() << "skipping route from" << ifAddr << "because it wasn't an estimated netmask";
221 #endif
222 continue;
223 }
224
225 QHostAddress destination(qt_QHostAddressFromTInetAddr(routeInfo.iDstAddr));
226 #if defined(QNETWORKINTERFACE_DEBUG)
227 qDebug() << "route from" << ifAddr << "to" << destination;
228 #endif
229 if (destination.isNull() || destination != ifAddr)
230 continue;
231
232 // search interfaces
233 for (int ifindex = 0; ifindex < interfaces.size(); ++ifindex) {
234 QNetworkInterfacePrivate *iface = interfaces.at(ifindex);
235 for (int eindex = 0; eindex < iface->addressEntries.size(); ++eindex) {
236 QNetworkAddressEntry entry = iface->addressEntries.at(eindex);
237 if (entry.ip() != ifAddr) {
238 continue;
239 } else if (!routeInfo.iNetMask.IsUnspecified()) {
240 //the route may also return 0.0.0.0 netmask, in which case don't use it.
241 QHostAddress netmask(qt_QHostAddressFromTInetAddr(routeInfo.iNetMask));
242 entry.setNetmask(netmask);
243 #if defined(QNETWORKINTERFACE_DEBUG)
244 qDebug() << " - route netmask" << routeInfo.iNetMask.Family() << netmask << " (using route determined netmask)";
245 #endif
246 iface->addressEntries.replace(eindex, entry);
247 }
248 }
249 }
250 }
251
252 socket.Close();
253
254 return interfaces;
255 }
256
scan()257 QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
258 {
259 return interfaceListing();
260 }
261
262 QT_END_NAMESPACE
263
264 #endif // QT_NO_NETWORKINTERFACE
265