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