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