OLD | NEW |
| (Empty) |
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 // Written in NSPR style to also be suitable for adding to the NSS demo suite | |
5 | |
6 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
7 // for details. All rights reserved. Use of this source code is governed by a | |
8 // BSD-style license that can be found in the LICENSE file. | |
9 | |
10 // This file is a modified copy of Chromium's src/net/base/nss_memio.c. | |
11 // char* has been changed to uint8_t* everywhere, and C++ casts are used. | |
12 // Revision 291806 (this should agree with "nss_rev" in DEPS). | |
13 | |
14 | |
15 /* memio is a simple NSPR I/O layer that lets you decouple NSS from | |
16 * the real network. It's rather like openssl's memory bio, | |
17 * and is useful when your app absolutely, positively doesn't | |
18 * want to let NSS do its own networking. | |
19 */ | |
20 #include "bin/net/nss_memio.h" | |
21 | |
22 #include <stdio.h> | |
23 #include <stdlib.h> | |
24 #include <string.h> | |
25 | |
26 #include "prerror.h" | |
27 #include "prinit.h" | |
28 #include "prlog.h" | |
29 | |
30 /*--------------- private memio types -----------------------*/ | |
31 | |
32 /*---------------------------------------------------------------------- | |
33 Simple private circular buffer class. Size cannot be changed once allocated. | |
34 ----------------------------------------------------------------------*/ | |
35 | |
36 struct memio_buffer { | |
37 int head; /* where to take next byte out of buf */ | |
38 int tail; /* where to put next byte into buf */ | |
39 int bufsize; /* number of bytes allocated to buf */ | |
40 /* TODO(port): error handling is pessimistic right now. | |
41 * Once an error is set, the socket is considered broken | |
42 * (PR_WOULD_BLOCK_ERROR not included). | |
43 */ | |
44 PRErrorCode last_err; | |
45 uint8_t* buf; | |
46 }; | |
47 | |
48 | |
49 /* The 'secret' field of a PRFileDesc created by memio_CreateIOLayer points | |
50 * to one of these. | |
51 * In the public header, we use struct memio_Private as a typesafe alias | |
52 * for this. This causes a few ugly typecasts in the private file, but | |
53 * seems safer. | |
54 */ | |
55 struct PRFilePrivate { | |
56 /* read requests are satisfied from this buffer */ | |
57 struct memio_buffer readbuf; | |
58 | |
59 /* write requests are satisfied from this buffer */ | |
60 struct memio_buffer writebuf; | |
61 | |
62 /* SSL needs to know socket peer's name */ | |
63 PRNetAddr peername; | |
64 | |
65 /* if set, empty I/O returns EOF instead of EWOULDBLOCK */ | |
66 int eof; | |
67 | |
68 /* if set, the number of bytes requested from readbuf that were not | |
69 * fulfilled (due to readbuf being empty) */ | |
70 int read_requested; | |
71 }; | |
72 | |
73 /*--------------- private memio_buffer functions ---------------------*/ | |
74 | |
75 /* Forward declarations. */ | |
76 | |
77 /* Allocate a memio_buffer of given size. */ | |
78 static void memio_buffer_new(struct memio_buffer *mb, int size); | |
79 | |
80 /* Deallocate a memio_buffer allocated by memio_buffer_new. */ | |
81 static void memio_buffer_destroy(struct memio_buffer *mb); | |
82 | |
83 /* How many bytes can be read out of the buffer without wrapping */ | |
84 static int memio_buffer_used_contiguous(const struct memio_buffer *mb); | |
85 | |
86 /* How many bytes exist after the wrap? */ | |
87 static int memio_buffer_wrapped_bytes(const struct memio_buffer *mb); | |
88 | |
89 /* How many bytes can be written into the buffer without wrapping */ | |
90 static int memio_buffer_unused_contiguous(const struct memio_buffer *mb); | |
91 | |
92 /* Write n bytes into the buffer. Returns number of bytes written. */ | |
93 static int memio_buffer_put(struct memio_buffer *mb, const uint8_t* buf, int n); | |
94 | |
95 /* Read n bytes from the buffer. Returns number of bytes read. */ | |
96 static int memio_buffer_get(struct memio_buffer *mb, uint8_t* buf, int n); | |
97 | |
98 /* Allocate a memio_buffer of given size. */ | |
99 static void memio_buffer_new(struct memio_buffer *mb, int size) { | |
100 mb->head = 0; | |
101 mb->tail = 0; | |
102 mb->bufsize = size; | |
103 mb->buf = static_cast<uint8_t*>(malloc(size)); | |
104 } | |
105 | |
106 /* Deallocate a memio_buffer allocated by memio_buffer_new. */ | |
107 static void memio_buffer_destroy(struct memio_buffer *mb) { | |
108 free(mb->buf); | |
109 mb->buf = NULL; | |
110 mb->bufsize = 0; | |
111 mb->head = 0; | |
112 mb->tail = 0; | |
113 } | |
114 | |
115 /* How many bytes can be read out of the buffer without wrapping */ | |
116 static int memio_buffer_used_contiguous(const struct memio_buffer *mb) { | |
117 return (((mb->tail >= mb->head) ? mb->tail : mb->bufsize) - mb->head); | |
118 } | |
119 | |
120 /* How many bytes exist after the wrap? */ | |
121 static int memio_buffer_wrapped_bytes(const struct memio_buffer *mb) { | |
122 return (mb->tail >= mb->head) ? 0 : mb->tail; | |
123 } | |
124 | |
125 /* How many bytes can be written into the buffer without wrapping */ | |
126 static int memio_buffer_unused_contiguous(const struct memio_buffer *mb) { | |
127 if (mb->head > mb->tail) return mb->head - mb->tail - 1; | |
128 return mb->bufsize - mb->tail - (mb->head == 0); | |
129 } | |
130 | |
131 /* Write n bytes into the buffer. Returns number of bytes written. */ | |
132 static int memio_buffer_put(struct memio_buffer *mb, | |
133 const uint8_t* buf, | |
134 int n) { | |
135 int len; | |
136 int transferred = 0; | |
137 | |
138 /* Handle part before wrap */ | |
139 len = PR_MIN(n, memio_buffer_unused_contiguous(mb)); | |
140 if (len > 0) { | |
141 /* Buffer not full */ | |
142 memmove(&mb->buf[mb->tail], buf, len); | |
143 mb->tail += len; | |
144 if (mb->tail == mb->bufsize) | |
145 mb->tail = 0; | |
146 n -= len; | |
147 buf += len; | |
148 transferred += len; | |
149 | |
150 /* Handle part after wrap */ | |
151 len = PR_MIN(n, memio_buffer_unused_contiguous(mb)); | |
152 if (len > 0) { | |
153 /* Output buffer still not full, input buffer still not empty */ | |
154 memmove(&mb->buf[mb->tail], buf, len); | |
155 mb->tail += len; | |
156 if (mb->tail == mb->bufsize) | |
157 mb->tail = 0; | |
158 transferred += len; | |
159 } | |
160 } | |
161 | |
162 return transferred; | |
163 } | |
164 | |
165 | |
166 /* Read n bytes from the buffer. Returns number of bytes read. */ | |
167 static int memio_buffer_get(struct memio_buffer *mb, uint8_t* buf, int n) { | |
168 int len; | |
169 int transferred = 0; | |
170 | |
171 /* Handle part before wrap */ | |
172 len = PR_MIN(n, memio_buffer_used_contiguous(mb)); | |
173 if (len) { | |
174 memmove(buf, &mb->buf[mb->head], len); | |
175 mb->head += len; | |
176 if (mb->head == mb->bufsize) | |
177 mb->head = 0; | |
178 n -= len; | |
179 buf += len; | |
180 transferred += len; | |
181 | |
182 /* Handle part after wrap */ | |
183 len = PR_MIN(n, memio_buffer_used_contiguous(mb)); | |
184 if (len) { | |
185 memmove(buf, &mb->buf[mb->head], len); | |
186 mb->head += len; | |
187 if (mb->head == mb->bufsize) | |
188 mb->head = 0; | |
189 transferred += len; | |
190 } | |
191 } | |
192 | |
193 return transferred; | |
194 } | |
195 | |
196 /*--------------- private memio functions -----------------------*/ | |
197 | |
198 static PRStatus PR_CALLBACK memio_Close(PRFileDesc *fd) { | |
199 struct PRFilePrivate *secret = fd->secret; | |
200 memio_buffer_destroy(&secret->readbuf); | |
201 memio_buffer_destroy(&secret->writebuf); | |
202 free(secret); | |
203 fd->dtor(fd); | |
204 return PR_SUCCESS; | |
205 } | |
206 | |
207 static PRStatus PR_CALLBACK memio_Shutdown(PRFileDesc *fd, PRIntn how) { | |
208 /* TODO: pass shutdown status to app somehow */ | |
209 return PR_SUCCESS; | |
210 } | |
211 | |
212 /* If there was a network error in the past taking bytes | |
213 * out of the buffer, return it to the next call that | |
214 * tries to read from an empty buffer. | |
215 */ | |
216 static int PR_CALLBACK memio_Recv(PRFileDesc *fd, | |
217 uint8_t *buf, | |
218 PRInt32 len, | |
219 PRIntn flags, | |
220 PRIntervalTime timeout) { | |
221 struct PRFilePrivate *secret; | |
222 struct memio_buffer *mb; | |
223 int rv; | |
224 | |
225 if (flags) { | |
226 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); | |
227 return -1; | |
228 } | |
229 | |
230 secret = fd->secret; | |
231 mb = &secret->readbuf; | |
232 PR_ASSERT(mb->bufsize); | |
233 rv = memio_buffer_get(mb, buf, len); | |
234 if (rv == 0 && !secret->eof) { | |
235 secret->read_requested = len; | |
236 /* If there is no more data in the buffer, report any pending errors | |
237 * that were previously observed. Note that both the readbuf and the | |
238 * writebuf are checked for errors, since the application may have | |
239 * encountered a socket error while writing that would otherwise not | |
240 * be reported until the application attempted to write again - which | |
241 * it may never do. | |
242 */ | |
243 if (mb->last_err) | |
244 PR_SetError(mb->last_err, 0); | |
245 else if (secret->writebuf.last_err) | |
246 PR_SetError(secret->writebuf.last_err, 0); | |
247 else | |
248 PR_SetError(PR_WOULD_BLOCK_ERROR, 0); | |
249 return -1; | |
250 } | |
251 | |
252 secret->read_requested = 0; | |
253 return rv; | |
254 } | |
255 | |
256 static int PR_CALLBACK memio_Read(PRFileDesc *fd, uint8_t *buf, PRInt32 len) { | |
257 /* pull bytes from buffer */ | |
258 return memio_Recv(fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT); | |
259 } | |
260 | |
261 static int PR_CALLBACK memio_Send(PRFileDesc *fd, | |
262 const uint8_t *buf, | |
263 PRInt32 len, | |
264 PRIntn flags, | |
265 PRIntervalTime timeout) { | |
266 struct PRFilePrivate *secret; | |
267 struct memio_buffer *mb; | |
268 int rv; | |
269 | |
270 secret = fd->secret; | |
271 mb = &secret->writebuf; | |
272 PR_ASSERT(mb->bufsize); | |
273 | |
274 /* Note that the read error state is not reported, because it cannot be | |
275 * reported until all buffered data has been read. If there is an error | |
276 * with the next layer, attempting to call Send again will report the | |
277 * error appropriately. | |
278 */ | |
279 if (mb->last_err) { | |
280 PR_SetError(mb->last_err, 0); | |
281 return -1; | |
282 } | |
283 rv = memio_buffer_put(mb, buf, len); | |
284 if (rv == 0) { | |
285 PR_SetError(PR_WOULD_BLOCK_ERROR, 0); | |
286 return -1; | |
287 } | |
288 return rv; | |
289 } | |
290 | |
291 static int PR_CALLBACK memio_Write(PRFileDesc *fd, | |
292 const uint8_t *buf, | |
293 PRInt32 len) { | |
294 /* append bytes to buffer */ | |
295 return memio_Send(fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT); | |
296 } | |
297 | |
298 static PRStatus PR_CALLBACK memio_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) { | |
299 /* TODO: fail if memio_SetPeerName has not been called */ | |
300 struct PRFilePrivate *secret = fd->secret; | |
301 *addr = secret->peername; | |
302 return PR_SUCCESS; | |
303 } | |
304 | |
305 static PRStatus memio_GetSocketOption(PRFileDesc *fd, | |
306 PRSocketOptionData *data) { | |
307 /* | |
308 * Even in the original version for real tcp sockets, | |
309 * PR_SockOpt_Nonblocking is a special case that does not | |
310 * translate to a getsockopt() call | |
311 */ | |
312 if (PR_SockOpt_Nonblocking == data->option) { | |
313 data->value.non_blocking = PR_TRUE; | |
314 return PR_SUCCESS; | |
315 } | |
316 PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); | |
317 return PR_FAILURE; | |
318 } | |
319 | |
320 /*--------------- private memio data -----------------------*/ | |
321 | |
322 /* | |
323 * Implement just the bare minimum number of methods needed to make ssl happy. | |
324 * | |
325 * Oddly, PR_Recv calls ssl_Recv calls ssl_SocketIsBlocking calls | |
326 * PR_GetSocketOption, so we have to provide an implementation of | |
327 * PR_GetSocketOption that just says "I'm nonblocking". | |
328 */ | |
329 | |
330 static struct PRIOMethods memio_layer_methods = { | |
331 PR_DESC_LAYERED, | |
332 memio_Close, | |
333 (PRReadFN)memio_Read, | |
334 (PRWriteFN)memio_Write, | |
335 NULL, | |
336 NULL, | |
337 NULL, | |
338 NULL, | |
339 NULL, | |
340 NULL, | |
341 NULL, | |
342 NULL, | |
343 NULL, | |
344 NULL, | |
345 NULL, | |
346 NULL, | |
347 memio_Shutdown, | |
348 (PRRecvFN)memio_Recv, | |
349 (PRSendFN)memio_Send, | |
350 NULL, | |
351 NULL, | |
352 NULL, | |
353 NULL, | |
354 NULL, | |
355 NULL, | |
356 memio_GetPeerName, | |
357 NULL, | |
358 NULL, | |
359 memio_GetSocketOption, | |
360 NULL, | |
361 NULL, | |
362 NULL, | |
363 NULL, | |
364 NULL, | |
365 NULL, | |
366 NULL, | |
367 }; | |
368 | |
369 static PRDescIdentity memio_identity = PR_INVALID_IO_LAYER; | |
370 | |
371 static PRStatus memio_InitializeLayerName(void) { | |
372 memio_identity = PR_GetUniqueIdentity("memio"); | |
373 return PR_SUCCESS; | |
374 } | |
375 | |
376 /*--------------- public memio functions -----------------------*/ | |
377 | |
378 PRFileDesc *memio_CreateIOLayer(int readbufsize, int writebufsize) { | |
379 PRFileDesc *fd; | |
380 struct PRFilePrivate *secret; | |
381 static PRCallOnceType once; | |
382 | |
383 PR_CallOnce(&once, memio_InitializeLayerName); | |
384 | |
385 fd = PR_CreateIOLayerStub(memio_identity, &memio_layer_methods); | |
386 secret = static_cast<PRFilePrivate*>(malloc(sizeof(struct PRFilePrivate))); | |
387 memset(secret, 0, sizeof(*secret)); | |
388 | |
389 memio_buffer_new(&secret->readbuf, readbufsize); | |
390 memio_buffer_new(&secret->writebuf, writebufsize); | |
391 fd->secret = secret; | |
392 return fd; | |
393 } | |
394 | |
395 void memio_SetPeerName(PRFileDesc* fd, const PRNetAddr* peername) { | |
396 PRFileDesc *memiofd = PR_GetIdentitiesLayer(fd, memio_identity); | |
397 struct PRFilePrivate *secret = memiofd->secret; | |
398 secret->peername = *peername; | |
399 } | |
400 | |
401 memio_Private* memio_GetSecret(PRFileDesc* fd) { | |
402 PRFileDesc* memiofd = PR_GetIdentitiesLayer(fd, memio_identity); | |
403 struct PRFilePrivate *secret = memiofd->secret; | |
404 return reinterpret_cast<memio_Private*>(secret); | |
405 } | |
406 | |
407 int memio_GetReadRequest(memio_Private *secret) { | |
408 return reinterpret_cast<PRFilePrivate*>(secret)->read_requested; | |
409 } | |
410 | |
411 int memio_GetReadParams(memio_Private* secret, uint8_t** buf) { | |
412 struct memio_buffer* mb = | |
413 &(reinterpret_cast<PRFilePrivate*>(secret)->readbuf); | |
414 PR_ASSERT(mb->bufsize); | |
415 | |
416 *buf = &mb->buf[mb->tail]; | |
417 return memio_buffer_unused_contiguous(mb); | |
418 } | |
419 | |
420 int memio_GetReadableBufferSize(memio_Private *secret) { | |
421 struct memio_buffer* mb = | |
422 &(reinterpret_cast<PRFilePrivate*>(secret)->readbuf); | |
423 PR_ASSERT(mb->bufsize); | |
424 | |
425 return memio_buffer_used_contiguous(mb); | |
426 } | |
427 | |
428 void memio_PutReadResult(memio_Private *secret, int bytes_read) { | |
429 struct memio_buffer* mb = | |
430 &(reinterpret_cast<PRFilePrivate*>(secret)->readbuf); | |
431 PR_ASSERT(mb->bufsize); | |
432 | |
433 if (bytes_read > 0) { | |
434 mb->tail += bytes_read; | |
435 if (mb->tail == mb->bufsize) | |
436 mb->tail = 0; | |
437 } else if (bytes_read == 0) { | |
438 /* Record EOF condition and report to caller when buffer runs dry */ | |
439 reinterpret_cast<PRFilePrivate*>(secret)->eof = PR_TRUE; | |
440 } else /* if (bytes_read < 0) */ { | |
441 mb->last_err = bytes_read; | |
442 } | |
443 } | |
444 | |
445 int memio_GetWriteParams(memio_Private *secret, | |
446 const uint8_t** buf1, unsigned int *len1, | |
447 const uint8_t** buf2, unsigned int *len2) { | |
448 struct memio_buffer* mb = | |
449 &(reinterpret_cast<PRFilePrivate*>(secret)->writebuf); | |
450 PR_ASSERT(mb->bufsize); | |
451 | |
452 if (mb->last_err) | |
453 return mb->last_err; | |
454 | |
455 *buf1 = &mb->buf[mb->head]; | |
456 *len1 = memio_buffer_used_contiguous(mb); | |
457 *buf2 = mb->buf; | |
458 *len2 = memio_buffer_wrapped_bytes(mb); | |
459 return 0; | |
460 } | |
461 | |
462 void memio_PutWriteResult(memio_Private *secret, int bytes_written) { | |
463 struct memio_buffer* mb = | |
464 &(reinterpret_cast<PRFilePrivate*>(secret)->writebuf); | |
465 PR_ASSERT(mb->bufsize); | |
466 | |
467 if (bytes_written > 0) { | |
468 mb->head += bytes_written; | |
469 if (mb->head >= mb->bufsize) | |
470 mb->head -= mb->bufsize; | |
471 } else if (bytes_written < 0) { | |
472 mb->last_err = bytes_written; | |
473 } | |
474 } | |
475 | |
476 /*--------------- private memio_buffer self-test -----------------*/ | |
477 | |
478 /* Even a trivial unit test is very helpful when doing circular buffers. */ | |
479 /*#define TRIVIAL_SELF_TEST*/ | |
480 #ifdef TRIVIAL_SELF_TEST | |
481 | |
482 #define TEST_BUFLEN 7 | |
483 | |
484 #define CHECKEQ(a, b) { \ | |
485 if ((a) != (b)) { \ | |
486 printf("%d != %d, Test failed line %d\n", a, b, __LINE__); \ | |
487 exit(1); \ | |
488 } \ | |
489 } | |
490 | |
491 #define FROM_STR(a) reinterpret_cast<const uint8_t*>(a) | |
492 | |
493 int main() { | |
494 struct memio_buffer mb; | |
495 uint8_t buf[100]; | |
496 int i; | |
497 | |
498 memio_buffer_new(&mb, TEST_BUFLEN); | |
499 | |
500 CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1); | |
501 CHECKEQ(memio_buffer_used_contiguous(&mb), 0); | |
502 | |
503 CHECKEQ(memio_buffer_put(&mb, FROM_STR("howdy"), 5), 5); | |
504 | |
505 CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1-5); | |
506 CHECKEQ(memio_buffer_used_contiguous(&mb), 5); | |
507 CHECKEQ(memio_buffer_wrapped_bytes(&mb), 0); | |
508 | |
509 CHECKEQ(memio_buffer_put(&mb, FROM_STR("!"), 1), 1); | |
510 | |
511 CHECKEQ(memio_buffer_unused_contiguous(&mb), 0); | |
512 CHECKEQ(memio_buffer_used_contiguous(&mb), 6); | |
513 CHECKEQ(memio_buffer_wrapped_bytes(&mb), 0); | |
514 | |
515 CHECKEQ(memio_buffer_get(&mb, buf, 6), 6); | |
516 CHECKEQ(memcmp(buf, FROM_STR("howdy!"), 6), 0); | |
517 | |
518 CHECKEQ(memio_buffer_unused_contiguous(&mb), 1); | |
519 CHECKEQ(memio_buffer_used_contiguous(&mb), 0); | |
520 | |
521 CHECKEQ(memio_buffer_put(&mb, FROM_STR("01234"), 5), 5); | |
522 | |
523 CHECKEQ(memio_buffer_used_contiguous(&mb), 1); | |
524 CHECKEQ(memio_buffer_wrapped_bytes(&mb), 4); | |
525 CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1-5); | |
526 | |
527 CHECKEQ(memio_buffer_put(&mb, FROM_STR("5"), 1), 1); | |
528 | |
529 CHECKEQ(memio_buffer_unused_contiguous(&mb), 0); | |
530 CHECKEQ(memio_buffer_used_contiguous(&mb), 1); | |
531 | |
532 /* TODO: add more cases */ | |
533 | |
534 printf("Test passed\n"); | |
535 exit(0); | |
536 } | |
537 | |
538 #endif | |
OLD | NEW |