summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-22 19:50:58 +0000
committerdkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-22 19:50:58 +0000
commitb43c97c9b09d545890942909d1a0a4102847430b (patch)
tree11bdec99230d9ed84f1432d8b99cd9b087bec827
parent3d13c830adf25a9884e5f1e5c0ee4ceae0d40d66 (diff)
downloadchromium_src-b43c97c9b09d545890942909d1a0a4102847430b.zip
chromium_src-b43c97c9b09d545890942909d1a0a4102847430b.tar.gz
chromium_src-b43c97c9b09d545890942909d1a0a4102847430b.tar.bz2
Port SSLClientSocket to Linux
Passes tests (once you enable them by removing DISABLED_). Probably want to add a mock https server so we can leave those tests enabled when we check in. Had to add full duplex support to TCPClientSocket on Linux to avoid kludgy plumbing issues. Also had to add dummy implementation of X509Certificate::~X509Certificate to prevent link error. Rediffed to current trunk, addressed all review issues. Review URL: http://codereview.chromium.org/4049 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3751 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/nss_init.cc13
-rw-r--r--net/base/client_socket.h12
-rw-r--r--net/base/client_socket_factory.cc4
-rw-r--r--net/base/nss_memio.c547
-rw-r--r--net/base/nss_memio.h86
-rw-r--r--net/base/ssl_client_socket_nss.cc395
-rw-r--r--net/base/ssl_client_socket_nss.h104
-rw-r--r--net/base/ssl_client_socket_unittest.cc1
-rw-r--r--net/base/tcp_client_socket.h42
-rw-r--r--net/base/tcp_client_socket_libevent.cc107
-rw-r--r--net/base/x509_certificate_nss.cc21
-rw-r--r--net/net_lib.scons7
-rw-r--r--net/net_unittests.scons5
13 files changed, 1291 insertions, 53 deletions
diff --git a/base/nss_init.cc b/base/nss_init.cc
index c5c10c4..c8ba44b 100644
--- a/base/nss_init.cc
+++ b/base/nss_init.cc
@@ -6,6 +6,12 @@
#include <nss.h>
+// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
+// until NSS 3.12.2 comes out and we update to it.
+#define Lock FOO_NSS_Lock
+#include <ssl.h>
+#undef Lock
+
#include "base/logging.h"
#include "base/singleton.h"
@@ -15,9 +21,16 @@ class NSSInitSingleton {
public:
NSSInitSingleton() {
CHECK(NSS_NoDB_Init(".") == SECSuccess);
+ // Enable ciphers
+ NSS_SetDomesticPolicy();
+ // Enable SSL
+ SSL_OptionSetDefault(SSL_SECURITY, PR_TRUE);
}
~NSSInitSingleton() {
+ // Have to clear the cache, or NSS_Shutdown fails with SEC_ERROR_BUSY
+ SSL_ClearSessionCache();
+
SECStatus status = NSS_Shutdown();
DCHECK(status == SECSuccess);
}
diff --git a/net/base/client_socket.h b/net/base/client_socket.h
index a156404..97495dd 100644
--- a/net/base/client_socket.h
+++ b/net/base/client_socket.h
@@ -5,7 +5,9 @@
#ifndef NET_BASE_CLIENT_SOCKET_H_
#define NET_BASE_CLIENT_SOCKET_H_
+#include "base/logging.h"
#include "net/base/socket.h"
+#include "net/base/net_errors.h"
namespace net {
@@ -40,6 +42,16 @@ class ClientSocket : public Socket {
// Called to test if the connection is still alive. Returns false if a
// connection wasn't established or the connection is dead.
virtual bool IsConnected() const = 0;
+
+#if defined(OS_LINUX)
+ // Identical to posix system call getpeername().
+ // Needed by ssl_client_socket_nss.
+ virtual int GetPeerName(struct sockaddr *name, socklen_t *namelen) {
+ // Default implementation just permits some unit tests to link.
+ NOTREACHED();
+ return ERR_UNEXPECTED;
+ }
+#endif
};
} // namespace net
diff --git a/net/base/client_socket_factory.cc b/net/base/client_socket_factory.cc
index 2c6d715..805e74e 100644
--- a/net/base/client_socket_factory.cc
+++ b/net/base/client_socket_factory.cc
@@ -8,6 +8,8 @@
#include "build/build_config.h"
#if defined(OS_WIN)
#include "net/base/ssl_client_socket_win.h"
+#elif defined(OS_LINUX)
+#include "net/base/ssl_client_socket_nss.h"
#elif defined(OS_MACOSX)
#include "net/base/ssl_client_socket_mac.h"
#endif
@@ -28,6 +30,8 @@ class DefaultClientSocketFactory : public ClientSocketFactory {
const SSLConfig& ssl_config) {
#if defined(OS_WIN)
return new SSLClientSocketWin(transport_socket, hostname, ssl_config);
+#elif defined(OS_LINUX)
+ return new SSLClientSocketNSS(transport_socket, hostname, ssl_config);
#elif defined(OS_MACOSX)
return new SSLClientSocketMac(transport_socket, hostname, ssl_config);
#else
diff --git a/net/base/nss_memio.c b/net/base/nss_memio.c
new file mode 100644
index 0000000..ca3421e
--- /dev/null
+++ b/net/base/nss_memio.c
@@ -0,0 +1,547 @@
+// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Written in NSPR style to also be suitable for adding to the NSS demo suite
+
+/* memio is a simple NSPR I/O layer that lets you decouple NSS from
+ * the real network. It's rather like openssl's memory bio,
+ * and is useful when your app absolutely, positively doesn't
+ * want to let NSS do its own networking.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <prerror.h>
+#include <prinit.h>
+#include <prlog.h>
+
+#include "nss_memio.h"
+
+/*--------------- private memio types -----------------------*/
+
+/*----------------------------------------------------------------------
+ Simple private circular buffer class. Size cannot be changed once allocated.
+----------------------------------------------------------------------*/
+
+struct memio_buffer {
+ int head; /* where to take next byte out of buf */
+ int tail; /* where to put next byte into buf */
+ int bufsize; /* number of bytes allocated to buf */
+ /* TODO(port): error handling is pessimistic right now.
+ * Once an error is set, the socket is considered broken
+ * (PR_WOULD_BLOCK_ERROR not included).
+ */
+ PRErrorCode last_err;
+ char *buf;
+};
+
+
+/* The 'secret' field of a PRFileDesc created by memio_CreateIOLayer points
+ * to one of these.
+ * In the public header, we use struct memio_Private as a typesafe alias
+ * for this. This causes a few ugly typecasts in the private file, but
+ * seems safer.
+ */
+struct PRFilePrivate {
+ /* read requests are satisfied from this buffer */
+ struct memio_buffer readbuf;
+
+ /* write requests are satisfied from this buffer */
+ struct memio_buffer writebuf;
+
+ /* SSL needs to know socket peer's name */
+ PRNetAddr peername;
+
+ /* if set, empty I/O returns EOF instead of EWOULDBLOCK */
+ int eof;
+};
+
+/*--------------- private memio_buffer functions ---------------------*/
+
+/* Forward declarations. */
+
+/* Allocate a memio_buffer of given size. */
+static void memio_buffer_new(struct memio_buffer *mb, int size);
+
+/* Deallocate a memio_buffer allocated by memio_buffer_new. */
+static void memio_buffer_destroy(struct memio_buffer *mb);
+
+/* How many bytes have been put into the buffer */
+static int memio_buffer_used(const struct memio_buffer *mb);
+
+/* How many bytes can be read out of the buffer without wrapping */
+static int memio_buffer_used_contiguous(const struct memio_buffer *mb);
+
+/* How many bytes can still be put into the buffer */
+static int memio_buffer_unused(const struct memio_buffer *mb);
+
+/* How many bytes can be written into the buffer without wrapping */
+static int memio_buffer_unused_contiguous(const struct memio_buffer *mb);
+
+/* Is buffer completely empty? */
+static int memio_buffer_empty(const struct memio_buffer *mb);
+
+/* Is buffer completely full? */
+static int memio_buffer_full(const struct memio_buffer *mb);
+
+/* Write n bytes into the buffer. Returns number of bytes written. */
+static int memio_buffer_put(struct memio_buffer *mb, const char *buf, int n);
+
+/* Read n bytes from the buffer. Returns number of bytes read. */
+static int memio_buffer_get(struct memio_buffer *mb, char *buf, int n);
+
+/* Allocate a memio_buffer of given size. */
+static void memio_buffer_new(struct memio_buffer *mb, int size)
+{
+ mb->head = 0;
+ mb->tail = 0;
+ mb->bufsize = size;
+ mb->buf = malloc(size);
+}
+
+/* Deallocate a memio_buffer allocated by memio_buffer_new. */
+static void memio_buffer_destroy(struct memio_buffer *mb)
+{
+ free(mb->buf);
+ mb->buf = NULL;
+ mb->head = 0;
+ mb->tail = 0;
+}
+
+/* How many bytes have been put into the buffer */
+static int memio_buffer_used(const struct memio_buffer *mb)
+{
+ int n = mb->tail - mb->head;
+ if (n < 0) n += mb->bufsize;
+ return n;
+}
+
+/* How many bytes can be read out of the buffer without wrapping */
+static int memio_buffer_used_contiguous(const struct memio_buffer *mb)
+{
+ return (((mb->tail >= mb->head) ? mb->tail : mb->bufsize) - mb->head);
+}
+
+/* How many bytes can still be put into the buffer */
+static int memio_buffer_unused(const struct memio_buffer *mb)
+{
+ return mb->bufsize - memio_buffer_used(mb) - 1;
+}
+
+/* How many bytes can be written into the buffer without wrapping */
+static int memio_buffer_unused_contiguous(const struct memio_buffer *mb)
+{
+ if (mb->head > mb->tail) return mb->head - mb->tail - 1;
+ return mb->bufsize - mb->tail - (mb->head == 0);
+}
+
+/* Is buffer completely empty? */
+static int memio_buffer_empty(const struct memio_buffer *mb)
+{
+ return mb->head == mb->tail;
+}
+
+/* Is buffer completely full? */
+static int memio_buffer_full(const struct memio_buffer *mb)
+{
+ return memio_buffer_unused(mb) == 0;
+}
+
+/* Write n bytes into the buffer. Returns number of bytes written. */
+static int memio_buffer_put(struct memio_buffer *mb, const char *buf, int n)
+{
+ int len;
+ int transferred = 0;
+
+ /* Handle part before wrap */
+ len = PR_MIN(n, memio_buffer_unused_contiguous(mb));
+ if (len > 0) {
+ /* Buffer not full */
+ memcpy(&mb->buf[mb->tail], buf, len);
+ mb->tail += len;
+ if (mb->tail == mb->bufsize)
+ mb->tail = 0;
+ n -= len;
+ buf += len;
+ transferred += len;
+
+ /* Handle part after wrap */
+ len = PR_MIN(n, memio_buffer_unused_contiguous(mb));
+ if (len > 0) {
+ /* Output buffer still not full, input buffer still not empty */
+ memcpy(&mb->buf[mb->tail], buf, len);
+ mb->tail += len;
+ if (mb->tail == mb->bufsize)
+ mb->tail = 0;
+ transferred += len;
+ }
+ }
+
+ return transferred;
+}
+
+
+/* Read n bytes from the buffer. Returns number of bytes read. */
+static int memio_buffer_get(struct memio_buffer *mb, char *buf, int n)
+{
+ int len;
+ int transferred = 0;
+
+ /* Handle part before wrap */
+ len = PR_MIN(n, memio_buffer_used_contiguous(mb));
+ if (len) {
+ memcpy(buf, &mb->buf[mb->head], len);
+ mb->head += len;
+ if (mb->head == mb->bufsize)
+ mb->head = 0;
+ n -= len;
+ buf += len;
+ transferred += len;
+
+ /* Handle part after wrap */
+ len = PR_MIN(n, memio_buffer_used_contiguous(mb));
+ if (len) {
+ memcpy(buf, &mb->buf[mb->head], len);
+ mb->head += len;
+ if (mb->head == mb->bufsize)
+ mb->head = 0;
+ transferred += len;
+ }
+ }
+
+ return transferred;
+}
+
+/*--------------- private memio functions -----------------------*/
+
+static PRStatus PR_CALLBACK memio_Close(PRFileDesc *fd)
+{
+ struct PRFilePrivate *secret = fd->secret;
+ memio_buffer_destroy(&secret->readbuf);
+ memio_buffer_destroy(&secret->writebuf);
+ free(secret);
+ fd->dtor(fd);
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK memio_Shutdown(PRFileDesc *fd, PRIntn how)
+{
+ /* TODO: pass shutdown status to app somehow */
+ return PR_SUCCESS;
+}
+
+/* If there was a network error in the past taking bytes
+ * out of the buffer, return it to the next call that
+ * tries to read from an empty buffer.
+ */
+static int PR_CALLBACK memio_Recv(PRFileDesc *fd, void *buf, PRInt32 len,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ struct PRFilePrivate *secret;
+ struct memio_buffer *mb;
+ int rv;
+
+ if (flags) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return -1;
+ }
+
+ secret = fd->secret;
+ mb = &secret->readbuf;
+ PR_ASSERT(mb->bufsize);
+ rv = memio_buffer_get(mb, buf, len);
+ if (rv == 0 && !secret->eof) {
+ if (mb->last_err)
+ PR_SetError(mb->last_err, 0);
+ else
+ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+ return -1;
+ }
+
+ return rv;
+}
+
+static int PR_CALLBACK memio_Read(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ /* pull bytes from buffer */
+ return memio_Recv(fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT);
+}
+
+static int PR_CALLBACK memio_Send(PRFileDesc *fd, const void *buf, PRInt32 len,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ struct PRFilePrivate *secret;
+ struct memio_buffer *mb;
+ int rv;
+
+ secret = fd->secret;
+ mb = &secret->writebuf;
+ PR_ASSERT(mb->bufsize);
+
+ if (mb->last_err) {
+ PR_SetError(mb->last_err, 0);
+ return -1;
+ }
+ rv = memio_buffer_put(mb, buf, len);
+ if (rv == 0) {
+ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+ return -1;
+ }
+ return rv;
+}
+
+static int PR_CALLBACK memio_Write(PRFileDesc *fd, const void *buf, PRInt32 len)
+{
+ /* append bytes to buffer */
+ return memio_Send(fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT);
+}
+
+static PRStatus PR_CALLBACK memio_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ /* TODO: fail if memio_SetPeerName has not been called */
+ struct PRFilePrivate *secret = fd->secret;
+ *addr = secret->peername;
+ return PR_SUCCESS;
+}
+
+static PRStatus memio_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
+{
+ /*
+ * Even in the original version for real tcp sockets,
+ * PR_SockOpt_Nonblocking is a special case that does not
+ * translate to a getsockopt() call
+ */
+ if (PR_SockOpt_Nonblocking == data->option) {
+ data->value.non_blocking = PR_TRUE;
+ return PR_SUCCESS;
+ }
+ PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+/*--------------- private memio data -----------------------*/
+
+/*
+ * Implement just the bare minimum number of methods needed to make ssl happy.
+ *
+ * Oddly, PR_Recv calls ssl_Recv calls ssl_SocketIsBlocking calls
+ * PR_GetSocketOption, so we have to provide an implementation of
+ * PR_GetSocketOption that just says "I'm nonblocking".
+ */
+
+static struct PRIOMethods memio_layer_methods = {
+ PR_DESC_LAYERED,
+ memio_Close,
+ memio_Read,
+ memio_Write,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ memio_Shutdown,
+ memio_Recv,
+ memio_Send,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ memio_GetPeerName,
+ NULL,
+ NULL,
+ memio_GetSocketOption,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static PRDescIdentity memio_identity = PR_INVALID_IO_LAYER;
+
+static PRStatus memio_InitializeLayerName(void)
+{
+ memio_identity = PR_GetUniqueIdentity("memio");
+ return PR_SUCCESS;
+}
+
+/*--------------- public memio functions -----------------------*/
+
+PRFileDesc *memio_CreateIOLayer(int bufsize)
+{
+ PRFileDesc *fd;
+ struct PRFilePrivate *secret;
+ static PRCallOnceType once;
+
+ PR_CallOnce(&once, memio_InitializeLayerName);
+
+ fd = PR_CreateIOLayerStub(memio_identity, &memio_layer_methods);
+ secret = malloc(sizeof(struct PRFilePrivate));
+ memset(secret, 0, sizeof(*secret));
+
+ memio_buffer_new(&secret->readbuf, bufsize);
+ memio_buffer_new(&secret->writebuf, bufsize);
+ fd->secret = secret;
+ return fd;
+}
+
+void memio_SetPeerName(PRFileDesc *fd, const PRNetAddr *peername)
+{
+ PRFileDesc *memiofd = PR_GetIdentitiesLayer(fd, memio_identity);
+ struct PRFilePrivate *secret = memiofd->secret;
+ secret->peername = *peername;
+}
+
+memio_Private *memio_GetSecret(PRFileDesc *fd)
+{
+ PRFileDesc *memiofd = PR_GetIdentitiesLayer(fd, memio_identity);
+ struct PRFilePrivate *secret = memiofd->secret;
+ return (memio_Private *)secret;
+}
+
+int memio_GetReadParams(memio_Private *secret, char **buf)
+{
+ struct memio_buffer* mb = &((PRFilePrivate *)secret)->readbuf;
+ PR_ASSERT(mb->bufsize);
+
+ *buf = &mb->buf[mb->tail];
+ return memio_buffer_unused_contiguous(mb);
+}
+
+void memio_PutReadResult(memio_Private *secret, int bytes_read)
+{
+ struct memio_buffer* mb = &((PRFilePrivate *)secret)->readbuf;
+ PR_ASSERT(mb->bufsize);
+
+ if (bytes_read > 0) {
+ mb->tail += bytes_read;
+ if (mb->tail == mb->bufsize)
+ mb->tail = 0;
+ } else if (bytes_read == 0) {
+ /* Record EOF condition and report to caller when buffer runs dry */
+ ((PRFilePrivate *)secret)->eof = PR_TRUE;
+ } else /* if (bytes_read < 0) */ {
+ mb->last_err = bytes_read;
+ }
+}
+
+int memio_GetWriteParams(memio_Private *secret, const char **buf)
+{
+ struct memio_buffer* mb = &((PRFilePrivate *)secret)->writebuf;
+ PR_ASSERT(mb->bufsize);
+
+ *buf = &mb->buf[mb->head];
+ return memio_buffer_used_contiguous(mb);
+}
+
+void memio_PutWriteResult(memio_Private *secret, int bytes_written)
+{
+ struct memio_buffer* mb = &((PRFilePrivate *)secret)->writebuf;
+ PR_ASSERT(mb->bufsize);
+
+ if (bytes_written > 0) {
+ mb->head += bytes_written;
+ if (mb->head == mb->bufsize)
+ mb->head = 0;
+ } else if (bytes_written < 0) {
+ mb->last_err = bytes_written;
+ }
+}
+
+/*--------------- private memio_buffer self-test -----------------*/
+
+/* Even a trivial unit test is very helpful when doing circular buffers. */
+/*#define TRIVIAL_SELF_TEST*/
+#ifdef TRIVIAL_SELF_TEST
+#include <stdio.h>
+
+#define TEST_BUFLEN 7
+
+#define CHECKEQ(a, b) { \
+ if ((a) != (b)) { \
+ printf("%d != %d, Test failed line %d\n", a, b, __LINE__); \
+ exit(1); \
+ } \
+}
+
+int main()
+{
+ struct memio_buffer mb;
+ char buf[100];
+ int i;
+
+ memio_buffer_new(&mb, TEST_BUFLEN);
+
+ CHECKEQ(memio_buffer_empty(&mb), TRUE);
+ CHECKEQ(memio_buffer_full(&mb), FALSE);
+ CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1);
+ CHECKEQ(memio_buffer_unused(&mb), TEST_BUFLEN-1);
+ CHECKEQ(memio_buffer_used_contiguous(&mb), 0);
+ CHECKEQ(memio_buffer_used(&mb), 0);
+
+ CHECKEQ(memio_buffer_put(&mb, "howdy", 5), 5);
+
+ CHECKEQ(memio_buffer_empty(&mb), FALSE);
+ CHECKEQ(memio_buffer_full(&mb), FALSE);
+ CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1-5);
+ CHECKEQ(memio_buffer_unused(&mb), TEST_BUFLEN-1-5);
+ CHECKEQ(memio_buffer_used_contiguous(&mb), 5);
+ CHECKEQ(memio_buffer_used(&mb), 5);
+
+ CHECKEQ(memio_buffer_put(&mb, "!", 1), 1);
+
+ CHECKEQ(memio_buffer_empty(&mb), FALSE);
+ CHECKEQ(memio_buffer_full(&mb), TRUE);
+ CHECKEQ(memio_buffer_unused_contiguous(&mb), 0);
+ CHECKEQ(memio_buffer_unused(&mb), 0);
+ CHECKEQ(memio_buffer_used_contiguous(&mb), 6);
+ CHECKEQ(memio_buffer_used(&mb), 6);
+
+ CHECKEQ(memio_buffer_get(&mb, buf, 6), 6);
+ CHECKEQ(memcmp(buf, "howdy!", 6), 0);
+
+ CHECKEQ(memio_buffer_empty(&mb), TRUE);
+ CHECKEQ(memio_buffer_full(&mb), FALSE);
+ CHECKEQ(memio_buffer_unused(&mb), TEST_BUFLEN-1);
+ CHECKEQ(memio_buffer_unused_contiguous(&mb), 1);
+ CHECKEQ(memio_buffer_used_contiguous(&mb), 0);
+ CHECKEQ(memio_buffer_used(&mb), 0);
+
+ CHECKEQ(memio_buffer_put(&mb, "01234", 5), 5);
+
+ CHECKEQ(memio_buffer_empty(&mb), FALSE);
+ CHECKEQ(memio_buffer_full(&mb), FALSE);
+ CHECKEQ(memio_buffer_used(&mb), 5);
+ CHECKEQ(memio_buffer_used_contiguous(&mb), 1);
+ CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1-5);
+ CHECKEQ(memio_buffer_unused(&mb), TEST_BUFLEN-1-5);
+
+ CHECKEQ(memio_buffer_put(&mb, "5", 1), 1);
+
+ CHECKEQ(memio_buffer_empty(&mb), FALSE);
+ CHECKEQ(memio_buffer_full(&mb), TRUE);
+ CHECKEQ(memio_buffer_unused_contiguous(&mb), 0);
+ CHECKEQ(memio_buffer_unused(&mb), 0);
+ CHECKEQ(memio_buffer_used_contiguous(&mb), 1);
+ CHECKEQ(memio_buffer_used(&mb), 6);
+
+ /* TODO: add more cases */
+
+ printf("Test passed\n");
+ exit(0);
+}
+
+#endif
diff --git a/net/base/nss_memio.h b/net/base/nss_memio.h
new file mode 100644
index 0000000..b766007
--- /dev/null
+++ b/net/base/nss_memio.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Written in NSPR style to also be suitable for adding to the NSS demo suite
+
+#ifndef __MEMIO_H
+#define __MEMIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "prio.h"
+
+/* Opaque structure. Really just a more typesafe alias for PRFilePrivate. */
+struct memio_Private;
+typedef struct memio_Private memio_Private;
+
+/*----------------------------------------------------------------------
+ NSPR I/O layer that terminates in a pair of circular buffers
+ rather than talking to the real network.
+ To use this with NSS:
+ 1) call memio_CreateIOLayer to create a fake NSPR socket
+ 2) call SSL_ImportFD to ssl-ify the socket
+ 3) Do your own networking calls to set up a TCP connection
+ 4) call memio_SetPeerName to tell NSS about the other end of the connection
+ 5) While at the same time doing plaintext nonblocking NSPR I/O as
+ usual to the nspr file descriptor returned by SSL_ImportFD,
+ your app must shuttle encrypted data between
+ the real network and memio's network buffers.
+ memio_GetReadParams/memio_PutReadResult
+ are the hooks you need to pump data into memio's input buffer,
+ and memio_GetWriteParams/memio_PutWriteResult
+ are the hooks you need to pump data out of memio's output buffer.
+----------------------------------------------------------------------*/
+
+/* Create the I/O layer and its two circular buffers. */
+PRFileDesc *memio_CreateIOLayer(int bufsize);
+
+/* Must call before trying to make an ssl connection */
+void memio_SetPeerName(PRFileDesc *fd, const PRNetAddr *peername);
+
+/* Return a private pointer needed by the following
+ * four functions. (We could have passed a PRFileDesc to
+ * them, but that would be slower. Better for the caller
+ * to grab the pointer once and cache it.
+ * This may be a premature optimization.)
+ */
+memio_Private *memio_GetSecret(PRFileDesc *fd);
+
+/* Ask memio where to put bytes from the network, and how many it can handle.
+ * Returns bytes available to write, or 0 if none available.
+ * Puts current buffer position into *buf.
+ */
+int memio_GetReadParams(memio_Private *secret, char **buf);
+
+/* Tell memio how many bytes were read from the network.
+ * If bytes_read is 0, causes EOF to be reported to
+ * NSS after it reads the last byte from the circular buffer.
+ * If bytes_read is < 0, it is treated as an NSPR error code.
+ * See nspr/pr/src/md/unix/unix_errors.c for how to
+ * map from Unix errors to NSPR error codes.
+ * On EWOULDBLOCK or the equivalent, don't call this function.
+ */
+void memio_PutReadResult(memio_Private *secret, int bytes_read);
+
+/* Ask memio what data it has to send to the network.
+ * Returns buffer space available to read into, or 0 if none available.
+ * Puts current buffer position into *buf.
+ */
+int memio_GetWriteParams(memio_Private *secret, const char **buf);
+
+/* Tell memio how many bytes were sent to the network.
+ * If bytes_written is < 0, it is treated as an NSPR error code.
+ * See nspr/pr/src/md/unix/unix_errors.c for how to
+ * map from Unix errors to NSPR error codes.
+ * On EWOULDBLOCK or the equivalent, don't call this function.
+ */
+void memio_PutWriteResult(memio_Private *secret, int bytes_written);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/net/base/ssl_client_socket_nss.cc b/net/base/ssl_client_socket_nss.cc
new file mode 100644
index 0000000..44fd1e9
--- /dev/null
+++ b/net/base/ssl_client_socket_nss.cc
@@ -0,0 +1,395 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/ssl_client_socket_nss.h"
+
+#include <nspr.h>
+#include <nss.h>
+// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
+// until NSS 3.12.2 comes out and we update to it.
+#define Lock FOO_NSS_Lock
+#include <ssl.h>
+#include <pk11pub.h>
+#undef Lock
+
+#include "base/logging.h"
+#include "base/nss_init.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/ssl_info.h"
+
+static const int kRecvBufferSize = 4096;
+
+/*
+ * nss calls this if an incoming certificate is invalid.
+ * TODO(port): expose to app via GetSSLInfo so it can put up
+ * the appropriate GUI and retry with override if desired
+ */
+static SECStatus
+ownBadCertHandler(void * arg, PRFileDesc * socket)
+{
+ PRErrorCode err = PR_GetError();
+ LOG(ERROR) << "server certificate is invalid; NSS error code " << err;
+ // Return SECSuccess to override the problem, SECFailure to let the original function fail
+ return SECSuccess; /* override, say it's OK. */
+}
+
+
+namespace net {
+
+bool SSLClientSocketNSS::nss_options_initialized_ = false;
+
+SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
+ const std::string& hostname,
+ const SSLConfig& ssl_config)
+ :
+ buffer_send_callback_(this, &SSLClientSocketNSS::BufferSendComplete),
+ buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete),
+ transport_send_busy_(false),
+ transport_recv_busy_(false),
+ io_callback_(this, &SSLClientSocketNSS::OnIOComplete),
+ transport_(transport_socket),
+ hostname_(hostname),
+ ssl_config_(ssl_config),
+ user_callback_(NULL),
+ user_buf_(NULL),
+ user_buf_len_(0),
+ completed_handshake_(false),
+ next_state_(STATE_NONE),
+ nss_fd_(NULL),
+ nss_bufs_(NULL) {
+}
+
+SSLClientSocketNSS::~SSLClientSocketNSS() {
+ Disconnect();
+}
+
+int SSLClientSocketNSS::Init() {
+ // Call NSS_NoDB_Init() in a threadsafe way.
+ base::EnsureNSSInit();
+
+ return OK;
+}
+
+int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
+ DCHECK(transport_.get());
+ DCHECK(next_state_ == STATE_NONE);
+ DCHECK(!user_callback_);
+
+ next_state_ = STATE_CONNECT;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+int SSLClientSocketNSS::ReconnectIgnoringLastError(CompletionCallback* callback) {
+ // TODO(darin): implement me!
+ return ERR_FAILED;
+}
+
+void SSLClientSocketNSS::Disconnect() {
+ // TODO(wtc): Send SSL close_notify alert.
+ if (nss_fd_ != NULL) {
+ PR_Close(nss_fd_);
+ nss_fd_ = NULL;
+ }
+ completed_handshake_ = false;
+ transport_->Disconnect();
+}
+
+bool SSLClientSocketNSS::IsConnected() const {
+ return completed_handshake_ && transport_->IsConnected();
+}
+
+int SSLClientSocketNSS::Read(char* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(completed_handshake_);
+ DCHECK(next_state_ == STATE_NONE);
+ DCHECK(!user_callback_);
+
+ user_buf_ = buf;
+ user_buf_len_ = buf_len;
+
+ next_state_ = STATE_PAYLOAD_READ;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+int SSLClientSocketNSS::Write(const char* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(completed_handshake_);
+ DCHECK(next_state_ == STATE_NONE);
+ DCHECK(!user_callback_);
+
+ user_buf_ = const_cast<char*>(buf);
+ user_buf_len_ = buf_len;
+
+ next_state_ = STATE_PAYLOAD_WRITE;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
+ // TODO(port): implement!
+ ssl_info->Reset();
+}
+
+void SSLClientSocketNSS::DoCallback(int rv) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(user_callback_);
+
+ // since Run may result in Read being called, clear user_callback_ up front.
+ CompletionCallback* c = user_callback_;
+ user_callback_ = NULL;
+ c->Run(rv);
+}
+
+void SSLClientSocketNSS::OnIOComplete(int result) {
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING)
+ DoCallback(rv);
+}
+
+// Map a Chromium net error code to an NSS error code
+// See _MD_unix_map_default_error in the NSS source
+// tree for inspiration.
+static PRErrorCode MapErrorToNSS(int result) {
+ if (result >=0)
+ return result;
+ // TODO(port): add real table
+ LOG(ERROR) << "MapErrorToNSS " << result;
+ return PR_UNKNOWN_ERROR;
+}
+
+/*
+ * Do network I/O between the given buffer and the given socket.
+ * Return 0 for EOF,
+ * > 0 for bytes transferred immediately,
+ * < 0 for error (or the non-error ERR_IO_PENDING).
+ */
+int SSLClientSocketNSS::BufferSend(void) {
+ if (transport_send_busy_) return ERR_IO_PENDING;
+
+ const char *buf;
+ int nb = memio_GetWriteParams(nss_bufs_, &buf);
+
+ int rv;
+ if (!nb) {
+ rv = OK;
+ } else {
+ rv = transport_->Write(buf, nb, &buffer_send_callback_);
+ if (rv == ERR_IO_PENDING)
+ transport_send_busy_ = true;
+ else
+ memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv));
+ }
+
+ return rv;
+}
+
+void SSLClientSocketNSS::BufferSendComplete(int result) {
+ memio_PutWriteResult(nss_bufs_, result);
+ transport_send_busy_ = false;
+ OnIOComplete(result);
+}
+
+
+int SSLClientSocketNSS::BufferRecv(void) {
+
+ if (transport_recv_busy_) return ERR_IO_PENDING;
+
+ char *buf;
+ int nb = memio_GetReadParams(nss_bufs_, &buf);
+ int rv;
+ if (!nb) {
+ // buffer too full to read into, so no I/O possible at moment
+ rv = ERR_IO_PENDING;
+ } else {
+ rv = transport_->Read(buf, nb, &buffer_recv_callback_);
+ if (rv == ERR_IO_PENDING)
+ transport_recv_busy_ = true;
+ else
+ memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv));
+ }
+
+ return rv;
+}
+
+void SSLClientSocketNSS::BufferRecvComplete(int result) {
+ memio_PutReadResult(nss_bufs_, result);
+ transport_recv_busy_ = false;
+ OnIOComplete(result);
+}
+
+
+int SSLClientSocketNSS::DoLoop(int last_io_result) {
+ DCHECK(next_state_ != STATE_NONE);
+ bool network_moved;
+ int rv = last_io_result;
+ do {
+ network_moved = false;
+ State state = next_state_;
+ //DLOG(INFO) << "DoLoop state " << state;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_CONNECT:
+ rv = DoConnect();
+ break;
+ case STATE_CONNECT_COMPLETE:
+ rv = DoConnectComplete(rv);
+ break;
+ case STATE_HANDSHAKE_READ:
+ rv = DoHandshakeRead();
+ break;
+ case STATE_PAYLOAD_READ:
+ rv = DoPayloadRead();
+ break;
+ case STATE_PAYLOAD_WRITE:
+ rv = DoPayloadWrite();
+ break;
+ default:
+ rv = ERR_UNEXPECTED;
+ NOTREACHED() << "unexpected state";
+ break;
+ }
+
+ // Do the actual network I/O
+ if (nss_bufs_ != NULL) {
+ int nsent = BufferSend();
+ int nreceived = BufferRecv();
+ network_moved = (nsent > 0 || nreceived >= 0);
+ }
+ } while ((rv != ERR_IO_PENDING || network_moved) && next_state_ != STATE_NONE);
+ return rv;
+}
+
+int SSLClientSocketNSS::DoConnect() {
+ next_state_ = STATE_CONNECT_COMPLETE;
+ return transport_->Connect(&io_callback_);
+}
+
+int SSLClientSocketNSS::DoConnectComplete(int result) {
+ if (result < 0)
+ return result;
+
+ if (Init() != OK) {
+ NOTREACHED() << "Couldn't initialize nss";
+ }
+
+ // Transport connected, now hook it up to nss
+ // TODO(port): specify rx and tx buffer sizes separately
+ nss_fd_ = memio_CreateIOLayer(kRecvBufferSize);
+ if (nss_fd_ == NULL) {
+ return 9999; // TODO(port): real error
+ }
+
+ // Tell NSS who we're connected to
+ PRNetAddr peername;
+ socklen_t len = sizeof(PRNetAddr);
+ int err = transport_->GetPeerName((struct sockaddr *)&peername, &len);
+ if (err) {
+ DLOG(ERROR) << "GetPeerName failed";
+ return 9999; // TODO(port): real error
+ }
+ memio_SetPeerName(nss_fd_, &peername);
+
+ // Grab pointer to buffers
+ nss_bufs_ = memio_GetSecret(nss_fd_);
+
+ /* Create SSL state machine */
+ /* Push SSL onto our fake I/O socket */
+ nss_fd_ = SSL_ImportFD(NULL, nss_fd_);
+ if (nss_fd_ == NULL) {
+ return ERR_SSL_PROTOCOL_ERROR; // TODO(port): real error
+ }
+ // TODO(port): set more ssl options! Check errors!
+
+ int rv;
+
+ rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, ssl_config_.ssl2_enabled);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.tls1_enabled);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ rv = SSL_BadCertHook(nss_fd_, ownBadCertHandler, NULL);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ // Tell SSL the hostname we're trying to connect to.
+ SSL_SetURL(nss_fd_, hostname_.c_str());
+
+ // Tell SSL we're a client; needed if not letting NSPR do socket I/O
+ SSL_ResetHandshake(nss_fd_, 0);
+ next_state_ = STATE_HANDSHAKE_READ;
+ // Return OK so DoLoop tries handshaking
+ return OK;
+}
+
+int SSLClientSocketNSS::DoHandshakeRead() {
+ int rv = SSL_ForceHandshake(nss_fd_);
+ if (rv == SECSuccess) {
+ // there's a callback for this, too
+ completed_handshake_ = true;
+ // Indicate we're ready to handle I/O. Badly named?
+ next_state_ = STATE_NONE;
+ return OK;
+ }
+ PRErrorCode prerr = PR_GetError();
+ if (prerr == PR_WOULD_BLOCK_ERROR) {
+ // at this point, it should have tried to send some bytes
+ next_state_ = STATE_HANDSHAKE_READ;
+ return ERR_IO_PENDING;
+ }
+ // TODO: map rv to net error code properly
+ return ERR_SSL_PROTOCOL_ERROR;
+}
+
+int SSLClientSocketNSS::DoPayloadRead() {
+ int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_);
+ if (rv >= 0)
+ return rv;
+ PRErrorCode prerr = PR_GetError();
+ if (prerr == PR_WOULD_BLOCK_ERROR) {
+ next_state_ = STATE_PAYLOAD_READ;
+ return ERR_IO_PENDING;
+ }
+ // TODO: map rv to net error code properly
+ return ERR_SSL_PROTOCOL_ERROR;
+}
+
+int SSLClientSocketNSS::DoPayloadWrite() {
+ int rv = PR_Write(nss_fd_, user_buf_, user_buf_len_);
+ if (rv >= 0)
+ return rv;
+ PRErrorCode prerr = PR_GetError();
+ if (prerr == PR_WOULD_BLOCK_ERROR) {
+ next_state_ = STATE_PAYLOAD_WRITE;
+ return ERR_IO_PENDING;
+ }
+ // TODO: map rv to net error code properly
+ return ERR_SSL_PROTOCOL_ERROR;
+}
+
+} // namespace net
+
diff --git a/net/base/ssl_client_socket_nss.h b/net/base/ssl_client_socket_nss.h
new file mode 100644
index 0000000..41098f3
--- /dev/null
+++ b/net/base/ssl_client_socket_nss.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_SSL_CLIENT_SOCKET_NSS_H_
+#define NET_BASE_SSL_CLIENT_SOCKET_NSS_H_
+
+#include "build/build_config.h"
+
+#include <prio.h>
+#include "net/base/nss_memio.h"
+
+#include <string>
+
+#include "base/scoped_ptr.h"
+#include "net/base/completion_callback.h"
+#include "net/base/ssl_client_socket.h"
+#include "net/base/ssl_config_service.h"
+
+namespace net {
+
+// An SSL client socket implemented with Mozilla NSS.
+class SSLClientSocketNSS : public SSLClientSocket {
+ public:
+ // Takes ownership of the transport_socket, which may already be connected.
+ // The given hostname will be compared with the name(s) in the server's
+ // certificate during the SSL handshake. ssl_config specifies the SSL
+ // settings.
+ SSLClientSocketNSS(ClientSocket* transport_socket,
+ const std::string& hostname,
+ const SSLConfig& ssl_config);
+ ~SSLClientSocketNSS();
+
+ // SSLClientSocket methods:
+ virtual void GetSSLInfo(SSLInfo* ssl_info);
+
+ // ClientSocket methods:
+ virtual int Connect(CompletionCallback* callback);
+ virtual int ReconnectIgnoringLastError(CompletionCallback* callback);
+ virtual void Disconnect();
+ virtual bool IsConnected() const;
+
+ // Socket methods:
+ virtual int Read(char* buf, int buf_len, CompletionCallback* callback);
+ virtual int Write(const char* buf, int buf_len, CompletionCallback* callback);
+
+ private:
+ void DoCallback(int result);
+ void OnIOComplete(int result);
+
+ int DoLoop(int last_io_result);
+ int DoConnect();
+ int DoConnectComplete(int result);
+ int DoHandshakeRead();
+ int DoPayloadRead();
+ int DoPayloadWrite();
+ int Init();
+ int BufferSend(void);
+ int BufferRecv(void);
+ void BufferSendComplete(int result);
+ void BufferRecvComplete(int result);
+
+ CompletionCallbackImpl<SSLClientSocketNSS> buffer_send_callback_;
+ CompletionCallbackImpl<SSLClientSocketNSS> buffer_recv_callback_;
+ bool transport_send_busy_;
+ bool transport_recv_busy_;
+
+ CompletionCallbackImpl<SSLClientSocketNSS> io_callback_;
+ scoped_ptr<ClientSocket> transport_;
+ std::string hostname_;
+ SSLConfig ssl_config_;
+
+ CompletionCallback* user_callback_;
+
+ // Used by both Read and Write functions.
+ char* user_buf_;
+ int user_buf_len_;
+
+ bool completed_handshake_;
+
+ enum State {
+ STATE_NONE,
+ STATE_CONNECT,
+ STATE_CONNECT_COMPLETE,
+ STATE_HANDSHAKE_READ,
+ // No STATE_HANDSHAKE_READ_COMPLETE needed, go to STATE_NONE instead.
+ STATE_PAYLOAD_WRITE,
+ STATE_PAYLOAD_READ,
+ };
+ State next_state_;
+
+ /* The NSS SSL state machine */
+ PRFileDesc* nss_fd_;
+
+ /* Buffers for the network end of the SSL state machine */
+ memio_Private* nss_bufs_;
+
+ static bool nss_options_initialized_;
+};
+
+} // namespace net
+
+#endif // NET_BASE_SSL_CLIENT_SOCKET_NSS_H_
+
diff --git a/net/base/ssl_client_socket_unittest.cc b/net/base/ssl_client_socket_unittest.cc
index 032ed4a..8e60b42 100644
--- a/net/base/ssl_client_socket_unittest.cc
+++ b/net/base/ssl_client_socket_unittest.cc
@@ -88,6 +88,7 @@ TEST_F(SSLClientSocketTest, DISABLED_Read) {
rv = callback.WaitForResult();
EXPECT_EQ(net::OK, rv);
}
+ EXPECT_TRUE(sock->IsConnected());
const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
rv = sock->Write(request_text, arraysize(request_text) - 1, &callback);
diff --git a/net/base/tcp_client_socket.h b/net/base/tcp_client_socket.h
index ebd10df..05a433e 100644
--- a/net/base/tcp_client_socket.h
+++ b/net/base/tcp_client_socket.h
@@ -12,6 +12,7 @@
#include "base/object_watcher.h"
#elif defined(OS_POSIX)
struct event; // From libevent
+#include <sys/socket.h> // for struct sockaddr
#define SOCKET int
#include "base/message_pump_libevent.h"
#endif
@@ -25,8 +26,10 @@ namespace net {
// A client socket that uses TCP as the transport layer.
//
-// NOTE: The implementation supports half duplex only. Read and Write calls
-// must not be in progress at the same time.
+// NOTE: The windows implementation supports half duplex only.
+// Read and Write calls must not be in progress at the same time.
+// The libevent implementation supports full duplex because that
+// made it slightly easier to implement ssl.
class TCPClientSocket : public ClientSocket,
#if defined(OS_WIN)
public base::ObjectWatcher::Delegate
@@ -49,11 +52,19 @@ class TCPClientSocket : public ClientSocket,
virtual bool IsConnected() const;
// Socket methods:
- // Multiple outstanding requests are not supported. In particular, full
- // duplex mode (reading and writing at the same time) is not supported.
+ // Multiple outstanding requests are not supported.
+ // Full duplex mode (reading and writing at the same time) is not supported
+ // on Windows (but is supported on Linux and Mac for ease of implementation
+ // of SSLClientSocket)
virtual int Read(char* buf, int buf_len, CompletionCallback* callback);
virtual int Write(const char* buf, int buf_len, CompletionCallback* callback);
+#if defined(OS_POSIX)
+ // Identical to posix system call of same name
+ // Needed by ssl_client_socket_nss
+ virtual int GetPeerName(struct sockaddr *name, socklen_t *namelen);
+#endif
+
private:
SOCKET socket_;
@@ -63,6 +74,7 @@ class TCPClientSocket : public ClientSocket,
// Where we are in above list, or NULL if all addrinfos have been tried.
const struct addrinfo* current_ai_;
+#if defined(OS_WIN)
enum WaitState {
NOT_WAITING,
WAITING_CONNECT,
@@ -71,7 +83,6 @@ class TCPClientSocket : public ClientSocket,
};
WaitState wait_state_;
-#if defined(OS_WIN)
// base::ObjectWatcher::Delegate methods:
virtual void OnObjectSignaled(HANDLE object);
@@ -79,25 +90,40 @@ class TCPClientSocket : public ClientSocket,
WSABUF buffer_;
base::ObjectWatcher watcher_;
+
+ void DidCompleteIO();
#elif defined(OS_POSIX)
+ // Whether we're currently waiting for connect() to complete
+ bool waiting_connect_;
+
// The socket's libevent wrapper
scoped_ptr<event> event_;
// Called by MessagePumpLibevent when the socket is ready to do I/O
void OnSocketReady(short flags);
- // The buffer used by OnSocketReady to retry Read and Write requests
+ // The buffer used by OnSocketReady to retry Read requests
char* buf_;
int buf_len_;
+
+ // The buffer used by OnSocketReady to retry Write requests
+ const char* write_buf_;
+ int write_buf_len_;
+
+ // External callback; called when write is complete.
+ CompletionCallback* write_callback_;
+
+ void DoWriteCallback(int rv);
+ void DidCompleteRead();
+ void DidCompleteWrite();
#endif
- // External callback; called when read or write is complete.
+ // External callback; called when read (and on Windows, write) is complete.
CompletionCallback* callback_;
int CreateSocket(const struct addrinfo* ai);
void DoCallback(int rv);
void DidCompleteConnect();
- void DidCompleteIO();
};
} // namespace net
diff --git a/net/base/tcp_client_socket_libevent.cc b/net/base/tcp_client_socket_libevent.cc
index abe1625..2bf3f8d 100644
--- a/net/base/tcp_client_socket_libevent.cc
+++ b/net/base/tcp_client_socket_libevent.cc
@@ -67,8 +67,10 @@ TCPClientSocket::TCPClientSocket(const AddressList& addresses)
: socket_(kInvalidSocket),
addresses_(addresses),
current_ai_(addresses_.head()),
- wait_state_(NOT_WAITING),
- event_(new event) {
+ waiting_connect_(false),
+ event_(new event),
+ write_callback_(NULL),
+ callback_(NULL) {
}
TCPClientSocket::~TCPClientSocket() {
@@ -80,7 +82,7 @@ int TCPClientSocket::Connect(CompletionCallback* callback) {
if (socket_ != kInvalidSocket)
return OK;
- DCHECK(wait_state_ == NOT_WAITING);
+ DCHECK(!waiting_connect_);
const addrinfo* ai = current_ai_;
DCHECK(ai);
@@ -98,7 +100,7 @@ int TCPClientSocket::Connect(CompletionCallback* callback) {
DCHECK(callback);
if (errno != EINPROGRESS) {
- LOG(ERROR) << "connect failed: " << errno;
+ DLOG(INFO) << "connect failed: " << errno;
return MapPosixError(errno);
}
@@ -109,7 +111,7 @@ int TCPClientSocket::Connect(CompletionCallback* callback) {
MessageLoopForIO::current()->WatchSocket(
socket_, EV_READ|EV_WRITE|EV_PERSIST, event_.get(), this);
- wait_state_ = WAITING_CONNECT;
+ waiting_connect_ = true;
callback_ = callback;
return ERR_IO_PENDING;
}
@@ -126,13 +128,14 @@ void TCPClientSocket::Disconnect() {
MessageLoopForIO::current()->UnwatchSocket(event_.get());
close(socket_);
socket_ = kInvalidSocket;
+ waiting_connect_ = false;
// Reset for next time.
current_ai_ = addresses_.head();
}
bool TCPClientSocket::IsConnected() const {
- if (socket_ == kInvalidSocket || wait_state_ == WAITING_CONNECT)
+ if (socket_ == kInvalidSocket || waiting_connect_)
return false;
// Check if connection is alive.
@@ -150,7 +153,7 @@ int TCPClientSocket::Read(char* buf,
int buf_len,
CompletionCallback* callback) {
DCHECK(socket_ != kInvalidSocket);
- DCHECK(wait_state_ == NOT_WAITING);
+ DCHECK(!waiting_connect_);
DCHECK(!callback_);
// Synchronous operation not supported
DCHECK(callback);
@@ -160,15 +163,16 @@ int TCPClientSocket::Read(char* buf,
if (nread >= 0) {
return nread;
}
- if (errno != EAGAIN && errno != EWOULDBLOCK)
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ DLOG(INFO) << "read failed, errno " << errno;
return MapPosixError(errno);
+ }
MessageLoopForIO::current()->WatchSocket(
socket_, EV_READ|EV_PERSIST, event_.get(), this);
buf_ = buf;
buf_len_ = buf_len;
- wait_state_ = WAITING_READ;
callback_ = callback;
return ERR_IO_PENDING;
}
@@ -177,8 +181,8 @@ int TCPClientSocket::Write(const char* buf,
int buf_len,
CompletionCallback* callback) {
DCHECK(socket_ != kInvalidSocket);
- DCHECK(wait_state_ == NOT_WAITING);
- DCHECK(!callback_);
+ DCHECK(!waiting_connect_);
+ DCHECK(!write_callback_);
// Synchronous operation not supported
DCHECK(callback);
DCHECK(buf_len > 0);
@@ -193,10 +197,9 @@ int TCPClientSocket::Write(const char* buf,
MessageLoopForIO::current()->WatchSocket(
socket_, EV_WRITE|EV_PERSIST, event_.get(), this);
- buf_ = const_cast<char*>(buf);
- buf_len_ = buf_len;
- wait_state_ = WAITING_WRITE;
- callback_ = callback;
+ write_buf_ = buf;
+ write_buf_len_ = buf_len;
+ write_callback_ = callback;
return ERR_IO_PENDING;
}
@@ -222,11 +225,19 @@ void TCPClientSocket::DoCallback(int rv) {
c->Run(rv);
}
+void TCPClientSocket::DoWriteCallback(int rv) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(write_callback_);
+
+ // since Run may result in Write being called, clear write_callback_ up front.
+ CompletionCallback* c = write_callback_;
+ write_callback_ = NULL;
+ c->Run(rv);
+}
+
void TCPClientSocket::DidCompleteConnect() {
int result = ERR_UNEXPECTED;
- wait_state_ = NOT_WAITING;
-
// Check to see if connect succeeded
int error_code = 0;
socklen_t len = sizeof(error_code);
@@ -236,7 +247,6 @@ void TCPClientSocket::DidCompleteConnect() {
if (error_code == EINPROGRESS || error_code == EALREADY) {
NOTREACHED(); // This indicates a bug in libevent or our code.
result = ERR_IO_PENDING;
- wait_state_ = WAITING_CONNECT; // And await next callback.
} else if (current_ai_->ai_next && (
error_code == EADDRNOTAVAIL ||
error_code == EAFNOSUPPORT ||
@@ -252,25 +262,16 @@ void TCPClientSocket::DidCompleteConnect() {
} else {
result = MapPosixError(error_code);
MessageLoopForIO::current()->UnwatchSocket(event_.get());
+ waiting_connect_ = false;
}
if (result != ERR_IO_PENDING)
DoCallback(result);
}
-void TCPClientSocket::DidCompleteIO() {
+void TCPClientSocket::DidCompleteRead() {
int bytes_transferred;
- switch (wait_state_) {
- case WAITING_READ:
- bytes_transferred = read(socket_, buf_, buf_len_);
- break;
- case WAITING_WRITE:
- bytes_transferred = write(socket_, buf_, buf_len_);
- break;
- default:
- NOTREACHED();
- return;
- }
+ bytes_transferred = read(socket_, buf_, buf_len_);
int result;
if (bytes_transferred >= 0) {
@@ -280,26 +281,48 @@ void TCPClientSocket::DidCompleteIO() {
}
if (result != ERR_IO_PENDING) {
- wait_state_ = NOT_WAITING;
+ buf_ = NULL;
+ buf_len_ = 0;
MessageLoopForIO::current()->UnwatchSocket(event_.get());
DoCallback(result);
}
}
+void TCPClientSocket::DidCompleteWrite() {
+ int bytes_transferred;
+ bytes_transferred = write(socket_, write_buf_, write_buf_len_);
+
+ int result;
+ if (bytes_transferred >= 0) {
+ result = bytes_transferred;
+ } else {
+ result = MapPosixError(errno);
+ }
+
+ if (result != ERR_IO_PENDING) {
+ write_buf_ = NULL;
+ write_buf_len_ = 0;
+ MessageLoopForIO::current()->UnwatchSocket(event_.get());
+ DoWriteCallback(result);
+ }
+}
+
void TCPClientSocket::OnSocketReady(short flags) {
- switch (wait_state_) {
- case WAITING_CONNECT:
- DidCompleteConnect();
- break;
- case WAITING_READ:
- case WAITING_WRITE:
- DidCompleteIO();
- break;
- default:
- NOTREACHED();
- break;
+ // the only used bits of flags are EV_READ and EV_WRITE
+
+ if (waiting_connect_) {
+ DidCompleteConnect();
+ } else {
+ if ((flags & EV_WRITE) && write_callback_)
+ DidCompleteWrite();
+ if ((flags & EV_READ) && callback_)
+ DidCompleteRead();
}
}
+int TCPClientSocket::GetPeerName(struct sockaddr *name, socklen_t *namelen) {
+ return ::getpeername(socket_, name, namelen);
+}
+
} // namespace net
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
new file mode 100644
index 0000000..3906ae1
--- /dev/null
+++ b/net/base/x509_certificate_nss.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/x509_certificate.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+// TODO(port): finish implementing.
+// At the moment, all that's here is just enough to prevent a link error.
+
+X509Certificate::~X509Certificate() {
+ // We might not be in the cache, but it is safe to remove ourselves anyway.
+ X509Certificate::Cache::GetInstance()->Remove(this);
+}
+
+
+} // namespace net
+
diff --git a/net/net_lib.scons b/net/net_lib.scons
index 7de96f0..9f147a9 100644
--- a/net/net_lib.scons
+++ b/net/net_lib.scons
@@ -54,7 +54,6 @@ input_files = [
'base/registry_controlled_domain.cc',
'base/sdch_filter.cc',
'base/sdch_manager.cc',
- 'base/ssl_client_socket_win.cc',
'base/ssl_config_service.cc',
'base/tcp_client_socket.cc',
'base/telnet_server.cc',
@@ -115,11 +114,9 @@ if env['PLATFORM'] in ('posix', 'darwin'):
# Remove files that still need to be ported from the input_files list.
# TODO(port): delete files from this list as they get ported.
to_be_ported_files = [
- 'base/client_socket_factory.cc',
'base/directory_lister.cc',
'base/dns_resolution_observer.cc',
'base/listen_socket.cc',
- 'base/ssl_client_socket_win.cc',
'base/ssl_config_service.cc',
'base/tcp_client_socket.cc',
'base/telnet_server.cc',
@@ -147,6 +144,7 @@ if env['PLATFORM'] == 'win32':
'base/file_input_stream_win.cc',
'base/net_util_win.cc',
'base/platform_mime_util_win.cc',
+ 'base/ssl_client_socket_win.cc',
'base/x509_certificate_win.cc',
'disk_cache/cache_util_win.cc',
'disk_cache/file_win.cc',
@@ -162,8 +160,11 @@ if env['PLATFORM'] == 'darwin':
if env['PLATFORM'] == 'posix':
input_files.extend([
+ 'base/nss_memio.c',
# TODO(tc): gnome-vfs? xdgmime? /etc/mime.types?
'base/platform_mime_util_linux.cc',
+ 'base/ssl_client_socket_nss.cc',
+ 'base/x509_certificate_nss.cc',
])
if env['PLATFORM'] in ('darwin', 'posix'):
diff --git a/net/net_unittests.scons b/net/net_unittests.scons
index dbb0534..8cfcdaa7 100644
--- a/net/net_unittests.scons
+++ b/net/net_unittests.scons
@@ -118,6 +118,11 @@ if env['PLATFORM'] == 'darwin':
'../base/platform_test_mac$OBJSUFFIX',
])
+if env['PLATFORM'] in ('darwin', 'posix'):
+ input_files.extend([
+ 'base/ssl_client_socket_unittest.cc',
+ ])
+
net_unittests = env.ChromeTestProgram('net_unittests', input_files)
installed_test = env.Install('$TARGET_ROOT', net_unittests)