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