Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
node_crypto_bio.cc
Go to the documentation of this file.
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #include "node_crypto_bio.h"
23 #include "openssl/bio.h"
24 #include "util.h"
25 #include "util-inl.h"
26 #include <limits.h>
27 #include <string.h>
28 
29 namespace node {
30 namespace crypto {
31 
32 const BIO_METHOD NodeBIO::method = {
33  BIO_TYPE_MEM,
34  "node.js SSL buffer",
35  NodeBIO::Write,
36  NodeBIO::Read,
37  NodeBIO::Puts,
38  NodeBIO::Gets,
39  NodeBIO::Ctrl,
41  NodeBIO::Free,
42  nullptr
43 };
44 
45 
46 BIO* NodeBIO::New() {
47  // The const_cast doesn't violate const correctness. OpenSSL's usage of
48  // BIO_METHOD is effectively const but BIO_new() takes a non-const argument.
49  return BIO_new(const_cast<BIO_METHOD*>(&method));
50 }
51 
52 
53 BIO* NodeBIO::NewFixed(const char* data, size_t len) {
54  BIO* bio = New();
55 
56  if (bio == nullptr ||
57  len > INT_MAX ||
58  BIO_write(bio, data, len) != static_cast<int>(len) ||
59  BIO_set_mem_eof_return(bio, 0) != 1) {
60  BIO_free(bio);
61  return nullptr;
62  }
63 
64  return bio;
65 }
66 
67 
68 void NodeBIO::AssignEnvironment(Environment* env) {
69  env_ = env;
70 }
71 
72 
73 int NodeBIO::New(BIO* bio) {
74  bio->ptr = new NodeBIO();
75 
76  // XXX Why am I doing it?!
77  bio->shutdown = 1;
78  bio->init = 1;
79  bio->num = -1;
80 
81  return 1;
82 }
83 
84 
85 int NodeBIO::Free(BIO* bio) {
86  if (bio == nullptr)
87  return 0;
88 
89  if (bio->shutdown) {
90  if (bio->init && bio->ptr != nullptr) {
91  delete FromBIO(bio);
92  bio->ptr = nullptr;
93  }
94  }
95 
96  return 1;
97 }
98 
99 
100 int NodeBIO::Read(BIO* bio, char* out, int len) {
101  int bytes;
102  BIO_clear_retry_flags(bio);
103 
104  bytes = FromBIO(bio)->Read(out, len);
105 
106  if (bytes == 0) {
107  bytes = bio->num;
108  if (bytes != 0) {
109  BIO_set_retry_read(bio);
110  }
111  }
112 
113  return bytes;
114 }
115 
116 
117 char* NodeBIO::Peek(size_t* size) {
118  *size = read_head_->write_pos_ - read_head_->read_pos_;
119  return read_head_->data_ + read_head_->read_pos_;
120 }
121 
122 
123 size_t NodeBIO::PeekMultiple(char** out, size_t* size, size_t* count) {
124  Buffer* pos = read_head_;
125  size_t max = *count;
126  size_t total = 0;
127 
128  size_t i;
129  for (i = 0; i < max; i++) {
130  size[i] = pos->write_pos_ - pos->read_pos_;
131  total += size[i];
132  out[i] = pos->data_ + pos->read_pos_;
133 
134  /* Don't get past write head */
135  if (pos == write_head_)
136  break;
137  else
138  pos = pos->next_;
139  }
140 
141  if (i == max)
142  *count = i;
143  else
144  *count = i + 1;
145 
146  return total;
147 }
148 
149 
150 int NodeBIO::Write(BIO* bio, const char* data, int len) {
151  BIO_clear_retry_flags(bio);
152 
153  FromBIO(bio)->Write(data, len);
154 
155  return len;
156 }
157 
158 
159 int NodeBIO::Puts(BIO* bio, const char* str) {
160  return Write(bio, str, strlen(str));
161 }
162 
163 
164 int NodeBIO::Gets(BIO* bio, char* out, int size) {
165  NodeBIO* nbio = FromBIO(bio);
166 
167  if (nbio->Length() == 0)
168  return 0;
169 
170  int i = nbio->IndexOf('\n', size);
171 
172  // Include '\n', if it's there. If not, don't read off the end.
173  if (i < size && i >= 0 && static_cast<size_t>(i) < nbio->Length())
174  i++;
175 
176  // Shift `i` a bit to nullptr-terminate string later
177  if (size == i)
178  i--;
179 
180  // Flush read data
181  nbio->Read(out, i);
182 
183  out[i] = 0;
184 
185  return i;
186 }
187 
188 
189 long NodeBIO::Ctrl(BIO* bio, int cmd, long num, // NOLINT(runtime/int)
190  void* ptr) {
191  NodeBIO* nbio;
192  long ret; // NOLINT(runtime/int)
193 
194  nbio = FromBIO(bio);
195  ret = 1;
196 
197  switch (cmd) {
198  case BIO_CTRL_RESET:
199  nbio->Reset();
200  break;
201  case BIO_CTRL_EOF:
202  ret = nbio->Length() == 0;
203  break;
204  case BIO_C_SET_BUF_MEM_EOF_RETURN:
205  bio->num = num;
206  break;
207  case BIO_CTRL_INFO:
208  ret = nbio->Length();
209  if (ptr != nullptr)
210  *reinterpret_cast<void**>(ptr) = nullptr;
211  break;
212  case BIO_C_SET_BUF_MEM:
213  CHECK(0 && "Can't use SET_BUF_MEM_PTR with NodeBIO");
214  break;
215  case BIO_C_GET_BUF_MEM_PTR:
216  CHECK(0 && "Can't use GET_BUF_MEM_PTR with NodeBIO");
217  ret = 0;
218  break;
219  case BIO_CTRL_GET_CLOSE:
220  ret = bio->shutdown;
221  break;
222  case BIO_CTRL_SET_CLOSE:
223  bio->shutdown = num;
224  break;
225  case BIO_CTRL_WPENDING:
226  ret = 0;
227  break;
228  case BIO_CTRL_PENDING:
229  ret = nbio->Length();
230  break;
231  case BIO_CTRL_DUP:
232  case BIO_CTRL_FLUSH:
233  ret = 1;
234  break;
235  case BIO_CTRL_PUSH:
236  case BIO_CTRL_POP:
237  default:
238  ret = 0;
239  break;
240  }
241  return ret;
242 }
243 
244 
245 void NodeBIO::TryMoveReadHead() {
246  // `read_pos_` and `write_pos_` means the position of the reader and writer
247  // inside the buffer, respectively. When they're equal - its safe to reset
248  // them, because both reader and writer will continue doing their stuff
249  // from new (zero) positions.
250  while (read_head_->read_pos_ != 0 &&
251  read_head_->read_pos_ == read_head_->write_pos_) {
252  // Reset positions
253  read_head_->read_pos_ = 0;
254  read_head_->write_pos_ = 0;
255 
256  // Move read_head_ forward, just in case if there're still some data to
257  // read in the next buffer.
258  if (read_head_ != write_head_)
259  read_head_ = read_head_->next_;
260  }
261 }
262 
263 
264 size_t NodeBIO::Read(char* out, size_t size) {
265  size_t bytes_read = 0;
266  size_t expected = Length() > size ? size : Length();
267  size_t offset = 0;
268  size_t left = size;
269 
270  while (bytes_read < expected) {
271  CHECK_LE(read_head_->read_pos_, read_head_->write_pos_);
272  size_t avail = read_head_->write_pos_ - read_head_->read_pos_;
273  if (avail > left)
274  avail = left;
275 
276  // Copy data
277  if (out != nullptr)
278  memcpy(out + offset, read_head_->data_ + read_head_->read_pos_, avail);
279  read_head_->read_pos_ += avail;
280 
281  // Move pointers
282  bytes_read += avail;
283  offset += avail;
284  left -= avail;
285 
286  TryMoveReadHead();
287  }
288  CHECK_EQ(expected, bytes_read);
289  length_ -= bytes_read;
290 
291  // Free all empty buffers, but write_head's child
292  FreeEmpty();
293 
294  return bytes_read;
295 }
296 
297 
298 void NodeBIO::FreeEmpty() {
299  if (write_head_ == nullptr)
300  return;
301  Buffer* child = write_head_->next_;
302  if (child == write_head_ || child == read_head_)
303  return;
304  Buffer* cur = child->next_;
305  if (cur == write_head_ || cur == read_head_)
306  return;
307 
308  Buffer* prev = child;
309  while (cur != read_head_) {
310  CHECK_NE(cur, write_head_);
311  CHECK_EQ(cur->write_pos_, cur->read_pos_);
312 
313  Buffer* next = cur->next_;
314  delete cur;
315  cur = next;
316  }
317  prev->next_ = cur;
318 }
319 
320 
321 size_t NodeBIO::IndexOf(char delim, size_t limit) {
322  size_t bytes_read = 0;
323  size_t max = Length() > limit ? limit : Length();
324  size_t left = limit;
325  Buffer* current = read_head_;
326 
327  while (bytes_read < max) {
328  CHECK_LE(current->read_pos_, current->write_pos_);
329  size_t avail = current->write_pos_ - current->read_pos_;
330  if (avail > left)
331  avail = left;
332 
333  // Walk through data
334  char* tmp = current->data_ + current->read_pos_;
335  size_t off = 0;
336  while (off < avail && *tmp != delim) {
337  off++;
338  tmp++;
339  }
340 
341  // Move pointers
342  bytes_read += off;
343  left -= off;
344 
345  // Found `delim`
346  if (off != avail) {
347  return bytes_read;
348  }
349 
350  // Move to next buffer
351  if (current->read_pos_ + avail == current->len_) {
352  current = current->next_;
353  }
354  }
355  CHECK_EQ(max, bytes_read);
356 
357  return max;
358 }
359 
360 
361 void NodeBIO::Write(const char* data, size_t size) {
362  size_t offset = 0;
363  size_t left = size;
364 
365  // Allocate initial buffer if the ring is empty
366  TryAllocateForWrite(left);
367 
368  while (left > 0) {
369  size_t to_write = left;
370  CHECK_LE(write_head_->write_pos_, write_head_->len_);
371  size_t avail = write_head_->len_ - write_head_->write_pos_;
372 
373  if (to_write > avail)
374  to_write = avail;
375 
376  // Copy data
377  memcpy(write_head_->data_ + write_head_->write_pos_,
378  data + offset,
379  to_write);
380 
381  // Move pointers
382  left -= to_write;
383  offset += to_write;
384  length_ += to_write;
385  write_head_->write_pos_ += to_write;
386  CHECK_LE(write_head_->write_pos_, write_head_->len_);
387 
388  // Go to next buffer if there still are some bytes to write
389  if (left != 0) {
390  CHECK_EQ(write_head_->write_pos_, write_head_->len_);
391  TryAllocateForWrite(left);
392  write_head_ = write_head_->next_;
393 
394  // Additionally, since we're moved to the next buffer, read head
395  // may be moved as well.
396  TryMoveReadHead();
397  }
398  }
399  CHECK_EQ(left, 0);
400 }
401 
402 
403 char* NodeBIO::PeekWritable(size_t* size) {
404  TryAllocateForWrite(*size);
405 
406  size_t available = write_head_->len_ - write_head_->write_pos_;
407  if (*size != 0 && available > *size)
408  available = *size;
409  else
410  *size = available;
411 
412  return write_head_->data_ + write_head_->write_pos_;
413 }
414 
415 
416 void NodeBIO::Commit(size_t size) {
417  write_head_->write_pos_ += size;
418  length_ += size;
419  CHECK_LE(write_head_->write_pos_, write_head_->len_);
420 
421  // Allocate new buffer if write head is full,
422  // and there're no other place to go
423  TryAllocateForWrite(0);
424  if (write_head_->write_pos_ == write_head_->len_) {
425  write_head_ = write_head_->next_;
426 
427  // Additionally, since we're moved to the next buffer, read head
428  // may be moved as well.
429  TryMoveReadHead();
430  }
431 }
432 
433 
434 void NodeBIO::TryAllocateForWrite(size_t hint) {
435  Buffer* w = write_head_;
436  Buffer* r = read_head_;
437  // If write head is full, next buffer is either read head or not empty.
438  if (w == nullptr ||
439  (w->write_pos_ == w->len_ &&
440  (w->next_ == r || w->next_->write_pos_ != 0))) {
441  size_t len = w == nullptr ? initial_ :
442  kThroughputBufferLength;
443  if (len < hint)
444  len = hint;
445  Buffer* next = new Buffer(env_, len);
446 
447  if (w == nullptr) {
448  next->next_ = next;
449  write_head_ = next;
450  read_head_ = next;
451  } else {
452  next->next_ = w->next_;
453  w->next_ = next;
454  }
455  }
456 }
457 
458 
459 void NodeBIO::Reset() {
460  if (read_head_ == nullptr)
461  return;
462 
463  while (read_head_->read_pos_ != read_head_->write_pos_) {
464  CHECK(read_head_->write_pos_ > read_head_->read_pos_);
465 
466  length_ -= read_head_->write_pos_ - read_head_->read_pos_;
467  read_head_->write_pos_ = 0;
468  read_head_->read_pos_ = 0;
469 
470  read_head_ = read_head_->next_;
471  }
472  write_head_ = read_head_;
473  CHECK_EQ(length_, 0);
474 }
475 
476 
477 NodeBIO::~NodeBIO() {
478  if (read_head_ == nullptr)
479  return;
480 
481  Buffer* current = read_head_;
482  do {
483  Buffer* next = current->next_;
484  delete current;
485  current = next;
486  } while (current != read_head_);
487 
488  read_head_ = nullptr;
489  write_head_ = nullptr;
490 }
491 
492 } // namespace crypto
493 } // namespace node
int len
Definition: cares_wrap.cc:485
Environment *const env_
union node::cares_wrap::@8::CaresAsyncData::@0 data
size_t Length(Local< Value > val)
Definition: node_buffer.cc:227
MaybeLocal< Object > New(Isolate *isolate, Local< String > string, enum encoding enc)
Definition: node_buffer.cc:241
method
Definition: node.d:195
this off
Definition: v8ustack.d:365