| 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 | |
| 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 | |
| 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 | |
| 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 | |
| OLD | NEW |