crypto: use better memory BIO implementation
This commit is contained in:
parent
0f460b03f3
commit
58f93ffc4a
3
node.gyp
3
node.gyp
@ -110,6 +110,7 @@
|
||||
'src/node_buffer.h',
|
||||
'src/node_constants.h',
|
||||
'src/node_crypto.h',
|
||||
'src/node_crypto_bio.h',
|
||||
'src/node_extensions.h',
|
||||
'src/node_file.h',
|
||||
'src/node_http_parser.h',
|
||||
@ -147,7 +148,7 @@
|
||||
'conditions': [
|
||||
[ 'node_use_openssl=="true"', {
|
||||
'defines': [ 'HAVE_OPENSSL=1' ],
|
||||
'sources': [ 'src/node_crypto.cc' ],
|
||||
'sources': [ 'src/node_crypto.cc', 'src/node_crypto_bio.cc' ],
|
||||
'conditions': [
|
||||
[ 'node_shared_openssl=="false"', {
|
||||
'dependencies': [ './deps/openssl/openssl.gyp:openssl' ],
|
||||
|
@ -20,6 +20,7 @@
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "node_crypto.h"
|
||||
#include "node_crypto_bio.h"
|
||||
#include "node_crypto_groups.h"
|
||||
#include "v8.h"
|
||||
|
||||
@ -291,7 +292,7 @@ int SecureContext::NewSessionCallback(SSL* s, SSL_SESSION* sess) {
|
||||
// Takes a string or buffer and loads it into a BIO.
|
||||
// Caller responsible for BIO_free-ing the returned object.
|
||||
static BIO* LoadBIO (Handle<Value> v) {
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
BIO *bio = BIO_new(NodeBIO::GetMethod());
|
||||
if (!bio) return NULL;
|
||||
|
||||
HandleScope scope;
|
||||
@ -544,7 +545,7 @@ Handle<Value> SecureContext::AddRootCerts(const Arguments& args) {
|
||||
root_cert_store = X509_STORE_new();
|
||||
|
||||
for (int i = 0; root_certs[i]; i++) {
|
||||
BIO *bp = BIO_new(BIO_s_mem());
|
||||
BIO *bp = BIO_new(NodeBIO::GetMethod());
|
||||
|
||||
if (!BIO_write(bp, root_certs[i], strlen(root_certs[i]))) {
|
||||
BIO_free(bp);
|
||||
@ -1201,8 +1202,8 @@ Handle<Value> Connection::New(const Arguments& args) {
|
||||
bool is_server = args[1]->BooleanValue();
|
||||
|
||||
p->ssl_ = SSL_new(sc->ctx_);
|
||||
p->bio_read_ = BIO_new(BIO_s_mem());
|
||||
p->bio_write_ = BIO_new(BIO_s_mem());
|
||||
p->bio_read_ = BIO_new(NodeBIO::GetMethod());
|
||||
p->bio_write_ = BIO_new(NodeBIO::GetMethod());
|
||||
|
||||
SSL_set_app_data(p->ssl_, p);
|
||||
|
||||
|
312
src/node_crypto_bio.cc
Normal file
312
src/node_crypto_bio.cc
Normal file
@ -0,0 +1,312 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "node_crypto_bio.h"
|
||||
#include "openssl/bio.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace node {
|
||||
|
||||
BIO_METHOD NodeBIO::method_ = {
|
||||
BIO_TYPE_MEM,
|
||||
"node.js SSL buffer",
|
||||
NodeBIO::Write,
|
||||
NodeBIO::Read,
|
||||
NodeBIO::Puts,
|
||||
NodeBIO::Gets,
|
||||
NodeBIO::Ctrl,
|
||||
NodeBIO::New,
|
||||
NodeBIO::Free,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
int NodeBIO::New(BIO* bio) {
|
||||
bio->ptr = new NodeBIO();
|
||||
|
||||
// XXX Why am I doing it?!
|
||||
bio->shutdown = 1;
|
||||
bio->init = 1;
|
||||
bio->num = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int NodeBIO::Free(BIO* bio) {
|
||||
if (bio == NULL) return 0;
|
||||
|
||||
if (bio->shutdown) {
|
||||
if (bio->init && bio->ptr != NULL) {
|
||||
delete FromBIO(bio);
|
||||
bio->ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int NodeBIO::Read(BIO* bio, char* out, int len) {
|
||||
int bytes;
|
||||
BIO_clear_retry_flags(bio);
|
||||
|
||||
bytes = FromBIO(bio)->Read(out, len);
|
||||
|
||||
if (bytes == 0) {
|
||||
bytes = bio->num;
|
||||
if (bytes != 0) {
|
||||
BIO_set_retry_read(bio);
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
int NodeBIO::Write(BIO* bio, const char* data, int len) {
|
||||
BIO_clear_retry_flags(bio);
|
||||
|
||||
FromBIO(bio)->Write(data, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
int NodeBIO::Puts(BIO* bio, const char* str) {
|
||||
return Write(bio, str, strlen(str));
|
||||
}
|
||||
|
||||
|
||||
int NodeBIO::Gets(BIO* bio, char* out, int size) {
|
||||
NodeBIO* nbio = FromBIO(bio);
|
||||
|
||||
if (nbio->Length() == 0)
|
||||
return 0;
|
||||
|
||||
int i = nbio->IndexOf('\n', size);
|
||||
|
||||
// Include '\n'
|
||||
if (i < size) i++;
|
||||
|
||||
// Shift `i` a bit to NULL-terminate string later
|
||||
if (size == i) i--;
|
||||
|
||||
// Flush read data
|
||||
nbio->Read(out, i);
|
||||
|
||||
out[i] = 0;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
long NodeBIO::Ctrl(BIO* bio, int cmd, long num, void* ptr) {
|
||||
NodeBIO* nbio;
|
||||
long ret;
|
||||
|
||||
nbio = FromBIO(bio);
|
||||
ret = 1;
|
||||
|
||||
switch (cmd) {
|
||||
case BIO_CTRL_RESET:
|
||||
nbio->Reset();
|
||||
break;
|
||||
case BIO_CTRL_EOF:
|
||||
ret = nbio->Length() == 0;
|
||||
break;
|
||||
case BIO_C_SET_BUF_MEM_EOF_RETURN:
|
||||
bio->num = num;
|
||||
break;
|
||||
case BIO_CTRL_INFO:
|
||||
ret = nbio->Length();
|
||||
if (ptr != NULL)
|
||||
*reinterpret_cast<void**>(ptr) = NULL;
|
||||
break;
|
||||
case BIO_C_SET_BUF_MEM:
|
||||
assert(0 && "Can't use SET_BUF_MEM_PTR with NodeBIO");
|
||||
abort();
|
||||
break;
|
||||
case BIO_C_GET_BUF_MEM_PTR:
|
||||
assert(0 && "Can't use GET_BUF_MEM_PTR with NodeBIO");
|
||||
ret = 0;
|
||||
break;
|
||||
case BIO_CTRL_GET_CLOSE:
|
||||
ret = bio->shutdown;
|
||||
break;
|
||||
case BIO_CTRL_SET_CLOSE:
|
||||
bio->shutdown = num;
|
||||
break;
|
||||
case BIO_CTRL_WPENDING:
|
||||
ret = 0;
|
||||
break;
|
||||
case BIO_CTRL_PENDING:
|
||||
ret = nbio->Length();
|
||||
break;
|
||||
case BIO_CTRL_DUP:
|
||||
case BIO_CTRL_FLUSH:
|
||||
ret = 1;
|
||||
break;
|
||||
case BIO_CTRL_PUSH:
|
||||
case BIO_CTRL_POP:
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t NodeBIO::Read(char* out, size_t size) {
|
||||
size_t bytes_read = 0;
|
||||
size_t expected = Length() > size ? size : Length();
|
||||
|
||||
while (bytes_read < expected) {
|
||||
assert(read_head_->read_pos_ <= read_head_->write_pos_);
|
||||
size_t avail = read_head_->write_pos_ - read_head_->read_pos_;
|
||||
if (avail > size)
|
||||
avail = size;
|
||||
|
||||
// Copy data
|
||||
if (out != NULL)
|
||||
memcpy(out, read_head_->data_ + read_head_->read_pos_, avail);
|
||||
read_head_->read_pos_ += avail;
|
||||
|
||||
// Move pointers
|
||||
bytes_read += avail;
|
||||
out += avail;
|
||||
size -= avail;
|
||||
|
||||
// Move to next buffer
|
||||
if (read_head_->read_pos_ == read_head_->write_pos_) {
|
||||
read_head_->read_pos_ = 0;
|
||||
read_head_->write_pos_ = 0;
|
||||
read_head_ = read_head_->next_;
|
||||
}
|
||||
}
|
||||
assert(expected == bytes_read);
|
||||
length_ -= bytes_read;
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
|
||||
size_t NodeBIO::IndexOf(char delim, size_t limit) {
|
||||
size_t bytes_read = 0;
|
||||
size_t max = Length() > limit ? limit : Length();
|
||||
Buffer* current = read_head_;
|
||||
|
||||
while (bytes_read < max) {
|
||||
assert(current->read_pos_ <= current->write_pos_);
|
||||
size_t avail = current->write_pos_ - current->read_pos_;
|
||||
if (avail > limit)
|
||||
avail = limit;
|
||||
|
||||
// Walk through data
|
||||
char* tmp = current->data_ + current->read_pos_;
|
||||
size_t off = 0;
|
||||
while (off < avail && *tmp != delim) {
|
||||
off++;
|
||||
tmp++;
|
||||
}
|
||||
|
||||
// Move pointers
|
||||
bytes_read += off;
|
||||
limit -= off;
|
||||
|
||||
// Found `delim`
|
||||
if (off != avail) {
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
// Move to next buffer
|
||||
if (current->read_pos_ + avail == kBufferLength) {
|
||||
current = current->next_;
|
||||
}
|
||||
}
|
||||
assert(max == bytes_read);
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
|
||||
void NodeBIO::Write(const char* data, size_t len) {
|
||||
while (len > 0) {
|
||||
size_t to_write = len;
|
||||
assert(write_head_->write_pos_ <= kBufferLength);
|
||||
size_t avail = kBufferLength - write_head_->write_pos_;
|
||||
|
||||
if (to_write > avail)
|
||||
to_write = avail;
|
||||
|
||||
// Copy data
|
||||
memcpy(write_head_->data_ + write_head_->write_pos_, data, to_write);
|
||||
write_head_->write_pos_ += to_write;
|
||||
assert(write_head_->write_pos_ <= kBufferLength);
|
||||
|
||||
// Move pointers
|
||||
len -= to_write;
|
||||
data += to_write;
|
||||
length_ += to_write;
|
||||
|
||||
// Still have some bytes left:
|
||||
// 1. Go to next buffer
|
||||
// 2. Allocate new if next is already full or is partially read
|
||||
// (is read head)
|
||||
if (write_head_->next_->write_pos_ == kBufferLength ||
|
||||
write_head_->next_->read_pos_ != 0) {
|
||||
Buffer* next = new Buffer();
|
||||
next->next_ = write_head_->next_;
|
||||
write_head_->next_ = next;
|
||||
}
|
||||
write_head_ = write_head_->next_;
|
||||
}
|
||||
assert(len == 0);
|
||||
}
|
||||
|
||||
|
||||
void NodeBIO::Reset() {
|
||||
while (read_head_->read_pos_ != read_head_->write_pos_) {
|
||||
assert(read_head_->write_pos_ > read_head_->read_pos_);
|
||||
|
||||
length_ -= read_head_->write_pos_ - read_head_->read_pos_;
|
||||
read_head_->write_pos_ = 0;
|
||||
read_head_->read_pos_ = 0;
|
||||
|
||||
read_head_ = read_head_->next_;
|
||||
}
|
||||
assert(length_ == 0);
|
||||
}
|
||||
|
||||
|
||||
NodeBIO::~NodeBIO() {
|
||||
Buffer* current = head_.next_;
|
||||
while (current != &head_) {
|
||||
Buffer* next = current->next_;
|
||||
delete current;
|
||||
current = next;
|
||||
}
|
||||
|
||||
read_head_ = NULL;
|
||||
write_head_ = NULL;
|
||||
}
|
||||
|
||||
} // namespace node
|
93
src/node_crypto_bio.h
Normal file
93
src/node_crypto_bio.h
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "openssl/bio.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace node {
|
||||
|
||||
class NodeBIO {
|
||||
public:
|
||||
static inline BIO_METHOD* GetMethod() {
|
||||
return &method_;
|
||||
}
|
||||
|
||||
static int New(BIO* bio);
|
||||
static int Free(BIO* bio);
|
||||
static int Read(BIO* bio, char* out, int len);
|
||||
static int Write(BIO* bio, const char* data, int len);
|
||||
static int Puts(BIO* bio, const char* str);
|
||||
static int Gets(BIO* bio, char* out, int size);
|
||||
static long Ctrl(BIO* bio, int cmd, long num, void* ptr);
|
||||
|
||||
protected:
|
||||
static const size_t kBufferLength = 16 * 1024;
|
||||
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer() : read_pos_(0), write_pos_(0), next_(NULL) {
|
||||
}
|
||||
|
||||
size_t read_pos_;
|
||||
size_t write_pos_;
|
||||
Buffer* next_;
|
||||
char data_[kBufferLength];
|
||||
};
|
||||
|
||||
NodeBIO() : length_(0), read_head_(&head_), write_head_(&head_) {
|
||||
// Loop head
|
||||
head_.next_ = &head_;
|
||||
}
|
||||
|
||||
~NodeBIO();
|
||||
|
||||
// Read `len` bytes maximum into `out`, return actual number of read bytes
|
||||
size_t Read(char* out, size_t size);
|
||||
|
||||
// Find first appearance of `delim` in buffer or `limit` if `delim`
|
||||
// wasn't found.
|
||||
size_t IndexOf(char delim, size_t limit);
|
||||
|
||||
// Discard all available data
|
||||
void Reset();
|
||||
|
||||
// Put `len` bytes from `data` into buffer
|
||||
void Write(const char* data, size_t len);
|
||||
|
||||
// Return size of buffer in bytes
|
||||
size_t inline Length() {
|
||||
return length_;
|
||||
}
|
||||
|
||||
static inline NodeBIO* FromBIO(BIO* bio) {
|
||||
assert(bio->ptr != NULL);
|
||||
return static_cast<NodeBIO*>(bio->ptr);
|
||||
}
|
||||
|
||||
size_t length_;
|
||||
Buffer head_;
|
||||
Buffer* read_head_;
|
||||
Buffer* write_head_;
|
||||
|
||||
static BIO_METHOD method_;
|
||||
};
|
||||
|
||||
} // namespace node
|
Loading…
x
Reference in New Issue
Block a user