summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)