sockstr.c revision 7d6c035b71d7c7b33a49c71bff266bf8aa9e0c24
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/inttypes.h>
#include <sys/sysmacros.h>
#include <sys/kmem_impl.h>
#include <sys/socketvar.h>
#define _SUN_TPI_VERSION 2
int so_default_version = SOV_SOCKSTREAM;
#ifdef DEBUG
/* Set sockdebug to print debug messages when SO_DEBUG is set */
int sockdebug = 0;
/* Set sockprinterr to print error messages when SO_DEBUG is set */
int sockprinterr = 0;
/*
* Set so_default_options to SO_DEBUG is all sockets should be created
* with SO_DEBUG set. This is needed to get debug printouts from the
* socket() call itself.
*/
int so_default_options = 0;
#endif /* DEBUG */
#ifdef SOCK_TEST
/*
* Set to number of ticks to limit cv_waits for code coverage testing.
* Set to 1000 when SO_DEBUG is set to 2.
*/
#endif /* SOCK_TEST */
/*
* handle T_INFO_REQ messages.
*/
int so_no_tinfo = 0;
/*
* Timeout for getting a T_CAPABILITY_ACK - it is possible for a provider
* to simply ignore the T_CAPABILITY_REQ.
*/
static int tlitosyserr(int terr);
/*
* Convert a socket to a stream. Invoked when the illusory sockmod
* is popped from the stream.
* Change the stream head back to default operation without losing
* any messages (T_conn_ind's are moved to the stream head queue).
*/
int
{
int error = 0;
int rval;
/*
* Tell the transport below that sockmod is being popped
*/
&rval);
if (error != 0) {
"_SIOCSOCKFALLBACK failed\n", so));
goto exit;
}
struct T_conn_ind *conn_ind;
/*
* strsock_proto() has already verified the length of
* this message block.
*/
if (conn_ind->OPT_length == 0 &&
conn_ind->OPT_offset == 0)
continue;
/*
* Copy the message block because it is used
* elsewhere, too.
*/
goto exit;
}
/*
* Link the new message block into the queue
* and free the old one.
*/
}
/*
* Remove options added by TCP for accept fast-path.
*/
conn_ind->OPT_length = 0;
conn_ind->OPT_offset = 0;
}
}
/*
* Remove the hooks in the stream head to avoid queuing more
* packets in sockfs.
*/
/*
* Clear any state related to urgent data. Leave any T_EXDATA_IND
* on the queue - the behavior of urgent data after a switch is
* left undefined.
*/
/*
* Flush the T_DISCON_IND on so_discon_ind_mp.
*/
/*
* Move any queued T_CONN_IND messages to stream head queue.
*/
}
("so_sock2stream(%p): moving T_CONN_IND\n",
so));
/* Drop lock across put() */
}
exit:
return (error);
}
/*
* Covert a stream back to a socket. This is invoked when the illusory
* sockmod is pushed on a stream (where the stream was "created" by
* popping the illusory sockmod).
* This routine can not recreate the socket state (certain aspects of
* sockets can not be recreated by asking the transport for information).
* Thus this routine implicitly assumes that the socket is in an initial
* state (as if it was just created). It flushes any messages queued on the
* read queue to avoid dealing with e.g. TPI acks or T_exdata_ind messages.
*/
void
{
so->so_pushcnt = 0;
/*
* Set a permenent error to force any thread in sorecvmsg to
* return (and drop SOREADLOCKED). Clear the error once
* we have SOREADLOCKED.
* This makes a read sleeping during the I_PUSH of sockmod return
* EIO.
*/
/*
* Get the read lock before flushing data to avoid
* problems with the T_EXDATA_IND MSG_PEEK code in sorecvmsg.
*/
/*
* Flush everything on the read queue.
* This ensures that no T_CONN_IND remain and that no T_EXDATA_IND
* remain; those types of messages would confuse sockfs.
*/
/*
* Flush the T_DISCON_IND on so_discon_ind_mp.
*/
}
/*
* Install the hooks in the stream head.
*/
void
{
}
/*
* Remove the hooks in the stream head.
*/
static void
{
/*
* Leave read behavior as it would have been for a normal
* stream i.e. a read of an M_PROTO will fail.
*/
}
/*
* Initialize the streams side of a socket including
* T_info_req/ack processing. If tso is not NULL its values are used thereby
* avoiding the T_INFO_REQ.
*/
int
{
int error;
/* Preallocate an unbind_req message */
#ifdef DEBUG
#endif /* DEBUG */
/*
* The T_CAPABILITY_REQ should be the first message sent down because
* at least TCP has a fast-path for this which avoids timeouts while
* waiting for the T_CAPABILITY_ACK under high system load.
*/
if (error)
return (error);
} else {
/* the following do_tcapability may update so->so_mode */
if (error)
return (error);
}
}
/*
* If the addr_size is 0 we treat it as already bound
* and connected. This is used by the routing socket.
* We set the addr_size to something to allocate a the address
* structures.
*/
if (so->so_addr_size == 0) {
/* Address size can vary with address families. */
so->so_addr_size =
(t_scalar_t)sizeof (struct sockaddr_in6);
else
so->so_addr_size =
(t_scalar_t)sizeof (struct sockaddr_in);
}
/*
* Allocate the addresses.
*/
+ so->so_laddr_maxlen);
/*
* Initialize AF_UNIX related fields.
*/
}
/*
* bytes.
*/
stp->sd_qn_minpsz = 0;
return (0);
}
static void
{
switch (tia->CURRENT_state) {
case TS_UNBND:
break;
case TS_IDLE:
so->so_laddr_len = 0;
break;
case TS_DATA_XFER:
so->so_laddr_len = 0;
so->so_faddr_len = 0;
break;
}
/*
* Heuristics for determining the socket mode flags
* (SM_ATOMIC, SM_CONNREQUIRED, SM_ADDR, SM_FDPASSING,
* and SM_EXDATA, SM_OPTDATA, and SM_BYTESTREAM)
* from the info ack.
*/
} else {
}
/* Semantics are to discard tail end of messages */
}
/* MAXPATHLEN + soun_family + nul termination */
sizeof (short) + 1);
}
/*
* Make it into a byte-stream transport.
* SOCK_SEQPACKET sockets are unchanged.
*/
so->so_tsdu_size = 0;
}
/*
* Logic extracted from sockmod - have to pick some max address
* length in order to preallocate the addresses.
*/
}
if (so->so_tsdu_size == 0)
}
static int
{
/* Consistency checks */
return (EPROTO);
}
return (EPROTO);
}
return (EPROTO);
}
("AF_INET must have sockaddr_in address length. Got %d\n",
so->so_addr_size));
return (EMSGSIZE);
}
("AF_INET6 must have sockaddr_in6 address length. Got %d\n",
so->so_addr_size));
return (EMSGSIZE);
}
"tinfo: serv %d tsdu %d, etsdu %d, addr %d, opt %d, tidu %d\n",
so->so_tidu_size));
return (0);
}
/*
* Send down T_info_req and wait for the ack.
* Record interesting T_info_ack values in the sonode.
*/
static int
{
struct T_info_req tir;
int error;
if (so_no_tinfo) {
so->so_addr_size = 0;
return (0);
}
/* Send T_INFO_REQ */
sizeof (struct T_info_req) + sizeof (struct T_info_ack),
return (ENOBUFS);
}
/* T_INFO_REQ has to be M_PCPROTO */
if (error) {
return (error);
}
/* Wait for T_INFO_ACK */
return (error);
}
return (check_tinfo(so));
}
/*
* Send down T_capability_req and wait for the ack.
* Record interesting T_capability_ack values in the sonode.
*/
static int
{
struct T_capability_req tcr;
struct T_capability_ack *tca;
int error;
if (so_no_tinfo) {
so->so_addr_size = 0;
return (0);
}
/* Send T_CAPABILITY_REQ */
sizeof (struct T_capability_req) + sizeof (struct T_capability_ack),
return (ENOBUFS);
}
/* T_CAPABILITY_REQ should be M_PCPROTO here */
if (error) {
return (error);
}
/* Wait for T_CAPABILITY_ACK */
/*
* If the T_CAPABILITY_REQ timed out and then a
* T_INFO_REQ gets a protocol error, most likely
* the capability was slow (vs. unsupported). Return
* ENOSR for this case as a best guess.
*/
}
}
return (0);
}
}
if (cap_bits1 & TC1_ACCEPTOR_ID) {
}
return (check_tinfo(so));
return (0);
}
/*
* Retrieve and clear the socket error.
*/
int
{
int error;
return (error);
}
/*
* This routine is registered with the stream head to retrieve read
* side errors.
* It does not clear the socket error for a peeking read side operation.
* It the error is to be cleared it sets *clearerr.
*/
int
{
int error;
if (ispeek) {
*clearerr = 0;
} else {
*clearerr = 1;
}
return (error);
}
/*
* This routine is registered with the stream head to retrieve write
* side errors.
* It does not clear the socket error for a peeking read side operation.
* It the error is to be cleared it sets *clearerr.
*/
int
{
int error;
*clearerr = 0;
} else {
if (ispeek) {
*clearerr = 0;
} else {
*clearerr = 1;
}
}
return (error);
}
/*
* Set a nonpersistent read and write error on the socket.
* Used when there is a T_uderror_ind for a connected socket.
* The caller also needs to call strsetrerror and strsetwerror
* after dropping the lock.
*/
void
{
}
void
{
}
void
{
}
/*
* The caller also needs to call strsetrerror, strsetwerror and strseteof.
*/
void
{
}
}
/*
* For connected AF_UNIX SOCK_DGRAM sockets when the peer closes.
* Does not affect write side.
* The caller also has to call strsetrerror.
*/
static void
{
}
/*
* Can no longer send.
* Caller must also call strsetwerror.
*
* We mark the peer address as no longer valid for getpeername, but
* leave it around for so_unix_close to notify the peer (that
* transport has no addressing held at that layer).
*/
void
{
}
/*
* The caller must call strseteof(,1) as well as this routine
* to change the socket state.
*/
void
{
}
/*
* The caller has sent down a "request_prim" primitive and wants to wait for
* an ack ("ack_prim") or an T_ERROR_ACK for it.
* The specified "ack_prim" can be a T_OK_ACK.
*
* Assumes that all the TPI acks are M_PCPROTO messages.
*
* Note that the socket is single-threaded (using so_lock_single)
* for all operations that generate TPI ack messages. Since
* only TPI ack messages are M_PCPROTO we should never receive
* anything except either the ack we are expecting or a T_ERROR_ACK
* for the same primitive.
*/
int
{
union T_primitives *tpr;
int error;
if (error)
return (error);
return (EPROTO);
}
/*
* Did we get the primitive that we were asking for?
* For T_OK_ACK we also check that it matches the request primitive.
*/
/* Found what we are looking for */
return (0);
}
/* Too short */
return (EPROTO);
}
/* Error to the primitive we were looking for */
} else {
}
error));
return (error);
}
/*
* Wrong primitive or T_ERROR_ACK for the wrong primitive
*/
#ifdef DEBUG
ack_prim, request_prim));
} else {
("unexpected primitive %d, expected %d for %d\n",
}
#endif /* DEBUG */
return (EPROTO);
}
/*
* Wait for a T_OK_ACK for the specified primitive.
*/
int
{
int error;
if (error)
return (error);
return (0);
}
/*
* Queue a received TPI ack message on so_ack_mp.
*/
void
{
"sockfs: received unexpected M_PROTO TPI ack. Prim %d\n",
return;
}
}
}
/*
* Wait for a TPI ack ignoring signals and errors.
*/
int
{
#ifdef SOCK_TEST
if (wait == 0 && sock_test_timelimit != 0)
#endif
if (wait != 0) {
/*
* Only wait for the time limit.
*/
now) == -1) {
return (ETIME);
}
}
else
}
#ifdef DEBUG
{
union T_primitives *tpr;
}
#endif /* DEBUG */
return (0);
}
/*
* Queue a received T_CONN_IND message on so_conn_ind_head/tail.
*/
void
{
"sockfs: received unexpected M_PCPROTO T_CONN_IND\n");
return;
}
} else {
}
/* Wakeup a single consumer of the T_CONN_IND */
}
/*
* Wait for a T_CONN_IND.
* Don't wait if nonblocking.
* Accept signals and socket errors.
*/
int
{
int error = 0;
if (error) {
return (error);
}
}
error = EWOULDBLOCK;
goto done;
}
goto done;
}
goto check_error;
}
}
done:
return (error);
}
/*
* Flush a T_CONN_IND matching the sequence number from the list.
* Return zero if found; non-zero otherwise.
* This is called very infrequently thus it is ok to do a linear search.
*/
int
{
struct T_conn_ind *tci;
("t_discon_ind: found T_CONN_IND %d\n", seqno));
/* Deleting last? */
}
/* Deleting first */
} else {
}
} else {
}
/*
* T_KSSL_PROXY_CONN_IND may carry a handle for
* an SSL context, and needs to be released.
*/
sizeof (kssl_ctx_t));
}
return (0);
}
}
return (-1);
}
/*
* Wait until the socket is connected or there is an error.
* fmode should contain any nonblocking flags. nosig should be
* set if the caller does not want the wait to be interrupted by a signal.
*/
int
{
int error;
return (EINPROGRESS);
if (nosig)
/*
* Return EINTR and let the application use
* nonblocking techniques for detecting when
* the connection has been established.
*/
return (EINTR);
}
}
return (error);
}
/*
* Could have received a T_ORDREL_IND or a T_DISCON_IND with
* zero errno. Or another thread could have consumed so_error
* e.g. by calling read.
*/
return (error);
}
return (0);
}
/*
* Handle the signal generation aspect of urgent data.
*/
static void
{
/*
* Signal has already been generated once for this
* urgent "event". However, since TCP can receive updated
* urgent pointers we still generate a signal.
*/
if (extrasig) {
*pollwakeups |= POLLRDBAND;
}
return;
}
so->so_oobsigcnt++;
/*
*/
/*
* New urgent data on the way so forget about any old
* urgent data.
*/
}
*pollwakeups |= POLLRDBAND;
}
/*
* Handle the processing of the T_EXDATA_IND with urgent data.
* Returns the T_EXDATA_IND if it should be queued on the read queue.
*/
/* ARGSUSED2 */
static mblk_t *
{
/*
* Set MSGMARK for SIOCATMARK.
*/
return (mp);
}
/*
* Handle the processing of the actual urgent data.
* Returns the data mblk if it should be queued on the read queue.
*/
static mblk_t *
{
/*
* For OOBINLINE we keep the data in the T_EXDATA_IND.
* Otherwise we store it in so_oobmsg.
*/
} else {
*pollwakeups |= POLLRDBAND;
}
return (mp);
}
/*
* Caller must hold the mutex.
* For delayed processing, save the T_DISCON_IND received
* from below on so_discon_ind_mp.
* When the message is processed the framework will call:
* (*func)(so, mp);
*/
static void
{
/*
* Discard new T_DISCON_IND if we have already received another.
* Currently the earlier message can either be on so_discon_ind_mp
* or being processed.
*/
"sockfs: received unexpected additional T_DISCON_IND\n");
return;
}
}
/*
* Caller must hold the mutex and make sure that either SOLOCKED
* or SOASYNC_UNBIND is set. Called from so_unlock_single().
* Perform delayed processing of T_DISCON_IND message on so_discon_ind_mp.
* Need to ensure that strsock_proto() will not end up sleeping for
* SOASYNC_UNBIND, while executing this function.
*/
void
{
/* Process T_DISCON_IND on so_discon_ind_mp */
/*
* This (*func) is supposed to generate a message downstream
* and we need to have a flag set until the corresponding
* upstream message reaches stream head.
* When processing T_DISCON_IND in strsock_discon_ind
* we hold SOASYN_UNBIND when sending T_UNBIND_REQ down and
* drop the flag after we get the ACK in strsock_proto.
*/
}
}
/*
* Caller must hold the mutex.
* Remove the T_DISCON_IND on so_discon_ind_mp.
*/
void
{
/*
* Remove T_DISCON_IND mblk at so_discon_ind_mp.
*/
}
}
/*
* Caller must hold the mutex.
*
* This function is used to process the T_DISCON_IND message. It does
* immediate processing when called from strsock_proto and delayed
* processing of discon_ind saved on so_discon_ind_mp when called from
* so_drain_discon_ind. When a T_DISCON_IND message is saved in
* so_discon_ind_mp for delayed processing, this function is registered
* as the callback function to process the message.
*
* SOASYNC_UNBIND should be held in this function, during the non-blocking
* unbind operation, and should be released only after we receive the ACK
* in strsock_proto, for the T_UNBIND_REQ sent here. Since SOLOCKED is not set,
* no TPI messages would be sent down at this time. This is to prevent M_FLUSH
* sent from either this function or tcp_unbind(), flushing away any TPI
* message that is being sent down and stays in a lower module's queue.
*
* This function drops so_lock and grabs it again.
*/
static void
{
union T_primitives *tpr;
struct T_unbind_req *ubr;
int error;
/*
* Not a listener
*/
/*
* This assumes that the name space for DISCON_reason
* is the errno name space.
*/
/*
* Unbind with the transport without blocking.
* If we've already received a T_DISCON_IND do not unbind.
*
* If there is no preallocated unbind message, we have already
* unbound with the transport
*
* If the socket is not bound, no need to unbind.
*/
} else {
/*
* Is another T_DISCON_IND being processed.
*/
/*
* Make strsock_proto ignore T_OK_ACK and T_ERROR_ACK for
* this unbind. Set SOASYNC_UNBIND. This should be cleared
* only after we receive the ACK in strsock_proto.
*/
/*
* Send down T_UNBIND_REQ ignoring flow control.
* XXX Assumes that MSG_IGNFLOW implies that this thread
* does not run service procedures.
*/
/*
* Flush the read and write side (except stream head read queue)
* and send down T_UNBIND_REQ.
*/
/* LINTED - warning: statement has no consequent: if */
if (error) {
}
}
/*
* strseteof takes care of read side wakeups,
* pollwakeups, and signals.
*/
/*
* Wake sleeping write
*/
}
/*
* strsendsig can handle multiple signals with a
* single call. Send SIGPOLL for S_OUTPUT event.
*/
}
/*
* This routine is registered with the stream head to receive M_PROTO
* and M_PCPROTO messages.
*
* Returns NULL if the message was consumed.
* Returns an mblk to make that mblk be processed (and queued) by the stream
* head.
*
* Sets the return parameters (*wakeups, *firstmsgsigs, *allmsgsigs, and
* *pollwakeups) for the stream head to take action on. Note that since
* sockets always deliver SIGIO for every new piece of data this routine
* never sets *firstmsgsigs; any signals are returned in *allmsgsigs.
*
* This routine handles all data related TPI messages independent of
* the type of the socket i.e. it doesn't care if T_UNITDATA_IND message
* arrive on a SOCK_STREAM.
*/
static mblk_t *
{
union T_primitives *tpr;
/* Set default return values */
/* The message is too short to even contain the primitive */
"sockfs: Too short TPI message received. Len = %ld\n",
return (NULL);
}
/* The read pointer is not aligned correctly for TPI */
"sockfs: Unaligned TPI message received. rptr = %p\n",
return (NULL);
}
case T_DATA_IND:
"sockfs: Too short T_DATA_IND. Len = %ld\n",
return (NULL);
}
/*
* Ignore zero-length T_DATA_IND messages. These might be
* generated by some transports.
* This is needed to prevent read (which skips the M_PROTO
* part) to unexpectedly return 0 (or return EWOULDBLOCK
* that data is available).
*/
("strsock_proto: zero length T_DATA_IND\n"));
return (NULL);
}
return (mp);
case T_UNITDATA_IND: {
void *addr;
"sockfs: Too short T_UNITDATA_IND. Len = %ld\n",
return (NULL);
}
/* Is this is not a connected datagram socket? */
/*
* Not a connected datagram socket. Look for
* the SO_UNIX_CLOSE option. If such an option is found
* discard the message (since it has no meaning
* unless connected).
*/
tudi->OPT_length != 0) {
void *opt;
"sockfs: T_unidata_ind with "
return (NULL);
}
return (NULL);
}
}
#ifdef C2_AUDIT
if (audit_active)
mp, 0);
#endif /* C2_AUDIT */
return (mp);
}
/*
* A connect datagram socket. For AF_INET{,6} we verify that
* the source address matches the "connected to" address.
* The semantics of AF_UNIX sockets is to not verify
* the source address.
* Note that this source address verification is transport
* specific. Thus the real fix would be to extent TPI
* to allow T_CONN_REQ messages to be send to connectionless
* transport providers and always let the transport provider
* do whatever filtering is needed.
*
* The verification/filtering semantics for transports
* other than AF_INET and AF_UNIX are unknown. The choice
* would be to either filter using bcmp or let all messages
* get through. This code does not filter other address
* families since this at least allows the application to
* work around any missing filtering.
*
* That would require passing e.g. a T_DISCON_REQ to UDP
* when the socket becomes unconnected.
*/
/*
* The alignment restriction is really to strict but
* we want enough alignment to inspect the fields of
* a sockaddr_in.
*/
"sockfs: T_unidata_ind with invalid "
return (NULL);
}
/*
* For AF_INET we allow wildcarding both sin_addr
* and sin_port.
*/
/* Prevent so_faddr_sa from changing while accessed */
(socklen_t)sizeof (struct sockaddr_in));
if (addrlen !=
(t_uscalar_t)sizeof (struct sockaddr_in) ||
#ifdef DEBUG
("sockfs: T_UNITDATA_IND mismatch: %s",
addrlen)));
#endif /* DEBUG */
return (NULL);
}
/*
* For AF_INET6 we allow wildcarding both sin6_addr
* and sin6_port.
*/
/* Prevent so_faddr_sa from changing while accessed */
(socklen_t)sizeof (struct sockaddr_in6));
/* XXX could we get a mapped address ::ffff:0.0.0.0 ? */
if (addrlen !=
(t_uscalar_t)sizeof (struct sockaddr_in6) ||
#ifdef DEBUG
("sockfs: T_UNITDATA_IND mismatch: %s",
addrlen)));
#endif /* DEBUG */
return (NULL);
}
tudi->OPT_length != 0) {
/*
* Attempt to extract AF_UNIX
* SO_UNIX_CLOSE indication from options.
*/
void *opt;
"sockfs: T_unidata_ind with invalid "
return (NULL);
}
/*
* If we received a unix close indication mark the
* socket and discard this message.
*/
return (NULL);
}
}
return (mp);
}
case T_OPTDATA_IND: {
"sockfs: Too short T_OPTDATA_IND. Len = %ld\n",
return (NULL);
}
/*
* Allow zero-length messages carrying options.
* This is used when carrying the SO_UNIX_CLOSE option.
*/
tdi->OPT_length != 0) {
/*
* Attempt to extract AF_UNIX close indication
* from the options. Ignore any other options -
* those are handled once the message is removed
* from the queue.
* The close indication message should not carry data.
*/
void *opt;
"sockfs: T_optdata_ind with invalid "
return (NULL);
}
/*
* If we received a close indication mark the
* socket and discard this message.
*/
return (NULL);
}
}
return (mp);
}
case T_EXDATA_IND: {
"sockfs: Too short T_EXDATA_IND. Len = %ld\n",
return (NULL);
}
/*
* Ignore zero-length T_EXDATA_IND messages. These might be
* generated by some transports.
*
* This is needed to prevent read (which skips the M_PROTO
* part) to unexpectedly return 0 (or return EWOULDBLOCK
* that data is available).
*/
("T_EXDATA_IND(%p): counts %d/%d state %s\n",
("strsock_proto: zero length T_EXDATA_IND\n"));
return (NULL);
}
/*
* Split into the T_EXDATA_IND and the M_DATA part.
* We process these three pieces separately:
* signal generation
* handling T_EXDATA_IND
* handling M_DATA component
*/
/*
* Pass the T_EXDATA_IND and the M_DATA back separately
* by using b_next linkage. (The stream head will queue any
* b_next linked messages separately.) This is needed
* since MSGMARK applies to the last by of the message
* hence we can not have any M_DATA component attached
* to the marked T_EXDATA_IND. Note that the stream head
* will not consolidate M_DATA messages onto an MSGMARK'ed
* message in order to preserve the constraint that
* the T_EXDATA_IND always is a separate message.
*/
#ifdef DEBUG
("after outofline T_EXDATA_IND(%p): "
"counts %d/%d poll 0x%x sig 0x%x state %s\n",
} else {
("after inline T_EXDATA_IND(%p): "
"counts %d/%d poll 0x%x sig 0x%x state %s\n",
}
#endif /* DEBUG */
return (mp);
}
case T_CONN_CON: {
struct T_conn_con *conn_con;
void *addr;
/*
* Verify the state, update the state to ISCONNECTED,
* record the potentially new address in the message,
* and drop the message.
*/
"sockfs: Too short T_CONN_CON. Len = %ld\n",
return (NULL);
}
return (NULL);
}
/*
* Allow the address to be of different size than sent down
* in the T_CONN_REQ as long as it doesn't exceed the maxlen.
* For AF_UNIX require the identical length.
*/
"sockfs: T_conn_con with different "
"length %u/%d\n",
/*
* strseteof takes care of read side wakeups,
* pollwakeups, and signals.
*/
*allmsgsigs = S_OUTPUT;
*pollwakeups = POLLOUT;
return (NULL);
}
"sockfs: T_conn_con with invalid "
/*
* strseteof takes care of read side wakeups,
* pollwakeups, and signals.
*/
*allmsgsigs = S_OUTPUT;
*pollwakeups = POLLOUT;
return (NULL);
}
/*
* Save for getpeername.
*/
}
/* Wakeup anybody sleeping in sowaitconnected */
/*
* The socket is now available for sending data.
*/
*allmsgsigs = S_OUTPUT;
*pollwakeups = POLLOUT;
return (NULL);
}
/*
* Extra processing in case of an SSL proxy, before queuing or
* forwarding to the fallback endpoint
*/
case T_SSL_PROXY_CONN_IND:
case T_CONN_IND:
/*
* Verify the min size and queue the message on
* the so_conn_ind_head/tail list.
*/
"sockfs: Too short T_CONN_IND. Len = %ld\n",
return (NULL);
}
#ifdef C2_AUDIT
if (audit_active)
#endif /* C2_AUDIT */
"sockfs: T_conn_ind on non-listening socket\n");
return (NULL);
}
/* No context: need to fall back */
/*
* No fallback: the remote will timeout and
* disconnect.
*/
return (NULL);
}
return (NULL);
}
return (NULL);
case T_ORDREL_IND:
"sockfs: Too short T_ORDREL_IND. Len = %ld\n",
return (NULL);
}
/*
* Some providers send this when not fully connected.
* SunLink X.25 needs to retrieve disconnect reason after
* disconnect for compatibility. It uses T_ORDREL_IND
* instead of T_DISCON_IND so that it may use the
* endpoint after a connect failure to retrieve the
* reason using an ioctl. Thus we explicitly clear
* SS_ISCONNECTING here for SunLink X.25.
* This is a needed TPI violation.
*/
/*
* strseteof takes care of read side wakeups,
* pollwakeups, and signals.
*/
return (NULL);
case T_DISCON_IND:
"sockfs: Too short T_DISCON_IND. Len = %ld\n",
return (NULL);
}
/*
* This is a listener. Look for a queued T_CONN_IND
* with a matching sequence number and remove it
* from the list.
* It is normal to not find the sequence number since
* the soaccept might have already dequeued it
* (in which case the T_CONN_RES will fail with
* TBADSEQ).
*/
return (0);
}
/*
* Not a listener
*
* If SS_CANTRCVMORE for AF_UNIX ignore the discon_reason.
* Such a discon_ind appears when the peer has first done
* a shutdown() followed by a close() in which case we just
* want to record socantsendmore.
* In this case sockfs first receives a T_ORDREL_IND followed
* by a T_DISCON_IND.
* Note that for other transports (e.g. TCP) we need to handle
* the discon_ind in this case since it signals an error.
*/
/*
* Set these variables for caller to process them.
* For the else part where T_DISCON_IND is processed,
* this will be done in the function being called
* (strsock_discon_ind())
*/
*allmsgsigs = S_OUTPUT;
*pollwakeups = POLLOUT;
/*
* Deferred processing of T_DISCON_IND
*/
} else {
/*
* Process T_DISCON_IND now
*/
}
return (NULL);
case T_UDERROR_IND: {
void *addr;
int error;
"sockfs: Too short T_UDERROR_IND. Len = %ld\n",
return (NULL);
}
/* Ignore on connection-oriented transports */
eprintsoline(so, 0);
"sockfs: T_uderror_ind on connection-oriented "
"transport\n");
return (NULL);
}
"sockfs: T_uderror_ind with invalid "
return (NULL);
}
/* Verify source address for connected socket. */
void *faddr;
case AF_INET: {
/* Compare just IP address and port */
if (addrlen == sizeof (struct sockaddr_in) &&
break;
}
case AF_INET6: {
/* Compare just IP address and port. Not flow */
if (addrlen == sizeof (struct sockaddr_in6) &&
break;
}
case AF_UNIX:
break;
default:
break;
}
if (!match) {
#ifdef DEBUG
("sockfs: T_UDERR_IND mismatch: %s - ",
addrlen)));
so->so_faddr_len)));
#endif /* DEBUG */
return (NULL);
}
/*
* Make the write error nonpersistent. If the error
* is zero we use ECONNRESET.
* This assumes that the name space for ERROR_type
* is the errno name space.
*/
if (tudi->ERROR_type != 0)
else
error = ECONNRESET;
return (NULL);
}
/*
* If the application asked for delayed errors
* record the T_UDERROR_IND so_eaddr_mp and the reason in
* so_delayed_error for delayed error posting. If the reason
* is zero use ECONNRESET.
* Note that delayed error indications do not make sense for
* AF_UNIX sockets since sendto checks that the destination
* address is valid at the time of the sendto.
*/
return (NULL);
}
if (tudi->ERROR_type != 0)
else
error = ECONNRESET;
return (NULL);
}
case T_ERROR_ACK:
("strsock_proto: T_ERROR_ACK for %d, error %d/%d\n",
"sockfs: Too short T_ERROR_ACK. Len = %ld\n",
return (NULL);
}
/*
* Check if we were waiting for the async message
*/
return (NULL);
}
return (NULL);
case T_OK_ACK:
"sockfs: Too short T_OK_ACK. Len = %ld\n",
return (NULL);
}
/*
* Check if we were waiting for the async message
*/
("strsock_proto: T_OK_ACK async unbind\n"));
return (NULL);
}
return (NULL);
case T_INFO_ACK:
"sockfs: Too short T_INFO_ACK. Len = %ld\n",
return (NULL);
}
return (NULL);
case T_CAPABILITY_ACK:
/*
* A T_capability_ack need only be large enough to hold
* the PRIM_type and CAP_bits1 fields; checking for anything
* larger might reject a correct response from an older
* provider.
*/
"sockfs: Too short T_CAPABILITY_ACK. Len = %ld\n",
return (NULL);
}
return (NULL);
case T_BIND_ACK:
"sockfs: Too short T_BIND_ACK. Len = %ld\n",
return (NULL);
}
return (NULL);
case T_OPTMGMT_ACK:
"sockfs: Too short T_OPTMGMT_ACK. Len = %ld\n",
return (NULL);
}
return (NULL);
default:
#ifdef DEBUG
"sockfs: unknown TPI primitive %d received\n",
#endif /* DEBUG */
return (NULL);
}
}
/*
* This routine is registered with the stream head to receive other
* (non-data, and non-proto) messages.
*
* Returns NULL if the message was consumed.
* Returns an mblk to make that mblk be processed by the stream head.
*
* Sets the return parameters (*wakeups, *firstmsgsigs, *allmsgsigs, and
* *pollwakeups) for the stream head to take action on.
*/
static mblk_t *
{
/* Set default return values */
case M_PCSIG:
/*
* This assumes that an M_PCSIG for the urgent data arrives
* before the corresponding T_EXDATA_IND.
*
* Note: Just like in SunOS 4.X and 4.4BSD a poll will be
* awoken before the urgent data shows up.
* For OOBINLINE this can result in select returning
* only exceptions as opposed to except|read.
*/
("SIGURG(%p): counts %d/%d state %s\n",
("after SIGURG(%p): counts %d/%d "
" poll 0x%x sig 0x%x state %s\n",
}
return (NULL);
case M_SIG:
case M_HANGUP:
case M_UNHANGUP:
case M_ERROR:
/* M_ERRORs etc are ignored */
return (NULL);
case M_FLUSH:
/*
* Do not flush read queue. If the M_FLUSH
* arrives because of an impending T_discon_ind
* we still have to keep any queued data - this is part of
* socket semantics.
*/
return (mp);
}
return (NULL);
default:
return (mp);
}
}
/* Register to receive signals for certain events */
int
{
/*
* Note that SOLOCKED will be set except for the call from soaccept().
*/
&rval));
}
/* Register for events matching the SS_ASYNC flag */
int
{
}
/* Change the SS_ASYNC flag, and update signal delivery if needed */
int
{
int error;
if (error)
return (error);
}
return (0);
}
/*
* any existing one. If passed zero, just clear the existing one.
*/
int
{
int error;
/*
* Change socket process (group).
*
* strioctl (via so_set_asyncsigs) will perform permission check and
* also keep a PID_HOLD to prevent the pid from being reused.
*/
if (pgrp != 0) {
if (error != 0) {
goto bad;
}
}
if (error != 0) {
error = 0;
}
}
return (0);
bad:
return (error);
}
/*
* Translate a TLI(/XTI) error into a system error as best we can.
*/
static const int tli_errs[] = {
0, /* no error */
EADDRNOTAVAIL, /* TBADADDR */
ENOPROTOOPT, /* TBADOPT */
EACCES, /* TACCES */
EBADF, /* TBADF */
EADDRNOTAVAIL, /* TNOADDR */
EPROTO, /* TOUTSTATE */
ECONNABORTED, /* TBADSEQ */
0, /* TSYSERR - will never get */
EPROTO, /* TLOOK - should never be sent by transport */
EMSGSIZE, /* TBADDATA */
EMSGSIZE, /* TBUFOVFLW */
EPROTO, /* TFLOW */
EWOULDBLOCK, /* TNODATA */
EPROTO, /* TNODIS */
EPROTO, /* TNOUDERR */
EINVAL, /* TBADFLAG */
EPROTO, /* TNOREL */
EOPNOTSUPP, /* TNOTSUPPORT */
EPROTO, /* TSTATECHNG */
/* following represent error namespace expansion with XTI */
EPROTO, /* TNOSTRUCTYPE - never sent by transport */
EPROTO, /* TBADNAME - never sent by transport */
EPROTO, /* TBADQLEN - never sent by transport */
EADDRINUSE, /* TADDRBUSY */
EBADF, /* TINDOUT */
EBADF, /* TPROVMISMATCH */
EBADF, /* TRESQLEN */
EBADF, /* TRESADDR */
EPROTO, /* TQFULL - never sent by transport */
EPROTO, /* TPROTO */
};
static int
tlitosyserr(int terr)
{
return (EPROTO);
else
}