Files
m241Neelima/vxworks-6.9/target/src/dsi/un/comp/uncomp_usrreq.c
T
2026-07-30 11:19:44 +05:30

2108 lines
60 KiB
C

/* uncomp_usrreq.c - AF_LOCAL/COMP protocol implementation */
/*
* Copyright (c) 2003-2009, 2011-2013 Wind River Systems, Inc.
*
* The right to copy, distribute, modify or otherwise make use
* of this software may be licensed only pursuant to the terms
* of an applicable Wind River license agreement.
*/
/*
modification history
--------------------
01z,04sep13,xms fix TAINTED_SCALAR error. (WIND00414265)
01y,11jul12,shi clean up warnings (WIND00357529)
01x,28jun11,shi LP64 conversion
01w,21apr09,hps LP64: move drv_val_t to private header iosLibP.h
01v,12may08,jpb Modifications for source build. Renamed
WV_INSTRUMENTATION to _WRS_CONFIG_SV_INSTRUMENTATION.
01u,19jun07,ebh Changes to allow COMP to run on SMP systems (SMP-safe)
01t,04may07,ebh update to handle EINTR on sends (Defect #91328)
01s,06sep06,kch corrected diab compiler errors.
01r,06sep06,kch cleanup coreip stack header files.
01q,21feb05,ebh update manually (zippy merge was not running)
01p,21jan05,vvv osdep.h cleanup
01o,03jan05,bwa BUG FIX: connect() returning wrong errno (SPR #102896).
01n,11oct04,p_r fixed pathname for dsi header (SPR #102446)
01m,30aug04,bwa fixed shutdown() exception when done on closed socket.
01l,13jul04,p_r removed e-mail address from comment (SPR #99392)
01k,01jun04,bwa added flow control (SPR #97987).
01j,26may04,bwa added WV instrumentation; fixed 'netun' includes (SPR #97714).
01i,23apr04,bwa removed references to soXXX routines.
01h,12apr04,bwa added '#include <dsiSockLib.h>'; #undef'ed DEBUG_SHOW.
01g,04mar04,bwa changed 'netinet' directory references to 'netun';
dropped max data queued from 64k to 64k-1;
optimized uncomp_send() and uncomp_rcvd() for the reg case;
BUG FIX: buffer leak when ENOTCONN and EINVAL error in send().
01f,10feb04,bwa added express messages (MSG_EXP) support;
BUG FIX: non-reclamation of buffers when EMSGSIZE error;
01e,06jan04,bwa BUG FIX: connect-send-close-accept sequence was not allowed.
01d,05dec03,xli change the include path;
01c,03dec03,bwa BUG FIX: peer's so_rcv.sb_cc was not updated properly;
BUG FIX: shutdown(0) would still allow send from peer;
BUG FIX: could send data larger than receiver's buffer.
01b,26nov03,bwa wrote explanation about DIAB warning when testing sblock()
return value; cleanup of warnings generated by GNU.
01a,03nov03,bwa written.
*/
/*
DESCRIPTION
This library implements the major protocol-specific routines of the
AF_LOCAL/COMP protocol for sockets.
The routines present in here are not to be called directly; they have to be
registered in the AF_LOCAL protocol switch entry for COMP. They are then called
by the socket upper layers (sockLib, bsdSockLib, uipc_socket libraries) when
needed.
INTERNAL:
SYSTEM VIEWER INSTRUMENTATION
Network Critical instrumentation level.
NOMANUAL
*/
/* include files */
/* VxWorks system includes */
#include <vxWorks.h>
#include <string.h> /* for strlen() */
#include <stdio.h> /* for (s)printf() */
#include <iosLib.h> /* for printErrno() */
#include <objLib.h> /* for objNameSet() */
#include <stdlib.h> /* for malloc/free */
#include <private/iosLibP.h>
/* networking includes */
#include <sys/socket.h>
#include <netBufLib.h>
#include <net/mbuf.h>
#include <net/domain.h>
#include <net/protosw.h>
#include <dsi/socketvar.h>
#include <net/unixLib.h>
#include <sys/un.h>
#include <dsi/backend/dsiSockLib.h>
#include <dsi/un/uncomp.h>
#include <dsi/un/uncomp_var.h>
#include <dsi/un/uncomp_fsm.h>
#include <wvNetDLib.h>
#ifdef _WRS_CONFIG_SV_INSTRUMENTATION
#ifdef INCLUDE_WVNETD
/* Set common fields of event identifiers for this module. */
LOCAL UCHAR wvNetDModuleId = WV_NETD_UNCOMP_USRREQ_MODULE; /* uncomp_usrreq.c */
LOCAL UCHAR wvNetDLocalFilter = WV_NETD_NONE; /* Available event filter */
LOCAL UCHAR wvNetDEntityId = WV_NETD_SOCKET; /* Entity ID */
#endif /* INCLUDE_WVNETD */
#endif /* _WRS_CONFIG_SV_INSTRUMENTATION */
/* set limits for largest message possible (64k-1 for COMP) */
/* XXX : NOTE - These limits should be the size of buffers allocated for UNCOMP
* usage. HOWEVER, this is not implemented yet and what it does
* is only enforcing limits on send() and recv() operations.
* It is thus impossible to send a packet larger than 64kB and
* impossible for the receiving socket to hold more than 64kB of
* data at once.
*/
LOCAL uint32_t uncomp_sendspace = (64 * 1024) - 1; /* UNCOMP send buffers */
LOCAL uint32_t uncomp_recvspace = (64 * 1024) - 1; /* UNCOMP recv buffers */
/*
* This is the list of AF_LOCAL/COMP sockets. It is a doubly-linked list in
* which the sockets are queued in ascending order by their name. If sockets
* a created following a connection and they are given the same name as the
* listening socket, they are queued after the parent, in the reverse order
* that they were created. One exception: socket that are not yet named, and
* thus sport the invalid name '0x0', are queued at the end of the list.
*/
extern DL_LIST uncompSocketList; /* list of AF_LOCAL/COMP sockets */
/* local defines */
#define UNCOMP_NAME_PREFIX "/comp/socket/0x"
#define UNCOMP_NAME_PREFIX_LEN 15
#define UNCOMP_NAME_SUFFIX_START 15
#define UNCOMP_NAME_LEN_MAX 19
#define UNCOMP_INVALID_NAME 0x0
#define UNCOMP_FIRST_VALID_NAME 0x1
#define UNCOMP_LAST_VALID_NAME 0xffff
#define UNCOMP_OFFSET_ASCII_0 0x30
#define UNCOMP_OFFSET_ASCII_A 0x37
#define UNCOMP_OFFSET_ASCII_a 0x57
/* debugging definitions */
#define UNCOMP_DEBUG 0
#define UNCOMP_DEBUG_SHOW 0
#define UNCOMP_USE_OLD_BSD_STUFF 0
/* function prototypes */
LOCAL int uncomp_abort (struct socket *so);
LOCAL int uncomp_accept (struct socket *so, struct sockaddr **nam);
LOCAL int uncomp_attach (struct socket *so, int proto, struct proc *p);
LOCAL int uncomp_bind (struct socket *so, struct sockaddr *nam,
struct proc *p);
LOCAL int uncomp_connect (struct socket *so, struct sockaddr *nam,
struct proc *p);
LOCAL int uncomp_connect2 (struct socket *so1, struct socket *so2);
LOCAL int uncomp_disconnect (struct socket *so);
LOCAL int uncomp_detach (struct socket *so);
LOCAL int uncomp_listen (struct socket *so, struct proc *p);
LOCAL int uncomp_peeraddr (struct socket *so, struct sockaddr **nam);
LOCAL int uncomp_rcvd (struct socket * so, int flags);
LOCAL int uncomp_send (struct socket *so, int flags, struct mbuf *m,
struct sockaddr *nam, struct mbuf *control,
struct proc *p);
LOCAL int uncomp_shutdown (struct socket *so);
LOCAL int uncomp_sockaddr (struct socket *so, struct sockaddr **nam);
/* create and fill in the protocol switch entry function table */
struct pr_usrreqs uncomp_usrreqs =
{
uncomp_abort, uncomp_accept, uncomp_attach, uncomp_bind,
uncomp_connect, uncomp_connect2, dsiControlNotSupp, uncomp_detach,
uncomp_disconnect, uncomp_listen, uncomp_peeraddr, uncomp_rcvd,
dsiRcvoobNotSupp, uncomp_send, dsiSenseNull, uncomp_shutdown,
uncomp_sockaddr, NULL, NULL, NULL
};
/* convenience routines */
/*******************************************************************************
*
* uncomp_firstAvailableNameGet - Returns first available COMP socket name
*
* RETURNS: A 16-bit number (the socket name), suffix of /comp/socket/0xXXXX, or
* UNCOMP_INVALID_NAME if list of names is exhausted
*
* POSSIBLE ERROR VALUES: N/A
*
* NOMANUAL
*/
LOCAL uint16_t uncomp_firstAvailableNameGet
(
UNCOMPCB ** ppSlot /* container, place where to insert the socket */
)
{
UNCOMPCB * pUnComp = (UNCOMPCB *) DLL_FIRST (&uncompSocketList);
UNCOMPCB * pUnCompPrev;
/* is list empty ? */
if (pUnComp == NULL)
{
*ppSlot = NULL;
return (UNCOMP_FIRST_VALID_NAME);
}
/* is slot before first node ? */
if (pUnComp->selfName != UNCOMP_FIRST_VALID_NAME)
{
*ppSlot = NULL;
return (UNCOMP_FIRST_VALID_NAME);
}
FOREVER /* find a slot: exhaust the list */
{
pUnCompPrev = pUnComp;
FOREVER /* skip name duplicates */
{
/* if there are duplicates up until the tail, tail is the slot */
if ((UNCOMPCB *) DLL_NEXT ((DL_NODE *)pUnComp) == NULL)
{
/* are all (2^16)-1 names already taken ? (HIGHLY unlikely) */
if (pUnComp->selfName == UNCOMP_LAST_VALID_NAME)
return (UNCOMP_INVALID_NAME);
/* if not, new socket will be inserted after the current one */
*ppSlot = pUnComp;
return (uint16_t)(pUnComp->selfName + 1);
}
/* next node is not NULL, move current indicator */
pUnComp = (UNCOMPCB *) DLL_NEXT ((DL_NODE *)pUnComp);
/* check if previous name == current name... */
if (pUnComp->selfName != pUnCompPrev->selfName)
{
/* ...it is not */
/* if (cur name != prev+1), there is a gap: this is the slot */
if (pUnComp->selfName != (pUnCompPrev->selfName + 1))
{
*ppSlot = (UNCOMPCB *) DLL_PREVIOUS ((DL_NODE *)pUnComp);
return (uint16_t)((*ppSlot)->selfName + 1);
}
else /* ...it is: no gap, continue searching */
break;
}
/*
* if we get here, two contiguous sockets have the same name:
* continue the loop to discard all duplicates
*/
}
}
}
/*******************************************************************************
*
* uncomp_nameDecode - extract the suffix from an AF_LOCAL/COMP socket name
*
* RETURNS: the XXXX part of /comp/socket/0xXXXX or UNCOMP_INVALID_NAME if
* illegal string
*
* POSSIBLE ERROR VALUES: N/A
*
* NOMANUAL
*/
LOCAL uint16_t uncomp_nameDecode
(
char * pName /* UNCOMP name in the format /comp/socket/0xXXXX */
)
{
uint16_t suffix = UNCOMP_INVALID_NAME;
int index = UNCOMP_NAME_PREFIX_LEN;
int len;
pName[103] = '\0';
len = (int)strlen (pName);
/*
* only names in the format /comp/socket/0xXXXX are accepted, where
* XXXX is a one-to-four digits hexadecimal number (i.e. 0x1 to 0xFFFF)
*/
/* verify lower and upper name len boundaries */
if (len < UNCOMP_NAME_PREFIX_LEN + 1) /* at least prefix + 1 */
return (UNCOMP_INVALID_NAME);
if (len > UNCOMP_NAME_LEN_MAX) /* at max prefix + 0xFFFF */
return (UNCOMP_INVALID_NAME);
/* verify the prefix passed is '/comp/socket/0x' */
if (strncmp (pName, UNCOMP_NAME_PREFIX, UNCOMP_NAME_PREFIX_LEN) != 0)
return UNCOMP_INVALID_NAME;
/* loop to numerize all digits in a XXXX hexadecimal character string */
len -= UNCOMP_NAME_PREFIX_LEN;
for (; len > 0; len--,index++)
{
if (pName[index] >= '0' && pName[index] <= '9')
suffix += (uint16_t)((pName[index] - UNCOMP_OFFSET_ASCII_0) << ((len-1) * 4));
else if (pName[index] >= 'A' && pName[index] <= 'F')
suffix += (uint16_t)((pName[index] - UNCOMP_OFFSET_ASCII_A) << ((len-1) * 4));
else if (pName[index] >= 'a' && pName[index] <= 'f')
suffix += (uint16_t)((pName[index] - UNCOMP_OFFSET_ASCII_a) << ((len-1) * 4));
else
return UNCOMP_INVALID_NAME;
}
return (suffix);
}
/*******************************************************************************
*
* uncomp_acceptSocketFind - find the accepting socket by name
*
* RETURNS: the accepting socket structure, or NULL if not found
*
* POSSIBLE ERROR VALUES: N/A
*
* NOMANUAL
*/
LOCAL struct socket * uncomp_acceptSocketFind
(
struct sockaddr_un * pName /* name of the socket to find, in format: */
/* /comp/socket/0xXXXX */
)
{
/* numerize name */
uint16_t name = uncomp_nameDecode (pName->sun_path);
/* grab first uncomp socket */
DL_NODE * pCur = (DL_NODE *) DLL_FIRST (&uncompSocketList);
/* something's wrong ... */
if ((name == UNCOMP_INVALID_NAME) || (pCur == NULL))
return (NULL);
/*
* loop through sockets list to find the correct accepting socket;
* the correct one is one with the correct name and free space on the
* accepting queue; bail out early if we're passed the wanted name.
*/
do
{
if (((UNCOMPCB *)pCur)->selfName == name)
if (((UNCOMPCB *)pCur)->pSelfSocket->so_qlen <
((UNCOMPCB *)pCur)->pSelfSocket->so_qlimit)
break;
if (((UNCOMPCB *)pCur)->selfName > name)
{
pCur = NULL;
break;
}
} while ((pCur = (DL_NODE *) DLL_NEXT (pCur)) != NULL);
if (pCur != NULL)
return (((UNCOMPCB *)pCur)->pSelfSocket);
else
return (NULL);
}
/*******************************************************************************
*
* uncomp_dupsockaddr - duplicate the sockaddr_un of a socket
*
* Memory is allocated, DSI_ADDR_FREE() must be called on the return value later
* if memory is to be reclaimed.
*
* RETURNS: the new instance of a sockaddr_un, or NULL if allocation failed
*
* POSSIBLE ERROR VALUES: N/A
*
* NOMANUAL
*/
LOCAL struct sockaddr_un * uncomp_dupsockaddr
(
UNCOMPCB * pUnComp /* the AF_LOCAL block in which to fetch the address */
)
{
struct sockaddr_un * pName;
int inc = UNCOMP_NAME_SUFFIX_START; /* increment counter */
char temp; /* temp holder for char */
int nameSize, pos;
pName = DSI_ADDR_ALLOC (struct sockaddr_un); /* allocate name structure */
if (pName == NULL)
return NULL;
pName->sun_len = UN_SOCKADDR_UN_SIZE; /* length of structure */
pName->sun_family = AF_LOCAL; /* address family */
strcpy (pName->sun_path, UNCOMP_NAME_PREFIX); /* prepend prefix */
/* go through all digits in the name and convert them to characters */
for (nameSize = 0x1000, pos = 4; pos > 0; nameSize >>= 4, pos--)
{
if (pUnComp->selfName >= nameSize)
{
temp = (char)((pUnComp->selfName >> ((pos - 1) * 4)) & 0xf);
temp += (char)(temp > 9 ? UNCOMP_OFFSET_ASCII_a : UNCOMP_OFFSET_ASCII_0);
pName->sun_path[inc++] = temp;
}
}
if (pUnComp->selfName == 0x0)
pName->sun_path[inc++] = '0';
pName->sun_path[inc] = '\0';
return (pName);
}
#if UNCOMP_DEBUG_SHOW
/*******************************************************************************
*
* uncomp_socketDump - dumps information about an AF_LOCAL/COMP socket
*
* Debug-only routine
*
* Usage:
* 'so' parameter: fd or socket pointer
* 'type' parameter: 0 tells 'so' is an fd, 1 tells 'so' is a pointer
*
* RETURNS: N/A
*
* POSSIBLE ERROR VALUES: N/A
*
* NOMANUAL
*/
void uncomp_socketDump
(
long so, /* socket to dump */
int type /* 0 = fd, 1 = socket ptr */
)
{
char * uncomp_stateGet (uint16_t state, char * pBuffer);
struct socket * pSocket = (type ?
(struct socket *)so :
(struct socket *)iosFdValue(so));
UNCOMPCB * pUnComp = (UNCOMPCB *)pSocket->so_pcb;
struct mbuf * pNext;
char pState[20];
printf ("--------------- %d ----------------\n", pSocket->so_fd);
printf ("socket dump for so %d\n", so);
printf ("socket structure: 0x%lx\n", (long)pSocket);
printf ("pSocket->so_state: 0x%x\n", pSocket->so_state);
printf ("pSocket->so_options: 0x%x\n", pSocket->so_options);
printf ("pSocket->so_head: 0x%lx\n", (long)pSocket->so_head);
printf ("pSocket->so_qlen: %d\n", pSocket->so_qlen);
printf ("first unaccepted socket: 0x%lx\n", (long)pSocket->so_comp.tqh_first);
printf ("pSocket->so_qlimit: %d\n", pSocket->so_qlimit);
printf ("pSocket->so_fd: %d\n", pSocket->so_fd);
printf ("&pSocket->so_snd: 0x%lx\n", (long)&pSocket->so_snd);
printf ("pSocket->so_snd.sb_lowat:0x%lx\n", (long)pSocket->so_snd.sb_lowat);
printf ("pSocket->so_snd.sb_hiwat:0x%lx\n", (long)pSocket->so_snd.sb_hiwat);
printf ("pSocket->so_snd.sb_mb: 0x%lx\n", (long)pSocket->so_snd.sb_mb);
printf ("&pSocket->so_rcv: 0x%lx\n", (long)&pSocket->so_rcv);
printf ("pSocket->so_rcv.sb_lowat:0x%lx\n", (long)pSocket->so_rcv.sb_lowat);
printf ("pSocket->so_rcv.sb_hiwat:0x%lx\n", (long)pSocket->so_rcv.sb_hiwat);
printf ("pSocket->so_rcv.sb_cc: 0x%lx\n", (long)pSocket->so_rcv.sb_cc);
printf ("pSocket->so_rcv.sb_mb: 0x%lx, len = %d\n",
(long)pSocket->so_rcv.sb_mb,
pSocket->so_rcv.sb_mb != NULL ? pSocket->so_rcv.sb_mb->m_len : 0);
if (pSocket->so_rcv.sb_mb == NULL)
goto uncomp_tag;
pNext = pSocket->so_rcv.sb_mb->m_nextpkt;
while (pNext)
{
printf (" 0x%lx, len = %d\n",
(long)pNext, pNext->m_len);
pNext = pNext->m_nextpkt;
}
uncomp_tag:
printf ("COMP: 0x%lx\n", (long)pUnComp);
printf ("COMP->pSelfSocket: 0x%lx\n", (long)pUnComp->pSelfSocket);
printf ("COMP->pPeerSocket: 0x%lx\n", (long)pUnComp->pPeerSocket);
printf ("COMP->selfName: 0x%x\n", pUnComp->selfName);
printf ("COMP->selfState: %s\n",
uncomp_stateGet (pUnComp->selfState, pState));
printf ("----------------------------------\n");
}
#endif /* UNCOMP_DEBUG_SHOW */
/*******************************************************************************
*
* uncomp_stateGet - returns a string for an AF_LOCAL/COMP state number
*
* Note: pBuffer is modified.
*
* RETURNS: the state string
*
* POSSIBLE ERROR VALUES: N/A
*
* NOMANUAL
*/
char * uncomp_stateGet
(
uint16_t state, /* state, UNCOMPS_XXX value */
char * pBuffer /* where to return the string */
)
{
switch (state)
{
case UNCOMPS_CLOSED:
bcopy ("CLOSED", pBuffer, strlen("CLOSED")+1 );
break;
case UNCOMPS_LISTENING:
bcopy ("LISTENING", pBuffer, strlen("LISTENING")+1 );
break;
case UNCOMPS_EXCHANGING:
bcopy ("EXCHANGING", pBuffer, strlen("EXCHANGING")+1);
break;
case UNCOMPS_DONE_SEND:
bcopy ("DONE_SEND", pBuffer, strlen("DONE_SEND")+1 );
break;
case UNCOMPS_DONE_RECV:
bcopy ("DONE_RECV", pBuffer, strlen("DONE_RECV")+1 );
break;
case UNCOMPS_REQUEST:
bcopy ("REQUEST", pBuffer, strlen("REQUEST")+1 );
break;
}
return pBuffer;
}
#if UNCOMP_DEBUG_SHOW
/*******************************************************************************
*
* uncomp_sdl - dumps the list of AF_LOCAL/COMP sockets and all their info
*
* Debug-only routine. 'sdl' stands for 'Socket Dump List'.
*
* RETURNS: N/A
*
* POSSIBLE ERROR VALUES: N/A
*
* NOMANUAL
*/
void uncomp_sdl (void)
{
DL_NODE * pCur = (DL_NODE *) DLL_FIRST (&uncompSocketList);
if (pCur == NULL)
{
printf ("uncompSocketList is empty\n");
return;
}
do
{
uncomp_socketDump ((long)((UNCOMPCB *)pCur)->pSelfSocket, 1);
} while ((pCur = (DL_NODE *) DLL_NEXT (pCur)) != NULL);
}
#endif /* UNCOMP_DEBUG_SHOW */
/* Local COMP protocol implementation */
/*******************************************************************************
*
* uncomp_abort - AF_LOCAL/COMP pru_abort routine
*
* Disconnect and detach a local socket and its peer; called when a close()
* command is issued, only when sockets are pending on the listen socket.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
*
* NOMANUAL
*/
LOCAL int uncomp_abort
(
struct socket *so /* socket on which to abort */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so);
if (pUnComp == NULL)
return EINVAL;
so->so_error = ECONNABORTED;
uncomp_detach(so);
return 0;
}
/*******************************************************************************
*
* uncomp_accept - AF_LOCAL/COMP pru_accept routine
*
* Pass back name of connected socket. This routine is actually called AFTER
* the sockets are connected via a connect() call by the client.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
* ENOBUFS
*
* NOMANUAL
*/
LOCAL int uncomp_accept
(
struct socket *so, /* new socket created by connect2()(dsiSoNewConn3) */
struct sockaddr **nam /* container for the name of the peer socket */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so);
if (pUnComp == NULL)
return EINVAL;
/*
* Pass back name of connected socket, if it was bound and we are still
* connected (our peer may have closed already!).
*/
if ((pUnComp->selfState == UNCOMPS_EXCHANGING) &&
(pUnComp->pPeerSocket != NULL))
{
/* returning peer's addr */
pUnComp = SO_TO_UNCOMPCB (pUnComp->pPeerSocket);
*nam = (struct sockaddr *) uncomp_dupsockaddr (pUnComp);
if (*nam == NULL)
return ENOBUFS;
}
/* else do nothing, there is no peer, so no address to return */
/*
* NOTE: the only case when this routine is called and there is no peer
* is if a peer did a socket-connect-send-close series of calls,
* and the peer has closed before the accept is done; in this case,
* the accept() call should succeed, not fail.
*/
return (0);
}
/*******************************************************************************
*
* uncomp_attach - AF_LOCAL/COMP pru_attach routine
*
* Create the 'so_pcb' field of an AF_LOCAL/COMP socket. The data structure
* created is of type 'UNCOMPCB'.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EISCONN
* ENOBUFS
*
* NOMANUAL
*/
LOCAL int uncomp_attach
(
struct socket *so, /* socket that needs the new UNCOMPCB data struct */
int proto, /* protocol, unused for now */
struct proc *p /* always NULL */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so);
int error = 0;
/* is socket already set up ? */
if (pUnComp != NULL)
return EISCONN;
/* set buffer limits */
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0)
error = dsiSoReserve (so, uncomp_sendspace, uncomp_recvspace);
if (error)
return (error);
/* allocate space for uncompcb */
pUnComp = DSI_PCB_ALLOC(UNCOMPCB);
if (pUnComp == NULL)
return (ENOBUFS);
/* initialize uncompcb part of socket */
bzero((char *)pUnComp, sizeof (UNCOMPCB)); /* ZERO everyting */
pUnComp->pSelfSocket = so; /* store back ptr to socket */
pUnComp->selfState = UNCOMPS_CLOSED; /* socket is in CLOSED state */
pUnComp->selfName = UNCOMP_INVALID_NAME; /* bogus name, not named yet */
dllAdd (&uncompSocketList, &pUnComp->node); /* add socket in uncomp list */
so->so_pcb = (caddr_t)pUnComp; /* store uncompcb in socket */
return (0);
}
/*******************************************************************************
*
* uncomp_bind - AF_LOCAL/COMP pru_bind routine
*
* Bind a user-provided name (AF_LOCAL address) to a socket. If the user didn't
* provide a name, one is assigned by the system.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
* EADDRINUSE
* EMFILE
*
* NOMANUAL
*/
LOCAL int uncomp_bind
(
struct socket *so, /* socket to bind name 'nam' on */
struct sockaddr *nam, /* name to be bound to socket 'so' */
struct proc *p /* always NULL */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so);
UNCOMPCB * pSlot; /* slot where to insert socket in list */
struct sockaddr_un * pName = (struct sockaddr_un *)nam;
uint16_t selfName; /* name of socket */
/* is it a local COMP socket ? */
if (pUnComp == NULL)
return EINVAL;
/* did user provide a name ? */
if ((pName == NULL) || (pName->sun_path[0] == '\0'))
{
/* find available socket name and slot where to put it in the queue */
pUnComp->selfName = uncomp_firstAvailableNameGet (&pSlot);
if (pUnComp->selfName == UNCOMP_INVALID_NAME)
return (EMFILE);
/* remove socket from tail of queue and put it after slot found */
dllRemove (&uncompSocketList, &pUnComp->node);
dllInsert (&uncompSocketList, pSlot != NULL ? &pSlot->node : NULL,
&pUnComp->node);
return (0);
}
/* grab real name (suffix from /comp/socket/suffix) */
selfName = uncomp_nameDecode (pName->sun_path);
/* is it a valid name ? */
if (selfName == UNCOMP_INVALID_NAME)
return (EINVAL);
/* traverse list to find insertion spot */
for (pSlot = (UNCOMPCB *)DLL_LAST (&uncompSocketList);
pSlot != NULL;
pSlot = (UNCOMPCB *)DLL_PREVIOUS (pSlot))
{
if ((pSlot->selfName < selfName) && (pSlot->selfName != 0))
break;
if (pSlot->selfName == selfName)
return (EADDRINUSE);
}
/* store name in uncompcb part of socket */
pUnComp->selfName = selfName;
dllRemove (&uncompSocketList, &pUnComp->node);
dllInsert (&uncompSocketList, pSlot != NULL ? &pSlot->node : NULL,
&pUnComp->node);
return (0);
}
/*******************************************************************************
*
* uncomp_connect - AF_LOCAL/COMP pru_connect routine
*
* Connect the local socket 'so' to another socket at the address 'nam'. Both
* sockets are local, this being the implementation of the AF_LOCAL/COMP
* protocol.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
* ECONNREFUSED
* EMFILE
*
* NOMANUAL
*/
LOCAL int uncomp_connect
(
struct socket *so, /* socket to connect */
struct sockaddr *nam, /* name of listening socket */
struct proc *p /* always NULL */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so); /* PCB of socket */
UNCOMPCB * pSlot; /* slot where to insert socket */
struct sockaddr_un * pName = (struct sockaddr_un *) nam;/* nam conversion */
struct socket * pAcceptSocket;
struct socket * pConnectedPeerSocket;
/* is socket a local COMP socket ? */
if (pUnComp == NULL)
return EINVAL;
/* can we find accepting socket that is named 'nam' ? */
if ((pAcceptSocket = uncomp_acceptSocketFind (pName)) == NULL)
return ECONNREFUSED;
/* is socket found really an accepting socket ? */
if ((pAcceptSocket->so_options & SO_ACCEPTCONN) == 0)
return ECONNREFUSED;
/* create new peer socket and place it in the 'CONNECTED' state */
pConnectedPeerSocket = dsiSoNewConn3 (pAcceptSocket, SS_ISCONNECTED, p);
if (pConnectedPeerSocket == NULL)
return ECONNREFUSED;
/* new connected socket's peer's name == same as the accepting socket's */
SO_TO_UNCOMPCB(pConnectedPeerSocket)->selfName =
SO_TO_UNCOMPCB(pAcceptSocket)->selfName;
/*
* remove new socket from the tail of the AF_LOCAL/COMP sockets queue
* and put it after the accepting socket in that queue
*/
dllRemove (&uncompSocketList,
&(SO_TO_UNCOMPCB(pConnectedPeerSocket)->node));
dllInsert (&uncompSocketList, &(SO_TO_UNCOMPCB(pAcceptSocket)->node),
&(SO_TO_UNCOMPCB(pConnectedPeerSocket)->node));
/* allocate a name for the socket we are connecting */
/* is socket already bound ? */
if (pUnComp->selfName == UNCOMP_INVALID_NAME)
{
/* find available socket name and slot where to put it in the list */
pUnComp->selfName = uncomp_firstAvailableNameGet (&pSlot);
if (pUnComp->selfName == UNCOMP_INVALID_NAME)
return (EMFILE);
/* remove socket from tail of queue and put it after slot found */
dllRemove (&uncompSocketList, &pUnComp->node);
dllInsert (&uncompSocketList, pSlot != NULL ? &pSlot->node : NULL,
&pUnComp->node);
}
/* finally, connect the two sockets locally */
return (uncomp_connect2 (so, pConnectedPeerSocket));
}
/*******************************************************************************
*
* uncomp_connect2 - AF_LOCAL/COMP pru_connect2 routine
*
* Connect two local sockets 'so1' and 'so2' together.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
*
* NOMANUAL
*/
LOCAL int uncomp_connect2
(
struct socket *so1, /* connecting socket */
struct socket *so2 /* socket created by connection */
)
{
UNCOMPCB *pUnComp1 = SO_TO_UNCOMPCB(so1);
UNCOMPCB *pUnComp2 = SO_TO_UNCOMPCB(so2);
/* are the two sockets valid local COMP sockets ? */
if ((pUnComp1 == NULL) || (pUnComp2 == NULL))
return EINVAL;
/* exchange local COMP information between peer sockets */
pUnComp2->pPeerSocket = pUnComp1->pSelfSocket;
pUnComp1->pPeerSocket = pUnComp2->pSelfSocket;
/* the two sockets are now connected */
dsiSoIsConnected (so1);
dsiSoIsConnected (so2);
pUnComp1->selfState = UNCOMPS_EXCHANGING;
pUnComp2->selfState = UNCOMPS_EXCHANGING;
return 0;
}
/* control is EOPNOTSUPP */
/*******************************************************************************
*
* uncomp_detach - AF_LOCAL/COMP pru_detach routine
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
*
* NOMANUAL
*/
LOCAL int uncomp_detach
(
struct socket *so /* socket that is closing */
)
{
UNCOMPCB *pUnComp = SO_TO_UNCOMPCB(so);
if (pUnComp == NULL)
return EINVAL;
/* remove socket from list of active AF_LOCAL/COMP socket */
DLL_REMOVE(&uncompSocketList, pUnComp);
/* disconnect if connected */
if (pUnComp->pPeerSocket != NULL)
uncomp_disconnect(pUnComp->pSelfSocket);
DSI_PCB_FREE(pUnComp);
so->so_pcb = NULL;
return 0;
}
/*******************************************************************************
*
* uncomp_disconnect - AF_LOCAL/COMP pru_disconnect routine
*
* Disconnect socket 'so' from its peer, and vice-versa. Both sockets are placed
* in the 'CLOSED' state.
*
* This routine is called by sodisconnect() (which is called by close() and
* soconnect() (in the case an error occurs)) and uncomp_detach().
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
* ENOTCONN
*
* NOMANUAL
*/
LOCAL int uncomp_disconnect
(
struct socket *so /* socket that is disconnecting */
)
{
UNCOMPCB * pUnCompPeer;
UNCOMPCB * pUnCompSelf = SO_TO_UNCOMPCB(so);
/* valid local COMP socket ? */
if (pUnCompSelf == NULL)
return EINVAL;
/* is socket connected ? */
if ((pUnCompPeer = SO_TO_UNCOMPCB(pUnCompSelf->pPeerSocket)) == NULL)
return ENOTCONN;
/* close both ends of the connection */
pUnCompSelf->pPeerSocket = pUnCompPeer->pPeerSocket = NULL;
pUnCompSelf->selfState = UNCOMPS_CLOSED;
pUnCompPeer->selfState = UNCOMPS_CLOSED;
dsiSoIsDisconnected (pUnCompSelf->pSelfSocket);
dsiSoIsDisconnected (pUnCompPeer->pSelfSocket);
return 0;
}
/*******************************************************************************
*
* uncomp_listen - AF_LOCAL/COMP pru_listen routine
*
* Called by solisten. Does little besides setting the listening socket's state
* to 'LISTENING'.
*
* bind()s a name if not already bound.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
*
* NOMANUAL
*/
LOCAL int uncomp_listen
(
struct socket *so, /* socket that is listening */
struct proc *p /* always NULL */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so);
int error;
if ((pUnComp == NULL) || (pUnComp->selfState != UNCOMPS_CLOSED))
return EINVAL;
if (pUnComp->selfName == UNCOMP_INVALID_NAME)
if ((error = uncomp_bind (so, NULL, p)) != 0)
return (error);
pUnComp->selfState = UNCOMPS_LISTENING;
return (0);
}
/*******************************************************************************
*
* uncomp_peeraddr - AF_LOCAL/COMP pru_peeraddr routine
*
* Return the address of the socket connected to socket 'so' through 'nam'.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* ENOBUFS
* ENOTCONN
*
* NOMANUAL
*/
LOCAL int uncomp_peeraddr
(
struct socket *so, /* socket to get peer's address from */
struct sockaddr **nam /* container, name of peer */
)
{
UNCOMPCB *pUnComp = SO_TO_UNCOMPCB(so);
/* return peer's address if socket is a COMP local one and is connected */
if (pUnComp == NULL)
return EINVAL;
if ((pUnComp->selfState == UNCOMPS_EXCHANGING) ||
(pUnComp->selfState == UNCOMPS_DONE_RECV) ||
(pUnComp->selfState == UNCOMPS_DONE_SEND))
{
pUnComp = SO_TO_UNCOMPCB(pUnComp->pPeerSocket);
/* memory allocation done here, caller will free memory */
*nam = (struct sockaddr *) uncomp_dupsockaddr(pUnComp);
if (*nam == NULL)
return ENOBUFS;
}
else
return ENOTCONN;
return 0;
}
/*******************************************************************************
*
* uncomp_rcvd - AF_LOCAL/COMP pru_rcvd routine
*
* Work done on AF_LOCAL/COMP sockets at the end of the recv operation.
*
* RETURNS: 0
*
* NOMANUAL
*/
LOCAL int uncomp_rcvd
(
struct socket * so, /* socket receiving */
int flags /* recv flags */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so);
/* update pointers */
if (pUnComp->pLastReg == pUnComp->pRecvPacket)
pUnComp->pLastReg = NULL;
else if (pUnComp->pLastExp == pUnComp->pRecvPacket)
pUnComp->pLastExp = NULL;
pUnComp->pRecvPacket = so->so_rcv.sb_mb;
/* only try to permit flow from peer if peer (still) exists */
if (pUnComp->pPeerSocket != NULL)
DSI_SO_W_WAKEUP (pUnComp->pPeerSocket);
return (0);
}
/* pru_rcvoob is EOPNOTSUPP */
/*******************************************************************************
*
* uncomp_send - AF_LOCAL/COMP pru_send routine
*
* Send data to peer socket. AF_LOCAL/COMP sockets MUST be connected to
* pass data.
*
* Packets are treated as whole messages and can't be split.
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
* ENOTCONN
* EMSGSIZE
*
* NOMANUAL
*/
LOCAL int uncomp_send
(
struct socket *so, /* socket to send on */
int flags, /* send flags */
struct mbuf *m, /* the message itself */
struct sockaddr *nam, /* always NULL */
struct mbuf *control, /* always NULL */
struct proc *p /* always NULL */
)
{
struct mbuf ** ppExpTail; /* tail of express q */
struct mbuf ** ppRegTail; /* tail of regular q */
struct mbuf ** ppRecvPacket; /* next received packet */
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so); /* socket's PCB */
UNCOMPCB * pPeerUnComp; /* peer socket's PCB */
int pktSz = 0; /* size of sent packet */
long clCnt = 0; /* cluster cnt of sent pkt */
int error = 0;
/* is socket an local COMP socket ? */
if (pUnComp == NULL)
{
netMblkClChainFree (m);
return EINVAL;
}
/*
* to send, socket must be connected, i.e. must meet these criteria:
* 1. must have a peer socket;
* 2. must be in a connected state, either
* a) EXCHANGING (both sides can send)
* or b) DONE_RECV (other side done sending)
* 3. must NOT have a peer that can't receive.
*
* The third condition might seem a bit weird, but the reason is that
* uncomp_shutdown() is not called by shutdown(0), and thus the COMP
* state is not updated.
*/
if ((pUnComp->pPeerSocket == NULL) ||
((pUnComp->selfState != UNCOMPS_EXCHANGING) &&
(pUnComp->selfState != UNCOMPS_DONE_RECV)) ||
(pUnComp->pPeerSocket->so_state & SS_CANTRCVMORE) != 0)
{
netMblkClChainFree (m);
WV_NET_EVENT_4 (NETD_IP4_DATAPATH_EVENT, WV_NETD_CRITICAL,
1, 1, WV_NETDEVENT_CRITICAL, WV_NETD_SEND,
uncomp_send, 0, ENOTCONN, so->so_fd)
return ENOTCONN;
}
/* obtain size and cluster count of packet */
DSI_PKT_SZ (m, pktSz, clCnt);
pPeerUnComp = SO_TO_UNCOMPCB(pUnComp->pPeerSocket);
/* flow control: loop until there is enough space in peer's recv buffer */
do
{
/* verify the packet doesn't bust the receiver's limit */
if (pUnComp->pPeerSocket->so_rcv.sb_hiwat >=
(pUnComp->pPeerSocket->so_rcv.sb_cc + pktSz))
{
goto enqueuepacket;
}
/* does message even fit in the whole receiver's buffer ? */
if (pktSz > pUnComp->pPeerSocket->so_rcv.sb_hiwat)
{
error = EMSGSIZE;
break;
}
if (so->so_state & SS_NBIO) /* non-blocking I/O: don't wait */
error = EWOULDBLOCK;
else /* sender wants to wait based on T/O */
{
if ((error = dsiSbWait (&so->so_snd)) == 0)
{
/*
* was awakened: must check that the sockets are still
* transmitting between each other.
*/
if (pUnComp->selfState != UNCOMPS_CLOSED)
{
if (!(pUnComp->pPeerSocket->so_state & SS_CANTRCVMORE))
continue;
else
{
error = EPIPE;
break;
}
}
else
{
netMblkClChainFree (m);
WV_NET_EVENT_4 (NETD_IP4_DATAPATH_EVENT,
WV_NETD_CRITICAL,
1, 2, WV_NETDEVENT_CRITICAL,
WV_NETD_SEND, uncomp_send, 0,
EPIPE, so->so_fd)
return EPIPE;
}
}
}
} while ((error != EWOULDBLOCK) && (error != EINTR)); /* this error signals a timeout */
pPeerUnComp->dropped++; /* update dropped packets stats */
netMblkClChainFree (m);
WV_NET_EVENT_4 (NETD_IP4_DATAPATH_EVENT, WV_NETD_CRITICAL,
1, 3, WV_NETDEVENT_CRITICAL, WV_NETD_SEND,
uncomp_send, 0, error, so->so_fd)
return error;
enqueuepacket:
/* update allocated buffer information in socket structure */
DSI_SB_ALLOC (&pUnComp->pPeerSocket->so_rcv, pktSz, clCnt);
/* update high watermark statistics on receiving socket */
if (pUnComp->pPeerSocket->so_rcv.sb_cc > pPeerUnComp->hiwatermark)
pPeerUnComp->hiwatermark = (uint16_t)(pUnComp->pPeerSocket->so_rcv.sb_cc);
/*
* if sending an EXP message, put it at the end of the EXP queue;
* if not, put it at the end of the 'regular message' queue
*/
ppExpTail = &pPeerUnComp->pLastExp;
ppRegTail = &pPeerUnComp->pLastReg;
ppRecvPacket = &pPeerUnComp->pRecvPacket;
if (!(flags & PRUS_EXP))
{
if (*ppRegTail == NULL)
{
if (*ppExpTail == NULL) /* msg q was completely empty */
{
pUnComp->pPeerSocket->so_rcv.sb_mb = m;
m->m_nextpkt = NULL;
*ppRecvPacket = *ppRegTail = m;
/* DSI_SO_R_WAKEUP_ONE(pUnComp->pPeerSocket); */
}
else /* exp msgs present, put m after them */
{
(*ppExpTail)->m_nextpkt = m;
m->m_nextpkt = NULL;
*ppExpTail = m;
}
}
else /* reg msgs present, put m at tail */
{
(*ppRegTail)->m_nextpkt = m;
m->m_nextpkt = NULL;
*ppRegTail = m;
}
}
else
{
if (*ppExpTail == NULL) /* q is empty, m is the new head */
{
m->m_nextpkt = pUnComp->pPeerSocket->so_rcv.sb_mb;
pUnComp->pPeerSocket->so_rcv.sb_mb = m;
*ppRecvPacket = *ppExpTail = m;
#if 0
if (*ppRegTail == NULL)
{
DSI_SO_R_WAKEUP_ONE(pUnComp->pPeerSocket);
}
#endif /* 0 */
}
else /* put m at tail of queue */
{
m->m_nextpkt = (*ppExpTail)->m_nextpkt;
(*ppExpTail)->m_nextpkt = m;
*ppExpTail = m;
}
}
/* always call to give one counting semaphore - not just when q was empty */
DSI_SO_R_WAKEUP_ONE(pUnComp->pPeerSocket);
return 0;
}
/* pru_sense is EOPNOTSUPP */
/*******************************************************************************
*
* uncomp_shutdown - AF_LOCAL/COMP pru_shutdown routine.
*
* This routine is called when a user does a call to shutdown with the SD_WR
* option.
*
* NOTE : this routine is actually only called to close the sending side
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
*
* NOMANUAL
*/
LOCAL int uncomp_shutdown
(
struct socket *so /* socket that is shutting down */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so);
/* is socket a local COMP socket ? */
if (pUnComp == NULL)
return EINVAL;
/* socket can't send anymore */
dsiSoCantSendMore(so);
/* if connected, socket can't send more and peer can't receive more */
if (pUnComp->selfState == UNCOMPS_EXCHANGING)
{
SO_TO_UNCOMPCB(pUnComp->pPeerSocket)->selfState = UNCOMPS_DONE_RECV;
pUnComp->selfState = UNCOMPS_DONE_SEND;
dsiSoCantRcvMore (pUnComp->pPeerSocket);
}
else if (pUnComp->selfState == UNCOMPS_DONE_RECV)
uncomp_disconnect (so);
return 0;
}
/*******************************************************************************
*
* uncomp_sockaddr - AF_LOCAL/COMP pru_sockaddr routine
*
* Return the socket's address
*
* RETURNS: 0 on success or an error value
*
* POSSIBLE ERROR VALUES:
* EINVAL
* ENOMEM
*
* NOMANUAL
*/
LOCAL int uncomp_sockaddr
(
struct socket *so, /* socket from which to get address */
struct sockaddr **nam /* container for the socket's name */
)
{
UNCOMPCB * pUnComp = SO_TO_UNCOMPCB(so);
if (pUnComp == NULL)
return EINVAL;
/* memory allocation done here, caller will free memory */
*nam = (struct sockaddr *) uncomp_dupsockaddr(pUnComp);
if (*nam == NULL)
return ENOBUFS;
return 0;
}
/* XXX : below this point is kept for reference only (work-in-progress) */
int
uncomp_ctloutput(so, sopt)
struct socket *so;
struct sockopt *sopt;
{
int error = 0;
#if UNCOMP_USE_OLD_BSD_STUFF
struct unpcb *unp = sotounpcb(so);
switch (sopt->sopt_dir) {
case SOPT_GET:
switch (sopt->sopt_name) {
case LOCAL_PEERCRED:
if (unp->unp_flags & UNP_HAVEPC)
error = sooptcopyout(sopt, &unp->unp_peercred,
sizeof(unp->unp_peercred));
else {
if (so->so_type == SOCK_STREAM)
error = ENOTCONN;
else
error = EINVAL;
}
break;
default:
error = EOPNOTSUPP;
break;
}
break;
case SOPT_SET:
default:
error = EOPNOTSUPP;
break;
}
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
return (error);
}
#if 0
#if UNCOMP_USE_OLD_BSD_STUFF
/*
* Both send and receive buffers are allocated PIPSIZ bytes of buffering
* for stream sockets, although the total for sender and receiver is
* actually only PIPSIZ.
* Datagram sockets really use the sendspace as the maximum datagram size,
* and don't really want to reserve the sendspace. Their recvspace should
* be large enough for at least one max-size datagram plus address.
*/
#ifndef PIPSIZ
#define PIPSIZ 8192
#endif /* PIPSIZ */
static u_long unpst_sendspace = PIPSIZ;
static u_long unpst_recvspace = PIPSIZ;
static u_long unpdg_sendspace = 2*1024; /* really max datagram size */
static u_long unpdg_recvspace = 4*1024;
static int unp_rights; /* file descriptors in flight */
SYSCTL_DECL(_net_local_stream);
SYSCTL_INT(_net_local_stream, OID_AUTO, sendspace, CTLFLAG_RW,
&unpst_sendspace, 0, "");
SYSCTL_INT(_net_local_stream, OID_AUTO, recvspace, CTLFLAG_RW,
&unpst_recvspace, 0, "");
SYSCTL_DECL(_net_local_dgram);
SYSCTL_INT(_net_local_dgram, OID_AUTO, maxdgram, CTLFLAG_RW,
&unpdg_sendspace, 0, "");
SYSCTL_INT(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW,
&unpdg_recvspace, 0, "");
SYSCTL_DECL(_net_local);
SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, "");
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
static int
prison_unpcb(struct proc *p, struct uncompcb *unp)
{
#if UNCOMP_USE_OLD_BSD_STUFF
if (!p->p_prison)
return (0);
if (p->p_fd->fd_rdir == unp->unp_rvnode)
return (0);
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
return (1);
}
static int
unp_pcblist(SYSCTL_HANDLER_ARGS)
{
#if UNCOMP_USE_OLD_BSD_STUFF
int error, i, n;
struct unpcb *unp, **unp_list;
unp_gen_t gencnt;
struct xunpgen xug;
struct unp_head *head;
head = ((intptr_t)arg1 == SOCK_DGRAM ? &unp_dhead : &unp_shead);
/*
* The process of preparing the PCB list is too time-consuming and
* resource-intensive to repeat twice on every request.
*/
if (req->oldptr == 0) {
n = unp_count;
req->oldidx = 2 * (sizeof xug)
+ (n + n/8) * sizeof(struct xunpcb);
return 0;
}
if (req->newptr != 0)
return EPERM;
/*
* OK, now we're committed to doing something.
*/
gencnt = unp_gencnt;
n = unp_count;
xug.xug_len = sizeof xug;
xug.xug_count = n;
xug.xug_gen = gencnt;
xug.xug_sogen = so_gencnt;
error = SYSCTL_OUT(req, &xug, sizeof xug);
if (error)
return error;
unp_list = malloc(n * sizeof *unp_list, M_TEMP, M_WAITOK);
if (unp_list == 0)
return ENOMEM;
for (unp = LIST_FIRST(head), i = 0; unp && i < n;
unp = LIST_NEXT(unp, unp_link)) {
if (unp->unp_gencnt <= gencnt && !prison_unpcb(req->p, unp))
unp_list[i++] = unp;
}
n = i; /* in case we lost some during malloc */
error = 0;
for (i = 0; i < n; i++) {
unp = unp_list[i];
if (unp->unp_gencnt <= gencnt) {
struct xunpcb xu;
xu.xu_len = sizeof xu;
xu.xu_unpp = unp;
/*
* XXX - need more locking here to protect against
* connect/disconnect races for SMP.
*/
if (unp->unp_addr)
bcopy(unp->unp_addr, &xu.xu_addr,
unp->unp_addr->sun_len);
if (unp->unp_conn && unp->unp_conn->unp_addr)
bcopy(unp->unp_conn->unp_addr,
&xu.xu_caddr,
unp->unp_conn->unp_addr->sun_len);
bcopy(unp, &xu.xu_unp, sizeof *unp);
sotoxsocket(unp->unp_socket, &xu.xu_socket);
error = SYSCTL_OUT(req, &xu, sizeof xu);
}
}
if (!error) {
/*
* Give the user an updated idea of our state.
* If the generation differs from what we told
* her before, she knows that something happened
* while we were processing this request, and it
* might be necessary to retry.
*/
xug.xug_gen = unp_gencnt;
xug.xug_sogen = so_gencnt;
xug.xug_count = unp_count;
error = SYSCTL_OUT(req, &xug, sizeof xug);
}
free(unp_list, M_TEMP);
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
int error = 0;
return error;
}
#if UNCOMP_USE_OLD_BSD_STUFF
SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist, CTLFLAG_RD,
(caddr_t)(long)SOCK_DGRAM, 0, unp_pcblist, "S,xunpcb",
"List of active local datagram sockets");
SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist, CTLFLAG_RD,
(caddr_t)(long)SOCK_STREAM, 0, unp_pcblist, "S,xunpcb",
"List of active local stream sockets");
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
int
unp_externalize(rights)
struct mbuf *rights;
{
#if UNCOMP_USE_OLD_BSD_STUFF
struct proc *p = curproc; /* XXX */
register int i;
register struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
register int *fdp;
register struct file **rp;
register struct file *fp;
int newfds = (cm->cmsg_len - (CMSG_DATA(cm) - (u_char *)cm))
/ sizeof (struct file *);
int f;
/*
* if the new FD's will not fit, then we free them all
*/
if (!fdavail(p, newfds)) {
rp = (struct file **)CMSG_DATA(cm);
for (i = 0; i < newfds; i++) {
fp = *rp;
/*
* zero the pointer before calling unp_discard,
* since it may end up in unp_gc()..
*/
*rp++ = 0;
unp_discard(fp);
}
return (EMSGSIZE);
}
/*
* now change each pointer to an fd in the global table to
* an integer that is the index to the local fd table entry
* that we set up to point to the global one we are transferring.
* If sizeof (struct file *) is bigger than or equal to sizeof int,
* then do it in forward order. In that case, an integer will
* always come in the same place or before its corresponding
* struct file pointer.
* If sizeof (struct file *) is smaller than sizeof int, then
* do it in reverse order.
*/
if (sizeof (struct file *) >= sizeof (int)) {
fdp = (int *)(cm + 1);
rp = (struct file **)CMSG_DATA(cm);
for (i = 0; i < newfds; i++) {
if (fdalloc(p, 0, &f))
panic("unp_externalize");
fp = *rp++;
p->p_fd->fd_ofiles[f] = fp;
fp->f_msgcount--;
unp_rights--;
*fdp++ = f;
}
} else {
fdp = (int *)(cm + 1) + newfds - 1;
rp = (struct file **)CMSG_DATA(cm) + newfds - 1;
for (i = 0; i < newfds; i++) {
if (fdalloc(p, 0, &f))
panic("unp_externalize");
fp = *rp--;
p->p_fd->fd_ofiles[f] = fp;
fp->f_msgcount--;
unp_rights--;
*fdp-- = f;
}
}
/*
* Adjust length, in case sizeof(struct file *) and sizeof(int)
* differs.
*/
cm->cmsg_len = CMSG_LEN(newfds * sizeof(int));
rights->m_len = cm->cmsg_len;
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
return (0);
}
void
unp_init(void)
{
#if UNCOMP_USE_OLD_BSD_STUFF
unp_zone = zinit("unpcb", sizeof(struct unpcb), nmbclusters, 0, 0);
if (unp_zone == 0)
panic("unp_init");
LIST_INIT(&unp_dhead);
LIST_INIT(&unp_shead);
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
}
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif /* MIN */
static int
unp_internalize(control, p)
struct mbuf *control;
struct proc *p;
{
#if UNCOMP_USE_OLD_BSD_STUFF
struct filedesc *fdescp = p->p_fd;
register struct cmsghdr *cm = mtod(control, struct cmsghdr *);
register struct file **rp;
register struct file *fp;
register int i, fd, *fdp;
register struct cmsgcred *cmcred;
int oldfds;
u_int newlen;
if ((cm->cmsg_type != SCM_RIGHTS && cm->cmsg_type != SCM_CREDS) ||
cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len)
return (EINVAL);
/*
* Fill in credential information.
*/
if (cm->cmsg_type == SCM_CREDS) {
cmcred = (struct cmsgcred *)(cm + 1);
cmcred->cmcred_pid = p->p_pid;
cmcred->cmcred_uid = p->p_cred->p_ruid;
cmcred->cmcred_gid = p->p_cred->p_rgid;
cmcred->cmcred_euid = p->p_ucred->cr_uid;
cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
CMGROUP_MAX);
for (i = 0; i < cmcred->cmcred_ngroups; i++)
cmcred->cmcred_groups[i] = p->p_ucred->cr_groups[i];
return(0);
}
oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
/*
* check that all the FDs passed in refer to legal OPEN files
* If not, reject the entire operation.
*/
fdp = (int *)(cm + 1);
for (i = 0; i < oldfds; i++) {
fd = *fdp++;
if ((unsigned)fd >= fdescp->fd_nfiles ||
fdescp->fd_ofiles[fd] == NULL)
return (EBADF);
if (fdescp->fd_ofiles[fd]->f_type == DTYPE_KQUEUE)
return (EOPNOTSUPP);
}
/*
* Now replace the integer FDs with pointers to
* the associated global file table entry..
* Allocate a bigger buffer as necessary. But if an cluster is not
* enough, return E2BIG.
*/
newlen = CMSG_LEN(oldfds * sizeof(struct file *));
if (newlen > MCLBYTES)
return (E2BIG);
if (newlen - control->m_len > M_TRAILINGSPACE(control)) {
if (control->m_flags & M_EXT)
return (E2BIG);
MCLGET(control, M_WAIT);
if ((control->m_flags & M_EXT) == 0)
return (ENOBUFS);
/* copy the data to the cluster */
memcpy(mtod(control, char *), cm, cm->cmsg_len);
cm = mtod(control, struct cmsghdr *);
}
/*
* Adjust length, in case sizeof(struct file *) and sizeof(int)
* differs.
*/
control->m_len = cm->cmsg_len = newlen;
/*
* Transform the file descriptors into struct file pointers.
* If sizeof (struct file *) is bigger than or equal to sizeof int,
* then do it in reverse order so that the int won't get until
* we're done.
* If sizeof (struct file *) is smaller than sizeof int, then
* do it in forward order.
*/
if (sizeof (struct file *) >= sizeof (int)) {
fdp = (int *)(cm + 1) + oldfds - 1;
rp = (struct file **)CMSG_DATA(cm) + oldfds - 1;
for (i = 0; i < oldfds; i++) {
fp = fdescp->fd_ofiles[*fdp--];
*rp-- = fp;
fp->f_count++;
fp->f_msgcount++;
unp_rights++;
}
} else {
fdp = (int *)(cm + 1);
rp = (struct file **)CMSG_DATA(cm);
for (i = 0; i < oldfds; i++) {
fp = fdescp->fd_ofiles[*fdp++];
*rp++ = fp;
fp->f_count++;
fp->f_msgcount++;
unp_rights++;
}
}
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
return (0);
}
static int unp_defer, unp_gcing;
static void
unp_gc()
{
#if UNCOMP_USE_OLD_BSD_STUFF
register struct file *fp, *nextfp;
register struct socket *so;
struct file **extra_ref, **fpp;
int nunref, i;
if (unp_gcing)
return;
unp_gcing = 1;
unp_defer = 0;
/*
* before going through all this, set all FDs to
* be NOT defered and NOT externally accessible
*/
LIST_FOREACH(fp, &filehead, f_list)
fp->f_flag &= ~(FMARK|FDEFER);
do {
LIST_FOREACH(fp, &filehead, f_list) {
/*
* If the file is not open, skip it
*/
if (fp->f_count == 0)
continue;
/*
* If we already marked it as 'defer' in a
* previous pass, then try process it this time
* and un-mark it
*/
if (fp->f_flag & FDEFER) {
fp->f_flag &= ~FDEFER;
unp_defer--;
} else {
/*
* if it's not defered, then check if it's
* already marked.. if so skip it
*/
if (fp->f_flag & FMARK)
continue;
/*
* If all references are from messages
* in transit, then skip it. it's not
* externally accessible.
*/
if (fp->f_count == fp->f_msgcount)
continue;
/*
* If it got this far then it must be
* externally accessible.
*/
fp->f_flag |= FMARK;
}
/*
* either it was defered, or it is externally
* accessible and not already marked so.
* Now check if it is possibly one of OUR sockets.
*/
if (fp->f_type != DTYPE_SOCKET ||
(so = (struct socket *)fp->f_data) == 0)
continue;
if (so->so_proto->pr_domain != &localdomain ||
(so->so_proto->pr_flags&PR_RIGHTS) == 0)
continue;
#ifdef notdef
if (so->so_rcv.sb_flags & SB_LOCK) {
/*
* This is problematical; it's not clear
* we need to wait for the sockbuf to be
* unlocked (on a uniprocessor, at least),
* and it's also not clear what to do
* if sbwait returns an error due to receipt
* of a signal. If sbwait does return
* an error, we'll go into an infinite
* loop. Delete all of this for now.
*/
(void) sbwait(&so->so_rcv);
goto restart;
}
#endif /* notdef */
/*
* So, Ok, it's one of our sockets and it IS externally
* accessible (or was defered). Now we look
* to see if we hold any file descriptors in its
* message buffers. Follow those links and mark them
* as accessible too.
*/
unp_scan(so->so_rcv.sb_mb, unp_mark);
}
} while (unp_defer);
/*
* We grab an extra reference to each of the file table entries
* that are not otherwise accessible and then free the rights
* that are stored in messages on them.
*
* The bug in the orginal code is a little tricky, so I'll describe
* what's wrong with it here.
*
* It is incorrect to simply unp_discard each entry for f_msgcount
* times -- consider the case of sockets A and B that contain
* references to each other. On a last close of some other socket,
* we trigger a gc since the number of outstanding rights (unp_rights)
* is non-zero. If during the sweep phase the gc code un_discards,
* we end up doing a (full) closef on the descriptor. A closef on A
* results in the following chain. Closef calls soo_close, which
* calls soclose. Soclose calls first (through the switch
* uncomp_usrreq) unp_detach, which re-invokes unp_gc. Unp_gc simply
* returns because the previous instance had set unp_gcing, and
* we return all the way back to soclose, which marks the socket
* with SS_NOFDREF, and then calls sofree. Sofree calls sorflush
* to free up the rights that are queued in messages on the socket A,
* i.e., the reference on B. The sorflush calls via the dom_dispose
* switch unp_dispose, which unp_scans with unp_discard. This second
* instance of unp_discard just calls closef on B.
*
* Well, a similar chain occurs on B, resulting in a sorflush on B,
* which results in another closef on A. Unfortunately, A is already
* being closed, and the descriptor has already been marked with
* SS_NOFDREF, and soclose panics at this point.
*
* Here, we first take an extra reference to each inaccessible
* descriptor. Then, we call sorflush ourself, since we know
* it is a Unix domain socket anyhow. After we destroy all the
* rights carried in messages, we do a last closef to get rid
* of our extra reference. This is the last close, and the
* unp_detach etc will shut down the socket.
*
*/
extra_ref = malloc(nfiles * sizeof(struct file *), M_FILE, M_WAITOK);
for (nunref = 0, fp = LIST_FIRST(&filehead), fpp = extra_ref; fp != 0;
fp = nextfp) {
nextfp = LIST_NEXT(fp, f_list);
/*
* If it's not open, skip it
*/
if (fp->f_count == 0)
continue;
/*
* If all refs are from msgs, and it's not marked accessible
* then it must be referenced from some unreachable cycle
* of (shut-down) FDs, so include it in our
* list of FDs to remove
*/
if (fp->f_count == fp->f_msgcount && !(fp->f_flag & FMARK)) {
*fpp++ = fp;
nunref++;
fp->f_count++;
}
}
/*
* for each FD on our hit list, do the following two things
*/
for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp) {
struct file *tfp = *fpp;
if (tfp->f_type == DTYPE_SOCKET && tfp->f_data != NULL)
sorflush((struct socket *)(tfp->f_data));
}
for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
closef(*fpp, (struct proc *) NULL);
free((caddr_t)extra_ref, M_FILE);
unp_gcing = 0;
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
}
void
unp_dispose(m)
struct mbuf *m;
{
#if UNCOMP_USE_OLD_BSD_STUFF
if (m)
unp_scan(m, unp_discard);
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
}
static void
unp_scan(m0, op)
register struct mbuf *m0;
void (*op) __P((struct file *));
{
#if UNCOMP_USE_OLD_BSD_STUFF
register struct mbuf *m;
register struct file **rp;
register struct cmsghdr *cm;
register int i;
int qfds;
while (m0) {
for (m = m0; m; m = m->m_next)
if (m->m_type == MT_CONTROL &&
m->m_len >= sizeof(*cm)) {
cm = mtod(m, struct cmsghdr *);
if (cm->cmsg_level != SOL_SOCKET ||
cm->cmsg_type != SCM_RIGHTS)
continue;
qfds = (cm->cmsg_len -
(CMSG_DATA(cm) - (u_char *)cm))
/ sizeof (struct file *);
rp = (struct file **)CMSG_DATA(cm);
for (i = 0; i < qfds; i++)
(*op)(*rp++);
break; /* XXX, but saves time */
}
m0 = m0->m_act;
}
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
}
static void
unp_mark(fp)
struct file *fp;
{
#if UNCOMP_USE_OLD_BSD_STUFF
if (fp->f_flag & FMARK)
return;
unp_defer++;
fp->f_flag |= (FMARK|FDEFER);
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
}
static void
unp_discard(fp)
struct file *fp;
{
#if UNCOMP_USE_OLD_BSD_STUFF
fp->f_msgcount--;
unp_rights--;
(void) closef(fp, (struct proc *)NULL);
#endif /* UNCOMP_USE_OLD_BSD_STUFF */
}
#endif /* if 0 */