summaryrefslogtreecommitdiffstats
path: root/src/network/socket
diff options
context:
space:
mode:
authorBradley T. Hughes <bradley.hughes@nokia.com>2010-05-05 14:20:07 (GMT)
committerBradley T. Hughes <bradley.hughes@nokia.com>2010-09-01 12:24:37 (GMT)
commit3c35b0607e541008daaf53221da4e727015b5441 (patch)
tree552b064253cec07c132637b77938324782b798cd /src/network/socket
parent2abb3642bce6e4b32f44c3724dc8fcc9acf9976c (diff)
downloadQt-3c35b0607e541008daaf53221da4e727015b5441.zip
Qt-3c35b0607e541008daaf53221da4e727015b5441.tar.gz
Qt-3c35b0607e541008daaf53221da4e727015b5441.tar.bz2
Implement multicast for the native socket engine on UNIX
According to the Stevens book, the socket must be in the bound state to be able to join the group, so we check for the same condition. We support ASM and SSM (for IPv4). Windows implementation TBD.
Diffstat (limited to 'src/network/socket')
-rw-r--r--src/network/socket/qnativesocketengine.cpp28
-rw-r--r--src/network/socket/qnativesocketengine_p.h13
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp119
3 files changed, 160 insertions, 0 deletions
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp
index a169ca0..d4092b6 100644
--- a/src/network/socket/qnativesocketengine.cpp
+++ b/src/network/socket/qnativesocketengine.cpp
@@ -647,6 +647,34 @@ int QNativeSocketEngine::accept()
}
/*!
+ \since 4.8
+*/
+bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false);
+ Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false);
+ return d->nativeJoinMulticastGroup(groupAddress, sourceAddress, interface);
+}
+
+/*!
+ \since 4.8
+*/
+bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false);
+ Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false);
+ return d->nativeLeaveMulticastGroup(groupAddress, sourceAddress, interface);
+}
+
+/*!
Returns the number of bytes that are currently available for
reading. On error, -1 is returned.
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index eca16f6..7ec1166 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -123,6 +123,13 @@ public:
int accept();
void close();
+ bool joinMulticastGroup(const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface);
+ bool leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface);
+
qint64 bytesAvailable() const;
qint64 read(char *data, qint64 maxlen);
@@ -237,6 +244,12 @@ public:
bool nativeBind(const QHostAddress &address, quint16 port);
bool nativeListen(int backlog);
int nativeAccept();
+ bool nativeJoinMulticastGroup(const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface);
+ bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface);
qint64 nativeBytesAvailable() const;
bool nativeHasPendingDatagrams() const;
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index a9f6921..f10c692 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -247,6 +247,9 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
case QNativeSocketEngine::KeepAliveOption:
n = SO_KEEPALIVE;
break;
+ case QNativeSocketEngine::MulticastLoopback:
+ n = IP_MULTICAST_LOOP;
+ break;
}
int v = -1;
@@ -330,6 +333,9 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
case QNativeSocketEngine::KeepAliveOption:
n = SO_KEEPALIVE;
break;
+ case QNativeSocketEngine::MulticastLoopback:
+ n = IP_MULTICAST_LOOP;
+ break;
}
return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0;
@@ -579,6 +585,119 @@ int QNativeSocketEnginePrivate::nativeAccept()
return acceptedDescriptor;
}
+static bool doMulticast(QNativeSocketEnginePrivate *d,
+ int howAsm6,
+ int howAsm4,
+ int howSsm4,
+ const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface)
+{
+ int sockOpt = 0;
+ void *sockArg;
+ int sockArgSize;
+
+ ip_mreq asm4;
+ ip_mreq_source ssm4;
+#ifndef QT_NO_IPV6
+ ipv6_mreq asm6;
+
+ if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ sockOpt = howAsm6;
+ sockArg = &asm6;
+ sockArgSize = sizeof(asm6);
+ memset(&asm6, 0, sizeof(asm6));
+ Q_IPV6ADDR ip6 = groupAddress.toIPv6Address();
+ memcpy(&asm6.ipv6mr_multiaddr, &ip6, sizeof(ip6));
+ Q_UNUSED(sourceAddress); // ### not possible to specify a single source when using IPv6?
+ asm6.ipv6mr_interface = interface.index();
+ } else
+#endif
+ if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+ if (!sourceAddress.isNull()) {
+ sockOpt = howSsm4;
+ sockArg = &ssm4;
+ sockArgSize = sizeof(ssm4);
+ memset(&ssm4, 0, sizeof(ssm4));
+ ssm4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address());
+ ssm4.imr_sourceaddr.s_addr = htonl(sourceAddress.toIPv4Address());
+ } else {
+ sockOpt = howAsm4;
+ sockArg = &asm4;
+ sockArgSize = sizeof(asm4);
+ memset(&asm4, 0, sizeof(asm4));
+ asm4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address());
+ }
+
+ if (interface.isValid()) {
+ QList<QNetworkAddressEntry> addressEntries = interface.addressEntries();
+ if (!addressEntries.isEmpty()) {
+ QHostAddress firstIP = addressEntries.first().ip();
+ asm4.imr_interface.s_addr = htonl(firstIP.toIPv4Address());
+ } else {
+ d->setError(QAbstractSocket::NetworkError,
+ QNativeSocketEnginePrivate::NetworkUnreachableErrorString);
+ return false;
+ }
+ } else {
+ asm4.imr_interface.s_addr = INADDR_ANY;
+ }
+ } else {
+ // unreachable
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString);
+ return false;
+ }
+
+ int res = setsockopt(d->socketDescriptor, IPPROTO_IP, sockOpt, sockArg, sockArgSize);
+ if (res == -1) {
+ switch (errno) {
+ case ENOPROTOOPT:
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::OperationUnsupportedErrorString);
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface)
+{
+ return doMulticast(this,
+#ifndef QT_NO_IPV6
+ IPV6_JOIN_GROUP,
+#else
+ 0,
+#endif
+ IP_ADD_MEMBERSHIP,
+ IP_ADD_SOURCE_MEMBERSHIP,
+ groupAddress,
+ sourceAddress,
+ interface);
+}
+
+bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
+ const QHostAddress &sourceAddress,
+ const QNetworkInterface &interface)
+{
+ return doMulticast(this,
+#ifndef QT_NO_IPV6
+ IPV6_LEAVE_GROUP,
+#else
+ 0,
+#endif
+ IP_DROP_MEMBERSHIP,
+ IP_DROP_SOURCE_MEMBERSHIP,
+ groupAddress,
+ sourceAddress,
+ interface);
+}
+
qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const
{
int nbytes = 0;