Index: src/OFTCPSocket.m ================================================================== --- src/OFTCPSocket.m +++ src/OFTCPSocket.m @@ -63,18 +63,39 @@ # endif #endif enum { flagUseMPTCP = 1, - flagUseConnectX = 2 + flagMapIPv4 = 2, + flagUseConnectX = 4 }; static const OFRunLoopMode connectRunLoopMode = @"OFTCPSocketConnectRunLoopMode"; static OFString *defaultSOCKS5Host = nil; static uint16_t defaultSOCKS5Port = 1080; + +#if defined(OF_LINUX) && defined(IPPROTO_MPTCP) +static OFSocketAddress +mapIPv4(const OFSocketAddress *IPv4Address) +{ + OFSocketAddress IPv6Address = { + .family = OFSocketAddressFamilyIPv6, + .length = sizeof(struct sockaddr_in6) + }; + + IPv6Address.sockaddr.in6.sin6_family = AF_INET6; + IPv6Address.sockaddr.in6.sin6_port = IPv4Address->sockaddr.in.sin_port; + memcpy(&IPv6Address.sockaddr.in6.sin6_addr.s6_addr[12], + &IPv4Address->sockaddr.in.sin_addr.s_addr, 4); + IPv6Address.sockaddr.in6.sin6_addr.s6_addr[10] = 0xFF; + IPv6Address.sockaddr.in6.sin6_addr.s6_addr[11] = 0xFF; + + return IPv6Address; +} +#endif @interface OFTCPSocket () @end @interface OFTCPSocketConnectDelegate: OFObject @@ -156,53 +177,48 @@ { #if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) int flags; #endif - if (_socket != OFInvalidSocketHandle) { + if (_socket != OFInvalidSocketHandle) @throw [OFAlreadyOpenException exceptionWithObject: self]; - } -#if defined(OF_LINUX) +#if defined(OF_LINUX) && defined(IPPROTO_MPTCP) if (_flags & flagUseMPTCP) { - if ((_socket = socket( - ((struct sockaddr *)&address->sockaddr)->sa_family, - SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_MPTCP)) == - OFInvalidSocketHandle) { - if ((_socket = socket( - ((struct sockaddr *)&address->sockaddr)->sa_family, - SOCK_STREAM | SOCK_CLOEXEC, 0)) == - OFInvalidSocketHandle) { - *errNo = _OFSocketErrNo(); - return false; - } - } - } else + /* + * For MPTCP sockets, we always use AF_INET6, so that IPv4 and + * IPv6 can both be used for a single connection. + */ + _socket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, + IPPROTO_MPTCP); + + if (_socket != OFInvalidSocketHandle && + address->family == OFSocketAddressFamilyIPv4) + _flags |= flagMapIPv4; + else + _flags &= ~flagMapIPv4; + } #elif defined(OF_MACOS) || defined(OF_IOS) if (_flags & flagUseMPTCP) { - if ((_socket = socket(AF_MULTIPATH, SOCK_STREAM | SOCK_CLOEXEC, - IPPROTO_TCP)) != OFInvalidSocketHandle) + _socket = socket(AF_MULTIPATH, SOCK_STREAM | SOCK_CLOEXEC, + IPPROTO_TCP); + + if (_socket != OFInvalidSocketHandle) _flags |= flagUseConnectX; - else { - if ((_socket = socket( - ((struct sockaddr *)&address->sockaddr)->sa_family, - SOCK_STREAM | SOCK_CLOEXEC, 0)) == - OFInvalidSocketHandle) { - *errNo = _OFSocketErrNo(); - return false; - } - + else _flags &= ~flagUseConnectX; - } - } else + } #endif + + if (_socket == OFInvalidSocketHandle) { if ((_socket = socket( ((struct sockaddr *)&address->sockaddr)->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle) { *errNo = _OFSocketErrNo(); return false; } + } #if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) fcntl(_socket, F_SETFD, flags | FD_CLOEXEC); #endif @@ -211,13 +227,28 @@ } - (bool)of_connectSocketToAddress: (const OFSocketAddress *)address errNo: (int *)errNo { +#if defined(OF_LINUX) && defined(IPPROTO_MPTCP) + OFSocketAddress mappedIPv4; +#endif + if (_socket == OFInvalidSocketHandle) { @throw [OFNotOpenException exceptionWithObject: self]; } + +#if defined(OF_LINUX) && defined(IPPROTO_MPTCP) + if (_flags & flagMapIPv4) { + /* + * For MPTCP sockets, we always use AF_INET6, so that IPv4 and + * IPv6 can both be used for a single connection. + */ + mappedIPv4 = mapIPv4(address); + address = &mappedIPv4; + } +#endif #if defined(OF_MACOS) || defined(OF_IOS) if (_flags & flagUseConnectX) { sa_endpoints_t endpoints = { .sae_dstaddr = (struct sockaddr *)&address->sockaddr, @@ -413,28 +444,26 @@ addressFamily: OFSocketAddressFamilyAny]; address = *(OFSocketAddress *)[socketAddresses itemAtIndex: 0]; OFSocketAddressSetIPPort(&address, port); -#ifdef OF_LINUX +#if defined(OF_LINUX) && defined(IPPROTO_MPTCP) if (_flags & flagUseMPTCP) { - if ((_socket = socket( - ((struct sockaddr *)&address.sockaddr)->sa_family, - SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_MPTCP)) == - OFInvalidSocketHandle) { - if ((_socket = socket( - ((struct sockaddr *)&address.sockaddr)->sa_family, - SOCK_STREAM | SOCK_CLOEXEC, 0)) == - OFInvalidSocketHandle) - @throw [OFBindIPSocketFailedException - exceptionWithHost: host - port: port - socket: self - errNo: _OFSocketErrNo()]; - } - } else + /* + * For MPTCP sockets, we always use AF_INET6, so that IPv4 and + * IPv6 can both be used for a single connection. + */ + _socket = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, + IPPROTO_MPTCP); + + if (_socket != OFInvalidSocketHandle && + address.family == OFSocketAddressFamilyIPv4) + address = mapIPv4(&address); + } #endif + + if (_socket == OFInvalidSocketHandle) if ((_socket = socket( ((struct sockaddr *)&address.sockaddr)->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle) @throw [OFBindIPSocketFailedException exceptionWithHost: host @@ -605,11 +634,11 @@ _flags &= ~flagUseMPTCP; } - (bool)usesMPTCP { -#ifdef OF_LINUX +#if defined(OF_LINUX) && defined(SOL_MPTCP) && defined(MPTCP_INFO) struct mptcp_info info; socklen_t infoLen = (socklen_t)sizeof(info); if (getsockopt(_socket, SOL_MPTCP, MPTCP_INFO, &info, &infoLen) != -1) return true;