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