/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This module implements the services provided by the rlogin daemon
* after the connection is set up. Mainly this means responding to
* interrupts and window size changes. It begins operation in "disabled"
* state, and sends a T_DATA_REQ to the daemon to indicate that it is
* in place and ready to be enabled. The daemon can then know when all
* data which sneaked passed rlmod (before it was pushed) has been received.
* The daemon may process this data, or send data back to be inserted in
* the read queue at the head with the RL_IOC_ENABLE ioctl.
*/
#include <sys/byteorder.h>
#include <sys/cryptmod.h>
extern struct streamtab rloginmodinfo;
"rlmod",
};
/*
* Module linkage information for the kernel.
*/
"rloginmod module",
&fsw
};
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
struct rlmod_info; /* forward reference for function prototype */
static mblk_t *make_expmblk(char);
static void rlmod_timer(void *);
static void rlmod_buffer(void *);
/*
* Stream module data structure definitions.
* generally pushed onto tcp by rlogin daemon
*
*/
RLMOD_ID, /* module id number */
"rlmod", /* module name */
0, /* minimum packet size */
INFPSZ, /* maximum packet size */
512, /* hi-water mark */
256 /* lo-water mark */
};
};
NULL,
NULL,
};
NULL,
};
/*
* Per-instance state struct for the rloginmod module.
*/
struct rlmod_info
{
int flags;
int rl_expdat;
int stopmode;
char startc;
char stopc;
};
/*
* Flag used in flags
*/
/*ARGSUSED*/
static void
{}
/*
* rlmodopen - open routine gets called when the
* module gets pushed onto the stream.
*/
/*ARGSUSED*/
static int
{
int error;
return (EINVAL);
/* It's already attached. */
return (0);
}
/*
* Allocate state structure.
*/
/*
* Cross-link.
*/
/*
* Allow only non-M_DATA blocks to pass up to in.rlogind until
* it is ready for M_DATA (indicated by RL_IOC_ENABLE).
*/
qprocson(q);
/*
* Since TCP operates in the TLI-inspired brain-dead fashion,
* the connection will revert to bound state if the connection
* is reset by the client. We must send a T_UNBIND_REQ in
* that case so the port doesn't get "wedged" (preventing
* inetd from being able to restart the listener). Allocate
* it here, so that we don't need to worry about allocb()
* failures later.
*/
if (!qwait_sig(q)) {
qunbufcall(q, id);
goto fail;
}
qunbufcall(q, id);
}
sizeof (struct T_unbind_req);
/*
* Send a M_PROTO msg of type T_DATA_REQ (this is unique for
* read queue since only write queue can get T_DATA_REQ).
* Readstream routine in the daemon will do a getmsg() till
* it receives this proto message.
*/
if (!qwait_sig(q)) {
qunbufcall(q, id);
goto fail;
}
qunbufcall(q, id);
}
return (0);
fail:
qprocsoff(q);
}
return (error);
}
/*
* rlmodclose - This routine gets called when the module
* gets popped off of the stream.
*/
/*ARGSUSED*/
static int
{
/*
* Flush any write-side data downstream. Ignoring flow
* control at this point is known to be safe because the
* M_HANGUP below poisons the stream such that no modules can
* be pushed again.
*/
/* Poison the stream head so that we can't be pushed again. */
(void) putnextctl(q, M_HANGUP);
qprocsoff(q);
}
}
}
}
}
}
return (0);
}
/*
* rlmodrput - Module read queue put procedure.
* This is called from the module or
* driver downstream.
*/
static int
{
"q %p, mp %p", q, mp);
/* if low (normal) priority... */
/* ...and data is already queued... */
((q->q_first) ||
/* ...or currently disabled and this is M_DATA... */
/* ...delay delivery of the message */
return (0);
}
case M_PROTO:
case M_PCPROTO:
case T_ORDREL_IND:
case T_DISCON_IND:
/* Make into M_HANGUP and putnext */
}
/*
* If we haven't already, send T_UNBIND_REQ to prevent
* TCP from going into "BOUND" state and locking up the
* port.
*/
NULL) {
} else {
}
break;
/*
* We only get T_OK_ACK when we issue the unbind, and it can
* be ignored safely.
*/
case T_OK_ACK:
break;
default:
}
break;
case M_DATA:
} else {
}
break;
case M_FLUSH:
/*
* Since M_FLUSH came from TCP, we mark it bound for
* daemon, not tty. This only happens when TCP expects
* to do a connection reset.
*/
break;
case M_PCSIG:
case M_ERROR:
case M_IOCACK:
case M_IOCNAK:
case M_SETOPTS:
else
break;
default:
#ifdef DEBUG
#endif
}
return (0);
}
/*
* rlmodrsrv - module read service procedure
*/
static int
{
"q %p", q);
case M_DATA:
"rlmodrsrv end: q %p, mp %p, %s", q, mp,
"disabled");
return (0);
}
if (!canputnext(q)) {
"rlmodrsrv end: q %p, mp %p, %s",
q, mp, "!canputnext");
return (0);
}
"rlmodrsrv end: q %p, mp %p, %s",
q, mp, "!rlmodrmsg");
return (0);
}
break;
case M_PROTO:
case T_ORDREL_IND:
case T_DISCON_IND:
/* Make into M_HANGUP and putnext */
}
/*
* If we haven't already, send T_UNBIND_REQ
* to prevent TCP from going into "BOUND"
* state and locking up the port.
*/
} else {
}
break;
/*
* We only get T_OK_ACK when we issue the unbind, and
* it can be ignored safely.
*/
case T_OK_ACK:
break;
default:
"rlmodrsrv: got 0x%x type PROTO msg",
}
break;
case M_SETOPTS:
if (!canputnext(q)) {
"rlmodrsrv end: q %p, mp %p, %s",
q, mp, "!canputnext M_SETOPTS");
return (0);
}
break;
default:
#ifdef DEBUG
"rlmodrsrv: unexpected msg type 0x%x",
#endif
}
}
return (0);
}
/*
* rlmodwput - Module write queue put procedure.
* All non-zero messages are send downstream unchanged
*/
static int
{
char cntl;
int rw;
"q %p, mp %p", q, mp);
/*
* call make_expmblk to create an expedited
* message block.
*/
if (!canputnext(q)) {
"rlmodwput end: q %p, mp %p, %s",
q, mp, "expdata && !canputnext");
return (0);
}
} else {
}
}
return (0);
}
case M_DATA:
if (!canputnext(q))
else
break;
case M_FLUSH:
/*
* We must take care to create and forward out-of-band data
* indicating the flush to the far side.
*/
/*
* Since all rlogin protocol data is sent in this
* direction as urgent data, and TCP does not flush
* urgent data, it is okay to actually forward this
* flush. (telmod cannot.)
*/
/*
* The putnextctl1() call can only fail if we're
* out of memory. Ideally, we might set a state
* bit and reschedule ourselves when memory
* becomes available, so we make sure not to miss
* sending the FLUSHW to TCP before the urgent
* byte. Not doing this just means in some cases
* a bit more trash passes before the flush takes
* hold.
*/
/*
* Notify peer of the write flush request.
*/
if (!canputnext(q)) {
"rlmodwput end: q %p, mp %p, %s",
q, mp, "flushw && !canputnext");
return (0);
}
"rlmodwput end: q %p, mp %p, %s",
q, mp, "!make_expmblk");
return (0);
}
}
break;
case M_IOCTL:
if (!rlmodwioctl(q, mp))
break;
case M_PROTO:
case T_EXDATA_REQ:
case T_ORDREL_REQ:
case T_DISCON_REQ:
break;
default:
#ifdef DEBUG
"rlmodwput: unexpected TPI primitive 0x%x",
#endif
}
break;
case M_PCPROTO:
T_DISCON_REQ) {
} else {
/* XXX.sparker Log unexpected message */
}
break;
default:
#ifdef DEBUG
"rlmodwput: unexpected msg type 0x%x",
#endif
break;
}
return (0);
}
/*
* rlmodwsrv - module write service procedure
*/
static int
{
char cntl;
"start: q %p", q);
/*
* call make_expmblk to create an expedited
* message block.
*/
if (!canputnext(q)) {
"rlmodwsrv end: q %p, mp %p, %s",
q, NULL, "!canputnext && expdat");
return (0);
}
} else {
"rlmodwsrv end: q %p, mp %p, %s",
q, NULL, "!make_expmblk");
return (0);
}
}
"rlmodwsrv end: q %p, mp %p, %s",
q, mp, "!canputnext || expdat");
return (0);
}
if (!rlmodwioctl(q, mp)) {
"rlmodwsrv end: q %p, mp %p, %s",
q, mp, "!rlmodwioctl");
return (0);
}
continue;
}
}
return (0);
}
/*
* This routine returns a message block with an expedited
* data request
*/
static mblk_t *
{
return (NULL);
return (NULL);
}
/*
* Send a 1 byte data message block with appropriate
* control character.
*/
return (bp);
}
/*
* This routine parses M_DATA messages checking for window size protocol
* from a given message block. It returns TRUE if no resource exhaustion
* conditions are found. This is for use in the service procedure, which
* needs to know whether to continue, or stop processing the queue.
*/
static int
{
/*
* Eliminate any zero length messages here, so we don't filter EOFs
* accidentally.
*/
goto out;
}
/*
* Check if we have stored a previous message block because a window
* update was split over TCP segments. If so, append the new one to
* the stored one and process the stored one as if it just arrived.
*/
}
while (mp) {
/*
* scan through the entire message block
*/
/*
* check for FF (rlogin magic escape sequence)
*/
if (tmp[0] == RLOGIN_MAGIC) {
/*
* Update bytes read so far.
*/
/*
* Pull together message chain in case
* window escape is split across blocks.
*/
return (NULL);
}
/*
* pullupmsg results in newmp consuming
* all message blocks in this chain, and
* therefor mp wants updating.
*/
/*
* adjust tmp to where we
* stopped - count keeps track
* of bytes read so far.
* reset newcount = 0.
*/
newcount = 0;
/*
* Use the variable tmp1 to compute where
* the end of the window escape (currently
* the only rlogin protocol sequence), then
* check to see if we got all those bytes.
*/
/*
* All the window escape bytes aren't
* in this TCP segment. Store this
* mblk to one side so we can append
* the rest of the escape to it when
* its segment arrives.
*/
return (TRUE);
}
/*
* check for FF FF s s pattern
*/
/*
* If rlwinsetup returns an error,
* we do recover with newmp which
* points to new chain of mblks after
* doing window control ioctls.
* rlwinsetup returns newmp which
* contains only data part.
* Note that buried inside rlwinsetup
* is where we do the putnext.
*/
return (NULL);
}
/*
* We have successfully consumed the
* window sequence, but rlwinsetup()
* and its children have moved memory
* up underneath us. This means that
* the byte underneath *tmp has not
* been scanned now. We will now need
* to rescan it.
*/
continue;
}
}
tmp++;
}
/*
* bump newcount to include size of this particular block.
*/
}
/*
* If we trimmed the message down to nothing to forward, don't
* send any M_DATA message. (Don't want to send EOF!)
*/
}
out:
if (newmp) {
if (!canputnext(q)) {
return (NULL);
} else {
}
}
return (TRUE);
}
/*
* This routine is called to handle window size changes.
* The routine returns 1 on success and 0 on error (allocb failure).
*/
static int
{
"mp %p", q, mp);
"q %p, mp %p, allocb failed", q, mp);
return (0);
}
/*
* create an M_IOCTL message type.
*/
"q %p, mp %p, done", q, mp);
return (1);
}
/*
* This routine sets up window size change protocol.
* The routine returns the new mblk after issuing rlwinctl
* for window size changes. New mblk contains only data part
* of the message block. The routine returns 0 on error.
*/
static mblk_t *
{
unsigned char *jmpmp;
/*
* Set jmpmp to where to jump, to get just past the end of the
* window size protocol sequence.
*/
return (0);
return (0);
}
if (left > 0) {
/*
* Must delete the window size protocol sequence. We do
* this by sliding all the stuff after the sequence (jmpmp)
* to where the sequence itself began (blk).
*/
} else
return (mp);
}
/*
* When an ioctl changes software flow control on the tty, we must notify
* the rlogin client, so it can adjust its behavior appropriately. This
* routine, called from either the put or service routine, determines if
* the flow handling has changed. If so, it tries to send the indication
* to the client. It returns true or false depending upon whether the
* message was fully processed. If it wasn't fully processed it queues
* the message for retry later when resources
* (allocb/canputnext) are available.
*/
static boolean_t
{
char cntl;
int error;
/*
* If it is a tty ioctl, save the output flow
* control flag and the start and stop flow control
* characters if they are available.
*/
case TCSETS:
case TCSETSW:
case TCSETSF:
if (error != 0) {
return (B_TRUE);
}
break;
case TCSETA:
case TCSETAW:
case TCSETAF:
if (error != 0) {
return (B_TRUE);
}
break;
default:
/*
* This function must never be called for an M_IOCTL
* except the listed ones.
*/
#ifdef DEBUG
#else
return (B_TRUE);
#endif
}
/*
* If tty ioctl processing is done, check for stopmode
*/
if (stop) {
return (B_FALSE);
}
if (!canputnext(q)) {
return (B_FALSE);
}
}
} else {
if (!stop) {
return (B_FALSE);
}
if (!canputnext(q)) {
return (B_FALSE);
}
}
}
return (B_TRUE);
}
/* rlmodwioctl - handle M_IOCTL messages on the write queue. */
static boolean_t
{
int error;
/*
* This is a special ioctl to reenable the queue.
* The initial data read from the stream head is
* put back on the queue.
*/
case RL_IOC_ENABLE:
/*
* Send negative ack if RL_DISABLED flag is not set
*/
break;
}
}
"rlmodwput end: q %p, mp %p, %s",
q, mp, "IOCACK enable");
return (B_TRUE);
/*
* If it is a tty ioctl, save the output flow
* control flag and the start and stop flow control
* characters if they are available.
*/
case TCSETS:
case TCSETSW:
case TCSETSF:
case TCSETA:
case TCSETAW:
case TCSETAF:
#ifdef DEBUG
case TIOCSWINSZ:
case TIOCSTI:
case TCSBRK:
break;
#endif
case CRYPTPASSTHRU:
if (error != 0) {
break;
}
else
break;
default:
} else {
#ifdef DEBUG
"rlmodwioctl: unexpected ioctl type 0x%x",
#endif
}
}
return (B_TRUE);
}
static void
{
} else {
}
enableok(q);
qenable(q);
}
static void
{
} else {
}
enableok(q);
qenable(q);
}
static void
{
/*
* Avoid re-enabling the queue.
*/
noenable(q);
}
static void
{
/*
* Make sure there is at most one outstanding request per queue.
*/
return;
} else {
return;
}
else
} else {
else
}
}