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