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