2051 lines
43 KiB
C
Executable File
2051 lines
43 KiB
C
Executable File
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
#ident "@(#)_utility.c 1.54 95/09/19 SMI" /* SVr4.0 1.20 */
|
|
|
|
/*
|
|
* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
* PROPRIETARY NOTICE (Combined)
|
|
*
|
|
* This source code is unpublished proprietary information
|
|
* constituting, or derived under license from AT&T's UNIX(r) System V.
|
|
* In addition, portions of such source code were derived from Berkeley
|
|
* 4.3 BSD under license from the Regents of the University of
|
|
* California.
|
|
*
|
|
*
|
|
*
|
|
* Copyright Notice
|
|
*
|
|
* Notice of copyright on this source code product does not indicate
|
|
* publication.
|
|
*
|
|
* (c) 1986,1987,1988.1989 Sun Microsystems, Inc
|
|
* (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
|
|
* All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#include "sockmt.h"
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mkdev.h>
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <stropts.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/socket.h>
|
|
#include <tiuser.h>
|
|
#include <sys/tihdr.h>
|
|
#include <sys/sockmod.h>
|
|
#include <sys/uio.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/un.h>
|
|
#include <poll.h>
|
|
#include <fcntl.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <netconfig.h>
|
|
#include "sock.h"
|
|
|
|
/*
|
|
* The following used to be in tiuser.h, but was causing too much namespace
|
|
* pollution.
|
|
*/
|
|
#define ROUNDUP(X) ((X + 0x03)&~0x03)
|
|
|
|
static struct _si_user *find_silink_unlocked(int s);
|
|
static struct _si_user *add_silink(int s);
|
|
static int delete_silink(int s);
|
|
static int tlitosyserr(int terr);
|
|
static int recvaccrights(struct _si_user *siptr, char *buf, int len,
|
|
struct sockaddr *from, int *fromlen,
|
|
char *accrights, int *accrightslen,
|
|
int fmode);
|
|
static int send_clts_rights(struct _si_user *siptr, char *buf, int len,
|
|
struct sockaddr *to, int tolen,
|
|
char *accrights, int accrightslen);
|
|
static int send_cots_rights(struct _si_user *siptr, char *buf, int len,
|
|
char *accrights, int accrightslen);
|
|
static int msgpeek(int s, struct strbuf *ctlbuf, struct strbuf *rcvbuf,
|
|
int fmode);
|
|
static int _s_alloc(int s, struct _si_user **siptr, int new);
|
|
static int _s_alloc_bufs(struct _si_user *siptr);
|
|
static void _s_free(struct _si_user *siptr);
|
|
static struct netconfig *_s_match_netconf(int family, int type, int proto,
|
|
void **nethandle);
|
|
|
|
/*
|
|
* Global, used to enable debugging.
|
|
*/
|
|
int _s_sockdebug;
|
|
|
|
/*
|
|
* The following two string arrays map a number as specified
|
|
* by a user of sockets, to the string as would be returned
|
|
* by a call to getnetconfig().
|
|
*
|
|
* They are used by _s_match_netconf();
|
|
*
|
|
* proto_sw contains protocol entries for which there is a corresponding
|
|
* /dev device. All others would presumably use raw IP and download the
|
|
* desired protocol.
|
|
*/
|
|
static char *proto_sw[] = {
|
|
"",
|
|
"icmp", /* 1 = ICMP */
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"tcp", /* 6 = TCP */
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"udp", /* 17 = UDP */
|
|
};
|
|
|
|
static char *family_sw[] = {
|
|
"-", /* 0 = AF_UNSPEC */
|
|
"loopback", /* 1 = AF_UNIX */
|
|
"inet", /* 2 = AF_INET */
|
|
"implink", /* 3 = AF_IMPLINK */
|
|
"pup", /* 4 = AF_PUP */
|
|
"chaos", /* 5 = AF_CHAOS */
|
|
"ns", /* 6 = AF_NS */
|
|
"nbs", /* 7 = AF_NBS */
|
|
"ecma", /* 8 = AF_ECMA */
|
|
"datakit", /* 9 = AF_DATAKIT */
|
|
"ccitt", /* 10 = AF_CCITT */
|
|
"sna", /* 11 = AF_SNA */
|
|
"decnet", /* 12 = AF_DECnet */
|
|
"dli", /* 13 = AF_DLI */
|
|
"lat", /* 14 = AF_LAT */
|
|
"hylink", /* 15 = AF_HYLINK */
|
|
"appletalk", /* 16 = AF_APPLETALK */
|
|
"nit", /* 17 = AF_NIT */
|
|
"ieee802", /* 18 = AF_802 */
|
|
"osi", /* 19 = AF_OSI */
|
|
"x25", /* 20 = AF_X25 */
|
|
"osinet", /* 21 = AF_OSINET */
|
|
"gosip", /* 22 = AF_GOSIP */
|
|
"ipx", /* 23 = AF_IPX */
|
|
};
|
|
|
|
static mutex_t _si_userlock = DEFAULTMUTEX; /* Protects hash_bucket[] */
|
|
|
|
/*
|
|
* Checkfd - checks validity of file descriptor
|
|
*/
|
|
struct _si_user *
|
|
_s_checkfd(s)
|
|
register int s;
|
|
{
|
|
struct _si_user *siptr;
|
|
sigset_t mask;
|
|
|
|
MUTEX_LOCK_SIGMASK(&_si_userlock, mask);
|
|
if ((siptr = find_silink_unlocked(s)) != NULL) {
|
|
MUTEX_UNLOCK_SIGMASK(&_si_userlock, mask);
|
|
return (siptr);
|
|
}
|
|
MUTEX_UNLOCK_SIGMASK(&_si_userlock, mask);
|
|
|
|
/*
|
|
* Maybe the descripter is valid, but we did an exec()
|
|
* and lost the information. Try to re-constitute the info.
|
|
*/
|
|
SOCKDEBUG((struct _si_user *)NULL,
|
|
"_s_checkfd: s %d: Not found, trying to reconstitute\n", s);
|
|
|
|
MUTEX_LOCK_PROCMASK(&_si_userlock, mask);
|
|
if (_s_alloc(s, &siptr, 0) < 0) {
|
|
MUTEX_UNLOCK_PROCMASK(&_si_userlock, mask);
|
|
return (NULL);
|
|
}
|
|
MUTEX_UNLOCK_PROCMASK(&_si_userlock, mask);
|
|
|
|
return (siptr);
|
|
}
|
|
|
|
/*
|
|
* Do common socket creation
|
|
*/
|
|
struct _si_user *
|
|
_s_socreate(family, type, protocol)
|
|
register int family;
|
|
register int type;
|
|
int protocol;
|
|
{
|
|
register int s;
|
|
struct _si_user *siptr;
|
|
register struct netconfig *net;
|
|
void *nethandle;
|
|
|
|
sigset_t mask;
|
|
int retval;
|
|
struct si_sockparams sockparams;
|
|
|
|
net = _s_match_netconf(family, type, protocol, &nethandle);
|
|
if (net == NULL)
|
|
return (NULL);
|
|
|
|
if (strcmp(net->nc_proto, NC_NOPROTO) != 0)
|
|
protocol = 0;
|
|
if ((s = open(net->nc_device, O_RDWR)) < 0)
|
|
return (NULL);
|
|
/*
|
|
* Sockmod will fail open with EALREADY if a sockmod is already pushed
|
|
*/
|
|
redo_push:
|
|
if (_ioctl(s, I_PUSH, "sockmod") < 0) {
|
|
if (errno == EINTR)
|
|
goto redo_push;
|
|
if (errno != EALREADY) {
|
|
(void) close(s);
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* deposit socket parameters with sockmod
|
|
* (can be retrieved even after fork/exec for use in accept())
|
|
*/
|
|
sockparams.sp_family = family;
|
|
sockparams.sp_type = type;
|
|
sockparams.sp_protocol = protocol;
|
|
|
|
do {
|
|
retval = _s_do_ioctl(s, (caddr_t)&sockparams,
|
|
sizeof (struct si_sockparams), SI_SOCKPARAMS, NULL);
|
|
} while (! retval && errno == EINTR);
|
|
|
|
/*
|
|
* Set stream head close time to 0.
|
|
*/
|
|
retval = 0;
|
|
(void) _ioctl(s, I_SETCLTIME, &retval);
|
|
|
|
/*
|
|
* Turn on SIGPIPE stream head write
|
|
* option.
|
|
*/
|
|
|
|
do {
|
|
retval = _ioctl(s, I_SWROPT, SNDPIPE);
|
|
} while (retval < 0 && errno == EINTR);
|
|
|
|
if (retval < 0) {
|
|
(void) close(s);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Get a new library entry and sync it
|
|
* with sockmod.
|
|
*/
|
|
MUTEX_LOCK_PROCMASK(&_si_userlock, mask);
|
|
if (_s_alloc(s, &siptr, 1) < 0) {
|
|
(void) close(s);
|
|
MUTEX_UNLOCK_PROCMASK(&_si_userlock, mask);
|
|
return (NULL);
|
|
}
|
|
MUTEX_UNLOCK_PROCMASK(&_si_userlock, mask);
|
|
|
|
if (protocol) {
|
|
/*
|
|
* Need to send down the protocol number.
|
|
*/
|
|
if (__setsockopt(siptr, SOL_SOCKET, SO_PROTOTYPE,
|
|
(caddr_t)&protocol, sizeof (protocol)) < 0) {
|
|
int sv_errno = errno;
|
|
(void) _s_close(siptr);
|
|
errno = sv_errno;
|
|
return (NULL);
|
|
}
|
|
}
|
|
endnetconfig(nethandle); /* finished with netconfig struct */
|
|
return (siptr);
|
|
}
|
|
|
|
/*
|
|
* Match config entry for protocol
|
|
* requested.
|
|
*/
|
|
static struct netconfig *
|
|
_s_match_netconf(family, type, proto, nethandle)
|
|
register int family;
|
|
register int type;
|
|
register int proto;
|
|
void **nethandle;
|
|
{
|
|
register struct netconfig *net;
|
|
register struct netconfig *maybe;
|
|
register char *oproto;
|
|
|
|
if (family < 0 || family >= sizeof (family_sw)/sizeof (char *) ||
|
|
proto < 0 || proto >= IPPROTO_MAX) {
|
|
errno = EPROTONOSUPPORT;
|
|
return (NULL);
|
|
}
|
|
if (proto) {
|
|
if (proto >= sizeof (proto_sw)/sizeof (char *))
|
|
oproto = "";
|
|
else oproto = proto_sw[proto];
|
|
}
|
|
|
|
/*
|
|
* Loop through each entry in netconfig
|
|
* until one matches or we reach the end.
|
|
*/
|
|
if ((*nethandle = setnetconfig()) == NULL) {
|
|
(void) syslog(LOG_ERR, "_s_match_netconf: setnetconfig failed");
|
|
return (NULL);
|
|
}
|
|
|
|
maybe = NULL;
|
|
while ((net = getnetconfig(*nethandle)) != NULL) {
|
|
/*
|
|
* We make a copy of net->nc_semantics rather than modifying
|
|
* it in place because the network selection code shares the
|
|
* structures returned by getnetconfig() among all its callers.
|
|
* See bug #1160886 for more details.
|
|
*/
|
|
unsigned long semantics = net->nc_semantics;
|
|
|
|
if (semantics == NC_TPI_COTS_ORD)
|
|
semantics = NC_TPI_COTS;
|
|
if (proto) {
|
|
if (strcmp(net->nc_protofmly, family_sw[family]) == 0 &&
|
|
semantics == type &&
|
|
strcmp(net->nc_proto, oproto) == 0)
|
|
break;
|
|
|
|
if (strcmp(net->nc_protofmly, family_sw[family]) == 0 &&
|
|
type == SOCK_RAW &&
|
|
semantics == SOCK_RAW &&
|
|
strcmp(net->nc_proto, NC_NOPROTO) == 0 &&
|
|
maybe == NULL)
|
|
maybe = net; /* in case no exact match */
|
|
|
|
continue;
|
|
} else {
|
|
if (strcmp(net->nc_protofmly, family_sw[family]) == 0 &&
|
|
semantics == type) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (net == NULL && maybe)
|
|
net = maybe;
|
|
|
|
if (net == NULL) {
|
|
endnetconfig(*nethandle);
|
|
errno = EPROTONOSUPPORT;
|
|
return (NULL);
|
|
}
|
|
|
|
return (net);
|
|
}
|
|
|
|
/*
|
|
* Try to resynchronize the user level state with the kernel
|
|
*
|
|
* Assumes that the caller is holding siptr lock.
|
|
*/
|
|
int
|
|
_s_synch(siptr)
|
|
register struct _si_user *siptr;
|
|
{
|
|
register int s = siptr->fd;
|
|
struct si_udata udata;
|
|
int retlen;
|
|
int retval;
|
|
int rval;
|
|
sigset_t procmask;
|
|
|
|
do {
|
|
rval = _s_do_ioctl(s, (caddr_t)&udata,
|
|
sizeof (struct si_udata), SI_GETUDATA, &retlen);
|
|
} while (! rval && errno == EINTR);
|
|
|
|
if (! rval) {
|
|
/*
|
|
* Could be EPIPE, ECONNREFUSED, ECONNRESET, or some other
|
|
* t_discon_ind error that got stuck on the stream head.
|
|
*/
|
|
siptr->udata.so_state |=
|
|
SS_CANTSENDMORE|SS_CANTRCVMORE;
|
|
siptr->udata.so_state &= ~SS_ISCONNECTED;
|
|
return (0);
|
|
}
|
|
|
|
if (retlen != sizeof (struct si_udata)) {
|
|
_s_free(siptr);
|
|
errno = EPROTO;
|
|
return (-1);
|
|
}
|
|
|
|
siptr->flags = 0;
|
|
siptr->udata = udata;
|
|
|
|
/* Block signals for malloc */
|
|
_s_blockallsignals(&procmask);
|
|
if (_s_alloc_bufs(siptr) < 0) {
|
|
_s_restoresigmask(&procmask);
|
|
return (-1);
|
|
}
|
|
_s_restoresigmask(&procmask);
|
|
|
|
/*
|
|
* Get SIGIO and SIGURG disposition
|
|
* and cache them.
|
|
*/
|
|
retval = 0;
|
|
if (_ioctl(s, I_GETSIG, &retval) < 0 && errno != EINVAL) {
|
|
(void) syslog(LOG_ERR, "ioctl: I_GETSIG failed %d\n", errno);
|
|
return (-1);
|
|
}
|
|
/*
|
|
* Normal return. Have to clear errno
|
|
* since other library code (notably _s_sosend/recv) uses
|
|
* errno to determine if an error has
|
|
* occured.
|
|
*/
|
|
errno = 0;
|
|
|
|
if (retval & (S_RDNORM|S_WRNORM))
|
|
siptr->flags |= S_SIGIO;
|
|
|
|
if (retval & (S_RDBAND|S_BANDURG))
|
|
siptr->flags |= S_SIGURG;
|
|
|
|
/* Mark fcntl flags as invalid */
|
|
s_invalfflags(siptr);
|
|
|
|
SOCKDEBUG(siptr, "_s_synch: siptr: %x\n", siptr);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Allocate a socket state structure and synch it with the kernel.
|
|
* *siptr is returned.
|
|
* Assumes that the caller is holding _si_userlock.
|
|
* signals are blocked even when not linked to libthread
|
|
* therefore no retry of interrupted (EINTR) calls and
|
|
* possibility of interrupts while modifying global data
|
|
* (MUTEX_LOCK_PROCMASK translates to sigprocmask when libthread is not
|
|
* linked)
|
|
*/
|
|
static int
|
|
_s_alloc(s, siptr, new)
|
|
register int s;
|
|
register struct _si_user **siptr;
|
|
register int new;
|
|
{
|
|
register struct _si_user *nsiptr;
|
|
struct si_udata udata;
|
|
int retlen;
|
|
int retval;
|
|
sigset_t mask;
|
|
|
|
if (!_s_do_ioctl(s, (caddr_t)&udata, sizeof (struct si_udata),
|
|
SI_GETUDATA, &retlen)) {
|
|
return (-1);
|
|
}
|
|
|
|
if (retlen != sizeof (struct si_udata)) {
|
|
errno = EPROTO;
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Allocate a link and initialize it.
|
|
*/
|
|
nsiptr = add_silink(s);
|
|
if (nsiptr == NULL) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
MUTEX_LOCK_SIGMASK(&nsiptr->lock, mask);
|
|
nsiptr->udata = udata; /* structure copy */
|
|
nsiptr->flags = 0;
|
|
if (_s_alloc_bufs(nsiptr) < 0) {
|
|
int sv_errno = errno;
|
|
MUTEX_UNLOCK_SIGMASK(&nsiptr->lock, mask);
|
|
_s_free(nsiptr);
|
|
errno = sv_errno;
|
|
return (-1);
|
|
}
|
|
MUTEX_UNLOCK_SIGMASK(&nsiptr->lock, mask);
|
|
if (!new) {
|
|
/*
|
|
* Get SIGIO and SIGURG disposition
|
|
* and cache them. Not done when the socket was just opened.
|
|
*/
|
|
retval = 0;
|
|
if (_ioctl(s, I_GETSIG, &retval) < 0 && errno != EINVAL) {
|
|
int sv_errno = errno;
|
|
(void) syslog(LOG_ERR, "ioctl: I_GETSIG failed %d\n",
|
|
errno);
|
|
MUTEX_UNLOCK_SIGMASK(&nsiptr->lock, mask);
|
|
_s_free(nsiptr);
|
|
errno = sv_errno;
|
|
return (-1);
|
|
}
|
|
/*
|
|
* Normal return. Have to clear errno
|
|
* since other library code (notably _s_sosend/recv) uses
|
|
* errno to determine if an error has
|
|
* occured.
|
|
*/
|
|
errno = 0;
|
|
|
|
if (retval & (S_RDNORM|S_WRNORM))
|
|
nsiptr->flags |= S_SIGIO;
|
|
|
|
if (retval & (S_RDBAND|S_BANDURG))
|
|
nsiptr->flags |= S_SIGURG;
|
|
|
|
/* Invalidate cached fcntl flags */
|
|
s_invalfflags(nsiptr);
|
|
}
|
|
|
|
*siptr = nsiptr;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Get access rights and associated data.
|
|
*
|
|
* Only UNIX domain supported.
|
|
*
|
|
* Returns:
|
|
* >0 Number of bytes read on success
|
|
* -1 If an error occurred.
|
|
*/
|
|
static int
|
|
recvaccrights(siptr, buf, len, from, fromlen, accrights, accrightslen,
|
|
fmode)
|
|
struct _si_user *siptr;
|
|
char *buf;
|
|
int len;
|
|
register struct sockaddr *from;
|
|
register int *fromlen;
|
|
char *accrights;
|
|
int *accrightslen;
|
|
register int fmode;
|
|
{
|
|
register int i;
|
|
register int nfd;
|
|
int *fdarray;
|
|
struct strrecvfd pipe;
|
|
int retval;
|
|
struct sockaddr_un addr;
|
|
sigset_t mask;
|
|
|
|
/*
|
|
* Make receiving access rights atomic with respect to signals
|
|
*/
|
|
_s_blockallsignals(&mask);
|
|
|
|
if (siptr->udata.sockparams.sp_family != AF_UNIX) {
|
|
_s_restoresigmask(&mask);
|
|
errno = EOPNOTSUPP;
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* First get the pipe channel.
|
|
*/
|
|
if (_ioctl(siptr->fd, I_RECVFD, &pipe) < 0) {
|
|
_s_restoresigmask(&mask);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* To ensure the following operations are atomic
|
|
* as far as the user is concerned, we reset
|
|
* (O_NDELAY|O_NONBLOCK) if they are on.
|
|
*/
|
|
if (fmode & (O_NDELAY|O_NONBLOCK)) {
|
|
if (_fcntl(siptr->fd, F_SETFL,
|
|
fmode & ~(O_NDELAY|O_NONBLOCK)) < 0) {
|
|
retval = -1;
|
|
goto recv_rights_done;
|
|
}
|
|
else
|
|
s_invalfflags(siptr);
|
|
siptr->fflags = s_getfflags(siptr) & ~(O_NDELAY|O_NONBLOCK);
|
|
}
|
|
|
|
/*
|
|
* We do the same whether the flags say MSG_PEEK or
|
|
* not.
|
|
*/
|
|
SOCKDEBUG(siptr, "recvaccrights: getting access rights\n", 0);
|
|
|
|
/*
|
|
* Dispose of rights, copying them into the users
|
|
* buffer if possible.
|
|
*/
|
|
fdarray = (int *)accrights;
|
|
nfd = *accrightslen/sizeof (int);
|
|
*accrightslen = 0;
|
|
i = 0;
|
|
for (;;) {
|
|
struct strrecvfd stfd;
|
|
|
|
retval = 0;
|
|
if (_ioctl(pipe.fd, I_RECVFD, &stfd) < 0)
|
|
break;
|
|
else {
|
|
SOCKDEBUG(siptr, "recvaccrights: got fd %d\n", stfd.fd);
|
|
if (i != nfd) {
|
|
fdarray[i] = stfd.fd;
|
|
*accrightslen += sizeof (int);
|
|
i++;
|
|
} else {
|
|
(void) close(stfd.fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (errno == EBADMSG) {
|
|
/*
|
|
* We have read all the access rights, get any data.
|
|
*/
|
|
errno = 0;
|
|
if (siptr->udata.servtype == T_CLTS) {
|
|
/*
|
|
* First get the source address.
|
|
*/
|
|
(void) memset((caddr_t)&addr, 0, sizeof (addr));
|
|
if (read(pipe.fd, (caddr_t)&addr, sizeof (addr))
|
|
!= sizeof (addr)) {
|
|
errno = EPROTO;
|
|
retval = -1;
|
|
goto recv_rights_done;
|
|
}
|
|
if (from && fromlen) {
|
|
if (*fromlen > sizeof (addr))
|
|
*fromlen = sizeof (addr);
|
|
(void) memcpy((char *)from, (caddr_t)&addr,
|
|
*fromlen);
|
|
}
|
|
}
|
|
|
|
retval = read(pipe.fd, buf, len);
|
|
SOCKDEBUG(siptr, "recvaccrights: got %d bytes\n", retval);
|
|
} else {
|
|
/*
|
|
* No data.
|
|
*/
|
|
if (errno == ENXIO) {
|
|
errno = 0;
|
|
retval = 0;
|
|
}
|
|
}
|
|
|
|
recv_rights_done:
|
|
(void) close(pipe.fd);
|
|
if (fmode & (O_NDELAY|O_NONBLOCK)) {
|
|
(void) _fcntl(siptr->fd, F_SETFL, fmode);
|
|
s_invalfflags(siptr);
|
|
}
|
|
_s_restoresigmask(&mask);
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* Peeks at a message. If no messages are
|
|
* present it will block in a poll().
|
|
* Note ioctl(I_PEEK) does not block.
|
|
*
|
|
* Returns:
|
|
* 0 On success
|
|
* -1 On error. In particular, EBADMSG is returned if access
|
|
* are present.
|
|
*/
|
|
static int
|
|
msgpeek(s, ctlbuf, rcvbuf, fmode)
|
|
register int s;
|
|
register struct strbuf *ctlbuf;
|
|
register struct strbuf *rcvbuf;
|
|
register int fmode;
|
|
{
|
|
register int retval;
|
|
struct strpeek strpeek;
|
|
|
|
strpeek.ctlbuf.buf = ctlbuf->buf;
|
|
strpeek.ctlbuf.maxlen = ctlbuf->maxlen;
|
|
strpeek.ctlbuf.len = 0;
|
|
strpeek.databuf.buf = rcvbuf->buf;
|
|
strpeek.databuf.maxlen = rcvbuf->maxlen;
|
|
strpeek.databuf.len = 0;
|
|
strpeek.flags = 0;
|
|
|
|
for (;;) {
|
|
do {
|
|
retval = _ioctl(s, I_PEEK, &strpeek);
|
|
} while (retval < 0 && errno == EINTR);
|
|
|
|
if (retval < 0)
|
|
return (-1);
|
|
|
|
if (retval == 1) {
|
|
ctlbuf->len = strpeek.ctlbuf.len;
|
|
rcvbuf->len = strpeek.databuf.len;
|
|
return (0);
|
|
} else if ((fmode & (O_NDELAY|O_NONBLOCK)) == 0) {
|
|
/*
|
|
* Sit in a poll()
|
|
*/
|
|
struct pollfd fds[1];
|
|
|
|
fds[0].fd = s;
|
|
fds[0].events = POLLIN;
|
|
fds[0].revents = 0;
|
|
for (;;) {
|
|
if (poll(fds, 1L, -1) < 0)
|
|
return (-1);
|
|
if (fds[0].revents != 0)
|
|
break;
|
|
}
|
|
} else {
|
|
errno = EAGAIN;
|
|
return (-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Common receive code.
|
|
*/
|
|
int
|
|
_s_soreceivexx(siptr, flags, buf, len, from, fromlen, accrights, accrightslen,
|
|
rcvmaskp)
|
|
struct _si_user *siptr;
|
|
register int flags;
|
|
char *buf;
|
|
int len;
|
|
register struct sockaddr *from;
|
|
register int *fromlen;
|
|
char *accrights;
|
|
int *accrightslen;
|
|
sigset_t *rcvmaskp;
|
|
{
|
|
register int fmode;
|
|
register int s;
|
|
sigset_t mask;
|
|
struct strbuf ctlbuf;
|
|
struct strbuf rcvbuf;
|
|
int flg;
|
|
int so_state = siptr->udata.so_state;
|
|
int servtype;
|
|
int didalloc = 0;
|
|
|
|
errno = 0;
|
|
servtype = siptr->udata.servtype;
|
|
if (so_state & SS_CANTRCVMORE) {
|
|
int bytes;
|
|
|
|
/*
|
|
* Make sure there's nothing enqueued on the
|
|
* streamhead. It's possible for sockmod to return
|
|
* so_state & SS_CANTRCVMORE on a SI_GETUDATA while
|
|
* there is still something enqueued on the
|
|
* streamhead. In that case, just ignore the flag.
|
|
*
|
|
* XXX Maybe make sockmod keep track of messages on
|
|
* the stream head, or mask SS_CANTRCVMORE since it
|
|
* seems to be managed by libsocket anyway?
|
|
*/
|
|
if ((ioctl(siptr->fd, I_NREAD, (char *) &bytes)) <= 0) {
|
|
errno = 0;
|
|
return (0);
|
|
}
|
|
/* One or more messages at stream head */
|
|
siptr->udata.so_state &= ~SS_CANTRCVMORE;
|
|
}
|
|
|
|
if (servtype == T_COTS || servtype == T_COTS_ORD) {
|
|
if ((so_state & SS_ISCONNECTED) == 0) {
|
|
if (_s_synch(siptr) < 0)
|
|
return (-1);
|
|
/* _s_synch might have modified errno */
|
|
errno = 0;
|
|
|
|
so_state = siptr->udata.so_state;
|
|
servtype = siptr->udata.servtype;
|
|
if ((so_state & SS_ISCONNECTED) == 0) {
|
|
errno = ENOTCONN;
|
|
return (-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((so_state & SS_ISBOUND) == 0) {
|
|
/*
|
|
* Need to bind it for TLI.
|
|
*/
|
|
if (__bind(siptr, NULL, 0, NULL, NULL) < 0)
|
|
return (-1);
|
|
}
|
|
|
|
s = siptr->fd;
|
|
|
|
if (len == 0 && accrightslen == 0)
|
|
return (0);
|
|
|
|
rcvbuf.buf = buf;
|
|
|
|
ctlbuf.buf = NULL;
|
|
tryagain:
|
|
rcvbuf.maxlen = len;
|
|
rcvbuf.len = 0;
|
|
|
|
ctlbuf.len = 0;
|
|
if (ctlbuf.buf == NULL) {
|
|
/*
|
|
* first entry (not through tryagain: label)
|
|
* acquire space for ctlbuf
|
|
*/
|
|
if (siptr->ctlbuf) {
|
|
ctlbuf.maxlen = siptr->ctlsize;
|
|
ctlbuf.buf = siptr->ctlbuf;
|
|
siptr->ctlbuf = NULL;
|
|
} else {
|
|
/*
|
|
* siptr->ctlbuf is in use
|
|
* allocate and free after use.
|
|
*/
|
|
if ((ctlbuf.maxlen = _s_cbuf_alloc(siptr,
|
|
&ctlbuf.buf)) < 0)
|
|
goto rcvout;
|
|
didalloc = 1;
|
|
}
|
|
}
|
|
|
|
if (flags & MSG_OOB) {
|
|
int rval;
|
|
/*
|
|
* Handles the case when MSG_PEEK is set
|
|
* or not.
|
|
* Note: it is safe to block signals here. Ioctl does not
|
|
* block if no oob data is available. It returns EWOULDBLOCK
|
|
* in sockmod.
|
|
*/
|
|
_s_blockallsignals(&mask);
|
|
rval = _s_do_ioctl(s, rcvbuf.buf, rcvbuf.maxlen, flags,
|
|
&rcvbuf.len);
|
|
_s_restoresigmask(&mask);
|
|
|
|
if (! rval)
|
|
goto rcvout;
|
|
SOCKDEBUG(siptr, "lrecvmsg: Got %d bytes OOB data\n",
|
|
rcvbuf.len);
|
|
} else if (flags & MSG_PEEK) {
|
|
fmode = s_getfflags(siptr);
|
|
|
|
if (msgpeek(s, &ctlbuf, &rcvbuf, fmode) < 0) {
|
|
if (errno == EBADMSG) {
|
|
errno = 0;
|
|
rcvbuf.len = recvaccrights(siptr, buf,
|
|
len, from, fromlen, accrights,
|
|
accrightslen, fmode);
|
|
}
|
|
goto rcvout;
|
|
}
|
|
} else {
|
|
int retval;
|
|
|
|
flg = 0;
|
|
|
|
/*
|
|
* Historical note: There used to be a comment here about
|
|
* having to prevent spurious SIGPOLL and code to call
|
|
* _s_blockallsignals for every call to getmsg. The
|
|
* original comment read:
|
|
* "Have to prevent spurious SIGPOLL signals
|
|
* which can be caused by the mechanism used
|
|
* to cause a SIGURG."
|
|
*
|
|
* Note: To enable signals to happen in MT case, we
|
|
* drop the lock and enable signals for the blocking
|
|
* getmsg call and acquire it again when done.
|
|
*/
|
|
MUTEX_UNLOCK_SIGMASK(&siptr->lock, *rcvmaskp);
|
|
if ((retval = getmsg(s, &ctlbuf, &rcvbuf, &flg)) < 0) {
|
|
int saved_errno = errno;
|
|
MUTEX_LOCK_SIGMASK(&siptr->lock, *rcvmaskp);
|
|
fmode = s_getfflags(siptr);
|
|
if (saved_errno == EBADMSG) {
|
|
|
|
errno = 0;
|
|
rcvbuf.len = recvaccrights(siptr, buf,
|
|
len, from, fromlen, accrights,
|
|
accrightslen, fmode);
|
|
}
|
|
goto rcvout;
|
|
}
|
|
|
|
if (servtype == T_CLTS) {
|
|
struct strbuf databuf;
|
|
char dbuf[256];
|
|
|
|
while (retval & MOREDATA) {
|
|
/*
|
|
* Discard all the extra data.
|
|
*/
|
|
databuf.maxlen = sizeof (dbuf);
|
|
databuf.len = 0;
|
|
databuf.buf = dbuf;
|
|
if ((retval = getmsg(s, NULL,
|
|
&databuf, &flg)) < 0) {
|
|
errno = EIO;
|
|
goto rcvout;
|
|
}
|
|
if (retval & MORECTL) {
|
|
/*
|
|
* There shouldn't be any control part.
|
|
*/
|
|
errno = EIO;
|
|
goto rcvout;
|
|
}
|
|
}
|
|
}
|
|
MUTEX_LOCK_SIGMASK(&siptr->lock, *rcvmaskp);
|
|
}
|
|
|
|
if (rcvbuf.len == -1)
|
|
rcvbuf.len = 0;
|
|
|
|
if (ctlbuf.len == sizeof (struct T_exdata_ind) &&
|
|
*(long *)ctlbuf.buf == T_EXDATA_IND &&
|
|
rcvbuf.len == 0) {
|
|
/*
|
|
* Must be the message indicating the position
|
|
* of urgent data in the data stream - the user
|
|
* should not see this.
|
|
*/
|
|
if (flags & MSG_PEEK) {
|
|
/*
|
|
* Better make sure it goes.
|
|
*/
|
|
flg = 0;
|
|
_s_blockallsignals(&mask);
|
|
(void) getmsg(s, &ctlbuf, &rcvbuf, &flg);
|
|
_s_restoresigmask(&mask);
|
|
}
|
|
goto tryagain;
|
|
}
|
|
|
|
|
|
/*
|
|
* Copy in source address if requested.
|
|
*/
|
|
rcvout:
|
|
if (errno == 0 && from && *fromlen) {
|
|
if (servtype == T_CLTS) {
|
|
if (ctlbuf.len != 0) {
|
|
register struct T_unitdata_ind *udata_ind;
|
|
|
|
udata_ind = (struct T_unitdata_ind *)ctlbuf.buf;
|
|
*fromlen = _s_cpaddr(siptr,
|
|
(char *)from,
|
|
*fromlen,
|
|
udata_ind->SRC_offset + ctlbuf.buf,
|
|
udata_ind->SRC_length);
|
|
|
|
}
|
|
} else {
|
|
/*
|
|
* The ctlbuf is not free at this point so there
|
|
* is malloc/free cost in the __getpeername call
|
|
* which can be avoided if we can avoid doing this call
|
|
*/
|
|
if (__getpeername(siptr, from, fromlen) < 0) {
|
|
errno = 0;
|
|
*fromlen = 0;
|
|
}
|
|
}
|
|
}
|
|
if (didalloc)
|
|
free(ctlbuf.buf);
|
|
else
|
|
siptr->ctlbuf = ctlbuf.buf;
|
|
|
|
if (errno)
|
|
return (-1);
|
|
return (rcvbuf.len);
|
|
}
|
|
|
|
/*
|
|
* Common send code.
|
|
*/
|
|
int
|
|
_s_sosendxx(siptr, flags, buf, len, to, tolen, accrights, accrightslen,
|
|
sndmaskp)
|
|
struct _si_user *siptr;
|
|
register int flags;
|
|
char *buf;
|
|
int len;
|
|
register struct sockaddr *to;
|
|
register int tolen;
|
|
char *accrights;
|
|
int accrightslen;
|
|
sigset_t *sndmaskp;
|
|
{
|
|
register int s;
|
|
register int retval;
|
|
struct strbuf ctlbuf;
|
|
struct strbuf databuf;
|
|
int so_state = siptr->udata.so_state;
|
|
int servtype;
|
|
int didalloc = 0;
|
|
|
|
errno = 0;
|
|
if (so_state & SS_CANTSENDMORE) {
|
|
(void) kill(getpid(), SIGPIPE);
|
|
errno = EPIPE;
|
|
return (-1);
|
|
}
|
|
|
|
servtype = siptr->udata.servtype;
|
|
s = siptr->fd;
|
|
if ((servtype == T_CLTS && tolen <= 0) || servtype != T_CLTS) {
|
|
if ((so_state & SS_ISCONNECTED) == 0) {
|
|
if (_s_synch(siptr) < 0)
|
|
return (-1);
|
|
/* _s_synch might have modified errno */
|
|
errno = 0;
|
|
|
|
so_state = siptr->udata.so_state;
|
|
servtype = siptr->udata.servtype;
|
|
if ((so_state & SS_ISCONNECTED) == 0) {
|
|
if (servtype == T_CLTS)
|
|
errno = EDESTADDRREQ;
|
|
else errno = ENOTCONN;
|
|
return (-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((so_state & SS_ISBOUND) == 0) {
|
|
/*
|
|
* Need to bind it for TLI.
|
|
*/
|
|
if (__bind(siptr, NULL, 0, NULL, NULL) < 0)
|
|
return (-1);
|
|
}
|
|
|
|
if (flags & MSG_DONTROUTE) {
|
|
int val;
|
|
|
|
val = 1;
|
|
if (__setsockopt(siptr, SOL_SOCKET, SO_DONTROUTE, (char *)&val,
|
|
sizeof (val)) < 0)
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Access rights only in UNIX domain.
|
|
*/
|
|
if (accrightslen) {
|
|
if (siptr->udata.sockparams.sp_family != AF_UNIX) {
|
|
errno = EOPNOTSUPP;
|
|
goto sndout_nobuf;
|
|
}
|
|
}
|
|
|
|
if (flags & MSG_OOB) {
|
|
/*
|
|
* If the socket is SOCK_DGRAM or
|
|
* AF_UNIX which we know is not to support
|
|
* MSG_OOB or the TP does not support the
|
|
* notion of expedited data then we fail.
|
|
*
|
|
* Otherwise we hope that the TP knows
|
|
* what to do.
|
|
*/
|
|
if (siptr->udata.sockparams.sp_family == AF_UNIX ||
|
|
servtype == T_CLTS ||
|
|
siptr->udata.etsdusize == 0) {
|
|
errno = EOPNOTSUPP;
|
|
goto sndout_nobuf;
|
|
}
|
|
}
|
|
/*
|
|
* acquire ctlbuf for use
|
|
*/
|
|
ctlbuf.len = 0;
|
|
if (siptr->ctlbuf) {
|
|
ctlbuf.buf = siptr->ctlbuf;
|
|
siptr->ctlbuf = NULL;
|
|
ctlbuf.maxlen = siptr->ctlsize;
|
|
} else {
|
|
/*
|
|
* siptr->ctlbuf is in use
|
|
* allocate and free after use.
|
|
*/
|
|
if ((ctlbuf.maxlen = _s_cbuf_alloc(siptr,
|
|
&ctlbuf.buf)) < 0)
|
|
return (-1);
|
|
didalloc = 1;
|
|
}
|
|
|
|
|
|
if (servtype == T_CLTS) {
|
|
register struct T_unitdata_req *udata_req;
|
|
register char *tmpbuf;
|
|
register int tmpcnt;
|
|
struct ux_dev ux_dev;
|
|
|
|
if (len < 0 || len > siptr->udata.tidusize) {
|
|
errno = EMSGSIZE;
|
|
goto sndout;
|
|
}
|
|
|
|
if ((so_state & SS_ISCONNECTED) == 0) {
|
|
switch (siptr->udata.sockparams.sp_family) {
|
|
case AF_INET:
|
|
if (tolen != sizeof (struct sockaddr_in))
|
|
errno = EINVAL;
|
|
break;
|
|
|
|
case AF_UNIX: {
|
|
struct stat rstat;
|
|
struct sockaddr_un *un;
|
|
int i;
|
|
|
|
if (tolen <= 0)
|
|
break;
|
|
|
|
un = (struct sockaddr_un *)to;
|
|
|
|
if (tolen > sizeof (*un) ||
|
|
(i = _s_uxpathlen(un)) ==
|
|
sizeof (un->sun_path)) {
|
|
errno = EINVAL;
|
|
goto sndout;
|
|
}
|
|
un->sun_path[i] = 0;
|
|
|
|
/*
|
|
* Substitute the user supplied address with the
|
|
* one that will have actually got bound to.
|
|
*/
|
|
if (un->sun_family != AF_UNIX) {
|
|
errno = EINVAL;
|
|
goto sndout;
|
|
}
|
|
|
|
/*
|
|
* stat the file.
|
|
*/
|
|
#if defined(i386)
|
|
if (_xstat(_STAT_VER, (caddr_t)un->sun_path,
|
|
&rstat) < 0)
|
|
#elif defined(sparc)
|
|
if (_stat((caddr_t)un->sun_path, &rstat) < 0)
|
|
#else
|
|
#error Undefined architecture
|
|
#endif
|
|
goto sndout;
|
|
|
|
if ((rstat.st_mode & S_IFMT) != S_IFIFO) {
|
|
errno = ENOTSOCK;
|
|
goto sndout;
|
|
}
|
|
|
|
(void) memset((caddr_t)&ux_dev, 0,
|
|
sizeof (ux_dev));
|
|
ux_dev.dev = rstat.st_dev;
|
|
ux_dev.ino = rstat.st_ino;
|
|
|
|
to = (struct sockaddr *)&ux_dev;
|
|
tolen = sizeof (ux_dev);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
if (tolen > siptr->udata.addrsize)
|
|
errno = EINVAL;
|
|
break;
|
|
}
|
|
if (errno)
|
|
goto sndout;
|
|
}
|
|
|
|
if (accrightslen) {
|
|
retval = send_clts_rights(siptr, buf, len, to,
|
|
tolen, accrights, accrightslen);
|
|
goto sndout;
|
|
}
|
|
|
|
tmpbuf = ctlbuf.buf;
|
|
udata_req = (struct T_unitdata_req *)tmpbuf;
|
|
udata_req->PRIM_type = T_UNITDATA_REQ;
|
|
udata_req->DEST_length = _s_min(tolen, siptr->udata.addrsize);
|
|
udata_req->DEST_offset = 0;
|
|
udata_req->OPT_length = 0;
|
|
udata_req->OPT_offset = 0;
|
|
tmpcnt = sizeof (*udata_req);
|
|
|
|
if (udata_req->DEST_length) {
|
|
_s_aligned_copy(tmpbuf, udata_req->DEST_length, tmpcnt,
|
|
(char *)to, (int *)&udata_req->DEST_offset);
|
|
tmpcnt += udata_req->DEST_length;
|
|
}
|
|
|
|
ctlbuf.len = tmpcnt;
|
|
|
|
databuf.len = len == 0 ? -1 : len;
|
|
databuf.buf = buf;
|
|
|
|
/*
|
|
* Calls to send data ( putmsg) can potentially
|
|
* block, for MT case, we drop the lock and enable signals here
|
|
* and aquire it back.
|
|
*/
|
|
MUTEX_UNLOCK_SIGMASK(&siptr->lock, *sndmaskp);
|
|
if (putmsg(s, &ctlbuf, &databuf, 0) >= 0) {
|
|
/* Not error */
|
|
retval = databuf.len == -1 ? 0 : databuf.len;
|
|
}
|
|
MUTEX_LOCK_SIGMASK(&siptr->lock, *sndmaskp);
|
|
goto sndout;
|
|
} else {
|
|
register struct T_data_req *data_req;
|
|
register int tmp;
|
|
register int tmpcnt;
|
|
register int error;
|
|
char *tmpbuf;
|
|
|
|
if (accrightslen) {
|
|
retval = send_cots_rights(siptr, buf, len,
|
|
accrights, accrightslen);
|
|
goto sndout;
|
|
}
|
|
|
|
/*
|
|
* Calls to send data (write or putmsg) can potentially
|
|
* block, for MT case, we drop the lock and enable signals here
|
|
* and acquire it back
|
|
*/
|
|
MUTEX_UNLOCK_SIGMASK(&siptr->lock, *sndmaskp);
|
|
|
|
data_req = (struct T_data_req *) ctlbuf.buf;
|
|
|
|
ctlbuf.len = sizeof (*data_req);
|
|
|
|
tmp = len;
|
|
tmpbuf = buf;
|
|
do {
|
|
if (siptr->udata.tsdusize != 0 || (flags & MSG_OOB)) {
|
|
/*
|
|
* transport provider supports TSDU concept
|
|
* (unlike TCP) or it is expedited data.
|
|
* In this case do the fragmentation
|
|
*/
|
|
if (flags & MSG_OOB)
|
|
data_req->PRIM_type = T_EXDATA_REQ;
|
|
else
|
|
data_req->PRIM_type = T_DATA_REQ;
|
|
if (tmp <= siptr->udata.tidusize) {
|
|
data_req->MORE_flag = 0;
|
|
tmpcnt = tmp;
|
|
} else {
|
|
data_req->MORE_flag = 1;
|
|
tmpcnt = siptr->udata.tidusize;
|
|
}
|
|
databuf.len = tmpcnt;
|
|
databuf.buf = tmpbuf;
|
|
error = putmsg(s, &ctlbuf, &databuf, 0);
|
|
} else {
|
|
/*
|
|
* transport provider does *not* support TSDU
|
|
* concept (e.g. TCP) and it is not expedited
|
|
* data. A perf. optimization is used.
|
|
*/
|
|
tmpcnt = tmp;
|
|
error = write(s, tmpbuf, tmpcnt);
|
|
if (error != tmpcnt && error >= 0) {
|
|
/* Amount that was actually sent */
|
|
tmpcnt = error;
|
|
}
|
|
}
|
|
if (error < 0) {
|
|
if (len == tmp) {
|
|
MUTEX_LOCK_SIGMASK(&siptr->lock,
|
|
*sndmaskp);
|
|
goto sndout;
|
|
} else {
|
|
errno = 0;
|
|
retval = len - tmp;
|
|
MUTEX_LOCK_SIGMASK(&siptr->lock,
|
|
*sndmaskp);
|
|
goto sndout;
|
|
}
|
|
}
|
|
tmp -= tmpcnt;
|
|
tmpbuf += tmpcnt;
|
|
} while (tmp);
|
|
retval = len - tmp;
|
|
MUTEX_LOCK_SIGMASK(&siptr->lock, *sndmaskp);
|
|
}
|
|
sndout:
|
|
if (didalloc)
|
|
free(ctlbuf.buf);
|
|
else
|
|
siptr->ctlbuf = ctlbuf.buf;
|
|
|
|
sndout_nobuf:
|
|
if (flags & MSG_DONTROUTE) {
|
|
int val;
|
|
|
|
val = 0;
|
|
(void) __setsockopt(siptr, SOL_SOCKET, SO_DONTROUTE,
|
|
(char *)&val, sizeof (val));
|
|
}
|
|
if (errno) {
|
|
if (errno == ENXIO || errno == EIO)
|
|
errno = EPIPE;
|
|
return (-1);
|
|
}
|
|
return (retval);
|
|
}
|
|
|
|
static int
|
|
send_clts_rights(siptr, buf, len, to, tolen, accrights, accrightslen)
|
|
struct _si_user *siptr;
|
|
char *buf;
|
|
int len;
|
|
register struct sockaddr *to;
|
|
register int tolen;
|
|
char *accrights;
|
|
int accrightslen;
|
|
{
|
|
int pipefd[2];
|
|
int *rights;
|
|
int retval;
|
|
int i;
|
|
struct sockaddr_un un_addr;
|
|
int addrlen;
|
|
sigset_t mask;
|
|
int saved_errno;
|
|
|
|
|
|
/*
|
|
* Make sending of access rigts atomic with respect to signals
|
|
*/
|
|
_s_blockallsignals(&mask);
|
|
|
|
/*
|
|
* Get a pipe.
|
|
*/
|
|
if (pipe(pipefd) < 0) {
|
|
_s_restoresigmask(&mask);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Link the transport.
|
|
*/
|
|
if (_s_do_ioctl(siptr->fd, (char *)to, tolen,
|
|
SI_TCL_LINK, NULL) == 0) {
|
|
retval = -1;
|
|
goto send_clts_done;
|
|
}
|
|
|
|
/*
|
|
* Send one end of the pipe.
|
|
*/
|
|
if (_ioctl(siptr->fd, I_SENDFD, pipefd[1]) < 0) {
|
|
retval = -1;
|
|
goto send_clts_done;
|
|
}
|
|
|
|
/*
|
|
* Send the fd's.
|
|
*/
|
|
SOCKDEBUG(siptr, "send_clts_rights: nmbr of fd's %d\n",
|
|
accrightslen/sizeof (int));
|
|
rights = (int *)accrights;
|
|
for (i = 0; i < accrightslen/sizeof (int); i++) {
|
|
if (_ioctl(pipefd[0], I_SENDFD, rights[i]) < 0) {
|
|
retval = -1;
|
|
goto send_clts_done;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send our address.
|
|
*/
|
|
(void) memset((caddr_t)&un_addr, 0, sizeof (un_addr));
|
|
addrlen = sizeof (un_addr);
|
|
if (__getsockname(siptr, (struct sockaddr *)&un_addr, &addrlen) < 0)
|
|
goto send_clts_done;
|
|
|
|
addrlen = sizeof (un_addr);
|
|
if (write(pipefd[0], (caddr_t)&un_addr, addrlen) != addrlen) {
|
|
errno = EPROTO;
|
|
goto send_clts_done;
|
|
}
|
|
|
|
/*
|
|
* Send the data.
|
|
*/
|
|
if (len) {
|
|
if (write(pipefd[0], buf, len) != len) {
|
|
retval = -1;
|
|
goto send_clts_done;
|
|
} else retval = len;
|
|
}
|
|
|
|
/*
|
|
* Unlink the transport.
|
|
*/
|
|
send_clts_done:
|
|
saved_errno = errno;
|
|
(void) _s_do_ioctl(siptr->fd, NULL, 0, SI_TCL_UNLINK, NULL);
|
|
(void) close(pipefd[0]);
|
|
(void) close(pipefd[1]);
|
|
|
|
_s_restoresigmask(&mask);
|
|
|
|
errno = saved_errno;
|
|
return (retval);
|
|
}
|
|
|
|
static
|
|
send_cots_rights(siptr, buf, len, accrights, accrightslen)
|
|
struct _si_user *siptr;
|
|
char *buf;
|
|
int len;
|
|
char *accrights;
|
|
int accrightslen;
|
|
{
|
|
int pipefd[2];
|
|
ulong intransit;
|
|
int *fds;
|
|
int retval;
|
|
int i;
|
|
sigset_t mask;
|
|
|
|
/*
|
|
* Make sending of access rigts atomic with respect to signals
|
|
*/
|
|
_s_blockallsignals(&mask);
|
|
/*
|
|
* Get a pipe.
|
|
*/
|
|
if (pipe(pipefd) < 0) {
|
|
_s_restoresigmask(&mask);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Ensure nothing in progress.
|
|
*/
|
|
intransit = 0;
|
|
for (;;) {
|
|
if (_s_do_ioctl(siptr->fd, (caddr_t)&intransit,
|
|
sizeof (intransit), SI_GETINTRANSIT, &i) == 0) {
|
|
retval = -1;
|
|
goto send_cots_done;
|
|
}
|
|
if (i != sizeof (intransit)) {
|
|
errno = EPROTO;
|
|
retval = -1;
|
|
goto send_cots_done;
|
|
}
|
|
if (intransit != 0)
|
|
(void) sleep(1);
|
|
else break;
|
|
}
|
|
|
|
/*
|
|
* Send pipe fd.
|
|
*/
|
|
if (_ioctl(siptr->fd, I_SENDFD, pipefd[1]) < 0) {
|
|
retval = -1;
|
|
goto send_cots_done;
|
|
}
|
|
|
|
/*
|
|
* Send the fd's.
|
|
*/
|
|
fds = (int *)accrights;
|
|
for (i = 0; i < accrightslen/sizeof (int); i++) {
|
|
if (_ioctl(pipefd[0], I_SENDFD, fds[i]) < 0) {
|
|
retval = -1;
|
|
goto send_cots_done;
|
|
}
|
|
}
|
|
/*
|
|
* Send the data.
|
|
*/
|
|
if (write(pipefd[0], buf, len) != len) {
|
|
errno = EPROTO;
|
|
retval = -1;
|
|
goto send_cots_done;
|
|
}
|
|
retval = len;
|
|
|
|
send_cots_done:
|
|
(void) close(pipefd[0]);
|
|
(void) close(pipefd[1]);
|
|
|
|
if (retval >= 0)
|
|
errno = 0;
|
|
_s_restoresigmask(&mask);
|
|
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* Copy data to output buffer and align it as in input buffer
|
|
* This is to ensure that if the user wants to align a network
|
|
* addr on a non-word boundry then it will happen.
|
|
*/
|
|
void
|
|
_s_aligned_copy(buf, len, init_offset, datap, rtn_offset)
|
|
register char *buf;
|
|
register char *datap;
|
|
register int *rtn_offset;
|
|
register int len;
|
|
register int init_offset;
|
|
{
|
|
*rtn_offset = ROUNDUP(init_offset) + ((unsigned int)datap&0x03);
|
|
(void) memcpy((caddr_t)(buf + *rtn_offset), datap, (int)len);
|
|
}
|
|
|
|
|
|
/*
|
|
* Max - return max between two ints
|
|
*/
|
|
int
|
|
_s_max(x, y)
|
|
int x;
|
|
int y;
|
|
{
|
|
if (x > y)
|
|
return (x);
|
|
else return (y);
|
|
}
|
|
|
|
int
|
|
_s_min(x, y)
|
|
int x;
|
|
int y;
|
|
{
|
|
if (x < y)
|
|
return (x);
|
|
else return (y);
|
|
}
|
|
|
|
|
|
/*
|
|
* Wait for T_OK_ACK
|
|
*/
|
|
int
|
|
_s_is_ok(siptr, type, ctlbufp)
|
|
register struct _si_user *siptr;
|
|
long type;
|
|
struct strbuf *ctlbufp;
|
|
{
|
|
|
|
register union T_primitives *pptr;
|
|
int flags;
|
|
int retval;
|
|
int fmode;
|
|
|
|
fmode = s_getfflags(siptr);
|
|
if (fmode & (O_NDELAY|O_NONBLOCK)) {
|
|
(void) _fcntl(siptr->fd, F_SETFL,
|
|
fmode & ~(O_NDELAY|O_NONBLOCK));
|
|
s_invalfflags(siptr);
|
|
}
|
|
|
|
ctlbufp->len = 0;
|
|
flags = RS_HIPRI;
|
|
|
|
while ((retval = getmsg(siptr->fd, ctlbufp, NULL, &flags)) < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Did I get entire message
|
|
*/
|
|
if (retval) {
|
|
errno = EIO;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Is ctl part large enough to determine type?
|
|
*/
|
|
if (ctlbufp->len < sizeof (long)) {
|
|
errno = EPROTO;
|
|
return (0);
|
|
}
|
|
|
|
if (fmode & (O_NDELAY|O_NONBLOCK)) {
|
|
(void) _fcntl(siptr->fd, F_SETFL, fmode);
|
|
s_invalfflags(siptr);
|
|
}
|
|
pptr = (union T_primitives *)ctlbufp->buf;
|
|
switch (pptr->type) {
|
|
case T_OK_ACK:
|
|
if ((ctlbufp->len < sizeof (struct T_ok_ack)) ||
|
|
(pptr->ok_ack.CORRECT_prim != type)) {
|
|
errno = EPROTO;
|
|
return (0);
|
|
}
|
|
return (1);
|
|
|
|
case T_ERROR_ACK:
|
|
if ((ctlbufp->len < sizeof (struct T_error_ack)) ||
|
|
(pptr->error_ack.ERROR_prim != type)) {
|
|
errno = EPROTO;
|
|
return (0);
|
|
}
|
|
if (pptr->error_ack.TLI_error == TSYSERR)
|
|
errno = pptr->error_ack.UNIX_error;
|
|
else errno = tlitosyserr(pptr->error_ack.TLI_error);
|
|
return (0);
|
|
|
|
default:
|
|
errno = EPROTO;
|
|
return (0);
|
|
}
|
|
}
|
|
/*
|
|
* Translate a TLI error into a system error as best we can.
|
|
*/
|
|
static ushort tli_errs[] = {
|
|
0, /* no error */
|
|
EADDRNOTAVAIL, /* TBADADDR */
|
|
ENOPROTOOPT, /* TBADOPT */
|
|
EACCES, /* TACCES */
|
|
EBADF, /* TBADF */
|
|
EADDRNOTAVAIL, /* TNOADDR */
|
|
EPROTO, /* TOUTSTATE */
|
|
EPROTO, /* TBADSEQ */
|
|
0, /* TSYSERR */
|
|
EPROTO, /* TLOOK */
|
|
EMSGSIZE, /* TBADDATA */
|
|
EMSGSIZE, /* TBUFOVFLW */
|
|
EPROTO, /* TFLOW */
|
|
EWOULDBLOCK, /* TNODATA */
|
|
EPROTO, /* TNODIS */
|
|
EPROTO, /* TNOUDERR */
|
|
EINVAL, /* TBADFLAG */
|
|
EPROTO, /* TNOREL */
|
|
EOPNOTSUPP, /* TNOTSUPPORT */
|
|
EPROTO, /* TSTATECHNG */
|
|
};
|
|
|
|
static int
|
|
tlitosyserr(terr)
|
|
register int terr;
|
|
{
|
|
if (terr > (sizeof (tli_errs) / sizeof (ushort)))
|
|
return (EPROTO);
|
|
else return (tli_errs[terr]);
|
|
}
|
|
|
|
|
|
/*
|
|
* sockmod ioctl
|
|
*/
|
|
int
|
|
_s_do_ioctl(s, buf, size, cmd, retlen)
|
|
register char *buf;
|
|
register int *retlen;
|
|
{
|
|
register int retval;
|
|
struct strioctl strioc;
|
|
|
|
strioc.ic_cmd = cmd;
|
|
strioc.ic_timout = -1;
|
|
strioc.ic_len = size;
|
|
strioc.ic_dp = buf;
|
|
|
|
if ((retval = _ioctl(s, I_STR, &strioc)) < 0) {
|
|
/*
|
|
* Map the errno as appropriate.
|
|
*/
|
|
switch (errno) {
|
|
case ENOTTY:
|
|
case ENODEV:
|
|
errno = ENOTSOCK;
|
|
break;
|
|
|
|
case EINVAL:
|
|
break;
|
|
|
|
case ENXIO:
|
|
errno = EPIPE;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
if (retval) {
|
|
if ((retval & 0xff) == TSYSERR)
|
|
errno = (retval >> 8) & 0xff;
|
|
else {
|
|
errno = tlitosyserr(retval & 0xff);
|
|
}
|
|
return (0);
|
|
}
|
|
if (retlen)
|
|
*retlen = strioc.ic_len;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Allocate buffers
|
|
* Assumes that the caller is holding siptr->lock.
|
|
* Also assumes caller has blocked signals (for safe
|
|
* malloc/free operations)
|
|
*/
|
|
static int
|
|
_s_alloc_bufs(siptr)
|
|
register struct _si_user *siptr;
|
|
{
|
|
unsigned size2;
|
|
register struct si_udata *udata = &siptr->udata;
|
|
|
|
size2 = sizeof (union T_primitives) + udata->addrsize + sizeof (long) +
|
|
udata->optsize + sizeof (long);
|
|
|
|
if (size2 == siptr->ctlsize)
|
|
return (0);
|
|
|
|
if (siptr->ctlbuf != NULL)
|
|
free(siptr->ctlbuf);
|
|
|
|
if ((siptr->ctlbuf = malloc(size2)) == NULL) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
|
|
siptr->ctlsize = size2;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Assumes caller has blocked signals (for safe
|
|
* malloc/free operations)
|
|
*/
|
|
int
|
|
_s_cbuf_alloc(siptr, retbuf)
|
|
register struct _si_user *siptr;
|
|
char **retbuf;
|
|
{
|
|
unsigned size2;
|
|
register struct si_udata *udata = &siptr->udata;
|
|
|
|
size2 = sizeof (union T_primitives) + udata->addrsize + sizeof (long) +
|
|
udata->optsize + sizeof (long);
|
|
|
|
if ((*retbuf = malloc(size2)) == NULL) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
return (size2);
|
|
}
|
|
|
|
void
|
|
_s_close(siptr)
|
|
register struct _si_user *siptr;
|
|
|
|
{
|
|
register int fd;
|
|
|
|
fd = siptr->fd;
|
|
_s_free(siptr);
|
|
(void) close(fd);
|
|
}
|
|
|
|
/*
|
|
* Like _s_close but does not close the file descriptor.
|
|
*/
|
|
static void
|
|
_s_free(struct _si_user *siptr)
|
|
{
|
|
register int fd;
|
|
sigset_t mask;
|
|
|
|
fd = siptr->fd;
|
|
MUTEX_LOCK_PROCMASK(&_si_userlock, mask);
|
|
free(siptr->ctlbuf);
|
|
(void) delete_silink(fd);
|
|
MUTEX_UNLOCK_PROCMASK(&_si_userlock, mask);
|
|
}
|
|
|
|
/*
|
|
* Link manipulation routines.
|
|
*
|
|
* NBUCKETS hash buckets are used to give fast
|
|
* access. The hashing is based on the descripter
|
|
* number. The number of hashbuckets assumes a
|
|
* maximum of about 100 descripters => a maximum
|
|
* of 10 entries per bucket.
|
|
*/
|
|
|
|
#define NBUCKETS 10
|
|
static struct _si_user *hash_bucket[NBUCKETS];
|
|
|
|
/*
|
|
* Allocates a new link and returns a pointer to it.
|
|
* Assumes that the caller is holding _si_userlock.
|
|
* and has signals blocked even when not linked to
|
|
* libthread
|
|
*/
|
|
static struct _si_user *
|
|
add_silink(s)
|
|
register int s;
|
|
{
|
|
register struct _si_user *siptr;
|
|
register struct _si_user *prevptr;
|
|
register struct _si_user *curptr;
|
|
register int x;
|
|
|
|
x = s % NBUCKETS;
|
|
if (hash_bucket[x] != NULL) {
|
|
/*
|
|
* Walk along the bucket looking for
|
|
* duplicate entry or the end.
|
|
*/
|
|
for (curptr = hash_bucket[x]; curptr != NULL;
|
|
curptr = curptr->next) {
|
|
if (curptr->fd == s) {
|
|
/*
|
|
* This can happen when the user has close(2)'ed
|
|
* a descripter and then been allocated it again
|
|
* via socket().
|
|
*
|
|
* We will re-use the existing _si_user struct
|
|
* in this case rather than allocating a
|
|
* new one. If there is a ctlbuf
|
|
* associated with the existing _si_user
|
|
* struct, we keep it for now and
|
|
* si_alloc_bufs checks that it has the correct
|
|
* size.
|
|
*
|
|
* Clear the flags fields. The udata field
|
|
* will be set by the caller.
|
|
*/
|
|
curptr->flags = 0;
|
|
s_invalfflags(curptr);
|
|
return (curptr);
|
|
}
|
|
prevptr = curptr;
|
|
}
|
|
if ((siptr = (struct _si_user *)malloc(sizeof (*siptr))) ==
|
|
NULL)
|
|
return (NULL);
|
|
|
|
/*
|
|
* Link in the new one.
|
|
*/
|
|
prevptr->next = siptr;
|
|
siptr->prev = prevptr;
|
|
siptr->next = NULL;
|
|
|
|
} else {
|
|
if ((siptr = (struct _si_user *)malloc(sizeof (*siptr))) ==
|
|
NULL)
|
|
return (NULL);
|
|
/*
|
|
* First entry.
|
|
*/
|
|
hash_bucket[x] = siptr;
|
|
siptr->next = NULL;
|
|
siptr->prev = NULL;
|
|
}
|
|
/* Initialize values. udata will be set by the caller. */
|
|
siptr->fd = s;
|
|
siptr->ctlsize = 0;
|
|
siptr->ctlbuf = NULL;
|
|
siptr->flags = 0;
|
|
s_invalfflags(siptr);
|
|
#ifdef _REENTRANT
|
|
mutex_init(&siptr->lock, USYNC_THREAD, NULL);
|
|
#endif /* _REENTRANT */
|
|
return (siptr);
|
|
}
|
|
|
|
/*
|
|
* Find a link by descriptor, holding _si_userlock while doing so.
|
|
*/
|
|
struct _si_user *
|
|
find_silink(s)
|
|
register int s;
|
|
{
|
|
struct _si_user *siptr;
|
|
sigset_t mask;
|
|
|
|
MUTEX_LOCK_SIGMASK(&_si_userlock, mask);
|
|
siptr = find_silink_unlocked(s);
|
|
MUTEX_UNLOCK_SIGMASK(&_si_userlock, mask);
|
|
|
|
return (siptr);
|
|
}
|
|
|
|
/*
|
|
* Find a link by descriptor
|
|
* Assumes that the caller is holding _si_userlock.
|
|
*/
|
|
static struct _si_user *
|
|
find_silink_unlocked(s)
|
|
register int s;
|
|
{
|
|
register struct _si_user *curptr;
|
|
register int x;
|
|
|
|
if (s >= 0) {
|
|
x = s % NBUCKETS;
|
|
if (hash_bucket[x] != NULL) {
|
|
/*
|
|
* Walk along the bucket looking for
|
|
* the descripter.
|
|
*/
|
|
for (curptr = hash_bucket[x]; curptr != NULL;
|
|
curptr = curptr->next) {
|
|
if (curptr->fd == s)
|
|
return (curptr);
|
|
}
|
|
}
|
|
}
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Assumes that the caller is holding _si_userlock.
|
|
* Also assumes all signals are blocked even when not
|
|
* linked to libthread
|
|
*/
|
|
static int
|
|
delete_silink(s)
|
|
register int s;
|
|
{
|
|
register struct _si_user *curptr;
|
|
register int x;
|
|
|
|
/*
|
|
* Find the link.
|
|
*/
|
|
x = s % NBUCKETS;
|
|
if (hash_bucket[x] != NULL) {
|
|
/*
|
|
* Walk along the bucket looking for
|
|
* the descripter.
|
|
*/
|
|
for (curptr = hash_bucket[x]; curptr != NULL;
|
|
curptr = curptr->next) {
|
|
if (curptr->fd == s) {
|
|
register struct _si_user *nextptr;
|
|
register struct _si_user *prevptr;
|
|
|
|
nextptr = curptr->next;
|
|
prevptr = curptr->prev;
|
|
if (prevptr)
|
|
prevptr->next = nextptr;
|
|
else
|
|
hash_bucket[x] = nextptr;
|
|
if (nextptr)
|
|
nextptr->prev = prevptr;
|
|
|
|
#ifdef _REENTRANT
|
|
mutex_destroy(&curptr->lock);
|
|
#endif /* _REENTRANT */
|
|
free(curptr);
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the number of bytes in the UNIX
|
|
* pathname, not including the null terminator
|
|
* (if any).
|
|
*/
|
|
int
|
|
_s_uxpathlen(un)
|
|
register struct sockaddr_un *un;
|
|
{
|
|
register int i;
|
|
|
|
for (i = 0; i < sizeof (un->sun_path); i++)
|
|
if (un->sun_path[i] == NULL)
|
|
return (i);
|
|
return (sizeof (un->sun_path));
|
|
}
|
|
|
|
int
|
|
_s_cpaddr(siptr, to, tolen, from, fromlen)
|
|
register struct _si_user *siptr;
|
|
register char *to;
|
|
register int tolen;
|
|
register char *from;
|
|
register int fromlen;
|
|
{
|
|
(void) memset(to, 0, tolen);
|
|
if (siptr->udata.sockparams.sp_family == AF_INET) {
|
|
if (tolen > sizeof (struct sockaddr_in))
|
|
tolen = sizeof (struct sockaddr_in);
|
|
} else if (tolen > fromlen)
|
|
tolen = fromlen;
|
|
(void) memcpy(to, from, _s_min(fromlen, tolen));
|
|
return (tolen);
|
|
}
|
|
|
|
void
|
|
_s_blockallsignals(maskp)
|
|
sigset_t *maskp;
|
|
{
|
|
sigset_t new;
|
|
|
|
(void) _sigfillset(&new);
|
|
(void) _sigprocmask(SIG_BLOCK, &new, maskp);
|
|
}
|
|
|
|
void
|
|
_s_restoresigmask(maskp)
|
|
sigset_t *maskp;
|
|
{
|
|
(void) _sigprocmask(SIG_SETMASK, maskp, (sigset_t *)NULL);
|
|
}
|