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