summaryrefslogtreecommitdiffstats
path: root/src/network/socket/qnativesocketengine_unix.cpp
diff options
context:
space:
mode:
authorAlex Trotsenko <alex1973tr@gmail.com>2014-09-24 17:31:33 +0300
committerAlex Trotsenko <alex1973tr@gmail.com>2016-07-27 06:37:24 +0000
commit75a9bd2a4f637fb8e8e3dc4609a7045547119e80 (patch)
tree73b8a0a950eb87a7407edada3ea6547f08baf328 /src/network/socket/qnativesocketengine_unix.cpp
parentc5a4b093d051680bf7f34bb5acff16eea9d4979f (diff)
Introduce SCTP sockets support
Add protocol-specific code and the QSctpServer, QSctpSocket classes. Change-Id: Ie9a1d87bd1fda866a2405043d1c15c12ded5a96e Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/network/socket/qnativesocketengine_unix.cpp')
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp182
1 files changed, 160 insertions, 22 deletions
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index fcb8a60c8f..2a9d600630 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -68,6 +68,11 @@
#endif
#include <netinet/tcp.h>
+#ifndef QT_NO_SCTP
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/sctp.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -142,6 +147,7 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
switch (opt) {
case QNativeSocketEngine::NonBlockingSocketOption: // fcntl, not setsockopt
case QNativeSocketEngine::BindExclusively: // not handled on Unix
+ case QNativeSocketEngine::MaxStreamsSocketOption:
Q_UNREACHABLE();
case QNativeSocketEngine::BroadcastSocketOption:
@@ -229,13 +235,28 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType,
QAbstractSocket::NetworkLayerProtocol &socketProtocol)
{
- int protocol = (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) ? AF_INET6 : AF_INET;
+#ifndef QT_NO_SCTP
+ int protocol = (socketType == QAbstractSocket::SctpSocket) ? IPPROTO_SCTP : 0;
+#else
+ if (socketType == QAbstractSocket::SctpSocket) {
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ ProtocolUnsupportedErrorString);
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::createNewSocket(%d, %d): unsupported protocol",
+ socketType, socketProtocol);
+#endif
+ return false;
+ }
+ int protocol = 0;
+#endif // QT_NO_SCTP
+ int domain = (socketProtocol == QAbstractSocket::IPv6Protocol
+ || socketProtocol == QAbstractSocket::AnyIPProtocol) ? AF_INET6 : AF_INET;
int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM;
- int socket = qt_safe_socket(protocol, type, 0, O_NONBLOCK);
+ int socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK);
if (socket < 0 && socketProtocol == QAbstractSocket::AnyIPProtocol && errno == EAFNOSUPPORT) {
- protocol = AF_INET;
- socket = qt_safe_socket(protocol, type, 0, O_NONBLOCK);
+ domain = AF_INET;
+ socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK);
socketProtocol = QAbstractSocket::IPv4Protocol;
}
@@ -291,10 +312,26 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
if (!q->isValid())
return -1;
- // handle non-getsockopt cases first
- if (opt == QNativeSocketEngine::BindExclusively || opt == QNativeSocketEngine::NonBlockingSocketOption
- || opt == QNativeSocketEngine::BroadcastSocketOption)
+ // handle non-getsockopt and specific cases first
+ switch (opt) {
+ case QNativeSocketEngine::BindExclusively:
+ case QNativeSocketEngine::NonBlockingSocketOption:
+ case QNativeSocketEngine::BroadcastSocketOption:
return true;
+ case QNativeSocketEngine::MaxStreamsSocketOption: {
+#ifndef QT_NO_SCTP
+ sctp_initmsg sctpInitMsg;
+ QT_SOCKOPTLEN_T sctpInitMsgSize = sizeof(sctpInitMsg);
+ if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg,
+ &sctpInitMsgSize) == 0)
+ return int(qMin(sctpInitMsg.sinit_num_ostreams, sctpInitMsg.sinit_max_instreams));
+#endif
+ return -1;
+ }
+
+ default:
+ break;
+ }
int n, level;
int v = -1;
@@ -317,7 +354,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
if (!q->isValid())
return false;
- // handle non-setsockopt cases first
+ // handle non-setsockopt and specific cases first
switch (opt) {
case QNativeSocketEngine::NonBlockingSocketOption: {
// Make the socket nonblocking.
@@ -351,6 +388,20 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
case QNativeSocketEngine::BindExclusively:
return true;
+ case QNativeSocketEngine::MaxStreamsSocketOption: {
+#ifndef QT_NO_SCTP
+ sctp_initmsg sctpInitMsg;
+ QT_SOCKOPTLEN_T sctpInitMsgSize = sizeof(sctpInitMsg);
+ if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg,
+ &sctpInitMsgSize) == 0) {
+ sctpInitMsg.sinit_num_ostreams = sctpInitMsg.sinit_max_instreams = uint16_t(v);
+ return ::setsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg,
+ sctpInitMsgSize) == 0;
+ }
+#endif
+ return false;
+ }
+
default:
break;
}
@@ -830,6 +881,9 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
#if !defined(IP_PKTINFO) && defined(IP_RECVIF) && defined(Q_OS_BSD4)
+ CMSG_SPACE(sizeof(sockaddr_dl))
#endif
+#ifndef QT_NO_SCTP
+ + CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))
+#endif
+ sizeof(quintptr) - 1) / sizeof(quintptr)];
struct msghdr msg;
@@ -848,7 +902,8 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
msg.msg_name = &aa;
msg.msg_namelen = sizeof(aa);
}
- if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination)) {
+ if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination
+ | QAbstractSocketEngine::WantStreamNumber)) {
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
}
@@ -859,13 +914,27 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
} while (recvResult == -1 && errno == EINTR);
if (recvResult == -1) {
- setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
+ switch (errno) {
+#if EWOULDBLOCK-0 && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ case EAGAIN:
+ // No datagram was available for reading
+ recvResult = -2;
+ break;
+ case ECONNREFUSED:
+ setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
+ break;
+ default:
+ setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
+ }
if (header)
header->clear();
} else if (options != QAbstractSocketEngine::WantNone) {
Q_ASSERT(header);
qt_socket_getPortAndAddress(&aa, &header->senderPort, &header->senderAddress);
header->destinationPort = localPort;
+ header->endOfRecord = (msg.msg_flags & MSG_EOR) != 0;
// parse the ancillary data
struct cmsghdr *cmsgptr;
@@ -912,6 +981,15 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
|| (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) {
header->hopLimit = *reinterpret_cast<int *>(CMSG_DATA(cmsgptr));
}
+
+#ifndef QT_NO_SCTP
+ if (cmsgptr->cmsg_level == IPPROTO_SCTP && cmsgptr->cmsg_type == SCTP_SNDRCV
+ && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(sctp_sndrcvinfo))) {
+ sctp_sndrcvinfo *rcvInfo = reinterpret_cast<sctp_sndrcvinfo *>(CMSG_DATA(cmsgptr));
+
+ header->streamNumber = int(rcvInfo->sinfo_stream);
+ }
+#endif
}
}
@@ -924,13 +1002,17 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
? header->senderPort : 0, (qint64) recvResult);
#endif
- return qint64(maxSize ? recvResult : recvResult == -1 ? -1 : 0);
+ return qint64((maxSize || recvResult < 0) ? recvResult : Q_INT64_C(0));
}
qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
{
// we use quintptr to force the alignment
- quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) + sizeof(quintptr) - 1) / sizeof(quintptr)];
+ quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))
+#ifndef QT_NO_SCTP
+ + CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))
+#endif
+ + sizeof(quintptr) - 1) / sizeof(quintptr)];
struct cmsghdr *cmsgptr = reinterpret_cast<struct cmsghdr *>(cbuf);
struct msghdr msg;
@@ -943,10 +1025,13 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
vec.iov_len = len;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
- msg.msg_name = &aa.a;
msg.msg_control = &cbuf;
- setPortAndAddress(header.destinationPort, header.destinationAddress, &aa, &msg.msg_namelen);
+ if (header.destinationPort != 0) {
+ msg.msg_name = &aa.a;
+ setPortAndAddress(header.destinationPort, header.destinationAddress,
+ &aa, &msg.msg_namelen);
+ }
if (msg.msg_namelen == sizeof(aa.a6)) {
if (header.hopLimit != -1) {
@@ -1001,15 +1086,37 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
#endif
}
+#ifndef QT_NO_SCTP
+ if (header.streamNumber != -1) {
+ struct sctp_sndrcvinfo *data = reinterpret_cast<sctp_sndrcvinfo *>(CMSG_DATA(cmsgptr));
+ memset(data, 0, sizeof(*data));
+ msg.msg_controllen += CMSG_SPACE(sizeof(sctp_sndrcvinfo));
+ cmsgptr->cmsg_len = CMSG_LEN(sizeof(sctp_sndrcvinfo));
+ cmsgptr->cmsg_level = IPPROTO_SCTP;
+ cmsgptr->cmsg_type = SCTP_SNDRCV;
+ data->sinfo_stream = uint16_t(header.streamNumber);
+ cmsgptr = reinterpret_cast<cmsghdr *>(reinterpret_cast<char *>(cmsgptr) + CMSG_SPACE(sizeof(*data)));
+ }
+#endif
+
if (msg.msg_controllen == 0)
msg.msg_control = 0;
ssize_t sentBytes = qt_safe_sendmsg(socketDescriptor, &msg, 0);
if (sentBytes < 0) {
switch (errno) {
+#if EWOULDBLOCK-0 && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ case EAGAIN:
+ sentBytes = -2;
+ break;
case EMSGSIZE:
setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString);
break;
+ case ECONNRESET:
+ setError(QAbstractSocket::RemoteHostClosedError, RemoteHostClosedErrorString);
+ break;
default:
setError(QAbstractSocket::NetworkError, SendDatagramErrorString);
}
@@ -1082,21 +1189,51 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters()
#endif
// Determine the remote address
- if (!::getpeername(socketDescriptor, &sa.a, &sockAddrSize)) {
+ bool connected = ::getpeername(socketDescriptor, &sa.a, &sockAddrSize) == 0;
+ if (connected) {
qt_socket_getPortAndAddress(&sa, &peerPort, &peerAddress);
inboundStreamCount = outboundStreamCount = 1;
}
- // Determine the socket type (UDP/TCP)
+ // Determine the socket type (UDP/TCP/SCTP)
int value = 0;
QT_SOCKOPTLEN_T valueSize = sizeof(int);
if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_TYPE, &value, &valueSize) == 0) {
- if (value == SOCK_STREAM)
- socketType = QAbstractSocket::TcpSocket;
- else if (value == SOCK_DGRAM)
- socketType = QAbstractSocket::UdpSocket;
- else
- socketType = QAbstractSocket::UnknownSocketType;
+ if (value == SOCK_STREAM) {
+#ifndef QT_NO_SCTP
+ if (option(QNativeSocketEngine::MaxStreamsSocketOption) != -1) {
+ socketType = QAbstractSocket::SctpSocket;
+ if (connected) {
+ sctp_status sctpStatus;
+ QT_SOCKOPTLEN_T sctpStatusSize = sizeof(sctpStatus);
+ sctp_event_subscribe sctpEvents;
+
+ memset(&sctpEvents, 0, sizeof(sctpEvents));
+ sctpEvents.sctp_data_io_event = 1;
+ if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_STATUS, &sctpStatus,
+ &sctpStatusSize) == 0 &&
+ ::setsockopt(socketDescriptor, SOL_SCTP, SCTP_EVENTS, &sctpEvents,
+ sizeof(sctpEvents)) == 0) {
+ inboundStreamCount = int(sctpStatus.sstat_instrms);
+ outboundStreamCount = int(sctpStatus.sstat_outstrms);
+ } else {
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ InvalidSocketErrorString);
+ return false;
+ }
+ }
+ } else {
+ socketType = QAbstractSocket::TcpSocket;
+ }
+#else
+ socketType = QAbstractSocket::TcpSocket;
+#endif
+ } else {
+ if (value == SOCK_DGRAM)
+ socketType = QAbstractSocket::UdpSocket;
+ else
+ socketType = QAbstractSocket::UnknownSocketType;
+ }
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
QString socketProtocolStr = QStringLiteral("UnknownProtocol");
@@ -1106,6 +1243,7 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters()
QString socketTypeStr = QStringLiteral("UnknownSocketType");
if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = QStringLiteral("TcpSocket");
else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = QStringLiteral("UdpSocket");
+ else if (socketType == QAbstractSocket::SctpSocket) socketTypeStr = QStringLiteral("SctpSocket");
qDebug("QNativeSocketEnginePrivate::fetchConnectionParameters() local == %s:%i,"
" peer == %s:%i, socket == %s - %s, inboundStreamCount == %i, outboundStreamCount == %i",