diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure index 6e811a8fbeed..07e17580efb1 100644 --- a/build/moz.configure/init.configure +++ b/build/moz.configure/init.configure @@ -504,6 +504,13 @@ set_config('HAVE_64BIT_BUILD', have_64_bit) set_define('HAVE_64BIT_BUILD', have_64_bit) add_old_configure_assignment('HAVE_64BIT_BUILD', have_64_bit) +@depends(host) +def host_os_kernel_major_version(host): + versions = host.raw_os.split('.') + version = ''.join(x for x in versions[0] if x.isdigit()) + return version + +set_config('HOST_MAJOR_VERSION', host_os_kernel_major_version) # Autoconf needs these set @depends(host) diff --git a/config/external/nspr/pr/moz.build b/config/external/nspr/pr/moz.build index b1b90d75f166..bbf22223b5f7 100644 --- a/config/external/nspr/pr/moz.build +++ b/config/external/nspr/pr/moz.build @@ -40,6 +40,10 @@ elif CONFIG['OS_TARGET'] in ('FreeBSD', 'OpenBSD', 'NetBSD'): SOURCES += ['/nsprpub/pr/src/md/unix/%s.c' % CONFIG['OS_TARGET'].lower()] elif CONFIG['OS_TARGET'] == 'Darwin': OS_LIBS += ['-framework CoreServices'] + if CONFIG['HOST_MAJOR_VERSION'] == '15': + DEFINES.update( + HAS_CONNECTX=True, + ) DEFINES.update( DARWIN=True, HAVE_BSD_FLOCK=True, diff --git a/nsprpub/configure b/nsprpub/configure index f7036ff4bc9d..959cf3bb4aad 100755 --- a/nsprpub/configure +++ b/nsprpub/configure @@ -6560,6 +6560,11 @@ fi $as_echo "#define HAVE_POINTER_LOCALTIME_R 1" >>confdefs.h + HOST_DARWIN_MAJOR=`echo "$build_os" | sed -E -e 's/^darwin([0-9]+).*$/\1/'` + if test "$HOST_DARWIN_MAJOR" -ge 15 ; then + $as_echo "#define HAS_CONNECTX 1" >>confdefs.h + fi + AS='$(CC) -x assembler-with-cpp' CFLAGS="$CFLAGS -Wall -fno-common" case "${target_cpu}" in diff --git a/nsprpub/configure.in b/nsprpub/configure.in index 395fe2a882b6..4b2e46d62882 100644 --- a/nsprpub/configure.in +++ b/nsprpub/configure.in @@ -1384,6 +1384,11 @@ case "$target" in AC_DEFINE(HAVE_BSD_FLOCK) AC_DEFINE(HAVE_SOCKLEN_T) AC_DEFINE(HAVE_POINTER_LOCALTIME_R) + changequote(,) + HOST_DARWIN_MAJOR=`echo "$build_os" | sed -E -e 's/^darwin([0-9]+).*$/\1/'` + changequote([,]) + if test "$HOST_DARWIN_MAJOR" -ge 15 ; then + AC_DEFINE(HAS_CONNECTX) AS='$(CC) -x assembler-with-cpp' CFLAGS="$CFLAGS -Wall -fno-common" case "${target_cpu}" in diff --git a/nsprpub/pr/include/md/_win95.h b/nsprpub/pr/include/md/_win95.h index d65e26437664..04f811d37d84 100644 --- a/nsprpub/pr/include/md/_win95.h +++ b/nsprpub/pr/include/md/_win95.h @@ -290,6 +290,9 @@ extern void _MD_MakeNonblock(PRFileDesc *f); extern PRInt32 _MD_CloseSocket(PROsfd osfd); #define _MD_CLOSE_SOCKET _MD_CloseSocket #define _MD_SENDTO _PR_MD_SENDTO +#ifdef _WIN64 +#define _MD_TCPSENDTO _PR_MD_TCPSENDTO +#endif #define _MD_RECVFROM _PR_MD_RECVFROM #define _MD_SOCKETPAIR(s, type, proto, sv) -1 #define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME diff --git a/nsprpub/pr/include/private/primpl.h b/nsprpub/pr/include/private/primpl.h index 63ba3ee46fcd..dc24a2572752 100644 --- a/nsprpub/pr/include/private/primpl.h +++ b/nsprpub/pr/include/private/primpl.h @@ -1225,6 +1225,13 @@ extern PRInt32 _PR_MD_SENDTO( const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout); #define _PR_MD_SENDTO _MD_SENDTO +#if defined(_WIN64) && defined(WIN95) +extern PRInt32 _PR_MD_TCPSENDTO( + PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout); +#define _PR_MD_TCPSENDTO _MD_TCPSENDTO +#endif + extern PRInt32 _PR_MD_SOCKETPAIR(int af, int type, int flags, PROsfd *osfd); #define _PR_MD_SOCKETPAIR _MD_SOCKETPAIR @@ -1747,6 +1754,18 @@ struct PRFilePrivate { * requires knowing the address family of the * socket, we save the address family here. */ #endif + +#if defined(_WIN64) + /* This is necessary for TCP Fast Open. TCP Fast Open in windows must + * use ConnectEx function which uses OVERLAPPED. TCPSendTo will call + * ConnectEx to send fast open data. If ConnectEx returns + * ERROR_IO_PENDING we need to save OVERLAPPED structure and we will + * use it in ConnectContinue to get the final result of ConnectEx. + */ + PRBool alreadyConnected; + PRBool overlappedActive; + OVERLAPPED ol; +#endif }; #ifdef _WIN64 diff --git a/nsprpub/pr/src/io/prio.c b/nsprpub/pr/src/io/prio.c index 78cbdf564baa..bf9763a2cd23 100644 --- a/nsprpub/pr/src/io/prio.c +++ b/nsprpub/pr/src/io/prio.c @@ -119,6 +119,10 @@ PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc( fd->methods = methods; fd->secret->state = _PR_FILEDESC_OPEN; fd->secret->md.osfd = osfd; +#if defined(_WIN64) + fd->secret->alreadyConnected = PR_FALSE; + fd->secret->overlappedActive = PR_FALSE; +#endif _PR_MD_INIT_FILEDESC(fd); } else { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); diff --git a/nsprpub/pr/src/io/prsocket.c b/nsprpub/pr/src/io/prsocket.c index f9dc939cf7e8..8cd4f0438371 100644 --- a/nsprpub/pr/src/io/prsocket.c +++ b/nsprpub/pr/src/io/prsocket.c @@ -7,6 +7,12 @@ #include +#if defined(_WIN64) +#ifndef SO_UPDATE_CONNECT_CONTEXT +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif +#endif + /************************************************************************/ /* These two functions are only used in assertions. */ @@ -304,6 +310,48 @@ static PRStatus PR_CALLBACK SocketConnectContinue( } PR_ASSERT(out_flags & PR_POLL_WRITE); + +#if defined(_WIN64) + if (fd->secret->alreadyConnected) { + fd->secret->alreadyConnected = PR_FALSE; + } + // TCP Fast Open on Windows must use ConnectEx, which uses overlapped + // input/output. + // To get result we need to use GetOverlappedResult. + if (fd->secret->overlappedActive) { + PR_ASSERT(fd->secret->nonblocking); + PRInt32 rvSent; + if (GetOverlappedResult(osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) { + fd->secret->overlappedActive = FALSE; + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketConnectContinue GetOverlappedResult succeeded\n")); + // When ConnectEx is used, all previously set socket options and + // property are not enabled and to enable them + // SO_UPDATE_CONNECT_CONTEXT option need to be set. + if (setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketConnectContinue setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err)); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; + } else { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketConnectContinue GetOverlappedResult failed %d\n", err)); + if (err != ERROR_IO_PENDING) { + _PR_MD_MAP_CONNECT_ERROR(err); + fd->secret->overlappedActive = FALSE; + return PR_FAILURE; + } else { + PR_SetError(PR_IN_PROGRESS_ERROR, 0); + return PR_FAILURE; + } + } + } +#endif + return PR_SUCCESS; #elif defined(XP_OS2) @@ -768,6 +816,56 @@ static PRInt32 PR_CALLBACK SocketSendTo( return count; } +#if defined(_WIN64) && defined(WIN95) +static PRInt32 PR_CALLBACK SocketTCPSendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 temp, count; + const PRNetAddr *addrp = addr; +#if defined(_PR_INET6) + PRNetAddr addrCopy; +#endif + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; + } +#endif + + count = 0; + while (amount > 0) { + temp = _PR_MD_TCPSENDTO(fd, buf, amount, flags, + addrp, PR_NETADDR_SIZE(addr), timeout); + if (temp < 0) { + count = -1; + break; + } + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + amount -= temp; + } + return count; +} +#endif + static PRInt32 PR_CALLBACK SocketRecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) { @@ -1066,6 +1164,15 @@ static PRInt16 PR_CALLBACK SocketPoll( PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) { *out_flags = 0; + +#if defined(_WIN64) + if (in_flags & PR_POLL_WRITE) { + if (fd->secret->alreadyConnected) { + out_flags = PR_POLL_WRITE; + return PR_POLL_WRITE; + } + } +#endif return in_flags; } /* SocketPoll */ @@ -1090,7 +1197,11 @@ static PRIOMethods tcpMethods = { SocketRecv, SocketSend, (PRRecvfromFN)_PR_InvalidInt, +#if defined(_WIN64) && defined(WIN95) + SocketTCPSendTo, // This is for fast open. We imitate Linux interface. +#else (PRSendtoFN)_PR_InvalidInt, +#endif SocketPoll, SocketAcceptRead, SocketTransmitFile, diff --git a/nsprpub/pr/src/md/windows/w95sock.c b/nsprpub/pr/src/md/windows/w95sock.c index 670e35da4241..89d547fb2a96 100644 --- a/nsprpub/pr/src/md/windows/w95sock.c +++ b/nsprpub/pr/src/md/windows/w95sock.c @@ -330,6 +330,167 @@ _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, return bytesSent; } +#if defined(_WIN64) + +static PRCallOnceType _pr_has_connectex_once; +typedef BOOL (WINAPI *_pr_win_connectex_ptr)(SOCKET, const struct sockaddr *, int, PVOID, DWORD, LPDWORD, LPOVERLAPPED); +#ifndef WSAID_CONNECTEX +#define WSAID_CONNECTEX \ + {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} +#endif +#ifndef SIO_GET_EXTENSION_FUNCTION_POINTER +#define SIO_GET_EXTENSION_FUNCTION_POINTER 0xC8000006 +#endif +#ifndef TCP_FASTOPEN +#define TCP_FASTOPEN 15 +#endif + +#ifndef SO_UPDATE_CONNECT_CONTEXT +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif + +static _pr_win_connectex_ptr _pr_win_connectex; + +static PRStatus PR_CALLBACK _pr_set_connectex(void) +{ + _pr_win_connectex = NULL; + SOCKET sock; + PRInt32 dwBytes; + int rc; + + /* Dummy socket needed for WSAIoctl */ + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) + return PR_SUCCESS; + + GUID guid = WSAID_CONNECTEX; + rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, sizeof(guid), + &_pr_win_connectex, sizeof(_pr_win_connectex), + &dwBytes, NULL, NULL); + if (rc != 0) { + _pr_win_connectex = NULL; + return PR_SUCCESS; + } + + rc = closesocket(sock); + return PR_SUCCESS; +} + +PRInt32 +_PR_MD_TCPSENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + if (PR_CallOnce(&_pr_has_connectex_once, _pr_set_connectex) != PR_SUCCESS) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; + } + + if (_pr_win_connectex == NULL) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; + } + + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRInt32 bytesSent = 0; + DWORD rvSent; + + BOOL option = 1; + rv = setsockopt((SOCKET)osfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&option, sizeof(option)); + if (rv != 0) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO error set opt TCP_FASTOPEN failed %d\n", err)); + if (err == WSAENOPROTOOPT) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + } else { + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + } + return -1; + } + + // ConnectEx requires the socket to be initially bound. We will use INADDR_ANY + PRNetAddr bindAddr; + memset(&bindAddr, 0, sizeof(bindAddr)); + if (addr->raw.family == PR_AF_INET) { + bindAddr.inet.family = PR_AF_INET; + } else if (addr->raw.family == PR_AF_INET6) { + bindAddr.ipv6.family = PR_AF_INET6; + } + rv = bind((SOCKET)osfd, (SOCKADDR*) &bindAddr, sizeof(bindAddr)); + if (rv != 0) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO error bind failed %d\n", err)); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + return -1; + } + + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO calling _pr_win_connectex %d %p\n", amount, (char*)buf)); + + rvSent = 0; + memset(&fd->secret->ol, 0, sizeof(fd->secret->ol)); + // ConnectEx return TRUE on a success and FALSE on an error. + if (_pr_win_connectex( (SOCKET)osfd, (struct sockaddr *) addr, + addrlen, buf, amount, + &rvSent, &fd->secret->ol) == TRUE) { + // When ConnectEx is used, all previously set socket options and + // property are not enabled and to enable them + // SO_UPDATE_CONNECT_CONTEXT option need to be set. + rv = setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0); + if (rv != 0) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err)); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + return -1; + } + // We imitate Linux here. SendTo will return number of bytes send but + // it can not return connection success at the same time, so we return + // number of bytes send and "connection success" will be return on the + // connectcontinue. + fd->secret->alreadyConnected = PR_TRUE; + return rvSent; + } else { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO error _pr_win_connectex failed %d\n", err)); + if (err != ERROR_IO_PENDING) { + _PR_MD_MAP_CONNECT_ERROR(err); + return -1; + } else if (fd->secret->nonblocking) { + // Remember that overlapped structure is set. We will neede to get + // the final result of ConnectEx call. + fd->secret->overlappedActive = PR_TRUE; + _PR_MD_MAP_CONNECT_ERROR(WSAEWOULDBLOCK); + // ConnectEx will copy supplied data to a internal buffer and send + // them during Fast Open or after connect. Therefore we can assumed + // this data already send. + return amount; + } + while (err == ERROR_IO_PENDING) { + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if ( rv < 0 ) { + return -1; + } + rv = GetOverlappedResult(osfd, &fd->secret->ol, &rvSent, FALSE); + if ( rv == TRUE ) { + return rvSent; + } else { + err = WSAGetLastError(); + if (err != ERROR_IO_PENDING) { + _PR_MD_MAP_CONNECT_ERROR(err); + return -1; + } + } + } + } + return -1; +} +#endif + PRInt32 _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) diff --git a/nsprpub/pr/src/pthreads/ptio.c b/nsprpub/pr/src/pthreads/ptio.c index 2c7be9cd336f..75e778a7f6bf 100644 --- a/nsprpub/pr/src/pthreads/ptio.c +++ b/nsprpub/pr/src/pthreads/ptio.c @@ -164,6 +164,9 @@ static ssize_t (*pt_aix_sendfile_fptr)() = NULL; #ifndef TCP_CORK #define TCP_CORK 3 #endif +#ifndef MSG_FASTOPEN +#define MSG_FASTOPEN 0x20000000 +#endif #endif #ifdef _PR_IPV6_V6ONLY_PROBE @@ -2053,6 +2056,99 @@ static PRInt32 pt_SendTo( return bytes; } /* pt_SendTo */ +// Linux uses SendTo to send data during TCP Fast Open. OSX uses connectx, but +// we will make it imitate the Linux's interface. +static PRInt32 pt_TCP_SendTo( + PRFileDesc *fd, const void *buf, + PRInt32 amount, PRIntn flags, const PRNetAddr *addr, + PRIntervalTime timeout) +{ +#if !defined(DARWIN) || HAS_CONNECTX + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) return bytes; + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + addr_len = PR_NETADDR_SIZE(addr); +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + // If _PR_INET6 is defined and it is PR_AF_INET6 we set family + // to AF_INET6. + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + +#ifdef _PR_HAVE_SOCKADDR_LEN + // if _PR_HAVE_SOCKADDR_LEN is defined and it is PR_AF_INET6 we set family + / to AF_INET6 and we set address length. + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + +#ifndef HAS_CONNECTX + bytes = sendto( + fd->secret->md.osfd, buf, amount, MSG_FASTOPEN, + (struct sockaddr*)addrp, addr_len); +#else + sa_endpoints_t endpoints; + endpoints.sae_srcif = 0; + endpoints.sae_srcaddr = NULL; + endpoints.sae_srcaddrlen = 0; + endpoints.sae_dstaddr = (struct sockaddr *)addrp; + endpoints.sae_dstaddrlen = addr_len; + struct iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = amount; + PRInt32 rv = connectx(fd->secret->md.osfd, &endpoints, SAE_ASSOCID_ANY, + CONNECT_DATA_IDEMPOTENT, iov, 1, &bytes, NULL); +#endif + syserrno = errno; + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) { + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else fNeedContinue = PR_TRUE; + } + if (fNeedContinue == PR_TRUE) { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = (PRNetAddr*)addrp; + op.timeout = timeout; + op.result.code = 0; /* initialize the number sent */ + op.function = pt_sendto_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno); + } + return bytes; +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; +#endif +} /* pt_TCP_SendTo */ + static PRInt32 pt_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) { @@ -3164,7 +3260,7 @@ static PRIOMethods _pr_tcp_methods = { pt_Recv, pt_Send, (PRRecvfromFN)_PR_InvalidInt, - (PRSendtoFN)_PR_InvalidInt, + pt_TCP_SendTo, // This is for TCP Fast Open. Linux uses SendTo function for this. OSX uses connectx, but we imitate Linux. pt_Poll, pt_AcceptRead, pt_TransmitFile,