Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
** File: prlayer.c
** Description: Routines for handling pushable protocol modules on sockets.
*/
#include "primpl.h"
#include "prerror.h"
#include "prmem.h"
#include "prlock.h"
#include "prlog.h"
#include "prio.h"
#include <string.h> /* for memset() */
static PRStatus _PR_DestroyIOLayer(PRFileDesc* stack);
void PR_CALLBACK pl_FDDestructor(PRFileDesc* fd) {
PR_ASSERT(fd != NULL);
if (NULL != fd->lower) {
fd->lower->higher = fd->higher;
}
if (NULL != fd->higher) {
fd->higher->lower = fd->lower;
}
PR_DELETE(fd);
}
/*
** Default methods that just call down to the next fd.
*/
static PRStatus PR_CALLBACK pl_TopClose(PRFileDesc* fd) {
PRFileDesc *top, *lower;
PRStatus rv;
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
PR_ASSERT(fd->secret == NULL);
PR_ASSERT(fd->methods->file_type == PR_DESC_LAYERED);
if (PR_IO_LAYER_HEAD == fd->identity) {
/*
* new style stack; close all the layers, before deleting the
* stack head
*/
rv = fd->lower->methods->close(fd->lower);
_PR_DestroyIOLayer(fd);
return rv;
}
if ((fd->higher) && (PR_IO_LAYER_HEAD == fd->higher->identity)) {
/*
* lower layers of new style stack
*/
lower = fd->lower;
/*
* pop and cleanup current layer
*/
top = PR_PopIOLayer(fd->higher, PR_TOP_IO_LAYER);
top->dtor(top);
/*
* then call lower layer
*/
return (lower->methods->close(lower));
} else {
/* old style stack */
top = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
top->dtor(top);
return (fd->methods->close)(fd);
}
}
static PRInt32 PR_CALLBACK pl_DefRead(PRFileDesc* fd, void* buf,
PRInt32 amount) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->read)(fd->lower, buf, amount);
}
static PRInt32 PR_CALLBACK pl_DefWrite(PRFileDesc* fd, const void* buf,
PRInt32 amount) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->write)(fd->lower, buf, amount);
}
static PRInt32 PR_CALLBACK pl_DefAvailable(PRFileDesc* fd) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->available)(fd->lower);
}
static PRInt64 PR_CALLBACK pl_DefAvailable64(PRFileDesc* fd) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->available64)(fd->lower);
}
static PRStatus PR_CALLBACK pl_DefFsync(PRFileDesc* fd) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->fsync)(fd->lower);
}
static PRInt32 PR_CALLBACK pl_DefSeek(PRFileDesc* fd, PRInt32 offset,
PRSeekWhence how) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->seek)(fd->lower, offset, how);
}
static PRInt64 PR_CALLBACK pl_DefSeek64(PRFileDesc* fd, PRInt64 offset,
PRSeekWhence how) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->seek64)(fd->lower, offset, how);
}
static PRStatus PR_CALLBACK pl_DefFileInfo(PRFileDesc* fd, PRFileInfo* info) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->fileInfo)(fd->lower, info);
}
static PRStatus PR_CALLBACK pl_DefFileInfo64(PRFileDesc* fd,
PRFileInfo64* info) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->fileInfo64)(fd->lower, info);
}
static PRInt32 PR_CALLBACK pl_DefWritev(PRFileDesc* fd, const PRIOVec* iov,
PRInt32 size, PRIntervalTime timeout) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->writev)(fd->lower, iov, size, timeout);
}
static PRStatus PR_CALLBACK pl_DefConnect(PRFileDesc* fd, const PRNetAddr* addr,
PRIntervalTime timeout) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->connect)(fd->lower, addr, timeout);
}
static PRStatus PR_CALLBACK pl_DefConnectcontinue(PRFileDesc* fd,
PRInt16 out_flags) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->connectcontinue)(fd->lower, out_flags);
}
static PRFileDesc* PR_CALLBACK pl_TopAccept(PRFileDesc* fd, PRNetAddr* addr,
PRIntervalTime timeout) {
PRStatus rv;
PRFileDesc *newfd, *layer = fd;
PRFileDesc* newstack;
PRBool newstyle_stack = PR_FALSE;
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
/* test for new style stack */
while (NULL != layer->higher) {
layer = layer->higher;
}
newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE;
newstack = PR_NEW(PRFileDesc);
if (NULL == newstack) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
return NULL;
}
*newstack = *fd; /* make a copy of the accepting layer */
newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout);
if (NULL == newfd) {
PR_DELETE(newstack);
return NULL;
}
if (newstyle_stack) {
newstack->lower = newfd;
newfd->higher = newstack;
return newstack;
}
/* this PR_PushIOLayer call cannot fail */
rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
PR_ASSERT(PR_SUCCESS == rv);
return newfd; /* that's it */
}
static PRStatus PR_CALLBACK pl_DefBind(PRFileDesc* fd, const PRNetAddr* addr) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->bind)(fd->lower, addr);
}
static PRStatus PR_CALLBACK pl_DefListen(PRFileDesc* fd, PRIntn backlog) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->listen)(fd->lower, backlog);
}
static PRStatus PR_CALLBACK pl_DefShutdown(PRFileDesc* fd, PRIntn how) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->shutdown)(fd->lower, how);
}
static PRInt32 PR_CALLBACK pl_DefRecv(PRFileDesc* fd, void* buf, PRInt32 amount,
PRIntn flags, PRIntervalTime timeout) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->recv)(fd->lower, buf, amount, flags, timeout);
}
static PRInt32 PR_CALLBACK pl_DefSend(PRFileDesc* fd, const void* buf,
PRInt32 amount, PRIntn flags,
PRIntervalTime timeout) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout);
}
static PRInt32 PR_CALLBACK pl_DefRecvfrom(PRFileDesc* fd, void* buf,
PRInt32 amount, PRIntn flags,
PRNetAddr* addr,
PRIntervalTime timeout) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->recvfrom)(fd->lower, buf, amount, flags, addr,
timeout);
}
static PRInt32 PR_CALLBACK pl_DefSendto(PRFileDesc* fd, const void* buf,
PRInt32 amount, PRIntn flags,
const PRNetAddr* addr,
PRIntervalTime timeout) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->sendto)(fd->lower, buf, amount, flags, addr,
timeout);
}
static PRInt16 PR_CALLBACK pl_DefPoll(PRFileDesc* fd, PRInt16 in_flags,
PRInt16* out_flags) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags);
}
static PRInt32 PR_CALLBACK pl_DefAcceptread(PRFileDesc* sd, PRFileDesc** nd,
PRNetAddr** raddr, void* buf,
PRInt32 amount, PRIntervalTime t) {
PRInt32 nbytes;
PRStatus rv;
PRFileDesc* newstack;
PRFileDesc* layer = sd;
PRBool newstyle_stack = PR_FALSE;
PR_ASSERT(sd != NULL);
PR_ASSERT(sd->lower != NULL);
/* test for new style stack */
while (NULL != layer->higher) {
layer = layer->higher;
}
newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE;
newstack = PR_NEW(PRFileDesc);
if (NULL == newstack) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
return -1;
}
*newstack = *sd; /* make a copy of the accepting layer */
nbytes = sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, t);
if (-1 == nbytes) {
PR_DELETE(newstack);
return nbytes;
}
if (newstyle_stack) {
newstack->lower = *nd;
(*nd)->higher = newstack;
*nd = newstack;
return nbytes;
}
/* this PR_PushIOLayer call cannot fail */
rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack);
PR_ASSERT(PR_SUCCESS == rv);
return nbytes;
}
static PRInt32 PR_CALLBACK pl_DefTransmitfile(PRFileDesc* sd, PRFileDesc* fd,
const void* headers, PRInt32 hlen,
PRTransmitFileFlags flags,
PRIntervalTime t) {
PR_ASSERT(sd != NULL);
PR_ASSERT(sd->lower != NULL);
return sd->lower->methods->transmitfile(sd->lower, fd, headers, hlen, flags,
t);
}
static PRStatus PR_CALLBACK pl_DefGetsockname(PRFileDesc* fd, PRNetAddr* addr) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->getsockname)(fd->lower, addr);
}
static PRStatus PR_CALLBACK pl_DefGetpeername(PRFileDesc* fd, PRNetAddr* addr) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->getpeername)(fd->lower, addr);
}
static PRStatus PR_CALLBACK pl_DefGetsocketoption(PRFileDesc* fd,
PRSocketOptionData* data) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->getsocketoption)(fd->lower, data);
}
static PRStatus PR_CALLBACK
pl_DefSetsocketoption(PRFileDesc* fd, const PRSocketOptionData* data) {
PR_ASSERT(fd != NULL);
PR_ASSERT(fd->lower != NULL);
return (fd->lower->methods->setsocketoption)(fd->lower, data);
}
static PRInt32 PR_CALLBACK pl_DefSendfile(PRFileDesc* sd, PRSendFileData* sfd,
PRTransmitFileFlags flags,
PRIntervalTime timeout) {
PR_ASSERT(sd != NULL);
PR_ASSERT(sd->lower != NULL);
return sd->lower->methods->sendfile(sd->lower, sfd, flags, timeout);
}
/* Methods for the top of the stack. Just call down to the next fd. */
static PRIOMethods pl_methods = {PR_DESC_LAYERED,
pl_TopClose,
pl_DefRead,
pl_DefWrite,
pl_DefAvailable,
pl_DefAvailable64,
pl_DefFsync,
pl_DefSeek,
pl_DefSeek64,
pl_DefFileInfo,
pl_DefFileInfo64,
pl_DefWritev,
pl_DefConnect,
pl_TopAccept,
pl_DefBind,
pl_DefListen,
pl_DefShutdown,
pl_DefRecv,
pl_DefSend,
pl_DefRecvfrom,
pl_DefSendto,
pl_DefPoll,
pl_DefAcceptread,
pl_DefTransmitfile,
pl_DefGetsockname,
pl_DefGetpeername,
(PRReservedFN)_PR_InvalidInt,
(PRReservedFN)_PR_InvalidInt,
pl_DefGetsocketoption,
pl_DefSetsocketoption,
pl_DefSendfile,
pl_DefConnectcontinue,
(PRReservedFN)_PR_InvalidInt,
(PRReservedFN)_PR_InvalidInt,
(PRReservedFN)_PR_InvalidInt,
(PRReservedFN)_PR_InvalidInt};
PR_IMPLEMENT(const PRIOMethods*) PR_GetDefaultIOMethods(void) {
return &pl_methods;
} /* PR_GetDefaultIOMethods */
PR_IMPLEMENT(PRFileDesc*)
PR_CreateIOLayerStub(PRDescIdentity ident, const PRIOMethods* methods) {
PRFileDesc* fd = NULL;
PR_ASSERT((PR_NSPR_IO_LAYER != ident) && (PR_TOP_IO_LAYER != ident));
if ((PR_NSPR_IO_LAYER == ident) || (PR_TOP_IO_LAYER == ident)) {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
} else {
fd = PR_NEWZAP(PRFileDesc);
if (NULL == fd) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
} else {
fd->methods = methods;
fd->dtor = pl_FDDestructor;
fd->identity = ident;
}
}
return fd;
} /* PR_CreateIOLayerStub */
/*
* PR_CreateIOLayer
* Create a new style stack, where the stack top is a dummy header.
* Unlike the old style stacks, the contents of the stack head
* are not modified when a layer is pushed onto or popped from a new
* style stack.
*/
PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayer(PRFileDesc* top) {
PRFileDesc* fd = NULL;
fd = PR_NEWZAP(PRFileDesc);
if (NULL == fd) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
} else {
fd->methods = &pl_methods;
fd->dtor = pl_FDDestructor;
fd->identity = PR_IO_LAYER_HEAD;
fd->higher = NULL;
fd->lower = top;
top->higher = fd;
top->lower = NULL;
}
return fd;
} /* PR_CreateIOLayer */
/*
* _PR_DestroyIOLayer
* Delete the stack head of a new style stack.
*/
static PRStatus _PR_DestroyIOLayer(PRFileDesc* stack) {
if (NULL == stack) {
return PR_FAILURE;
}
PR_DELETE(stack);
return PR_SUCCESS;
} /* _PR_DestroyIOLayer */
PR_IMPLEMENT(PRStatus)
PR_PushIOLayer(PRFileDesc* stack, PRDescIdentity id, PRFileDesc* fd) {
PRFileDesc* insert = PR_GetIdentitiesLayer(stack, id);
PR_ASSERT(fd != NULL);
PR_ASSERT(stack != NULL);
PR_ASSERT(insert != NULL);
PR_ASSERT(PR_IO_LAYER_HEAD != id);
if ((NULL == stack) || (NULL == fd) || (NULL == insert)) {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return PR_FAILURE;
}
if (stack == insert) {
/* going on top of the stack */
/* old-style stack */
PRFileDesc copy = *stack;
*stack = *fd;
*fd = copy;
fd->higher = stack;
if (fd->lower) {
PR_ASSERT(fd->lower->higher == stack);
fd->lower->higher = fd;
}
stack->lower = fd;
stack->higher = NULL;
} else {
/*
* going somewhere in the middle of the stack for both old and new
* style stacks, or going on top of stack for new style stack
*/
fd->lower = insert;
fd->higher = insert->higher;
insert->higher->lower = fd;
insert->higher = fd;
}
return PR_SUCCESS;
}
PR_IMPLEMENT(PRFileDesc*) PR_PopIOLayer(PRFileDesc* stack, PRDescIdentity id) {
PRFileDesc* extract = PR_GetIdentitiesLayer(stack, id);
PR_ASSERT(0 != id);
PR_ASSERT(NULL != stack);
PR_ASSERT(NULL != extract);
if ((NULL == stack) || (0 == id) || (NULL == extract)) {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return NULL;
}
if (extract == stack) {
/* popping top layer of the stack */
/* old style stack */
PRFileDesc copy = *stack;
extract = stack->lower;
*stack = *extract;
*extract = copy;
stack->higher = NULL;
if (stack->lower) {
PR_ASSERT(stack->lower->higher == extract);
stack->lower->higher = stack;
}
} else if ((PR_IO_LAYER_HEAD == stack->identity) &&
(extract == stack->lower) && (extract->lower == NULL)) {
/*
* new style stack
* popping the only layer in the stack; delete the stack too
*/
stack->lower = NULL;
_PR_DestroyIOLayer(stack);
} else {
/* for both kinds of stacks */
extract->lower->higher = extract->higher;
extract->higher->lower = extract->lower;
}
extract->higher = extract->lower = NULL;
return extract;
} /* PR_PopIOLayer */
#define ID_CACHE_INCREMENT 16
typedef struct _PRIdentity_cache {
PRLock* ml;
char** name;
PRIntn length;
PRDescIdentity ident;
} _PRIdentity_cache;
static _PRIdentity_cache identity_cache;
PR_IMPLEMENT(PRDescIdentity) PR_GetUniqueIdentity(const char* layer_name) {
PRDescIdentity identity, length;
char **names = NULL, *name = NULL, **old = NULL;
if (!_pr_initialized) {
_PR_ImplicitInitialization();
}
PR_ASSERT((PRDescIdentity)0x7fff > identity_cache.ident);
if (NULL != layer_name) {
name = (char*)PR_Malloc(strlen(layer_name) + 1);
if (NULL == name) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
return PR_INVALID_IO_LAYER;
}
strcpy(name, layer_name);
}
/* this initial code runs unsafe */
retry:
PR_ASSERT(NULL == names);
/*
* In the initial round, both identity_cache.ident and
* identity_cache.length are 0, so (identity_cache.ident + 1) is greater
* than length. In later rounds, identity_cache.ident is always less
* than length, so (identity_cache.ident + 1) can be equal to but cannot
* be greater than length.
*/
length = identity_cache.length;
if ((identity_cache.ident + 1) >= length) {
length += ID_CACHE_INCREMENT;
names = (char**)PR_CALLOC(length * sizeof(char*));
if (NULL == names) {
if (NULL != name) {
PR_DELETE(name);
}
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
return PR_INVALID_IO_LAYER;
}
}
/* now we get serious about thread safety */
PR_Lock(identity_cache.ml);
PR_ASSERT(identity_cache.length == 0 ||
identity_cache.ident < identity_cache.length);
identity = identity_cache.ident + 1;
if (identity >= identity_cache.length) /* there's no room */
{
/* we have to do something - hopefully it's already done */
if ((NULL != names) && (identity < length)) {
/* what we did is still okay */
if (identity_cache.length != 0) {
memcpy(names, identity_cache.name,
identity_cache.length * sizeof(char*));
}
old = identity_cache.name;
identity_cache.name = names;
identity_cache.length = length;
names = NULL;
} else {
PR_Unlock(identity_cache.ml);
if (NULL != names) {
PR_DELETE(names);
}
goto retry;
}
}
if (NULL != name) /* there's a name to be stored */
{
identity_cache.name[identity] = name;
}
identity_cache.ident = identity;
PR_ASSERT(identity_cache.ident < identity_cache.length);
PR_Unlock(identity_cache.ml);
if (NULL != old) {
PR_DELETE(old);
}
if (NULL != names) {
PR_DELETE(names);
}
return identity;
} /* PR_GetUniqueIdentity */
PR_IMPLEMENT(const char*) PR_GetNameForIdentity(PRDescIdentity ident) {
const char* rv = NULL;
if (!_pr_initialized) {
_PR_ImplicitInitialization();
}
if ((PR_TOP_IO_LAYER != ident) && (ident >= 0)) {
PR_Lock(identity_cache.ml);
PR_ASSERT(ident <= identity_cache.ident);
rv = (ident > identity_cache.ident) ? NULL : identity_cache.name[ident];
PR_Unlock(identity_cache.ml);
}
return rv;
} /* PR_GetNameForIdentity */
PR_IMPLEMENT(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd) {
PR_ASSERT(NULL != fd);
if (PR_IO_LAYER_HEAD == fd->identity) {
PR_ASSERT(NULL != fd->lower);
return fd->lower->identity;
}
return fd->identity;
} /* PR_GetLayersIdentity */
PR_IMPLEMENT(PRFileDesc*)
PR_GetIdentitiesLayer(PRFileDesc* fd, PRDescIdentity id) {
PRFileDesc* layer = fd;
if (PR_TOP_IO_LAYER == id) {
if (PR_IO_LAYER_HEAD == fd->identity) {
return fd->lower;
}
return fd;
}
for (layer = fd; layer != NULL; layer = layer->lower) {
if (id == layer->identity) {
return layer;
}
}
for (layer = fd; layer != NULL; layer = layer->higher) {
if (id == layer->identity) {
return layer;
}
}
return NULL;
} /* PR_GetIdentitiesLayer */
void _PR_InitLayerCache(void) {
memset(&identity_cache, 0, sizeof(identity_cache));
identity_cache.ml = PR_NewLock();
PR_ASSERT(NULL != identity_cache.ml);
} /* _PR_InitLayerCache */
void _PR_CleanupLayerCache(void) {
if (identity_cache.ml) {
PR_DestroyLock(identity_cache.ml);
identity_cache.ml = NULL;
}
if (identity_cache.name) {
PRDescIdentity ident;
for (ident = 0; ident <= identity_cache.ident; ident++) {
PR_DELETE(identity_cache.name[ident]);
}
PR_DELETE(identity_cache.name);
}
} /* _PR_CleanupLayerCache */
/* prlayer.c */