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