Skip to content

Commit

Permalink
crypto: use better memory BIO implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny committed Apr 5, 2013
1 parent 0f460b0 commit 58f93ff
Show file tree
Hide file tree
Showing 4 changed files with 412 additions and 5 deletions.
3 changes: 2 additions & 1 deletion node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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' ],
Expand Down
9 changes: 5 additions & 4 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
312 changes: 312 additions & 0 deletions src/node_crypto_bio.cc
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 58f93ff

Please sign in to comment.