|
|
@ -58,6 +58,10 @@ static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL |
|
|
|
| XN_FLAG_FN_SN; |
|
|
|
|
|
|
|
|
|
|
|
size_t TLSCallbacks::error_off_; |
|
|
|
char TLSCallbacks::error_buf_[1024]; |
|
|
|
|
|
|
|
|
|
|
|
TLSCallbacks::TLSCallbacks(Environment* env, |
|
|
|
Kind kind, |
|
|
|
Handle<Object> sc, |
|
|
@ -71,15 +75,16 @@ TLSCallbacks::TLSCallbacks(Environment* env, |
|
|
|
enc_out_(NULL), |
|
|
|
clear_in_(NULL), |
|
|
|
write_size_(0), |
|
|
|
pending_write_item_(NULL), |
|
|
|
started_(false), |
|
|
|
established_(false), |
|
|
|
shutdown_(false), |
|
|
|
error_(NULL), |
|
|
|
eof_(false) { |
|
|
|
node::Wrap<TLSCallbacks>(object(), this); |
|
|
|
|
|
|
|
// Initialize queue for clearIn writes
|
|
|
|
QUEUE_INIT(&write_item_queue_); |
|
|
|
QUEUE_INIT(&pending_write_items_); |
|
|
|
|
|
|
|
// We've our own session callbacks
|
|
|
|
SSL_CTX_sess_set_get_cb(sc_->ctx_, SSLWrap<TLSCallbacks>::GetSessionCallback); |
|
|
@ -102,25 +107,52 @@ TLSCallbacks::~TLSCallbacks() { |
|
|
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB |
|
|
|
sni_context_.Dispose(); |
|
|
|
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
|
|
|
|
|
|
|
// Move all writes to pending
|
|
|
|
MakePending(); |
|
|
|
|
|
|
|
// And destroy
|
|
|
|
while (!QUEUE_EMPTY(&pending_write_items_)) { |
|
|
|
QUEUE* q = QUEUE_HEAD(&pending_write_items_); |
|
|
|
QUEUE_REMOVE(q); |
|
|
|
|
|
|
|
WriteItem* wi = QUEUE_DATA(q, WriteItem, member_); |
|
|
|
delete wi; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void TLSCallbacks::InvokeQueued(int status) { |
|
|
|
// Empty queue - ignore call
|
|
|
|
if (pending_write_item_ == NULL) |
|
|
|
void TLSCallbacks::MakePending() { |
|
|
|
// Aliases
|
|
|
|
QUEUE* from = &write_item_queue_; |
|
|
|
QUEUE* to = &pending_write_items_; |
|
|
|
|
|
|
|
if (QUEUE_EMPTY(from)) |
|
|
|
return; |
|
|
|
|
|
|
|
QUEUE* q = &pending_write_item_->member_; |
|
|
|
pending_write_item_ = NULL; |
|
|
|
// Add items to pending
|
|
|
|
QUEUE_ADD(to, from); |
|
|
|
|
|
|
|
// Empty original queue
|
|
|
|
QUEUE_INIT(from); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool TLSCallbacks::InvokeQueued(int status) { |
|
|
|
if (QUEUE_EMPTY(&pending_write_items_)) |
|
|
|
return false; |
|
|
|
|
|
|
|
// Process old queue
|
|
|
|
while (q != &write_item_queue_) { |
|
|
|
QUEUE* next = static_cast<QUEUE*>(QUEUE_NEXT(q)); |
|
|
|
WriteItem* wi = CONTAINER_OF(q, WriteItem, member_); |
|
|
|
while (!QUEUE_EMPTY(&pending_write_items_)) { |
|
|
|
QUEUE* q = QUEUE_HEAD(&pending_write_items_); |
|
|
|
QUEUE_REMOVE(q); |
|
|
|
|
|
|
|
WriteItem* wi = QUEUE_DATA(q, WriteItem, member_); |
|
|
|
wi->cb_(&wi->w_->req_, status); |
|
|
|
delete wi; |
|
|
|
q = next; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -276,15 +308,12 @@ void TLSCallbacks::EncOut() { |
|
|
|
return; |
|
|
|
|
|
|
|
// Split-off queue
|
|
|
|
if (established_ && !QUEUE_EMPTY(&write_item_queue_)) { |
|
|
|
pending_write_item_ = CONTAINER_OF(QUEUE_NEXT(&write_item_queue_), |
|
|
|
WriteItem, |
|
|
|
member_); |
|
|
|
QUEUE_INIT(&write_item_queue_); |
|
|
|
} |
|
|
|
if (established_ && !QUEUE_EMPTY(&write_item_queue_)) |
|
|
|
MakePending(); |
|
|
|
|
|
|
|
// No data to write
|
|
|
|
if (BIO_pending(enc_out_) == 0) { |
|
|
|
if (clear_in_->Length() == 0) |
|
|
|
InvokeQueued(0); |
|
|
|
return; |
|
|
|
} |
|
|
@ -314,7 +343,6 @@ void TLSCallbacks::EncOut() { |
|
|
|
|
|
|
|
void TLSCallbacks::EncOutCb(uv_write_t* req, int status) { |
|
|
|
TLSCallbacks* callbacks = static_cast<TLSCallbacks*>(req->data); |
|
|
|
Environment* env = callbacks->env(); |
|
|
|
|
|
|
|
// Handle error
|
|
|
|
if (status) { |
|
|
@ -323,12 +351,6 @@ void TLSCallbacks::EncOutCb(uv_write_t* req, int status) { |
|
|
|
return; |
|
|
|
|
|
|
|
// Notify about error
|
|
|
|
HandleScope handle_scope(env->isolate()); |
|
|
|
Context::Scope context_scope(env->context()); |
|
|
|
Local<Value> arg = String::Concat( |
|
|
|
FIXED_ONE_BYTE_STRING(node_isolate, "write cb error, status: "), |
|
|
|
Integer::New(status, node_isolate)->ToString()); |
|
|
|
callbacks->MakeCallback(env->onerror_string(), 1, &arg); |
|
|
|
callbacks->InvokeQueued(status); |
|
|
|
return; |
|
|
|
} |
|
|
@ -342,7 +364,33 @@ void TLSCallbacks::EncOutCb(uv_write_t* req, int status) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Local<Value> TLSCallbacks::GetSSLError(int status, int* err) { |
|
|
|
int TLSCallbacks::PrintErrorsCb(const char* str, size_t len, void* arg) { |
|
|
|
size_t to_copy = error_off_; |
|
|
|
size_t avail = sizeof(error_buf_) - error_off_ - 1; |
|
|
|
|
|
|
|
if (avail > to_copy) |
|
|
|
to_copy = avail; |
|
|
|
|
|
|
|
memcpy(error_buf_, str, avail); |
|
|
|
error_off_ += avail; |
|
|
|
assert(error_off_ < sizeof(error_buf_)); |
|
|
|
|
|
|
|
// Zero-terminate
|
|
|
|
error_buf_[error_off_] = '\0'; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const char* TLSCallbacks::PrintErrors() { |
|
|
|
error_off_ = 0; |
|
|
|
ERR_print_errors_cb(PrintErrorsCb, this); |
|
|
|
|
|
|
|
return error_buf_; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Local<Value> TLSCallbacks::GetSSLError(int status, int* err, const char** msg) { |
|
|
|
HandleScope scope(node_isolate); |
|
|
|
|
|
|
|
*err = SSL_get_error(ssl_, status); |
|
|
@ -356,19 +404,18 @@ Local<Value> TLSCallbacks::GetSSLError(int status, int* err) { |
|
|
|
break; |
|
|
|
default: |
|
|
|
{ |
|
|
|
BUF_MEM* mem; |
|
|
|
BIO* bio; |
|
|
|
|
|
|
|
assert(*err == SSL_ERROR_SSL || *err == SSL_ERROR_SYSCALL); |
|
|
|
|
|
|
|
bio = BIO_new(BIO_s_mem()); |
|
|
|
assert(bio != NULL); |
|
|
|
ERR_print_errors(bio); |
|
|
|
BIO_get_mem_ptr(bio, &mem); |
|
|
|
const char* buf = PrintErrors(); |
|
|
|
|
|
|
|
Local<String> message = |
|
|
|
OneByteString(node_isolate, mem->data, mem->length); |
|
|
|
OneByteString(node_isolate, buf, strlen(buf)); |
|
|
|
Local<Value> exception = Exception::Error(message); |
|
|
|
BIO_free_all(bio); |
|
|
|
|
|
|
|
if (msg != NULL) { |
|
|
|
assert(*msg == NULL); |
|
|
|
*msg = buf; |
|
|
|
} |
|
|
|
|
|
|
|
return scope.Close(exception); |
|
|
|
} |
|
|
@ -409,7 +456,7 @@ void TLSCallbacks::ClearOut() { |
|
|
|
|
|
|
|
if (read == -1) { |
|
|
|
int err; |
|
|
|
Handle<Value> arg = GetSSLError(read, &err); |
|
|
|
Local<Value> arg = GetSSLError(read, &err, NULL); |
|
|
|
|
|
|
|
if (!arg.IsEmpty()) { |
|
|
|
MakeCallback(env()->onerror_string(), 1, &arg); |
|
|
@ -445,15 +492,25 @@ bool TLSCallbacks::ClearIn() { |
|
|
|
|
|
|
|
// Error or partial write
|
|
|
|
int err; |
|
|
|
Handle<Value> arg = GetSSLError(written, &err); |
|
|
|
Local<Value> arg = GetSSLError(written, &err, &error_); |
|
|
|
if (!arg.IsEmpty()) { |
|
|
|
MakeCallback(env()->onerror_string(), 1, &arg); |
|
|
|
MakePending(); |
|
|
|
if (!InvokeQueued(UV_EPROTO)) |
|
|
|
error_ = NULL; |
|
|
|
clear_in_->Reset(); |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const char* TLSCallbacks::Error() { |
|
|
|
const char* ret = error_; |
|
|
|
error_ = NULL; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int TLSCallbacks::DoWrite(WriteWrap* w, |
|
|
|
uv_buf_t* bufs, |
|
|
|
size_t count, |
|
|
@ -508,11 +565,9 @@ int TLSCallbacks::DoWrite(WriteWrap* w, |
|
|
|
int err; |
|
|
|
HandleScope handle_scope(env()->isolate()); |
|
|
|
Context::Scope context_scope(env()->context()); |
|
|
|
Handle<Value> arg = GetSSLError(written, &err); |
|
|
|
if (!arg.IsEmpty()) { |
|
|
|
MakeCallback(env()->onerror_string(), 1, &arg); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
Local<Value> arg = GetSSLError(written, &err, &error_); |
|
|
|
if (!arg.IsEmpty()) |
|
|
|
return UV_EPROTO; |
|
|
|
|
|
|
|
// No errors, queue rest
|
|
|
|
for (; i < count; i++) |
|
|
|