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