summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDragana Damjanovic <dd.mozilla@gmail.com>2017-05-09 13:33:18 +0200
committerDragana Damjanovic <dd.mozilla@gmail.com>2017-05-09 13:33:18 +0200
commitd89663eee064a748a4d8fba28dcfb7c007ca0a2f (patch)
treec1210e2384e7345160f277ca56dc4084c669d80d
parentcf49191a98de820e5936533ddb625d2c7c0a4b5b (diff)
downloadnspr-hg-d89663eee064a748a4d8fba28dcfb7c007ca0a2f.tar.gz
Bug 1310197 - Implement fast open nspr part. r=mcmanus,bagder
-rw-r--r--configure.in5
-rw-r--r--pr/include/md/_win95.h3
-rw-r--r--pr/include/private/primpl.h19
-rw-r--r--pr/src/io/prio.c4
-rw-r--r--pr/src/io/prsocket.c111
-rw-r--r--pr/src/md/windows/w95sock.c161
-rw-r--r--pr/src/pthreads/ptio.c98
7 files changed, 400 insertions, 1 deletions
diff --git a/configure.in b/configure.in
index 395fe2a8..4b2e46d6 100644
--- a/configure.in
+++ b/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/pr/include/md/_win95.h b/pr/include/md/_win95.h
index d65e2643..04f811d3 100644
--- a/pr/include/md/_win95.h
+++ b/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/pr/include/private/primpl.h b/pr/include/private/primpl.h
index 63ba3ee4..dc24a257 100644
--- a/pr/include/private/primpl.h
+++ b/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/pr/src/io/prio.c b/pr/src/io/prio.c
index 78cbdf56..bf9763a2 100644
--- a/pr/src/io/prio.c
+++ b/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/pr/src/io/prsocket.c b/pr/src/io/prsocket.c
index f9dc939c..8cd4f043 100644
--- a/pr/src/io/prsocket.c
+++ b/pr/src/io/prsocket.c
@@ -7,6 +7,12 @@
#include <string.h>
+#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/pr/src/md/windows/w95sock.c b/pr/src/md/windows/w95sock.c
index 670e35da..89d547fb 100644
--- a/pr/src/md/windows/w95sock.c
+++ b/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/pr/src/pthreads/ptio.c b/pr/src/pthreads/ptio.c
index 2c7be9cd..655d15b4 100644
--- a/pr/src/pthreads/ptio.c
+++ b/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,