• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base_object-inl.h"
23 #include "memory_tracker-inl.h"
24 #include "node_crypto_bio.h"
25 #include "openssl/bio.h"
26 #include "util-inl.h"
27 #include <climits>
28 #include <cstring>
29 
30 namespace node {
31 namespace crypto {
32 
New(Environment * env)33 BIOPointer NodeBIO::New(Environment* env) {
34   BIOPointer bio(BIO_new(GetMethod()));
35   if (bio && env != nullptr)
36     NodeBIO::FromBIO(bio.get())->env_ = env;
37   return bio;
38 }
39 
40 
NewFixed(const char * data,size_t len,Environment * env)41 BIOPointer NodeBIO::NewFixed(const char* data, size_t len, Environment* env) {
42   BIOPointer bio = New(env);
43 
44   if (!bio ||
45       len > INT_MAX ||
46       BIO_write(bio.get(), data, len) != static_cast<int>(len) ||
47       BIO_set_mem_eof_return(bio.get(), 0) != 1) {
48     return BIOPointer();
49   }
50 
51   return bio;
52 }
53 
54 
New(BIO * bio)55 int NodeBIO::New(BIO* bio) {
56   BIO_set_data(bio, new NodeBIO());
57   BIO_set_init(bio, 1);
58 
59   return 1;
60 }
61 
62 
Free(BIO * bio)63 int NodeBIO::Free(BIO* bio) {
64   if (bio == nullptr)
65     return 0;
66 
67   if (BIO_get_shutdown(bio)) {
68     if (BIO_get_init(bio) && BIO_get_data(bio) != nullptr) {
69       delete FromBIO(bio);
70       BIO_set_data(bio, nullptr);
71     }
72   }
73 
74   return 1;
75 }
76 
77 
Read(BIO * bio,char * out,int len)78 int NodeBIO::Read(BIO* bio, char* out, int len) {
79   BIO_clear_retry_flags(bio);
80 
81   NodeBIO* nbio = FromBIO(bio);
82   int bytes = nbio->Read(out, len);
83 
84   if (bytes == 0) {
85     bytes = nbio->eof_return();
86     if (bytes != 0) {
87       BIO_set_retry_read(bio);
88     }
89   }
90 
91   return bytes;
92 }
93 
94 
Peek(size_t * size)95 char* NodeBIO::Peek(size_t* size) {
96   *size = read_head_->write_pos_ - read_head_->read_pos_;
97   return read_head_->data_ + read_head_->read_pos_;
98 }
99 
100 
PeekMultiple(char ** out,size_t * size,size_t * count)101 size_t NodeBIO::PeekMultiple(char** out, size_t* size, size_t* count) {
102   Buffer* pos = read_head_;
103   size_t max = *count;
104   size_t total = 0;
105 
106   size_t i;
107   for (i = 0; i < max; i++) {
108     size[i] = pos->write_pos_ - pos->read_pos_;
109     total += size[i];
110     out[i] = pos->data_ + pos->read_pos_;
111 
112     /* Don't get past write head */
113     if (pos == write_head_)
114       break;
115     else
116       pos = pos->next_;
117   }
118 
119   if (i == max)
120     *count = i;
121   else
122     *count = i + 1;
123 
124   return total;
125 }
126 
127 
Write(BIO * bio,const char * data,int len)128 int NodeBIO::Write(BIO* bio, const char* data, int len) {
129   BIO_clear_retry_flags(bio);
130 
131   FromBIO(bio)->Write(data, len);
132 
133   return len;
134 }
135 
136 
Puts(BIO * bio,const char * str)137 int NodeBIO::Puts(BIO* bio, const char* str) {
138   return Write(bio, str, strlen(str));
139 }
140 
141 
Gets(BIO * bio,char * out,int size)142 int NodeBIO::Gets(BIO* bio, char* out, int size) {
143   NodeBIO* nbio = FromBIO(bio);
144 
145   if (nbio->Length() == 0)
146     return 0;
147 
148   int i = nbio->IndexOf('\n', size);
149 
150   // Include '\n', if it's there.  If not, don't read off the end.
151   if (i < size && i >= 0 && static_cast<size_t>(i) < nbio->Length())
152     i++;
153 
154   // Shift `i` a bit to nullptr-terminate string later
155   if (size == i)
156     i--;
157 
158   // Flush read data
159   nbio->Read(out, i);
160 
161   out[i] = 0;
162 
163   return i;
164 }
165 
166 
Ctrl(BIO * bio,int cmd,long num,void * ptr)167 long NodeBIO::Ctrl(BIO* bio, int cmd, long num,  // NOLINT(runtime/int)
168                    void* ptr) {
169   NodeBIO* nbio;
170   long ret;  // NOLINT(runtime/int)
171 
172   nbio = FromBIO(bio);
173   ret = 1;
174 
175   switch (cmd) {
176     case BIO_CTRL_RESET:
177       nbio->Reset();
178       break;
179     case BIO_CTRL_EOF:
180       ret = nbio->Length() == 0;
181       break;
182     case BIO_C_SET_BUF_MEM_EOF_RETURN:
183       nbio->set_eof_return(num);
184       break;
185     case BIO_CTRL_INFO:
186       ret = nbio->Length();
187       if (ptr != nullptr)
188         *reinterpret_cast<void**>(ptr) = nullptr;
189       break;
190     case BIO_C_SET_BUF_MEM:
191       CHECK(0 && "Can't use SET_BUF_MEM_PTR with NodeBIO");
192       break;
193     case BIO_C_GET_BUF_MEM_PTR:
194       CHECK(0 && "Can't use GET_BUF_MEM_PTR with NodeBIO");
195       ret = 0;
196       break;
197     case BIO_CTRL_GET_CLOSE:
198       ret = BIO_get_shutdown(bio);
199       break;
200     case BIO_CTRL_SET_CLOSE:
201       BIO_set_shutdown(bio, num);
202       break;
203     case BIO_CTRL_WPENDING:
204       ret = 0;
205       break;
206     case BIO_CTRL_PENDING:
207       ret = nbio->Length();
208       break;
209     case BIO_CTRL_DUP:
210     case BIO_CTRL_FLUSH:
211       ret = 1;
212       break;
213     case BIO_CTRL_PUSH:
214     case BIO_CTRL_POP:
215     default:
216       ret = 0;
217       break;
218   }
219   return ret;
220 }
221 
222 
GetMethod()223 const BIO_METHOD* NodeBIO::GetMethod() {
224   // This is called from InitCryptoOnce() to avoid race conditions during
225   // initialization.
226   static BIO_METHOD* method = nullptr;
227 
228   if (method == nullptr) {
229     method = BIO_meth_new(BIO_TYPE_MEM, "node.js SSL buffer");
230     BIO_meth_set_write(method, Write);
231     BIO_meth_set_read(method, Read);
232     BIO_meth_set_puts(method, Puts);
233     BIO_meth_set_gets(method, Gets);
234     BIO_meth_set_ctrl(method, Ctrl);
235     BIO_meth_set_create(method, New);
236     BIO_meth_set_destroy(method, Free);
237   }
238 
239   return method;
240 }
241 
242 
TryMoveReadHead()243 void NodeBIO::TryMoveReadHead() {
244   // `read_pos_` and `write_pos_` means the position of the reader and writer
245   // inside the buffer, respectively. When they're equal - its safe to reset
246   // them, because both reader and writer will continue doing their stuff
247   // from new (zero) positions.
248   while (read_head_->read_pos_ != 0 &&
249          read_head_->read_pos_ == read_head_->write_pos_) {
250     // Reset positions
251     read_head_->read_pos_ = 0;
252     read_head_->write_pos_ = 0;
253 
254     // Move read_head_ forward, just in case if there're still some data to
255     // read in the next buffer.
256     if (read_head_ != write_head_)
257       read_head_ = read_head_->next_;
258   }
259 }
260 
261 
Read(char * out,size_t size)262 size_t NodeBIO::Read(char* out, size_t size) {
263   size_t bytes_read = 0;
264   size_t expected = Length() > size ? size : Length();
265   size_t offset = 0;
266   size_t left = size;
267 
268   while (bytes_read < expected) {
269     CHECK_LE(read_head_->read_pos_, read_head_->write_pos_);
270     size_t avail = read_head_->write_pos_ - read_head_->read_pos_;
271     if (avail > left)
272       avail = left;
273 
274     // Copy data
275     if (out != nullptr)
276       memcpy(out + offset, read_head_->data_ + read_head_->read_pos_, avail);
277     read_head_->read_pos_ += avail;
278 
279     // Move pointers
280     bytes_read += avail;
281     offset += avail;
282     left -= avail;
283 
284     TryMoveReadHead();
285   }
286   CHECK_EQ(expected, bytes_read);
287   length_ -= bytes_read;
288 
289   // Free all empty buffers, but write_head's child
290   FreeEmpty();
291 
292   return bytes_read;
293 }
294 
295 
FreeEmpty()296 void NodeBIO::FreeEmpty() {
297   if (write_head_ == nullptr)
298     return;
299   Buffer* child = write_head_->next_;
300   if (child == write_head_ || child == read_head_)
301     return;
302   Buffer* cur = child->next_;
303   if (cur == write_head_ || cur == read_head_)
304     return;
305 
306   Buffer* prev = child;
307   while (cur != read_head_) {
308     CHECK_NE(cur, write_head_);
309     CHECK_EQ(cur->write_pos_, cur->read_pos_);
310 
311     Buffer* next = cur->next_;
312     delete cur;
313     cur = next;
314   }
315   prev->next_ = cur;
316 }
317 
318 
IndexOf(char delim,size_t limit)319 size_t NodeBIO::IndexOf(char delim, size_t limit) {
320   size_t bytes_read = 0;
321   size_t max = Length() > limit ? limit : Length();
322   size_t left = limit;
323   Buffer* current = read_head_;
324 
325   while (bytes_read < max) {
326     CHECK_LE(current->read_pos_, current->write_pos_);
327     size_t avail = current->write_pos_ - current->read_pos_;
328     if (avail > left)
329       avail = left;
330 
331     // Walk through data
332     char* tmp = current->data_ + current->read_pos_;
333     size_t off = 0;
334     while (off < avail && *tmp != delim) {
335       off++;
336       tmp++;
337     }
338 
339     // Move pointers
340     bytes_read += off;
341     left -= off;
342 
343     // Found `delim`
344     if (off != avail) {
345       return bytes_read;
346     }
347 
348     // Move to next buffer
349     if (current->read_pos_ + avail == current->len_) {
350       current = current->next_;
351     }
352   }
353   CHECK_EQ(max, bytes_read);
354 
355   return max;
356 }
357 
358 
Write(const char * data,size_t size)359 void NodeBIO::Write(const char* data, size_t size) {
360   size_t offset = 0;
361   size_t left = size;
362 
363   // Allocate initial buffer if the ring is empty
364   TryAllocateForWrite(left);
365 
366   while (left > 0) {
367     size_t to_write = left;
368     CHECK_LE(write_head_->write_pos_, write_head_->len_);
369     size_t avail = write_head_->len_ - write_head_->write_pos_;
370 
371     if (to_write > avail)
372       to_write = avail;
373 
374     // Copy data
375     memcpy(write_head_->data_ + write_head_->write_pos_,
376            data + offset,
377            to_write);
378 
379     // Move pointers
380     left -= to_write;
381     offset += to_write;
382     length_ += to_write;
383     write_head_->write_pos_ += to_write;
384     CHECK_LE(write_head_->write_pos_, write_head_->len_);
385 
386     // Go to next buffer if there still are some bytes to write
387     if (left != 0) {
388       CHECK_EQ(write_head_->write_pos_, write_head_->len_);
389       TryAllocateForWrite(left);
390       write_head_ = write_head_->next_;
391 
392       // Additionally, since we're moved to the next buffer, read head
393       // may be moved as well.
394       TryMoveReadHead();
395     }
396   }
397   CHECK_EQ(left, 0);
398 }
399 
400 
PeekWritable(size_t * size)401 char* NodeBIO::PeekWritable(size_t* size) {
402   TryAllocateForWrite(*size);
403 
404   size_t available = write_head_->len_ - write_head_->write_pos_;
405   if (*size == 0 || available <= *size)
406     *size = available;
407 
408   return write_head_->data_ + write_head_->write_pos_;
409 }
410 
411 
Commit(size_t size)412 void NodeBIO::Commit(size_t size) {
413   write_head_->write_pos_ += size;
414   length_ += size;
415   CHECK_LE(write_head_->write_pos_, write_head_->len_);
416 
417   // Allocate new buffer if write head is full,
418   // and there're no other place to go
419   TryAllocateForWrite(0);
420   if (write_head_->write_pos_ == write_head_->len_) {
421     write_head_ = write_head_->next_;
422 
423     // Additionally, since we're moved to the next buffer, read head
424     // may be moved as well.
425     TryMoveReadHead();
426   }
427 }
428 
429 
TryAllocateForWrite(size_t hint)430 void NodeBIO::TryAllocateForWrite(size_t hint) {
431   Buffer* w = write_head_;
432   Buffer* r = read_head_;
433   // If write head is full, next buffer is either read head or not empty.
434   if (w == nullptr ||
435       (w->write_pos_ == w->len_ &&
436        (w->next_ == r || w->next_->write_pos_ != 0))) {
437     size_t len = w == nullptr ? initial_ :
438                              kThroughputBufferLength;
439     if (len < hint)
440       len = hint;
441 
442     // If there is a one time allocation size hint, use it.
443     if (allocate_hint_ > len) {
444       len = allocate_hint_;
445       allocate_hint_ = 0;
446     }
447 
448     Buffer* next = new Buffer(env_, len);
449 
450     if (w == nullptr) {
451       next->next_ = next;
452       write_head_ = next;
453       read_head_ = next;
454     } else {
455       next->next_ = w->next_;
456       w->next_ = next;
457     }
458   }
459 }
460 
461 
Reset()462 void NodeBIO::Reset() {
463   if (read_head_ == nullptr)
464     return;
465 
466   while (read_head_->read_pos_ != read_head_->write_pos_) {
467     CHECK(read_head_->write_pos_ > read_head_->read_pos_);
468 
469     length_ -= read_head_->write_pos_ - read_head_->read_pos_;
470     read_head_->write_pos_ = 0;
471     read_head_->read_pos_ = 0;
472 
473     read_head_ = read_head_->next_;
474   }
475   write_head_ = read_head_;
476   CHECK_EQ(length_, 0);
477 }
478 
479 
~NodeBIO()480 NodeBIO::~NodeBIO() {
481   if (read_head_ == nullptr)
482     return;
483 
484   Buffer* current = read_head_;
485   do {
486     Buffer* next = current->next_;
487     delete current;
488     current = next;
489   } while (current != read_head_);
490 
491   read_head_ = nullptr;
492   write_head_ = nullptr;
493 }
494 
495 
FromBIO(BIO * bio)496 NodeBIO* NodeBIO::FromBIO(BIO* bio) {
497   CHECK_NOT_NULL(BIO_get_data(bio));
498   return static_cast<NodeBIO*>(BIO_get_data(bio));
499 }
500 
501 
502 }  // namespace crypto
503 }  // namespace node
504