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