• 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 
60 /*--------------- private memio_buffer functions ---------------------*/
61 
62 /* Forward declarations.  */
63 
64 /* Allocate a memio_buffer of given size. */
65 static void memio_buffer_new(struct memio_buffer *mb, int size);
66 
67 /* Deallocate a memio_buffer allocated by memio_buffer_new. */
68 static void memio_buffer_destroy(struct memio_buffer *mb);
69 
70 /* How many bytes can be read out of the buffer without wrapping */
71 static int memio_buffer_used_contiguous(const struct memio_buffer *mb);
72 
73 /* How many bytes exist after the wrap? */
74 static int memio_buffer_wrapped_bytes(const struct memio_buffer *mb);
75 
76 /* How many bytes can be written into the buffer without wrapping */
77 static int memio_buffer_unused_contiguous(const struct memio_buffer *mb);
78 
79 /* Write n bytes into the buffer.  Returns number of bytes written. */
80 static int memio_buffer_put(struct memio_buffer *mb, const char *buf, int n);
81 
82 /* Read n bytes from the buffer.  Returns number of bytes read. */
83 static int memio_buffer_get(struct memio_buffer *mb, char *buf, int n);
84 
85 /* Allocate a memio_buffer of given size. */
memio_buffer_new(struct memio_buffer * mb,int size)86 static void memio_buffer_new(struct memio_buffer *mb, int size)
87 {
88     mb->head = 0;
89     mb->tail = 0;
90     mb->bufsize = size;
91     mb->buf = malloc(size);
92 }
93 
94 /* Deallocate a memio_buffer allocated by memio_buffer_new. */
memio_buffer_destroy(struct memio_buffer * mb)95 static void memio_buffer_destroy(struct memio_buffer *mb)
96 {
97     free(mb->buf);
98     mb->buf = NULL;
99     mb->head = 0;
100     mb->tail = 0;
101 }
102 
103 /* How many bytes can be read out of the buffer without wrapping */
memio_buffer_used_contiguous(const struct memio_buffer * mb)104 static int memio_buffer_used_contiguous(const struct memio_buffer *mb)
105 {
106     return (((mb->tail >= mb->head) ? mb->tail : mb->bufsize) - mb->head);
107 }
108 
109 /* How many bytes exist after the wrap? */
memio_buffer_wrapped_bytes(const struct memio_buffer * mb)110 static int memio_buffer_wrapped_bytes(const struct memio_buffer *mb)
111 {
112     return (mb->tail >= mb->head) ? 0 : mb->tail;
113 }
114 
115 /* How many bytes can be written into the buffer without wrapping */
memio_buffer_unused_contiguous(const struct memio_buffer * mb)116 static int memio_buffer_unused_contiguous(const struct memio_buffer *mb)
117 {
118     if (mb->head > mb->tail) return mb->head - mb->tail - 1;
119     return mb->bufsize - mb->tail - (mb->head == 0);
120 }
121 
122 /* Write n bytes into the buffer.  Returns number of bytes written. */
memio_buffer_put(struct memio_buffer * mb,const char * buf,int n)123 static int memio_buffer_put(struct memio_buffer *mb, const char *buf, int n)
124 {
125     int len;
126     int transferred = 0;
127 
128     /* Handle part before wrap */
129     len = PR_MIN(n, memio_buffer_unused_contiguous(mb));
130     if (len > 0) {
131         /* Buffer not full */
132         memcpy(&mb->buf[mb->tail], buf, len);
133         mb->tail += len;
134         if (mb->tail == mb->bufsize)
135             mb->tail = 0;
136         n -= len;
137         buf += len;
138         transferred += len;
139 
140         /* Handle part after wrap */
141         len = PR_MIN(n, memio_buffer_unused_contiguous(mb));
142         if (len > 0) {
143             /* Output buffer still not full, input buffer still not empty */
144             memcpy(&mb->buf[mb->tail], buf, len);
145             mb->tail += len;
146             if (mb->tail == mb->bufsize)
147                 mb->tail = 0;
148                 transferred += len;
149         }
150     }
151 
152     return transferred;
153 }
154 
155 
156 /* Read n bytes from the buffer.  Returns number of bytes read. */
memio_buffer_get(struct memio_buffer * mb,char * buf,int n)157 static int memio_buffer_get(struct memio_buffer *mb, char *buf, int n)
158 {
159     int len;
160     int transferred = 0;
161 
162     /* Handle part before wrap */
163     len = PR_MIN(n, memio_buffer_used_contiguous(mb));
164     if (len) {
165         memcpy(buf, &mb->buf[mb->head], len);
166         mb->head += len;
167         if (mb->head == mb->bufsize)
168             mb->head = 0;
169         n -= len;
170         buf += len;
171         transferred += len;
172 
173         /* Handle part after wrap */
174         len = PR_MIN(n, memio_buffer_used_contiguous(mb));
175         if (len) {
176         memcpy(buf, &mb->buf[mb->head], len);
177         mb->head += len;
178             if (mb->head == mb->bufsize)
179                 mb->head = 0;
180                 transferred += len;
181         }
182     }
183 
184     return transferred;
185 }
186 
187 /*--------------- private memio functions -----------------------*/
188 
memio_Close(PRFileDesc * fd)189 static PRStatus PR_CALLBACK memio_Close(PRFileDesc *fd)
190 {
191     struct PRFilePrivate *secret = fd->secret;
192     memio_buffer_destroy(&secret->readbuf);
193     memio_buffer_destroy(&secret->writebuf);
194     free(secret);
195     fd->dtor(fd);
196     return PR_SUCCESS;
197 }
198 
memio_Shutdown(PRFileDesc * fd,PRIntn how)199 static PRStatus PR_CALLBACK memio_Shutdown(PRFileDesc *fd, PRIntn how)
200 {
201     /* TODO: pass shutdown status to app somehow */
202     return PR_SUCCESS;
203 }
204 
205 /* If there was a network error in the past taking bytes
206  * out of the buffer, return it to the next call that
207  * tries to read from an empty buffer.
208  */
memio_Recv(PRFileDesc * fd,void * buf,PRInt32 len,PRIntn flags,PRIntervalTime timeout)209 static int PR_CALLBACK memio_Recv(PRFileDesc *fd, void *buf, PRInt32 len,
210                                   PRIntn flags, PRIntervalTime timeout)
211 {
212     struct PRFilePrivate *secret;
213     struct memio_buffer *mb;
214     int rv;
215 
216     if (flags) {
217         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
218         return -1;
219     }
220 
221     secret = fd->secret;
222     mb = &secret->readbuf;
223     PR_ASSERT(mb->bufsize);
224     rv = memio_buffer_get(mb, buf, len);
225     if (rv == 0 && !secret->eof) {
226         if (mb->last_err)
227             PR_SetError(mb->last_err, 0);
228         else
229             PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
230         return -1;
231     }
232 
233     return rv;
234 }
235 
memio_Read(PRFileDesc * fd,void * buf,PRInt32 len)236 static int PR_CALLBACK memio_Read(PRFileDesc *fd, void *buf, PRInt32 len)
237 {
238     /* pull bytes from buffer */
239     return memio_Recv(fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT);
240 }
241 
memio_Send(PRFileDesc * fd,const void * buf,PRInt32 len,PRIntn flags,PRIntervalTime timeout)242 static int PR_CALLBACK memio_Send(PRFileDesc *fd, const void *buf, PRInt32 len,
243                                   PRIntn flags, PRIntervalTime timeout)
244 {
245     struct PRFilePrivate *secret;
246     struct memio_buffer *mb;
247     int rv;
248 
249     secret = fd->secret;
250     mb = &secret->writebuf;
251     PR_ASSERT(mb->bufsize);
252 
253     if (mb->last_err) {
254         PR_SetError(mb->last_err, 0);
255         return -1;
256     }
257     rv = memio_buffer_put(mb, buf, len);
258     if (rv == 0) {
259         PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
260         return -1;
261     }
262     return rv;
263 }
264 
memio_Write(PRFileDesc * fd,const void * buf,PRInt32 len)265 static int PR_CALLBACK memio_Write(PRFileDesc *fd, const void *buf, PRInt32 len)
266 {
267     /* append bytes to buffer */
268     return memio_Send(fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT);
269 }
270 
memio_GetPeerName(PRFileDesc * fd,PRNetAddr * addr)271 static PRStatus PR_CALLBACK memio_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
272 {
273     /* TODO: fail if memio_SetPeerName has not been called */
274     struct PRFilePrivate *secret = fd->secret;
275     *addr = secret->peername;
276     return PR_SUCCESS;
277 }
278 
memio_GetSocketOption(PRFileDesc * fd,PRSocketOptionData * data)279 static PRStatus memio_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
280 {
281     /*
282      * Even in the original version for real tcp sockets,
283      * PR_SockOpt_Nonblocking is a special case that does not
284      * translate to a getsockopt() call
285      */
286     if (PR_SockOpt_Nonblocking == data->option) {
287         data->value.non_blocking = PR_TRUE;
288         return PR_SUCCESS;
289     }
290     PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
291     return PR_FAILURE;
292 }
293 
294 /*--------------- private memio data -----------------------*/
295 
296 /*
297  * Implement just the bare minimum number of methods needed to make ssl happy.
298  *
299  * Oddly, PR_Recv calls ssl_Recv calls ssl_SocketIsBlocking calls
300  * PR_GetSocketOption, so we have to provide an implementation of
301  * PR_GetSocketOption that just says "I'm nonblocking".
302  */
303 
304 static struct PRIOMethods  memio_layer_methods = {
305     PR_DESC_LAYERED,
306     memio_Close,
307     memio_Read,
308     memio_Write,
309     NULL,
310     NULL,
311     NULL,
312     NULL,
313     NULL,
314     NULL,
315     NULL,
316     NULL,
317     NULL,
318     NULL,
319     NULL,
320     NULL,
321     memio_Shutdown,
322     memio_Recv,
323     memio_Send,
324     NULL,
325     NULL,
326     NULL,
327     NULL,
328     NULL,
329     NULL,
330     memio_GetPeerName,
331     NULL,
332     NULL,
333     memio_GetSocketOption,
334     NULL,
335     NULL,
336     NULL,
337     NULL,
338     NULL,
339     NULL,
340     NULL,
341 };
342 
343 static PRDescIdentity memio_identity = PR_INVALID_IO_LAYER;
344 
memio_InitializeLayerName(void)345 static PRStatus memio_InitializeLayerName(void)
346 {
347     memio_identity = PR_GetUniqueIdentity("memio");
348     return PR_SUCCESS;
349 }
350 
351 /*--------------- public memio functions -----------------------*/
352 
memio_CreateIOLayer(int bufsize)353 PRFileDesc *memio_CreateIOLayer(int bufsize)
354 {
355     PRFileDesc *fd;
356     struct PRFilePrivate *secret;
357     static PRCallOnceType once;
358 
359     PR_CallOnce(&once, memio_InitializeLayerName);
360 
361     fd = PR_CreateIOLayerStub(memio_identity, &memio_layer_methods);
362     secret = malloc(sizeof(struct PRFilePrivate));
363     memset(secret, 0, sizeof(*secret));
364 
365     memio_buffer_new(&secret->readbuf, bufsize);
366     memio_buffer_new(&secret->writebuf, bufsize);
367     fd->secret = secret;
368     return fd;
369 }
370 
memio_SetPeerName(PRFileDesc * fd,const PRNetAddr * peername)371 void memio_SetPeerName(PRFileDesc *fd, const PRNetAddr *peername)
372 {
373     PRFileDesc *memiofd = PR_GetIdentitiesLayer(fd, memio_identity);
374     struct PRFilePrivate *secret = memiofd->secret;
375     secret->peername = *peername;
376 }
377 
memio_GetSecret(PRFileDesc * fd)378 memio_Private *memio_GetSecret(PRFileDesc *fd)
379 {
380     PRFileDesc *memiofd = PR_GetIdentitiesLayer(fd, memio_identity);
381     struct PRFilePrivate *secret =  memiofd->secret;
382     return (memio_Private *)secret;
383 }
384 
memio_GetReadParams(memio_Private * secret,char ** buf)385 int memio_GetReadParams(memio_Private *secret, char **buf)
386 {
387     struct memio_buffer* mb = &((PRFilePrivate *)secret)->readbuf;
388     PR_ASSERT(mb->bufsize);
389 
390     *buf = &mb->buf[mb->tail];
391     return memio_buffer_unused_contiguous(mb);
392 }
393 
memio_PutReadResult(memio_Private * secret,int bytes_read)394 void memio_PutReadResult(memio_Private *secret, int bytes_read)
395 {
396     struct memio_buffer* mb = &((PRFilePrivate *)secret)->readbuf;
397     PR_ASSERT(mb->bufsize);
398 
399     if (bytes_read > 0) {
400         mb->tail += bytes_read;
401         if (mb->tail == mb->bufsize)
402             mb->tail = 0;
403     } else if (bytes_read == 0) {
404         /* Record EOF condition and report to caller when buffer runs dry */
405         ((PRFilePrivate *)secret)->eof = PR_TRUE;
406     } else /* if (bytes_read < 0) */ {
407         mb->last_err = bytes_read;
408     }
409 }
410 
memio_GetWriteParams(memio_Private * secret,const char ** buf1,unsigned int * len1,const char ** buf2,unsigned int * len2)411 void memio_GetWriteParams(memio_Private *secret,
412                           const char **buf1, unsigned int *len1,
413                           const char **buf2, unsigned int *len2)
414 {
415     struct memio_buffer* mb = &((PRFilePrivate *)secret)->writebuf;
416     PR_ASSERT(mb->bufsize);
417 
418     *buf1 = &mb->buf[mb->head];
419     *len1 = memio_buffer_used_contiguous(mb);
420     *buf2 = mb->buf;
421     *len2 = memio_buffer_wrapped_bytes(mb);
422 }
423 
memio_PutWriteResult(memio_Private * secret,int bytes_written)424 void memio_PutWriteResult(memio_Private *secret, int bytes_written)
425 {
426     struct memio_buffer* mb = &((PRFilePrivate *)secret)->writebuf;
427     PR_ASSERT(mb->bufsize);
428 
429     if (bytes_written > 0) {
430         mb->head += bytes_written;
431         if (mb->head >= mb->bufsize)
432             mb->head -= mb->bufsize;
433     } else if (bytes_written < 0) {
434         mb->last_err = bytes_written;
435     }
436 }
437 
438 /*--------------- private memio_buffer self-test -----------------*/
439 
440 /* Even a trivial unit test is very helpful when doing circular buffers. */
441 /*#define TRIVIAL_SELF_TEST*/
442 #ifdef TRIVIAL_SELF_TEST
443 #include <stdio.h>
444 
445 #define TEST_BUFLEN 7
446 
447 #define CHECKEQ(a, b) { \
448     if ((a) != (b)) { \
449         printf("%d != %d, Test failed line %d\n", a, b, __LINE__); \
450         exit(1); \
451     } \
452 }
453 
main()454 int main()
455 {
456     struct memio_buffer mb;
457     char buf[100];
458     int i;
459 
460     memio_buffer_new(&mb, TEST_BUFLEN);
461 
462     CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1);
463     CHECKEQ(memio_buffer_used_contiguous(&mb), 0);
464 
465     CHECKEQ(memio_buffer_put(&mb, "howdy", 5), 5);
466 
467     CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1-5);
468     CHECKEQ(memio_buffer_used_contiguous(&mb), 5);
469     CHECKEQ(memio_buffer_wrapped_bytes(&mb), 0);
470 
471     CHECKEQ(memio_buffer_put(&mb, "!", 1), 1);
472 
473     CHECKEQ(memio_buffer_unused_contiguous(&mb), 0);
474     CHECKEQ(memio_buffer_used_contiguous(&mb), 6);
475     CHECKEQ(memio_buffer_wrapped_bytes(&mb), 0);
476 
477     CHECKEQ(memio_buffer_get(&mb, buf, 6), 6);
478     CHECKEQ(memcmp(buf, "howdy!", 6), 0);
479 
480     CHECKEQ(memio_buffer_unused_contiguous(&mb), 1);
481     CHECKEQ(memio_buffer_used_contiguous(&mb), 0);
482 
483     CHECKEQ(memio_buffer_put(&mb, "01234", 5), 5);
484 
485     CHECKEQ(memio_buffer_used_contiguous(&mb), 1);
486     CHECKEQ(memio_buffer_wrapped_bytes(&mb), 4);
487     CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1-5);
488 
489     CHECKEQ(memio_buffer_put(&mb, "5", 1), 1);
490 
491     CHECKEQ(memio_buffer_unused_contiguous(&mb), 0);
492     CHECKEQ(memio_buffer_used_contiguous(&mb), 1);
493 
494     /* TODO: add more cases */
495 
496     printf("Test passed\n");
497     exit(0);
498 }
499 
500 #endif
501