diff options
-rw-r--r-- | base/nss_init.cc | 13 | ||||
-rw-r--r-- | net/base/client_socket.h | 12 | ||||
-rw-r--r-- | net/base/client_socket_factory.cc | 4 | ||||
-rw-r--r-- | net/base/nss_memio.c | 547 | ||||
-rw-r--r-- | net/base/nss_memio.h | 86 | ||||
-rw-r--r-- | net/base/ssl_client_socket_nss.cc | 395 | ||||
-rw-r--r-- | net/base/ssl_client_socket_nss.h | 104 | ||||
-rw-r--r-- | net/base/ssl_client_socket_unittest.cc | 1 | ||||
-rw-r--r-- | net/base/tcp_client_socket.h | 42 | ||||
-rw-r--r-- | net/base/tcp_client_socket_libevent.cc | 107 | ||||
-rw-r--r-- | net/base/x509_certificate_nss.cc | 21 | ||||
-rw-r--r-- | net/net_lib.scons | 7 | ||||
-rw-r--r-- | net/net_unittests.scons | 5 |
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) |