Merge remote-tracking branch 'upstream/v0.10'
[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 int NodeBIO::Write(BIO* bio, const char* data, int len) {
100   BIO_clear_retry_flags(bio);
101
102   FromBIO(bio)->Write(data, len);
103
104   return len;
105 }
106
107
108 int NodeBIO::Puts(BIO* bio, const char* str) {
109   return Write(bio, str, strlen(str));
110 }
111
112
113 int NodeBIO::Gets(BIO* bio, char* out, int size) {
114   NodeBIO* nbio =  FromBIO(bio);
115
116   if (nbio->Length() == 0)
117     return 0;
118
119   int i = nbio->IndexOf('\n', size);
120
121   // Include '\n'
122   if (i < size)
123     i++;
124
125   // Shift `i` a bit to NULL-terminate string later
126   if (size == i)
127     i--;
128
129   // Flush read data
130   nbio->Read(out, i);
131
132   out[i] = 0;
133
134   return i;
135 }
136
137
138 long NodeBIO::Ctrl(BIO* bio, int cmd, long num, void* ptr) {
139   NodeBIO* nbio;
140   long ret;
141
142   nbio = FromBIO(bio);
143   ret = 1;
144
145   switch (cmd) {
146     case BIO_CTRL_RESET:
147       nbio->Reset();
148       break;
149     case BIO_CTRL_EOF:
150       ret = nbio->Length() == 0;
151       break;
152     case BIO_C_SET_BUF_MEM_EOF_RETURN:
153       bio->num = num;
154       break;
155     case BIO_CTRL_INFO:
156       ret = nbio->Length();
157       if (ptr != NULL)
158         *reinterpret_cast<void**>(ptr) = NULL;
159       break;
160     case BIO_C_SET_BUF_MEM:
161       assert(0 && "Can't use SET_BUF_MEM_PTR with NodeBIO");
162       abort();
163       break;
164     case BIO_C_GET_BUF_MEM_PTR:
165       assert(0 && "Can't use GET_BUF_MEM_PTR with NodeBIO");
166       ret = 0;
167       break;
168     case BIO_CTRL_GET_CLOSE:
169       ret = bio->shutdown;
170       break;
171     case BIO_CTRL_SET_CLOSE:
172       bio->shutdown = num;
173       break;
174     case BIO_CTRL_WPENDING:
175       ret = 0;
176       break;
177     case BIO_CTRL_PENDING:
178       ret = nbio->Length();
179       break;
180     case BIO_CTRL_DUP:
181     case BIO_CTRL_FLUSH:
182       ret = 1;
183       break;
184     case BIO_CTRL_PUSH:
185     case BIO_CTRL_POP:
186     default:
187       ret = 0;
188       break;
189   }
190   return ret;
191 }
192
193
194 void NodeBIO::TryMoveReadHead() {
195   // `read_pos_` and `write_pos_` means the position of the reader and writer
196   // inside the buffer, respectively. When they're equal - its safe to reset
197   // them, because both reader and writer will continue doing their stuff
198   // from new (zero) positions.
199   if (read_head_->read_pos_ != read_head_->write_pos_)
200     return;
201
202   // Reset positions
203   read_head_->read_pos_ = 0;
204   read_head_->write_pos_ = 0;
205
206   // Move read_head_ forward, just in case if there're still some data to
207   // read in the next buffer.
208   if (read_head_ != write_head_)
209     read_head_ = read_head_->next_;
210 }
211
212
213 size_t NodeBIO::Read(char* out, size_t size) {
214   size_t bytes_read = 0;
215   size_t expected = Length() > size ? size : Length();
216   size_t offset = 0;
217   size_t left = size;
218
219   while (bytes_read < expected) {
220     assert(read_head_->read_pos_ <= read_head_->write_pos_);
221     size_t avail = read_head_->write_pos_ - read_head_->read_pos_;
222     if (avail > left)
223       avail = left;
224
225     // Copy data
226     if (out != NULL)
227       memcpy(out + offset, read_head_->data_ + read_head_->read_pos_, avail);
228     read_head_->read_pos_ += avail;
229
230     // Move pointers
231     bytes_read += avail;
232     offset += avail;
233     left -= avail;
234
235     TryMoveReadHead();
236   }
237   assert(expected == bytes_read);
238   length_ -= bytes_read;
239
240   // Free all empty buffers, but write_head's child
241   FreeEmpty();
242
243   return bytes_read;
244 }
245
246
247 void NodeBIO::FreeEmpty() {
248   Buffer* child = write_head_->next_;
249   if (child == write_head_ || child == read_head_)
250     return;
251   Buffer* cur = child->next_;
252   if (cur == write_head_ || cur == read_head_)
253     return;
254
255   Buffer* prev = child;
256   while (cur != read_head_) {
257     // Skip embedded buffer, and continue deallocating again starting from it
258     if (cur == &head_) {
259       prev->next_ = cur;
260       prev = cur;
261       cur = head_.next_;
262       continue;
263     }
264     assert(cur != write_head_);
265     assert(cur->write_pos_ == cur->read_pos_);
266
267     Buffer* next = cur->next_;
268     delete cur;
269     cur = next;
270   }
271   assert(prev == child || prev == &head_);
272   prev->next_ = cur;
273 }
274
275
276 size_t NodeBIO::IndexOf(char delim, size_t limit) {
277   size_t bytes_read = 0;
278   size_t max = Length() > limit ? limit : Length();
279   size_t left = limit;
280   Buffer* current = read_head_;
281
282   while (bytes_read < max) {
283     assert(current->read_pos_ <= current->write_pos_);
284     size_t avail = current->write_pos_ - current->read_pos_;
285     if (avail > left)
286       avail = left;
287
288     // Walk through data
289     char* tmp = current->data_ + current->read_pos_;
290     size_t off = 0;
291     while (off < avail && *tmp != delim) {
292       off++;
293       tmp++;
294     }
295
296     // Move pointers
297     bytes_read += off;
298     left -= off;
299
300     // Found `delim`
301     if (off != avail) {
302       return bytes_read;
303     }
304
305     // Move to next buffer
306     if (current->read_pos_ + avail == kBufferLength) {
307       current = current->next_;
308     }
309   }
310   assert(max == bytes_read);
311
312   return max;
313 }
314
315
316 void NodeBIO::Write(const char* data, size_t size) {
317   size_t offset = 0;
318   size_t left = size;
319   while (left > 0) {
320     size_t to_write = left;
321     assert(write_head_->write_pos_ <= kBufferLength);
322     size_t avail = kBufferLength - write_head_->write_pos_;
323
324     if (to_write > avail)
325       to_write = avail;
326
327     // Copy data
328     memcpy(write_head_->data_ + write_head_->write_pos_,
329            data + offset,
330            to_write);
331
332     // Move pointers
333     left -= to_write;
334     offset += to_write;
335     length_ += to_write;
336     write_head_->write_pos_ += to_write;
337     assert(write_head_->write_pos_ <= kBufferLength);
338
339     // Go to next buffer if there still are some bytes to write
340     if (left != 0) {
341       assert(write_head_->write_pos_ == kBufferLength);
342       TryAllocateForWrite();
343       write_head_ = write_head_->next_;
344
345       // Additionally, since we're moved to the next buffer, read head
346       // may be moved as well.
347       TryMoveReadHead();
348     }
349   }
350   assert(left == 0);
351 }
352
353
354 char* NodeBIO::PeekWritable(size_t* size) {
355   size_t available = kBufferLength - write_head_->write_pos_;
356   if (*size != 0 && available > *size)
357     available = *size;
358   else
359     *size = available;
360
361   return write_head_->data_ + write_head_->write_pos_;
362 }
363
364
365 void NodeBIO::Commit(size_t size) {
366   write_head_->write_pos_ += size;
367   length_ += size;
368   assert(write_head_->write_pos_ <= kBufferLength);
369
370   // Allocate new buffer if write head is full,
371   // and there're no other place to go
372   TryAllocateForWrite();
373   if (write_head_->write_pos_ == kBufferLength)
374     write_head_ = write_head_->next_;
375 }
376
377
378 void NodeBIO::TryAllocateForWrite() {
379   // If write head is full, next buffer is either read head or not empty.
380   if (write_head_->write_pos_ == kBufferLength &&
381       (write_head_->next_ == read_head_ ||
382        write_head_->next_->write_pos_ != 0)) {
383     Buffer* next = new Buffer();
384     next->next_ = write_head_->next_;
385     write_head_->next_ = next;
386   }
387 }
388
389
390 void NodeBIO::Reset() {
391   while (read_head_->read_pos_ != read_head_->write_pos_) {
392     assert(read_head_->write_pos_ > read_head_->read_pos_);
393
394     length_ -= read_head_->write_pos_ - read_head_->read_pos_;
395     read_head_->write_pos_ = 0;
396     read_head_->read_pos_ = 0;
397
398     read_head_ = read_head_->next_;
399   }
400   write_head_ = read_head_;
401   assert(length_ == 0);
402 }
403
404
405 NodeBIO::~NodeBIO() {
406   Buffer* current = head_.next_;
407   while (current != &head_) {
408     Buffer* next = current->next_;
409     delete current;
410     current = next;
411   }
412
413   read_head_ = NULL;
414   write_head_ = NULL;
415 }
416
417 }  // namespace node