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