• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. */
memio_buffer_new(struct memio_buffer * mb,int 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. */
memio_buffer_destroy(struct memio_buffer * mb)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 */
memio_buffer_used_contiguous(const struct memio_buffer * mb)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? */
memio_buffer_wrapped_bytes(const struct memio_buffer * mb)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 */
memio_buffer_unused_contiguous(const struct memio_buffer * mb)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. */
memio_buffer_put(struct memio_buffer * mb,const char * buf,int n)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. */
memio_buffer_get(struct memio_buffer * mb,char * buf,int n)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 
memio_Close(PRFileDesc * fd)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 
memio_Shutdown(PRFileDesc * fd,PRIntn how)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  */
memio_Recv(PRFileDesc * fd,void * buf,PRInt32 len,PRIntn flags,PRIntervalTime timeout)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 
memio_Read(PRFileDesc * fd,void * buf,PRInt32 len)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 
memio_Send(PRFileDesc * fd,const void * buf,PRInt32 len,PRIntn flags,PRIntervalTime timeout)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 
memio_Write(PRFileDesc * fd,const void * buf,PRInt32 len)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 
memio_GetPeerName(PRFileDesc * fd,PRNetAddr * addr)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 
memio_GetSocketOption(PRFileDesc * fd,PRSocketOptionData * data)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 
memio_InitializeLayerName(void)366 static PRStatus memio_InitializeLayerName(void)
367 {
368     memio_identity = PR_GetUniqueIdentity("memio");
369     return PR_SUCCESS;
370 }
371 
372 /*--------------- public memio functions -----------------------*/
373 
memio_CreateIOLayer(int readbufsize,int writebufsize)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 
memio_SetPeerName(PRFileDesc * fd,const PRNetAddr * peername)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 
memio_GetSecret(PRFileDesc * fd)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 
memio_GetReadRequest(memio_Private * secret)406 int memio_GetReadRequest(memio_Private *secret)
407 {
408     return ((PRFilePrivate *)secret)->read_requested;
409 }
410 
memio_GetReadParams(memio_Private * secret,char ** buf)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 
memio_GetReadableBufferSize(memio_Private * secret)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 
memio_PutReadResult(memio_Private * secret,int bytes_read)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 
memio_GetWriteParams(memio_Private * secret,const char ** buf1,unsigned int * len1,const char ** buf2,unsigned int * len2)445 int memio_GetWriteParams(memio_Private *secret,
446                          const char **buf1, unsigned int *len1,
447                          const char **buf2, unsigned int *len2)
448 {
449     struct memio_buffer* mb = &((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 
memio_PutWriteResult(memio_Private * secret,int bytes_written)462 void memio_PutWriteResult(memio_Private *secret, int bytes_written)
463 {
464     struct memio_buffer* mb = &((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 #include <stdio.h>
482 
483 #define TEST_BUFLEN 7
484 
485 #define CHECKEQ(a, b) { \
486     if ((a) != (b)) { \
487         printf("%d != %d, Test failed line %d\n", a, b, __LINE__); \
488         exit(1); \
489     } \
490 }
491 
main()492 int main()
493 {
494     struct memio_buffer mb;
495     char 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, "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, "!", 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, "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, "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, "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
539