summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/socket/qabstractsocket.cpp7
-rw-r--r--src/network/socket/qabstractsocketengine_p.h3
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp26
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp61
4 files changed, 93 insertions, 4 deletions
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index ce3626304c8..f2474ad7e97 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -432,6 +432,7 @@
#include "qabstractsocket.h"
#include "qabstractsocket_p.h"
+#include "qnetworkinterface.h"
#include "private/qhostinfo_p.h"
@@ -1505,8 +1506,6 @@ bool QAbstractSocketPrivate::bind(const QHostAddress &address, quint16 port, QAb
{
Q_Q(QAbstractSocket);
- Q_UNUSED(iface); // will be used in a follow-up patch
-
// now check if the socket engine is initialized and to the right type
if (!socketEngine || !socketEngine->isValid()) {
QHostAddress nullAddress;
@@ -1538,6 +1537,10 @@ bool QAbstractSocketPrivate::bind(const QHostAddress &address, quint16 port, QAb
socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0);
#endif
}
+#if QT_CONFIG(networkinterface)
+ if (iface && iface->isValid())
+ socketEngine->setOption(QAbstractSocketEngine::BindInterfaceIndex, iface->index());
+#endif
bool result = socketEngine->bind(address, port);
cachedSocketDescriptor = socketEngine->socketDescriptor();
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
index b9cdf38b5e0..48ebfed71a2 100644
--- a/src/network/socket/qabstractsocketengine_p.h
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -75,7 +75,8 @@ public:
ReceivePacketInformation,
ReceiveHopLimit,
MaxStreamsSocketOption,
- PathMtuInformation
+ PathMtuInformation,
+ BindInterfaceIndex,
};
enum PacketHeaderOption {
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index a9bc2d92b44..b5fbb5c2e00 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -11,6 +11,7 @@
#include "qhostaddress.h"
#include "qvarlengtharray.h"
#include "qnetworkinterface.h"
+#include "qnetworkinterface_p.h"
#include "qendian.h"
#ifdef Q_OS_WASM
#include <private/qeventdispatcher_wasm_p.h>
@@ -82,6 +83,9 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
case QNativeSocketEngine::MaxStreamsSocketOption:
Q_UNREACHABLE();
+ case QNativeSocketEngine::BindInterfaceIndex:
+ Q_UNREACHABLE(); // handled directly in setOption()
+
case QNativeSocketEngine::BroadcastSocketOption:
n = SO_BROADCAST;
break;
@@ -373,7 +377,27 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
#endif
return false;
}
-
+ case QNativeSocketEngine::BindInterfaceIndex: {
+#if defined(SO_BINDTOIFINDEX) // seen on Linux
+ return ::setsockopt(socketDescriptor, SOL_SOCKET, SO_BINDTOIFINDEX,
+ &v, sizeof(v)) == 0;
+#elif defined(IPV6_BOUND_IF) && defined(IP_BOUND_IF) // seen on Darwin
+ // note: on Darwin, this only limits sending the data, not receiving it
+ if (socketProtocol == QAbstractSocket::IPv6Protocol
+ || socketProtocol == QAbstractSocket::AnyIPProtocol) {
+ return ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_BOUND_IF, &v, sizeof(v)) == 0;
+ } else {
+ return ::setsockopt(socketDescriptor, IPPROTO_IP, IP_BOUND_IF, &v, sizeof(v)) == 0;
+ }
+#elif defined(SO_BINDTODEVICE) && QT_CONFIG(networkinterface)
+ // need to convert to interface name
+ const QByteArray name = QNetworkInterfaceManager::interfaceNameFromIndex(v).toLatin1();
+ return ::setsockopt(socketDescriptor, SOL_SOCKET, SO_BINDTODEVICE,
+ name.data(), socklen_t(name.size())) == 0;
+#else
+ return false;
+#endif
+ }
default:
break;
}
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 9933ea06745..598467ef629 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -153,6 +153,9 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
case QNativeSocketEngine::MaxStreamsSocketOption:
Q_UNREACHABLE();
+ case QNativeSocketEngine::BindInterfaceIndex:
+ Q_UNREACHABLE(); // handled directly in setOption()
+
case QNativeSocketEngine::ReceiveBufferSocketOption:
n = SO_RCVBUF;
break;
@@ -441,6 +444,64 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
return true;
}
+ case QNativeSocketEngine::BindInterfaceIndex: {
+ int ret = 0;
+ if (socketProtocol == QAbstractSocket::IPv6Protocol
+ || socketProtocol == QAbstractSocket::AnyIPProtocol) {
+ // IPv6 - uses host byte order
+ // Bind outgoing datagrams to the interface
+ if (!ret) {
+ ret = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_UNICAST_IF,
+ reinterpret_cast<char *>(&v), sizeof(v));
+ }
+ if (!ret) {
+ ret = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ reinterpret_cast<char *>(&v), sizeof(v));
+ }
+ // Bind incoming datagrams to the interface
+ if (!ret) {
+ const int enable = 1;
+ ret = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_IFLIST,
+ reinterpret_cast<const char *>(&enable), sizeof(enable));
+ if (!ret) {
+ ret = ::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_ADD_IFLIST,
+ reinterpret_cast<char *>(&v), sizeof(v));
+ }
+ }
+ }
+ bool result = !ret;
+ if (result) {
+ // Try to set the IPv4 options unconditionally, but ignore
+ // the result if the protocol is not IPv4-only
+
+ // IPv4 - uses network byte order
+ int netIdx = htonl(v);
+ // Bind outgoing datagrams to the interface
+ if (!ret) {
+ ret = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_UNICAST_IF,
+ reinterpret_cast<char *>(&netIdx), sizeof(netIdx));
+ }
+ if (!ret) {
+ ret = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF,
+ reinterpret_cast<char *>(&netIdx), sizeof(netIdx));
+ }
+ // Bind incoming datagrams to the interface
+ if (!ret) {
+ const int enable = 1;
+ ret = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_IFLIST,
+ reinterpret_cast<const char *>(&enable), sizeof(enable));
+ if (!ret) {
+ // uses host byte order here
+ ret = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_ADD_IFLIST,
+ reinterpret_cast<char *>(&v), sizeof(v));
+ }
+ }
+ if (socketProtocol == QAbstractSocket::IPv4Protocol)
+ result = !ret;
+ }
+ return result;
+ }
+
default:
break;
}