diff options
author | Bradley T. Hughes <bradley.hughes@nokia.com> | 2010-05-05 14:20:07 (GMT) |
---|---|---|
committer | Bradley T. Hughes <bradley.hughes@nokia.com> | 2010-09-01 12:24:37 (GMT) |
commit | 3c35b0607e541008daaf53221da4e727015b5441 (patch) | |
tree | 552b064253cec07c132637b77938324782b798cd /src/network/socket/qnativesocketengine_unix.cpp | |
parent | 2abb3642bce6e4b32f44c3724dc8fcc9acf9976c (diff) | |
download | Qt-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/qnativesocketengine_unix.cpp')
-rw-r--r-- | src/network/socket/qnativesocketengine_unix.cpp | 119 |
1 files changed, 119 insertions, 0 deletions
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; |