xref: /illumos-gate/usr/src/uts/common/io/telmod.c (revision c97b1070adbc3ae2291128243229991dd705d2d6)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * This module implements the "fast path" processing for the telnet protocol.
297c478bd9Sstevel@tonic-gate  * Since it only knows a very small number of the telnet protocol options,
307c478bd9Sstevel@tonic-gate  * the daemon is required to assist this module.  This module must be run
317c478bd9Sstevel@tonic-gate  * underneath logindmux, which handles switching messages between the
327c478bd9Sstevel@tonic-gate  * daemon and the pty master stream appropriately.  When an unknown telnet
337c478bd9Sstevel@tonic-gate  * option is received it is handled as a stop-and-wait operation.  The
347c478bd9Sstevel@tonic-gate  * module refuses to forward data in either direction, and waits for the
357c478bd9Sstevel@tonic-gate  * daemon to deal with the option, and forward any unprocessed data back
367c478bd9Sstevel@tonic-gate  * to the daemon.
377c478bd9Sstevel@tonic-gate  */
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #include <sys/types.h>
407c478bd9Sstevel@tonic-gate #include <sys/param.h>
417c478bd9Sstevel@tonic-gate #include <sys/stream.h>
427c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
437c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
447c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
457c478bd9Sstevel@tonic-gate #include <sys/errno.h>
467c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
477c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
487c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
497c478bd9Sstevel@tonic-gate #include <sys/ptem.h>
507c478bd9Sstevel@tonic-gate #include <sys/logindmux.h>
517c478bd9Sstevel@tonic-gate #include <sys/telioctl.h>
527c478bd9Sstevel@tonic-gate #include <sys/termios.h>
537c478bd9Sstevel@tonic-gate #include <sys/debug.h>
547c478bd9Sstevel@tonic-gate #include <sys/conf.h>
557c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
567c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
577c478bd9Sstevel@tonic-gate #include <sys/cryptmod.h>
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #define	IAC	255
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate extern struct streamtab telmodinfo;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate #define	TELMOD_ID	105
647c478bd9Sstevel@tonic-gate #define	SIMWAIT		(1*hz)
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate /*
677c478bd9Sstevel@tonic-gate  * Module state flags
687c478bd9Sstevel@tonic-gate  */
697c478bd9Sstevel@tonic-gate #define		TEL_IOCPASSTHRU	0x100
707c478bd9Sstevel@tonic-gate #define		TEL_STOPPED	0x80
717c478bd9Sstevel@tonic-gate #define		TEL_CRRCV	0x40
727c478bd9Sstevel@tonic-gate #define		TEL_CRSND	0x20
737c478bd9Sstevel@tonic-gate #define		TEL_GETBLK	0x10
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate /*
767c478bd9Sstevel@tonic-gate  * NOTE: values TEL_BINARY_IN and TEL_BINARY_OUT are defined in
777c478bd9Sstevel@tonic-gate  * telioctl.h, passed in the TEL_IOC_MODE ioctl and stored (bitwise)
787c478bd9Sstevel@tonic-gate  * in the module state flag.  So those values are not available
797c478bd9Sstevel@tonic-gate  * even though they are not defined here.
807c478bd9Sstevel@tonic-gate  */
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate /*
857c478bd9Sstevel@tonic-gate  * Per queue instances are single-threaded since the q_ptr
867c478bd9Sstevel@tonic-gate  * field of queues need to be shared among threads.
877c478bd9Sstevel@tonic-gate  */
887c478bd9Sstevel@tonic-gate static struct fmodsw fsw = {
897c478bd9Sstevel@tonic-gate 	"telmod",
907c478bd9Sstevel@tonic-gate 	&telmodinfo,
917c478bd9Sstevel@tonic-gate 	D_MTQPAIR | D_MP
927c478bd9Sstevel@tonic-gate };
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate /*
957c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
967c478bd9Sstevel@tonic-gate  */
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate static struct modlstrmod modlstrmod = {
997c478bd9Sstevel@tonic-gate 	&mod_strmodops,
1007c478bd9Sstevel@tonic-gate 	"telnet module",
1017c478bd9Sstevel@tonic-gate 	&fsw
1027c478bd9Sstevel@tonic-gate };
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1057c478bd9Sstevel@tonic-gate 	MODREV_1, &modlstrmod, NULL
1067c478bd9Sstevel@tonic-gate };
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate int
_init()1097c478bd9Sstevel@tonic-gate _init()
1107c478bd9Sstevel@tonic-gate {
1117c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
1127c478bd9Sstevel@tonic-gate }
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate int
_fini()1157c478bd9Sstevel@tonic-gate _fini()
1167c478bd9Sstevel@tonic-gate {
1177c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
1187c478bd9Sstevel@tonic-gate }
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)1217c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate static int	telmodopen(queue_t *, dev_t *, int, int, cred_t *);
1277c478bd9Sstevel@tonic-gate static int	telmodclose(queue_t *, int, cred_t *);
128*c97b1070SToomas Soome static int	telmodrput(queue_t *, mblk_t *);
129*c97b1070SToomas Soome static int	telmodrsrv(queue_t *);
130*c97b1070SToomas Soome static int	telmodwput(queue_t *, mblk_t *);
131*c97b1070SToomas Soome static int	telmodwsrv(queue_t *);
1327c478bd9Sstevel@tonic-gate static int	rcv_parse(queue_t *q, mblk_t *mp);
1337c478bd9Sstevel@tonic-gate static int	snd_parse(queue_t *q, mblk_t *mp);
1347c478bd9Sstevel@tonic-gate static void	telmod_timer(void *);
1357c478bd9Sstevel@tonic-gate static void	telmod_buffer(void *);
1367c478bd9Sstevel@tonic-gate static void	recover(queue_t *, mblk_t *, size_t);
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate static struct module_info telmodoinfo = {
1397c478bd9Sstevel@tonic-gate 	TELMOD_ID,				/* module id number */
1407c478bd9Sstevel@tonic-gate 	"telmod",				/* module name */
1417c478bd9Sstevel@tonic-gate 	0,					/* minimum packet size */
1427c478bd9Sstevel@tonic-gate 	INFPSZ,					/* maximum packet size */
1437c478bd9Sstevel@tonic-gate 	512,					/* hi-water mark */
1447c478bd9Sstevel@tonic-gate 	256					/* lo-water mark */
1457c478bd9Sstevel@tonic-gate };
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate static struct qinit telmodrinit = {
148*c97b1070SToomas Soome 	telmodrput,
149*c97b1070SToomas Soome 	telmodrsrv,
1507c478bd9Sstevel@tonic-gate 	telmodopen,
1517c478bd9Sstevel@tonic-gate 	telmodclose,
1527c478bd9Sstevel@tonic-gate 	nulldev,
1537c478bd9Sstevel@tonic-gate 	&telmodoinfo,
1547c478bd9Sstevel@tonic-gate 	NULL
1557c478bd9Sstevel@tonic-gate };
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate static struct qinit telmodwinit = {
158*c97b1070SToomas Soome 	telmodwput,
159*c97b1070SToomas Soome 	telmodwsrv,
1607c478bd9Sstevel@tonic-gate 	NULL,
1617c478bd9Sstevel@tonic-gate 	NULL,
1627c478bd9Sstevel@tonic-gate 	nulldev,
1637c478bd9Sstevel@tonic-gate 	&telmodoinfo,
1647c478bd9Sstevel@tonic-gate 	NULL
1657c478bd9Sstevel@tonic-gate };
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate struct streamtab telmodinfo = {
1687c478bd9Sstevel@tonic-gate 	&telmodrinit,
1697c478bd9Sstevel@tonic-gate 	&telmodwinit,
1707c478bd9Sstevel@tonic-gate 	NULL,
1717c478bd9Sstevel@tonic-gate 	NULL
1727c478bd9Sstevel@tonic-gate };
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate /*
1757c478bd9Sstevel@tonic-gate  * Per-instance state struct for the telnet module.
1767c478bd9Sstevel@tonic-gate  */
1777c478bd9Sstevel@tonic-gate struct telmod_info {
1787c478bd9Sstevel@tonic-gate 	int		flags;
1797c478bd9Sstevel@tonic-gate 	bufcall_id_t	wbufcid;
1807c478bd9Sstevel@tonic-gate 	bufcall_id_t	rbufcid;
1817c478bd9Sstevel@tonic-gate 	timeout_id_t	wtimoutid;
1827c478bd9Sstevel@tonic-gate 	timeout_id_t	rtimoutid;
1837c478bd9Sstevel@tonic-gate 	mblk_t		*unbind_mp;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate };
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1887c478bd9Sstevel@tonic-gate static void
dummy_callback(void * arg)1897c478bd9Sstevel@tonic-gate dummy_callback(void *arg)
1907c478bd9Sstevel@tonic-gate {}
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /*
1937c478bd9Sstevel@tonic-gate  * telmodopen -
1947c478bd9Sstevel@tonic-gate  *	A variety of telnet options can never really be processed in the
1957c478bd9Sstevel@tonic-gate  *	kernel.  For example, TELOPT_TTYPE, must be based in the TERM
1967c478bd9Sstevel@tonic-gate  *	environment variable to the login process.  Also, data may already
1977c478bd9Sstevel@tonic-gate  *	have reached the stream head before telmod was pushed on the stream.
1987c478bd9Sstevel@tonic-gate  *	So when telmod is opened, it begins in stopped state, preventing
1997c478bd9Sstevel@tonic-gate  *	further data passing either direction through it.  It sends a
2007c478bd9Sstevel@tonic-gate  *	T_DATA_REQ messages up toward the daemon.  This is so the daemon
2017c478bd9Sstevel@tonic-gate  *	can be sure that all data which was not processed by telmod
2027c478bd9Sstevel@tonic-gate  *	(because it wasn't yet pushed) has been received at the stream head.
2037c478bd9Sstevel@tonic-gate  */
2047c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2057c478bd9Sstevel@tonic-gate static int
telmodopen(queue_t * q,dev_t * devp,int oflag,int sflag,cred_t * credp)2067c478bd9Sstevel@tonic-gate telmodopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
2077c478bd9Sstevel@tonic-gate {
2087c478bd9Sstevel@tonic-gate 	struct telmod_info	*tmip;
2097c478bd9Sstevel@tonic-gate 	mblk_t *bp;
2107c478bd9Sstevel@tonic-gate 	union T_primitives *tp;
2117c478bd9Sstevel@tonic-gate 	int	error;
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	if (sflag != MODOPEN)
2147c478bd9Sstevel@tonic-gate 		return (EINVAL);
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	if (q->q_ptr != NULL) {
2177c478bd9Sstevel@tonic-gate 		/* It's already attached. */
2187c478bd9Sstevel@tonic-gate 		return (0);
2197c478bd9Sstevel@tonic-gate 	}
2207c478bd9Sstevel@tonic-gate 	/*
2217c478bd9Sstevel@tonic-gate 	 * Allocate state structure.
2227c478bd9Sstevel@tonic-gate 	 */
2237c478bd9Sstevel@tonic-gate 	tmip = kmem_zalloc(sizeof (*tmip), KM_SLEEP);
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/*
2267c478bd9Sstevel@tonic-gate 	 * Cross-link.
2277c478bd9Sstevel@tonic-gate 	 */
2287c478bd9Sstevel@tonic-gate 	q->q_ptr = tmip;
2297c478bd9Sstevel@tonic-gate 	WR(q)->q_ptr = tmip;
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	noenable(q);
2327c478bd9Sstevel@tonic-gate 	tmip->flags |= TEL_STOPPED;
2337c478bd9Sstevel@tonic-gate 	qprocson(q);
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 	/*
2367c478bd9Sstevel@tonic-gate 	 * Since TCP operates in the TLI-inspired brain-dead fashion,
2377c478bd9Sstevel@tonic-gate 	 * the connection will revert to bound state if the connection
2387c478bd9Sstevel@tonic-gate 	 * is reset by the client.  We must send a T_UNBIND_REQ in
2397c478bd9Sstevel@tonic-gate 	 * that case so the port doesn't get "wedged" (preventing
2407c478bd9Sstevel@tonic-gate 	 * inetd from being able to restart the listener).  Allocate
2417c478bd9Sstevel@tonic-gate 	 * it here, so that we don't need to worry about allocb()
2427c478bd9Sstevel@tonic-gate 	 * failures later.
2437c478bd9Sstevel@tonic-gate 	 */
2447c478bd9Sstevel@tonic-gate 	while ((tmip->unbind_mp = allocb(sizeof (union T_primitives),
2457c478bd9Sstevel@tonic-gate 	    BPRI_HI)) == NULL) {
2467c478bd9Sstevel@tonic-gate 		bufcall_id_t id = qbufcall(q, sizeof (union T_primitives),
2477c478bd9Sstevel@tonic-gate 		    BPRI_HI, dummy_callback, NULL);
2487c478bd9Sstevel@tonic-gate 		if (!qwait_sig(q)) {
2497c478bd9Sstevel@tonic-gate 			qunbufcall(q, id);
2507c478bd9Sstevel@tonic-gate 			error = EINTR;
2517c478bd9Sstevel@tonic-gate 			goto fail;
2527c478bd9Sstevel@tonic-gate 		}
2537c478bd9Sstevel@tonic-gate 		qunbufcall(q, id);
2547c478bd9Sstevel@tonic-gate 	}
2557c478bd9Sstevel@tonic-gate 	tmip->unbind_mp->b_wptr = tmip->unbind_mp->b_rptr +
2567c478bd9Sstevel@tonic-gate 	    sizeof (struct T_unbind_req);
2577c478bd9Sstevel@tonic-gate 	tmip->unbind_mp->b_datap->db_type = M_PROTO;
2587c478bd9Sstevel@tonic-gate 	tp = (union T_primitives *)tmip->unbind_mp->b_rptr;
2597c478bd9Sstevel@tonic-gate 	tp->type = T_UNBIND_REQ;
2607c478bd9Sstevel@tonic-gate 	/*
2617c478bd9Sstevel@tonic-gate 	 * Send a M_PROTO msg of type T_DATA_REQ (this is unique for
2627c478bd9Sstevel@tonic-gate 	 * read queue since only write queue can get T_DATA_REQ).
2637c478bd9Sstevel@tonic-gate 	 * Readstream routine in telnet daemon will do a getmsg() till
2647c478bd9Sstevel@tonic-gate 	 * it receives this proto message
2657c478bd9Sstevel@tonic-gate 	 */
2667c478bd9Sstevel@tonic-gate 	while ((bp = allocb(sizeof (union T_primitives), BPRI_HI)) == NULL) {
2677c478bd9Sstevel@tonic-gate 		bufcall_id_t id = qbufcall(q, sizeof (union T_primitives),
2687c478bd9Sstevel@tonic-gate 		    BPRI_HI, dummy_callback, NULL);
2697c478bd9Sstevel@tonic-gate 		if (!qwait_sig(q)) {
2707c478bd9Sstevel@tonic-gate 			qunbufcall(q, id);
2717c478bd9Sstevel@tonic-gate 			error = EINTR;
2727c478bd9Sstevel@tonic-gate 			goto fail;
2737c478bd9Sstevel@tonic-gate 		}
2747c478bd9Sstevel@tonic-gate 		qunbufcall(q, id);
2757c478bd9Sstevel@tonic-gate 	}
2767c478bd9Sstevel@tonic-gate 	bp->b_datap->db_type = M_PROTO;
2777c478bd9Sstevel@tonic-gate 	bp->b_wptr = bp->b_rptr + sizeof (union T_primitives);
2787c478bd9Sstevel@tonic-gate 	tp = (union T_primitives *)bp->b_rptr;
2797c478bd9Sstevel@tonic-gate 	tp->type = T_DATA_REQ;
2807c478bd9Sstevel@tonic-gate 	tp->data_req.MORE_flag = 0;
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	putnext(q, bp);
2837c478bd9Sstevel@tonic-gate 	return (0);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate fail:
2867c478bd9Sstevel@tonic-gate 	qprocsoff(q);
2877c478bd9Sstevel@tonic-gate 	if (tmip->unbind_mp != NULL) {
2887c478bd9Sstevel@tonic-gate 		freemsg(tmip->unbind_mp);
2897c478bd9Sstevel@tonic-gate 	}
2907c478bd9Sstevel@tonic-gate 	kmem_free(tmip, sizeof (struct telmod_info));
2917c478bd9Sstevel@tonic-gate 	q->q_ptr = NULL;
2927c478bd9Sstevel@tonic-gate 	WR(q)->q_ptr = NULL;
2937c478bd9Sstevel@tonic-gate 	return (error);
2947c478bd9Sstevel@tonic-gate }
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate /*
2987c478bd9Sstevel@tonic-gate  * telmodclose - just the normal streams clean-up is required.
2997c478bd9Sstevel@tonic-gate  */
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3027c478bd9Sstevel@tonic-gate static int
telmodclose(queue_t * q,int flag,cred_t * credp)3037c478bd9Sstevel@tonic-gate telmodclose(queue_t *q, int flag, cred_t *credp)
3047c478bd9Sstevel@tonic-gate {
3057c478bd9Sstevel@tonic-gate 	struct telmod_info   *tmip = (struct telmod_info *)q->q_ptr;
3067c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	/*
3097c478bd9Sstevel@tonic-gate 	 * Flush any write-side data downstream.  Ignoring flow
3107c478bd9Sstevel@tonic-gate 	 * control at this point is known to be safe because the
3117c478bd9Sstevel@tonic-gate 	 * M_HANGUP below poisons the stream such that no modules can
3127c478bd9Sstevel@tonic-gate 	 * be pushed again.
3137c478bd9Sstevel@tonic-gate 	 */
3147c478bd9Sstevel@tonic-gate 	while (mp = getq(WR(q)))
3157c478bd9Sstevel@tonic-gate 		putnext(WR(q), mp);
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	/* Poison the stream head so that we can't be pushed again. */
3187c478bd9Sstevel@tonic-gate 	(void) putnextctl(q, M_HANGUP);
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	qprocsoff(q);
3217c478bd9Sstevel@tonic-gate 	if (tmip->wbufcid) {
3227c478bd9Sstevel@tonic-gate 		qunbufcall(q, tmip->wbufcid);
3237c478bd9Sstevel@tonic-gate 		tmip->wbufcid = 0;
3247c478bd9Sstevel@tonic-gate 	}
3257c478bd9Sstevel@tonic-gate 	if (tmip->rbufcid) {
3267c478bd9Sstevel@tonic-gate 		qunbufcall(q, tmip->rbufcid);
3277c478bd9Sstevel@tonic-gate 		tmip->rbufcid = 0;
3287c478bd9Sstevel@tonic-gate 	}
3297c478bd9Sstevel@tonic-gate 	if (tmip->wtimoutid) {
3307c478bd9Sstevel@tonic-gate 		(void) quntimeout(q, tmip->wtimoutid);
3317c478bd9Sstevel@tonic-gate 		tmip->wtimoutid = 0;
3327c478bd9Sstevel@tonic-gate 	}
3337c478bd9Sstevel@tonic-gate 	if (tmip->rtimoutid) {
3347c478bd9Sstevel@tonic-gate 		(void) quntimeout(q, tmip->rtimoutid);
3357c478bd9Sstevel@tonic-gate 		tmip->rtimoutid = 0;
3367c478bd9Sstevel@tonic-gate 	}
3377c478bd9Sstevel@tonic-gate 	if (tmip->unbind_mp != NULL) {
3387c478bd9Sstevel@tonic-gate 		freemsg(tmip->unbind_mp);
3397c478bd9Sstevel@tonic-gate 	}
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	kmem_free(q->q_ptr, sizeof (struct telmod_info));
3427c478bd9Sstevel@tonic-gate 	q->q_ptr = WR(q)->q_ptr = NULL;
3437c478bd9Sstevel@tonic-gate 	return (0);
3447c478bd9Sstevel@tonic-gate }
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate /*
3477c478bd9Sstevel@tonic-gate  * telmodrput:
3487c478bd9Sstevel@tonic-gate  * Be sure to preserve data order.  If the daemon is waiting for additional
3497c478bd9Sstevel@tonic-gate  * data (TEL_GETBLK state) forward new data.  Otherwise, apply normal
3507c478bd9Sstevel@tonic-gate  * telnet protocol processing to M_DATA.  Take notice of TLI messages
3517c478bd9Sstevel@tonic-gate  * indicating connection tear-down, and change them into M_HANGUP's.
3527c478bd9Sstevel@tonic-gate  */
353*c97b1070SToomas Soome static int
telmodrput(queue_t * q,mblk_t * mp)3547c478bd9Sstevel@tonic-gate telmodrput(queue_t *q, mblk_t *mp)
3557c478bd9Sstevel@tonic-gate {
3567c478bd9Sstevel@tonic-gate 	mblk_t	*newmp;
3577c478bd9Sstevel@tonic-gate 	struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
3587c478bd9Sstevel@tonic-gate 	union T_primitives *tip;
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	if ((mp->b_datap->db_type < QPCTL) &&
3617c478bd9Sstevel@tonic-gate 	    ((q->q_first) || ((tmip->flags & TEL_STOPPED) &&
3627c478bd9Sstevel@tonic-gate 	    !(tmip->flags & TEL_GETBLK)) || !canputnext(q))) {
3637c478bd9Sstevel@tonic-gate 		(void) putq(q, mp);
364*c97b1070SToomas Soome 		return (0);
3657c478bd9Sstevel@tonic-gate 	}
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
3687c478bd9Sstevel@tonic-gate 	case M_DATA:
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 		/*
3717c478bd9Sstevel@tonic-gate 		 * If the user level daemon requests for 1 more
3727c478bd9Sstevel@tonic-gate 		 * block of data (needs more data for protocol processing)
3737c478bd9Sstevel@tonic-gate 		 * create a M_CTL message block with the mp.
3747c478bd9Sstevel@tonic-gate 		 */
3757c478bd9Sstevel@tonic-gate is_mdata:
3767c478bd9Sstevel@tonic-gate 		if (tmip->flags & TEL_GETBLK) {
3777c478bd9Sstevel@tonic-gate 			if ((newmp = allocb(sizeof (char), BPRI_MED)) == NULL) {
3787c478bd9Sstevel@tonic-gate 				recover(q, mp, msgdsize(mp));
379*c97b1070SToomas Soome 				return (0);
3807c478bd9Sstevel@tonic-gate 			}
3817c478bd9Sstevel@tonic-gate 			newmp->b_datap->db_type = M_CTL;
3827c478bd9Sstevel@tonic-gate 			newmp->b_wptr = newmp->b_rptr + 1;
3837c478bd9Sstevel@tonic-gate 			*(newmp->b_rptr) = M_CTL_MAGIC_NUMBER;
3847c478bd9Sstevel@tonic-gate 			newmp->b_cont = mp;
3857c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_GETBLK;
3867c478bd9Sstevel@tonic-gate 			noenable(q);
3877c478bd9Sstevel@tonic-gate 			tmip->flags |= TEL_STOPPED;
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 			putnext(q, newmp);
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 			break;
3927c478bd9Sstevel@tonic-gate 		}
3937c478bd9Sstevel@tonic-gate 		/*
3947c478bd9Sstevel@tonic-gate 		 * call the protocol parsing routine which processes
3957c478bd9Sstevel@tonic-gate 		 * the data part of the message block first. Then it
3967c478bd9Sstevel@tonic-gate 		 * handles protocol and CR/LF processing.
3977c478bd9Sstevel@tonic-gate 		 * If an error is found inside allocb/dupb, recover
3987c478bd9Sstevel@tonic-gate 		 * routines inside rcv_parse will queue up the
3997c478bd9Sstevel@tonic-gate 		 * original message block in its service queue.
4007c478bd9Sstevel@tonic-gate 		 */
4017c478bd9Sstevel@tonic-gate 		(void) rcv_parse(q, mp);
4027c478bd9Sstevel@tonic-gate 		break;
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 	case M_FLUSH:
4057c478bd9Sstevel@tonic-gate 		/*
4067c478bd9Sstevel@tonic-gate 		 * Since M_FLUSH came from TCP, we mark it bound for
4077c478bd9Sstevel@tonic-gate 		 * daemon, not tty.  This only happens when TCP expects
4087c478bd9Sstevel@tonic-gate 		 * to do a connection reset.
4097c478bd9Sstevel@tonic-gate 		 */
4107c478bd9Sstevel@tonic-gate 		mp->b_flag |= MSGMARK;
4117c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR)
4127c478bd9Sstevel@tonic-gate 			flushq(q, FLUSHALL);
4137c478bd9Sstevel@tonic-gate 		putnext(q, mp);
4147c478bd9Sstevel@tonic-gate 		break;
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	case M_PCSIG:
4177c478bd9Sstevel@tonic-gate 	case M_ERROR:
4187c478bd9Sstevel@tonic-gate 		if (tmip->flags & TEL_GETBLK)
4197c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_GETBLK;
4207c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
4217c478bd9Sstevel@tonic-gate 	case M_IOCACK:
4227c478bd9Sstevel@tonic-gate 	case M_IOCNAK:
4237c478bd9Sstevel@tonic-gate 	case M_SETOPTS:
4247c478bd9Sstevel@tonic-gate 		putnext(q, mp);
4257c478bd9Sstevel@tonic-gate 		break;
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 	case M_PROTO:
4287c478bd9Sstevel@tonic-gate 	case M_PCPROTO:
4297c478bd9Sstevel@tonic-gate 		if (tmip->flags & TEL_GETBLK)
4307c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_GETBLK;
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 		tip = (union T_primitives *)mp->b_rptr;
4337c478bd9Sstevel@tonic-gate 		switch (tip->type) {
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 		case T_ORDREL_IND:
4367c478bd9Sstevel@tonic-gate 		case T_DISCON_IND:
4377c478bd9Sstevel@tonic-gate 			/* Make into M_HANGUP and putnext */
4387c478bd9Sstevel@tonic-gate 			ASSERT(mp->b_cont == NULL);
4397c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_HANGUP;
4407c478bd9Sstevel@tonic-gate 			mp->b_wptr = mp->b_rptr;
4417c478bd9Sstevel@tonic-gate 			if (mp->b_cont) {
4427c478bd9Sstevel@tonic-gate 				freemsg(mp->b_cont);
4437c478bd9Sstevel@tonic-gate 				mp->b_cont = NULL;
4447c478bd9Sstevel@tonic-gate 			}
4457c478bd9Sstevel@tonic-gate 			/*
4467c478bd9Sstevel@tonic-gate 			 * If we haven't already, send T_UNBIND_REQ to prevent
4477c478bd9Sstevel@tonic-gate 			 * TCP from going into "BOUND" state and locking up the
4487c478bd9Sstevel@tonic-gate 			 * port.
4497c478bd9Sstevel@tonic-gate 			 */
4507c478bd9Sstevel@tonic-gate 			if (tip->type == T_DISCON_IND && tmip->unbind_mp !=
4517c478bd9Sstevel@tonic-gate 			    NULL) {
4527c478bd9Sstevel@tonic-gate 				putnext(q, mp);
4537c478bd9Sstevel@tonic-gate 				qreply(q, tmip->unbind_mp);
4547c478bd9Sstevel@tonic-gate 				tmip->unbind_mp = NULL;
4557c478bd9Sstevel@tonic-gate 			} else {
4567c478bd9Sstevel@tonic-gate 				putnext(q, mp);
4577c478bd9Sstevel@tonic-gate 			}
4587c478bd9Sstevel@tonic-gate 			break;
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 		case T_EXDATA_IND:
4617c478bd9Sstevel@tonic-gate 		case T_DATA_IND:	/* conform to TPI, but never happens */
4627c478bd9Sstevel@tonic-gate 			newmp = mp->b_cont;
4637c478bd9Sstevel@tonic-gate 			freeb(mp);
4647c478bd9Sstevel@tonic-gate 			mp = newmp;
4657c478bd9Sstevel@tonic-gate 			if (mp) {
4667c478bd9Sstevel@tonic-gate 				ASSERT(mp->b_datap->db_type == M_DATA);
4677c478bd9Sstevel@tonic-gate 				if (msgdsize(mp) != 0) {
4687c478bd9Sstevel@tonic-gate 					goto is_mdata;
4697c478bd9Sstevel@tonic-gate 				}
4707c478bd9Sstevel@tonic-gate 				freemsg(mp);
4717c478bd9Sstevel@tonic-gate 			}
4727c478bd9Sstevel@tonic-gate 			break;
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 		/*
4757c478bd9Sstevel@tonic-gate 		 * We only get T_OK_ACK when we issue the unbind, and it can
4767c478bd9Sstevel@tonic-gate 		 * be ignored safely.
4777c478bd9Sstevel@tonic-gate 		 */
4787c478bd9Sstevel@tonic-gate 		case T_OK_ACK:
4797c478bd9Sstevel@tonic-gate 			ASSERT(tmip->unbind_mp == NULL);
4807c478bd9Sstevel@tonic-gate 			freemsg(mp);
4817c478bd9Sstevel@tonic-gate 			break;
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 		default:
4847c478bd9Sstevel@tonic-gate #ifdef DEBUG
4857c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE,
4867c478bd9Sstevel@tonic-gate 			    "telmodrput: unexpected TLI primitive msg "
4877c478bd9Sstevel@tonic-gate 			    "type 0x%x", tip->type);
4887c478bd9Sstevel@tonic-gate #endif
4897c478bd9Sstevel@tonic-gate 			freemsg(mp);
4907c478bd9Sstevel@tonic-gate 		}
4917c478bd9Sstevel@tonic-gate 		break;
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	default:
4947c478bd9Sstevel@tonic-gate #ifdef DEBUG
4957c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE,
4967c478bd9Sstevel@tonic-gate 		    "telmodrput: unexpected msg type 0x%x",
4977c478bd9Sstevel@tonic-gate 		    mp->b_datap->db_type);
4987c478bd9Sstevel@tonic-gate #endif
4997c478bd9Sstevel@tonic-gate 		freemsg(mp);
5007c478bd9Sstevel@tonic-gate 	}
501*c97b1070SToomas Soome 	return (0);
5027c478bd9Sstevel@tonic-gate }
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate /*
5057c478bd9Sstevel@tonic-gate  * telmodrsrv:
5067c478bd9Sstevel@tonic-gate  * Mostly we end up here because of M_DATA processing delayed due to flow
5077c478bd9Sstevel@tonic-gate  * control or lack of memory.  XXX.sparker: TLI primitives here?
5087c478bd9Sstevel@tonic-gate  */
509*c97b1070SToomas Soome static int
telmodrsrv(queue_t * q)5107c478bd9Sstevel@tonic-gate telmodrsrv(queue_t *q)
5117c478bd9Sstevel@tonic-gate {
5127c478bd9Sstevel@tonic-gate 	mblk_t	*mp, *newmp;
5137c478bd9Sstevel@tonic-gate 	struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
5147c478bd9Sstevel@tonic-gate 	union T_primitives *tip;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
5177c478bd9Sstevel@tonic-gate 		if (((tmip->flags & TEL_STOPPED) &&
5187c478bd9Sstevel@tonic-gate 		    !(tmip->flags & TEL_GETBLK)) || !canputnext(q)) {
5197c478bd9Sstevel@tonic-gate 			(void) putbq(q, mp);
520*c97b1070SToomas Soome 			return (0);
5217c478bd9Sstevel@tonic-gate 		}
5227c478bd9Sstevel@tonic-gate 		switch (mp->b_datap->db_type) {
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 		case M_DATA:
5257c478bd9Sstevel@tonic-gate is_mdata:
5267c478bd9Sstevel@tonic-gate 			if (tmip->flags & TEL_GETBLK) {
5277c478bd9Sstevel@tonic-gate 				if ((newmp = allocb(sizeof (char),
5287c478bd9Sstevel@tonic-gate 				    BPRI_MED)) == NULL) {
5297c478bd9Sstevel@tonic-gate 					recover(q, mp, msgdsize(mp));
530*c97b1070SToomas Soome 					return (0);
5317c478bd9Sstevel@tonic-gate 				}
5327c478bd9Sstevel@tonic-gate 				newmp->b_datap->db_type = M_CTL;
5337c478bd9Sstevel@tonic-gate 				newmp->b_wptr = newmp->b_rptr + 1;
5347c478bd9Sstevel@tonic-gate 				*(newmp->b_rptr) = M_CTL_MAGIC_NUMBER;
5357c478bd9Sstevel@tonic-gate 				newmp->b_cont = mp;
5367c478bd9Sstevel@tonic-gate 				tmip->flags &= ~TEL_GETBLK;
5377c478bd9Sstevel@tonic-gate 				noenable(q);
5387c478bd9Sstevel@tonic-gate 				tmip->flags |= TEL_STOPPED;
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 				putnext(q, newmp);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 				break;
5437c478bd9Sstevel@tonic-gate 			}
5447c478bd9Sstevel@tonic-gate 			if (!rcv_parse(q, mp)) {
545*c97b1070SToomas Soome 				return (0);
5467c478bd9Sstevel@tonic-gate 			}
5477c478bd9Sstevel@tonic-gate 			break;
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 		case M_PROTO:
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 			tip = (union T_primitives *)mp->b_rptr;
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 			/*
5547c478bd9Sstevel@tonic-gate 			 * Unless the M_PROTO message indicates data, clear
5557c478bd9Sstevel@tonic-gate 			 * TEL_GETBLK so that we stop passing our messages
5567c478bd9Sstevel@tonic-gate 			 * up to the telnet daemon.
5577c478bd9Sstevel@tonic-gate 			 */
5587c478bd9Sstevel@tonic-gate 			if (tip->type != T_DATA_IND &&
5597c478bd9Sstevel@tonic-gate 			    tip->type != T_EXDATA_IND)
5607c478bd9Sstevel@tonic-gate 				tmip->flags &= ~TEL_GETBLK;
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 			switch (tip->type) {
5637c478bd9Sstevel@tonic-gate 			case T_ORDREL_IND:
5647c478bd9Sstevel@tonic-gate 			case T_DISCON_IND:
5657c478bd9Sstevel@tonic-gate 			/* Make into M_HANGUP and putnext */
5667c478bd9Sstevel@tonic-gate 				ASSERT(mp->b_cont == NULL);
5677c478bd9Sstevel@tonic-gate 				mp->b_datap->db_type = M_HANGUP;
5687c478bd9Sstevel@tonic-gate 				mp->b_wptr = mp->b_rptr;
5697c478bd9Sstevel@tonic-gate 				if (mp->b_cont) {
5707c478bd9Sstevel@tonic-gate 					freemsg(mp->b_cont);
5717c478bd9Sstevel@tonic-gate 					mp->b_cont = NULL;
5727c478bd9Sstevel@tonic-gate 				}
5737c478bd9Sstevel@tonic-gate 				/*
5747c478bd9Sstevel@tonic-gate 				 * If we haven't already, send T_UNBIND_REQ
5757c478bd9Sstevel@tonic-gate 				 * to prevent TCP from going into "BOUND"
5767c478bd9Sstevel@tonic-gate 				 * state and locking up the port.
5777c478bd9Sstevel@tonic-gate 				 */
5787c478bd9Sstevel@tonic-gate 				if (tip->type == T_DISCON_IND &&
5797c478bd9Sstevel@tonic-gate 				    tmip->unbind_mp != NULL) {
5807c478bd9Sstevel@tonic-gate 					putnext(q, mp);
5817c478bd9Sstevel@tonic-gate 					qreply(q, tmip->unbind_mp);
5827c478bd9Sstevel@tonic-gate 					tmip->unbind_mp = NULL;
5837c478bd9Sstevel@tonic-gate 				} else {
5847c478bd9Sstevel@tonic-gate 					putnext(q, mp);
5857c478bd9Sstevel@tonic-gate 				}
5867c478bd9Sstevel@tonic-gate 				break;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 			case T_DATA_IND: /* conform to TPI, but never happens */
5897c478bd9Sstevel@tonic-gate 			case T_EXDATA_IND:
5907c478bd9Sstevel@tonic-gate 				newmp = mp->b_cont;
5917c478bd9Sstevel@tonic-gate 				freeb(mp);
5927c478bd9Sstevel@tonic-gate 				mp = newmp;
5937c478bd9Sstevel@tonic-gate 				if (mp) {
5947c478bd9Sstevel@tonic-gate 					ASSERT(mp->b_datap->db_type == M_DATA);
5957c478bd9Sstevel@tonic-gate 					if (msgdsize(mp) != 0) {
5967c478bd9Sstevel@tonic-gate 						goto is_mdata;
5977c478bd9Sstevel@tonic-gate 					}
5987c478bd9Sstevel@tonic-gate 					freemsg(mp);
5997c478bd9Sstevel@tonic-gate 				}
6007c478bd9Sstevel@tonic-gate 				break;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 			/*
6037c478bd9Sstevel@tonic-gate 			 * We only get T_OK_ACK when we issue the unbind, and
6047c478bd9Sstevel@tonic-gate 			 * it can be ignored safely.
6057c478bd9Sstevel@tonic-gate 			 */
6067c478bd9Sstevel@tonic-gate 			case T_OK_ACK:
6077c478bd9Sstevel@tonic-gate 				ASSERT(tmip->unbind_mp == NULL);
6087c478bd9Sstevel@tonic-gate 				freemsg(mp);
6097c478bd9Sstevel@tonic-gate 				break;
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 			default:
6127c478bd9Sstevel@tonic-gate #ifdef DEBUG
6137c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE,
6147c478bd9Sstevel@tonic-gate 				    "telmodrsrv: unexpected TLI primitive "
6157c478bd9Sstevel@tonic-gate 				    "msg type 0x%x", tip->type);
6167c478bd9Sstevel@tonic-gate #endif
6177c478bd9Sstevel@tonic-gate 				freemsg(mp);
6187c478bd9Sstevel@tonic-gate 			}
6197c478bd9Sstevel@tonic-gate 			break;
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 		case M_SETOPTS:
6227c478bd9Sstevel@tonic-gate 			putnext(q, mp);
6237c478bd9Sstevel@tonic-gate 			break;
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 		default:
6267c478bd9Sstevel@tonic-gate #ifdef DEBUG
6277c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE,
6287c478bd9Sstevel@tonic-gate 			    "telmodrsrv: unexpected msg type 0x%x",
6297c478bd9Sstevel@tonic-gate 			    mp->b_datap->db_type);
6307c478bd9Sstevel@tonic-gate #endif
6317c478bd9Sstevel@tonic-gate 			freemsg(mp);
6327c478bd9Sstevel@tonic-gate 		}
6337c478bd9Sstevel@tonic-gate 	}
634*c97b1070SToomas Soome 	return (0);
6357c478bd9Sstevel@tonic-gate }
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate /*
6387c478bd9Sstevel@tonic-gate  * telmodwput:
6397c478bd9Sstevel@tonic-gate  * M_DATA is processed and forwarded if we aren't stopped awaiting the daemon
6407c478bd9Sstevel@tonic-gate  * to process something.  M_CTL's are data from the daemon bound for the
6417c478bd9Sstevel@tonic-gate  * network.  We forward them immediately.  There are two classes of ioctl's
6427c478bd9Sstevel@tonic-gate  * we must handle here also.  One is ioctl's forwarded by ptem which we
6437c478bd9Sstevel@tonic-gate  * ignore.  The other is ioctl's issued by the daemon to control us.
6447c478bd9Sstevel@tonic-gate  * Process them appropriately.  M_PROTO's we pass along, figuring they are
6457c478bd9Sstevel@tonic-gate  * are TPI operations for TCP.  M_FLUSH requires careful processing, since
6467c478bd9Sstevel@tonic-gate  * telnet cannot tolerate flushing its protocol requests.  Also the flushes
6477c478bd9Sstevel@tonic-gate  * can be running either daemon<->TCP or application<->telmod.  We must
6487c478bd9Sstevel@tonic-gate  * carefully deal with this.
6497c478bd9Sstevel@tonic-gate  */
650*c97b1070SToomas Soome static int
telmodwput(queue_t * q,mblk_t * mp)6517c478bd9Sstevel@tonic-gate telmodwput(
6527c478bd9Sstevel@tonic-gate 	queue_t *q,	/* Pointer to the read queue */
6537c478bd9Sstevel@tonic-gate 	mblk_t *mp)	/* Pointer to current message block */
6547c478bd9Sstevel@tonic-gate {
6557c478bd9Sstevel@tonic-gate 	struct telmod_info	*tmip;
6567c478bd9Sstevel@tonic-gate 	struct iocblk *ioc;
6577c478bd9Sstevel@tonic-gate 	mblk_t *savemp;
6587c478bd9Sstevel@tonic-gate 	int rw;
6597c478bd9Sstevel@tonic-gate 	int error;
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	tmip = (struct telmod_info *)q->q_ptr;
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
6647c478bd9Sstevel@tonic-gate 	case M_DATA:
6657c478bd9Sstevel@tonic-gate 		if (!canputnext(q) || (tmip->flags & TEL_STOPPED) ||
6667c478bd9Sstevel@tonic-gate 		    (q->q_first)) {
6677c478bd9Sstevel@tonic-gate 			noenable(q);
6687c478bd9Sstevel@tonic-gate 			(void) putq(q, mp);
6697c478bd9Sstevel@tonic-gate 			break;
6707c478bd9Sstevel@tonic-gate 		}
6717c478bd9Sstevel@tonic-gate 		/*
6727c478bd9Sstevel@tonic-gate 		 * This routine parses data generating from ptm side.
6737c478bd9Sstevel@tonic-gate 		 * Insert a null character if carraige return
6747c478bd9Sstevel@tonic-gate 		 * is not followed by line feed unless we are in binary mode.
6757c478bd9Sstevel@tonic-gate 		 * Also, duplicate IAC if found in the data.
6767c478bd9Sstevel@tonic-gate 		 */
6777c478bd9Sstevel@tonic-gate 		(void) snd_parse(q, mp);
6787c478bd9Sstevel@tonic-gate 		break;
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	case M_CTL:
6817c478bd9Sstevel@tonic-gate 		if (((mp->b_wptr - mp->b_rptr) == 1) &&
6827c478bd9Sstevel@tonic-gate 		    (*(mp->b_rptr) == M_CTL_MAGIC_NUMBER)) {
6837c478bd9Sstevel@tonic-gate 			savemp = mp->b_cont;
6847c478bd9Sstevel@tonic-gate 			freeb(mp);
6857c478bd9Sstevel@tonic-gate 			mp = savemp;
6867c478bd9Sstevel@tonic-gate 		}
6877c478bd9Sstevel@tonic-gate 		putnext(q, mp);
6887c478bd9Sstevel@tonic-gate 		break;
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	case M_IOCTL:
6917c478bd9Sstevel@tonic-gate 		ioc = (struct iocblk *)mp->b_rptr;
6927c478bd9Sstevel@tonic-gate 		switch (ioc->ioc_cmd) {
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 		/*
6957c478bd9Sstevel@tonic-gate 		 * This ioctl is issued by user level daemon to
6967c478bd9Sstevel@tonic-gate 		 * request one more message block to process protocol
6977c478bd9Sstevel@tonic-gate 		 */
6987c478bd9Sstevel@tonic-gate 		case TEL_IOC_GETBLK:
6997c478bd9Sstevel@tonic-gate 			if (!(tmip->flags & TEL_STOPPED)) {
7007c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
7017c478bd9Sstevel@tonic-gate 				break;
7027c478bd9Sstevel@tonic-gate 			}
7037c478bd9Sstevel@tonic-gate 			tmip->flags |= TEL_GETBLK;
7047c478bd9Sstevel@tonic-gate 			qenable(RD(q));
7057c478bd9Sstevel@tonic-gate 			enableok(RD(q));
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
7087c478bd9Sstevel@tonic-gate 			break;
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 		/*
7117c478bd9Sstevel@tonic-gate 		 * This ioctl is issued by user level daemon to reenable the
7127c478bd9Sstevel@tonic-gate 		 * read and write queues. This is issued during startup time
7137c478bd9Sstevel@tonic-gate 		 * after setting up the mux links and also after processing
7147c478bd9Sstevel@tonic-gate 		 * the protocol.  It is also issued after each time an
7157c478bd9Sstevel@tonic-gate 		 * an unrecognized telnet option is forwarded to the daemon.
7167c478bd9Sstevel@tonic-gate 		 */
7177c478bd9Sstevel@tonic-gate 		case TEL_IOC_ENABLE:
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 			/*
7207c478bd9Sstevel@tonic-gate 			 * Send negative ack if TEL_STOPPED flag is not set
7217c478bd9Sstevel@tonic-gate 			 */
7227c478bd9Sstevel@tonic-gate 			if (!(tmip->flags & TEL_STOPPED)) {
7237c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
7247c478bd9Sstevel@tonic-gate 				break;
7257c478bd9Sstevel@tonic-gate 			}
7267c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_STOPPED;
7277c478bd9Sstevel@tonic-gate 			if (mp->b_cont) {
7287c478bd9Sstevel@tonic-gate 				(void) putbq(RD(q), mp->b_cont);
7297c478bd9Sstevel@tonic-gate 				mp->b_cont = 0;
7307c478bd9Sstevel@tonic-gate 			}
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 			qenable(RD(q));
7337c478bd9Sstevel@tonic-gate 			enableok(RD(q));
7347c478bd9Sstevel@tonic-gate 			qenable(q);
7357c478bd9Sstevel@tonic-gate 			enableok(q);
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
7387c478bd9Sstevel@tonic-gate 			break;
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 		/*
7417c478bd9Sstevel@tonic-gate 		 * Set binary/normal mode for input and output
7427c478bd9Sstevel@tonic-gate 		 * according to the instructions from the daemon.
7437c478bd9Sstevel@tonic-gate 		 */
7447c478bd9Sstevel@tonic-gate 		case TEL_IOC_MODE:
7457c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uchar_t));
7467c478bd9Sstevel@tonic-gate 			if (error != 0) {
7477c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
7487c478bd9Sstevel@tonic-gate 				break;
7497c478bd9Sstevel@tonic-gate 			}
7507c478bd9Sstevel@tonic-gate 			tmip->flags |= *(mp->b_cont->b_rptr) &
7517c478bd9Sstevel@tonic-gate 			    (TEL_BINARY_IN|TEL_BINARY_OUT);
7527c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
7537c478bd9Sstevel@tonic-gate 			break;
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate #ifdef DEBUG
7567c478bd9Sstevel@tonic-gate 		case TCSETAF:
7577c478bd9Sstevel@tonic-gate 		case TCSETSF:
7587c478bd9Sstevel@tonic-gate 		case TCSETA:
7597c478bd9Sstevel@tonic-gate 		case TCSETAW:
7607c478bd9Sstevel@tonic-gate 		case TCSETS:
7617c478bd9Sstevel@tonic-gate 		case TCSETSW:
7627c478bd9Sstevel@tonic-gate 		case TCSBRK:
7637c478bd9Sstevel@tonic-gate 		case TIOCSTI:
7647c478bd9Sstevel@tonic-gate 		case TIOCSWINSZ:
7657c478bd9Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
7667c478bd9Sstevel@tonic-gate 			break;
7677c478bd9Sstevel@tonic-gate #endif
7687c478bd9Sstevel@tonic-gate 		case CRYPTPASSTHRU:
7697c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uchar_t));
7707c478bd9Sstevel@tonic-gate 			if (error != 0) {
7717c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
7727c478bd9Sstevel@tonic-gate 				break;
7737c478bd9Sstevel@tonic-gate 			}
7747c478bd9Sstevel@tonic-gate 			if (*(mp->b_cont->b_rptr) == 0x01)
7757c478bd9Sstevel@tonic-gate 				tmip->flags |= TEL_IOCPASSTHRU;
7767c478bd9Sstevel@tonic-gate 			else
7777c478bd9Sstevel@tonic-gate 				tmip->flags &= ~TEL_IOCPASSTHRU;
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
7807c478bd9Sstevel@tonic-gate 			break;
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 		default:
7837c478bd9Sstevel@tonic-gate 			if (tmip->flags & TEL_IOCPASSTHRU) {
7847c478bd9Sstevel@tonic-gate 				putnext(q, mp);
7857c478bd9Sstevel@tonic-gate 			} else {
7867c478bd9Sstevel@tonic-gate #ifdef DEBUG
7877c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE,
7887c478bd9Sstevel@tonic-gate 				    "telmodwput: unexpected ioctl type 0x%x",
7897c478bd9Sstevel@tonic-gate 				    ioc->ioc_cmd);
7907c478bd9Sstevel@tonic-gate #endif
7917c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
7927c478bd9Sstevel@tonic-gate 			}
7937c478bd9Sstevel@tonic-gate 			break;
7947c478bd9Sstevel@tonic-gate 		}
7957c478bd9Sstevel@tonic-gate 		break;
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	case M_FLUSH:
7987c478bd9Sstevel@tonic-gate 		/*
7997c478bd9Sstevel@tonic-gate 		 * Flushing is tricky:  We try to flush all we can, but certain
8007c478bd9Sstevel@tonic-gate 		 * data cannot be flushed.  Telnet protocol sequences cannot
8017c478bd9Sstevel@tonic-gate 		 * be flushed.  So, TCP's queues cannot be flushed since we
8027c478bd9Sstevel@tonic-gate 		 * cannot tell what might be telnet protocol data.  Then we
8037c478bd9Sstevel@tonic-gate 		 * must take care to create and forward out-of-band data
8047c478bd9Sstevel@tonic-gate 		 * indicating the flush to the far side.
8057c478bd9Sstevel@tonic-gate 		 */
8067c478bd9Sstevel@tonic-gate 		rw = *mp->b_rptr;
8077c478bd9Sstevel@tonic-gate 		if (rw & FLUSHR) {
8087c478bd9Sstevel@tonic-gate 			/*
8097c478bd9Sstevel@tonic-gate 			 * We cannot flush our read queue, since there may
8107c478bd9Sstevel@tonic-gate 			 * be telnet protocol bits in the queue, awaiting
8117c478bd9Sstevel@tonic-gate 			 * processing.  However, once it leaves this module
8127c478bd9Sstevel@tonic-gate 			 * it's guaranteed that all protocol data is in
8137c478bd9Sstevel@tonic-gate 			 * M_CTL, so we do flush read data beyond us, expecting
8147c478bd9Sstevel@tonic-gate 			 * them (actually logindmux) to do FLUSHDATAs also.
8157c478bd9Sstevel@tonic-gate 			 */
8167c478bd9Sstevel@tonic-gate 			*mp->b_rptr = rw & ~FLUSHW;
8177c478bd9Sstevel@tonic-gate 			qreply(q, mp);
8187c478bd9Sstevel@tonic-gate 		} else {
8197c478bd9Sstevel@tonic-gate 			freemsg(mp);
8207c478bd9Sstevel@tonic-gate 		}
8217c478bd9Sstevel@tonic-gate 		if (rw & FLUSHW) {
8227c478bd9Sstevel@tonic-gate 			/*
8237c478bd9Sstevel@tonic-gate 			 * Since all telnet protocol data comes from the
8247c478bd9Sstevel@tonic-gate 			 * daemon, stored as M_CTL messages, flushq will
8257c478bd9Sstevel@tonic-gate 			 * do exactly what's needed:  Flush bytes which do
8267c478bd9Sstevel@tonic-gate 			 * not have telnet protocol data.
8277c478bd9Sstevel@tonic-gate 			 */
8287c478bd9Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
8297c478bd9Sstevel@tonic-gate 		}
8307c478bd9Sstevel@tonic-gate 		break;
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	case M_PCPROTO:
8337c478bd9Sstevel@tonic-gate 		putnext(q, mp);
8347c478bd9Sstevel@tonic-gate 		break;
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 	case M_PROTO:
8377c478bd9Sstevel@tonic-gate 		/* We may receive T_DISCON_REQ from the mux */
8387c478bd9Sstevel@tonic-gate 		if (!canputnext(q) || q->q_first != NULL)
8397c478bd9Sstevel@tonic-gate 			(void) putq(q, mp);
8407c478bd9Sstevel@tonic-gate 		else
8417c478bd9Sstevel@tonic-gate 			putnext(q, mp);
8427c478bd9Sstevel@tonic-gate 		break;
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 	default:
8457c478bd9Sstevel@tonic-gate #ifdef DEBUG
8467c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE,
8477c478bd9Sstevel@tonic-gate 		    "telmodwput: unexpected msg type 0x%x",
8487c478bd9Sstevel@tonic-gate 		    mp->b_datap->db_type);
8497c478bd9Sstevel@tonic-gate #endif
8507c478bd9Sstevel@tonic-gate 		freemsg(mp);
8517c478bd9Sstevel@tonic-gate 		break;
8527c478bd9Sstevel@tonic-gate 	}
853*c97b1070SToomas Soome 	return (0);
8547c478bd9Sstevel@tonic-gate }
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate /*
8577c478bd9Sstevel@tonic-gate  * telmodwsrv - module write service procedure
8587c478bd9Sstevel@tonic-gate  */
859*c97b1070SToomas Soome static int
telmodwsrv(queue_t * q)8607c478bd9Sstevel@tonic-gate telmodwsrv(queue_t *q)
8617c478bd9Sstevel@tonic-gate {
8627c478bd9Sstevel@tonic-gate 	mblk_t	*mp, *savemp;
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	struct	telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
8677c478bd9Sstevel@tonic-gate 		if (!canputnext(q)) {
8687c478bd9Sstevel@tonic-gate 			ASSERT(mp->b_datap->db_type < QPCTL);
8697c478bd9Sstevel@tonic-gate 			(void) putbq(q, mp);
870*c97b1070SToomas Soome 			return (0);
8717c478bd9Sstevel@tonic-gate 		}
8727c478bd9Sstevel@tonic-gate 		switch (mp->b_datap->db_type) {
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate 		case M_DATA:
8757c478bd9Sstevel@tonic-gate 			if (tmip->flags & TEL_STOPPED) {
8767c478bd9Sstevel@tonic-gate 				(void) putbq(q, mp);
877*c97b1070SToomas Soome 				return (0);
8787c478bd9Sstevel@tonic-gate 			}
8797c478bd9Sstevel@tonic-gate 			/*
8807c478bd9Sstevel@tonic-gate 			 * Insert a null character if carraige return
8817c478bd9Sstevel@tonic-gate 			 * is not followed by line feed
8827c478bd9Sstevel@tonic-gate 			 */
8837c478bd9Sstevel@tonic-gate 			if (!snd_parse(q, mp)) {
884*c97b1070SToomas Soome 				return (0);
8857c478bd9Sstevel@tonic-gate 			}
8867c478bd9Sstevel@tonic-gate 			break;
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 		case M_CTL:
8897c478bd9Sstevel@tonic-gate 			if (((mp->b_wptr - mp->b_rptr) == 1) &&
8907c478bd9Sstevel@tonic-gate 			    (*(mp->b_rptr) == M_CTL_MAGIC_NUMBER)) {
8917c478bd9Sstevel@tonic-gate 				savemp = mp->b_cont;
8927c478bd9Sstevel@tonic-gate 				freeb(mp);
8937c478bd9Sstevel@tonic-gate 				mp = savemp;
8947c478bd9Sstevel@tonic-gate 			}
8957c478bd9Sstevel@tonic-gate 			putnext(q, mp);
8967c478bd9Sstevel@tonic-gate 			break;
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 		case M_PROTO:
8997c478bd9Sstevel@tonic-gate 			putnext(q, mp);
9007c478bd9Sstevel@tonic-gate 			break;
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 		default:
9037c478bd9Sstevel@tonic-gate #ifdef DEBUG
9047c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE,
9057c478bd9Sstevel@tonic-gate 			    "telmodwsrv: unexpected msg type 0x%x",
9067c478bd9Sstevel@tonic-gate 			    mp->b_datap->db_type);
9077c478bd9Sstevel@tonic-gate #endif
9087c478bd9Sstevel@tonic-gate 			freemsg(mp);
9097c478bd9Sstevel@tonic-gate 		}
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 	}
912*c97b1070SToomas Soome 	return (0);
9137c478bd9Sstevel@tonic-gate }
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate /*
9167c478bd9Sstevel@tonic-gate  * This routine is called from read put/service procedure and parses
9177c478bd9Sstevel@tonic-gate  * message block to check for telnet protocol by detecting an IAC.
9187c478bd9Sstevel@tonic-gate  * The routine processes the data part of the message block first and
9197c478bd9Sstevel@tonic-gate  * then sends protocol followed after IAC to the telnet daemon. The
9207c478bd9Sstevel@tonic-gate  * routine also processes CR/LF by eliminating LF/NULL followed after CR.
9217c478bd9Sstevel@tonic-gate  *
9227c478bd9Sstevel@tonic-gate  * Since the code to do this with streams mblks is complicated, some
9237c478bd9Sstevel@tonic-gate  * explanations are in order.  If an IAC is found, a dupb() is done,
9247c478bd9Sstevel@tonic-gate  * and the pointers are adjusted to create two streams message.  The
9257c478bd9Sstevel@tonic-gate  * (possibly empty) first message contains preceeding data, and the
9267c478bd9Sstevel@tonic-gate  * second begins with the IAC and contains the rest of the streams
9277c478bd9Sstevel@tonic-gate  * message.
9287c478bd9Sstevel@tonic-gate  *
9297c478bd9Sstevel@tonic-gate  * The variables:
9307c478bd9Sstevel@tonic-gate  * datamp:	Points to the head of a chain of mblks containing data
9317c478bd9Sstevel@tonic-gate  *		which requires no expansion, and can be forwarded directly
9327c478bd9Sstevel@tonic-gate  *		to the pty.
9337c478bd9Sstevel@tonic-gate  * prevmp:	Points to the last mblk on the datamp chain, used to add
9347c478bd9Sstevel@tonic-gate  *		to the chain headed by datamp.
9357c478bd9Sstevel@tonic-gate  * newmp:	When an M_CTL header is required, this pointer references
9367c478bd9Sstevel@tonic-gate  *		that "header" mblk.
9377c478bd9Sstevel@tonic-gate  * protomp:	When an IAC is discovered, a dupb() is done on the first mblk
9387c478bd9Sstevel@tonic-gate  *		containing an IAC.  protomp points to this dup'ed mblk.
9397c478bd9Sstevel@tonic-gate  *		This mblk is eventually forwarded to the daemon.
9407c478bd9Sstevel@tonic-gate  */
9417c478bd9Sstevel@tonic-gate static int
rcv_parse(queue_t * q,mblk_t * mp)9427c478bd9Sstevel@tonic-gate rcv_parse(queue_t *q, mblk_t *mp)
9437c478bd9Sstevel@tonic-gate {
9447c478bd9Sstevel@tonic-gate 	mblk_t	*protomp, *newmp, *datamp, *prevmp;
9457c478bd9Sstevel@tonic-gate 	unsigned char *tmp;
9467c478bd9Sstevel@tonic-gate 	size_t	msgsize;
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	datamp = mp;
9517c478bd9Sstevel@tonic-gate 	prevmp = protomp = 0;
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 	while (mp) {
9547c478bd9Sstevel@tonic-gate 		/*
9557c478bd9Sstevel@tonic-gate 		 * If the mblk is empty, just continue scanning.
9567c478bd9Sstevel@tonic-gate 		 */
9577c478bd9Sstevel@tonic-gate 		if (mp->b_rptr == mp->b_wptr) {
9587c478bd9Sstevel@tonic-gate 			prevmp = mp;
9597c478bd9Sstevel@tonic-gate 			mp = mp->b_cont;
9607c478bd9Sstevel@tonic-gate 			continue;
9617c478bd9Sstevel@tonic-gate 		}
9627c478bd9Sstevel@tonic-gate 		/*
9637c478bd9Sstevel@tonic-gate 		 * First check to see if we have received CR and are checking
9647c478bd9Sstevel@tonic-gate 		 * for a following LF/NULL.  If so, do what's necessary to
9657c478bd9Sstevel@tonic-gate 		 * trim the LF/NULL.  This case is for when the LF/NULL is
9667c478bd9Sstevel@tonic-gate 		 * at the beginning of a subsequent mblk.
9677c478bd9Sstevel@tonic-gate 		 */
9687c478bd9Sstevel@tonic-gate 		if (!(tmip->flags & TEL_BINARY_IN) &&
9697c478bd9Sstevel@tonic-gate 		    (tmip->flags & TEL_CRRCV)) {
970c86160f0SToomas Soome 			if ((*mp->b_rptr == '\n') || (*mp->b_rptr == '\0')) {
9717c478bd9Sstevel@tonic-gate 				if (mp->b_wptr == (mp->b_rptr + 1)) {
9727c478bd9Sstevel@tonic-gate 					tmip->flags &= ~TEL_CRRCV;
9737c478bd9Sstevel@tonic-gate 					if (prevmp) {
9747c478bd9Sstevel@tonic-gate 						prevmp->b_cont = mp->b_cont;
9757c478bd9Sstevel@tonic-gate 						freeb(mp);
9767c478bd9Sstevel@tonic-gate 						mp = prevmp->b_cont;
9777c478bd9Sstevel@tonic-gate 						continue;
9787c478bd9Sstevel@tonic-gate 					} else {
9797c478bd9Sstevel@tonic-gate 						datamp = mp->b_cont;
9807c478bd9Sstevel@tonic-gate 						freeb(mp);
9817c478bd9Sstevel@tonic-gate 						if (datamp == NULL) {
9827c478bd9Sstevel@tonic-gate 							/*
9837c478bd9Sstevel@tonic-gate 							 * Message contained
9847c478bd9Sstevel@tonic-gate 							 * only a '\0' after
9857c478bd9Sstevel@tonic-gate 							 * a '\r' in a previous
9867c478bd9Sstevel@tonic-gate 							 * message, so we can
9877c478bd9Sstevel@tonic-gate 							 * read more, even
9887c478bd9Sstevel@tonic-gate 							 * though we have
9897c478bd9Sstevel@tonic-gate 							 * nothing to putnext.
9907c478bd9Sstevel@tonic-gate 							 */
9917c478bd9Sstevel@tonic-gate 							return (1);
9927c478bd9Sstevel@tonic-gate 						} else {
9937c478bd9Sstevel@tonic-gate 							mp = datamp;
9947c478bd9Sstevel@tonic-gate 							continue;
9957c478bd9Sstevel@tonic-gate 						}
9967c478bd9Sstevel@tonic-gate 					}
9977c478bd9Sstevel@tonic-gate 				}
9987c478bd9Sstevel@tonic-gate 				mp->b_rptr += 1;
9997c478bd9Sstevel@tonic-gate 			}
10007c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_CRRCV;
10017c478bd9Sstevel@tonic-gate 		}
10027c478bd9Sstevel@tonic-gate 		tmp = mp->b_rptr;
10037c478bd9Sstevel@tonic-gate 		/*
10047c478bd9Sstevel@tonic-gate 		 * Now scan through the entire message block, for IACs
10057c478bd9Sstevel@tonic-gate 		 * and CR characters, which need processing.
10067c478bd9Sstevel@tonic-gate 		 */
10077c478bd9Sstevel@tonic-gate 		while (tmp < mp->b_wptr) {
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 			if (tmp[0] == IAC) {
10107c478bd9Sstevel@tonic-gate 				/*
10117c478bd9Sstevel@tonic-gate 				 * Telnet protocol - parse it now
10127c478bd9Sstevel@tonic-gate 				 * process data part of mblk
10137c478bd9Sstevel@tonic-gate 				 * before sending the protocol.
10147c478bd9Sstevel@tonic-gate 				 */
10157c478bd9Sstevel@tonic-gate 				if (tmp > mp->b_rptr) {
10167c478bd9Sstevel@tonic-gate 					if ((protomp = dupb(mp)) == NULL) {
10177c478bd9Sstevel@tonic-gate 						msgsize = msgdsize(datamp);
10187c478bd9Sstevel@tonic-gate 						recover(q, datamp, msgsize);
10197c478bd9Sstevel@tonic-gate 						return (0);
10207c478bd9Sstevel@tonic-gate 					}
10217c478bd9Sstevel@tonic-gate 					ASSERT(tmp >= mp->b_datap->db_base);
10227c478bd9Sstevel@tonic-gate 					ASSERT(tmp <= mp->b_datap->db_lim);
10237c478bd9Sstevel@tonic-gate 					ASSERT(tmp >=
10247c478bd9Sstevel@tonic-gate 					    protomp->b_datap->db_base);
10257c478bd9Sstevel@tonic-gate 					ASSERT(tmp <= protomp->b_datap->db_lim);
10267c478bd9Sstevel@tonic-gate 					mp->b_wptr = tmp;
10277c478bd9Sstevel@tonic-gate 					protomp->b_rptr = tmp;
10287c478bd9Sstevel@tonic-gate 					protomp->b_cont = mp->b_cont;
10297c478bd9Sstevel@tonic-gate 					mp->b_cont = 0;
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate 					if (prevmp)
10327c478bd9Sstevel@tonic-gate 						prevmp->b_cont = mp;
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 				} else {
10357c478bd9Sstevel@tonic-gate 					protomp = mp;
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 					if (prevmp)
10387c478bd9Sstevel@tonic-gate 						prevmp->b_cont = 0;
10397c478bd9Sstevel@tonic-gate 					else
10407c478bd9Sstevel@tonic-gate 						datamp = 0;
10417c478bd9Sstevel@tonic-gate 				}
10427c478bd9Sstevel@tonic-gate 				if (datamp) {
10437c478bd9Sstevel@tonic-gate 					putnext(q, datamp);
10447c478bd9Sstevel@tonic-gate 				}
10457c478bd9Sstevel@tonic-gate 				/*
10467c478bd9Sstevel@tonic-gate 				 * create a 1 byte M_CTL message block with
10477c478bd9Sstevel@tonic-gate 				 * protomp and send it down.
10487c478bd9Sstevel@tonic-gate 				 */
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 				if ((newmp = allocb(sizeof (char),
10517c478bd9Sstevel@tonic-gate 				    BPRI_MED)) == NULL) {
10527c478bd9Sstevel@tonic-gate 					/*
10537c478bd9Sstevel@tonic-gate 					 * Save the dup'ed mp containing
10547c478bd9Sstevel@tonic-gate 					 * the protocol information which
10557c478bd9Sstevel@tonic-gate 					 * we couldn't get an M_CTL header
10567c478bd9Sstevel@tonic-gate 					 * for.
10577c478bd9Sstevel@tonic-gate 					 */
10587c478bd9Sstevel@tonic-gate 					msgsize = msgdsize(protomp);
10597c478bd9Sstevel@tonic-gate 					recover(q, protomp, msgsize);
10607c478bd9Sstevel@tonic-gate 					return (0);
10617c478bd9Sstevel@tonic-gate 				}
10627c478bd9Sstevel@tonic-gate 				newmp->b_datap->db_type = M_CTL;
10637c478bd9Sstevel@tonic-gate 				newmp->b_wptr = newmp->b_rptr + 1;
10647c478bd9Sstevel@tonic-gate 				*(newmp->b_rptr) = M_CTL_MAGIC_NUMBER;
10657c478bd9Sstevel@tonic-gate 				newmp->b_cont = protomp;
10667c478bd9Sstevel@tonic-gate 				noenable(q);
10677c478bd9Sstevel@tonic-gate 				tmip->flags |= TEL_STOPPED;
10687c478bd9Sstevel@tonic-gate 				putnext(q, newmp);
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 				return (0);
10717c478bd9Sstevel@tonic-gate 			}
10727c478bd9Sstevel@tonic-gate 			if (!(tmip->flags & TEL_BINARY_IN)) {
10737c478bd9Sstevel@tonic-gate 				/*
10747c478bd9Sstevel@tonic-gate 				 * Set TEL_CRRCV flag if last character is CR
10757c478bd9Sstevel@tonic-gate 				 */
10767c478bd9Sstevel@tonic-gate 				if ((tmp == (mp->b_wptr - 1)) &&
10777c478bd9Sstevel@tonic-gate 				    (tmp[0] == '\r')) {
10787c478bd9Sstevel@tonic-gate 					tmip->flags |= TEL_CRRCV;
10797c478bd9Sstevel@tonic-gate 					break;
10807c478bd9Sstevel@tonic-gate 				}
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 				/*
10837c478bd9Sstevel@tonic-gate 				 * If CR is followed by LF/NULL, get rid of
10847c478bd9Sstevel@tonic-gate 				 * LF/NULL and realign the message block.
10857c478bd9Sstevel@tonic-gate 				 */
10867c478bd9Sstevel@tonic-gate 				if ((tmp[0] == '\r') && ((tmp[1] == '\n') ||
1087c86160f0SToomas Soome 				    (tmp[1] == '\0'))) {
10887c478bd9Sstevel@tonic-gate 					/*
10897c478bd9Sstevel@tonic-gate 					 * If CR is in the middle of a block,
10907c478bd9Sstevel@tonic-gate 					 * we need to get rid of LF and join
10917c478bd9Sstevel@tonic-gate 					 * the two pieces together.
10927c478bd9Sstevel@tonic-gate 					 */
10937c478bd9Sstevel@tonic-gate 					if (mp->b_wptr > (tmp + 2)) {
10947c478bd9Sstevel@tonic-gate 						bcopy(tmp + 2, tmp + 1,
10957c478bd9Sstevel@tonic-gate 						    (mp->b_wptr - tmp - 2));
10967c478bd9Sstevel@tonic-gate 						mp->b_wptr -= 1;
10977c478bd9Sstevel@tonic-gate 					} else {
10987c478bd9Sstevel@tonic-gate 						mp->b_wptr = tmp + 1;
10997c478bd9Sstevel@tonic-gate 					}
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 					if (prevmp)
11027c478bd9Sstevel@tonic-gate 						prevmp->b_cont = mp;
11037c478bd9Sstevel@tonic-gate 				}
11047c478bd9Sstevel@tonic-gate 			}
11057c478bd9Sstevel@tonic-gate 			tmp++;
11067c478bd9Sstevel@tonic-gate 		}
11077c478bd9Sstevel@tonic-gate 		prevmp = mp;
11087c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
11097c478bd9Sstevel@tonic-gate 	}
11107c478bd9Sstevel@tonic-gate 	putnext(q, datamp);
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate 	return (1);
11137c478bd9Sstevel@tonic-gate }
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate /*
11167c478bd9Sstevel@tonic-gate  * This routine is called from write put/service procedures and processes
11177c478bd9Sstevel@tonic-gate  * CR-LF. If CR is not followed by LF, it inserts a NULL character if we are
11187c478bd9Sstevel@tonic-gate  * in non binary mode. Also, duplicate IAC(0xFF) if found in the mblk.
11197c478bd9Sstevel@tonic-gate  * This routine is pessimistic:  It pre-allocates a buffer twice the size
11207c478bd9Sstevel@tonic-gate  * of the incoming message, which is the maximum size a message can become
11217c478bd9Sstevel@tonic-gate  * after IAC expansion.
11227c478bd9Sstevel@tonic-gate  *
11237c478bd9Sstevel@tonic-gate  * savemp:	Points at the original message, so it can be freed when
11247c478bd9Sstevel@tonic-gate  *		processing is complete.
11257c478bd9Sstevel@tonic-gate  * mp:		The current point of scanning the message.
11267c478bd9Sstevel@tonic-gate  * newmp:	New message being created with the processed output.
11277c478bd9Sstevel@tonic-gate  */
11287c478bd9Sstevel@tonic-gate static int
snd_parse(queue_t * q,mblk_t * mp)11297c478bd9Sstevel@tonic-gate snd_parse(queue_t *q, mblk_t *mp)
11307c478bd9Sstevel@tonic-gate {
11317c478bd9Sstevel@tonic-gate 	unsigned char *tmp, *tmp1;
11327c478bd9Sstevel@tonic-gate 	mblk_t	*newmp, *savemp;
11337c478bd9Sstevel@tonic-gate 	struct  telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
11347c478bd9Sstevel@tonic-gate 	size_t size = msgdsize(mp);
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	savemp = mp;
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	if (size == 0) {
11397c478bd9Sstevel@tonic-gate 		putnext(q, mp);
11407c478bd9Sstevel@tonic-gate 		return (1);
11417c478bd9Sstevel@tonic-gate 	}
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	/*
11447c478bd9Sstevel@tonic-gate 	 * Extra byte to allocb() takes care of the case when there was
11457c478bd9Sstevel@tonic-gate 	 * a '\r' at the end of the previous message and there's a '\r'
11467c478bd9Sstevel@tonic-gate 	 * at the beginning of the current message.
11477c478bd9Sstevel@tonic-gate 	 */
11487c478bd9Sstevel@tonic-gate 	if ((newmp = allocb((2 * size)+1, BPRI_MED)) == NULL) {
11497c478bd9Sstevel@tonic-gate 		recover(q, mp, (2 * size)+1);
11507c478bd9Sstevel@tonic-gate 		return (0);
11517c478bd9Sstevel@tonic-gate 	}
11527c478bd9Sstevel@tonic-gate 	newmp->b_datap->db_type = M_DATA;
11537c478bd9Sstevel@tonic-gate 
11547c478bd9Sstevel@tonic-gate 	tmp1 = newmp->b_rptr;
11557c478bd9Sstevel@tonic-gate 	while (mp) {
11567c478bd9Sstevel@tonic-gate 		if (!(tmip->flags & TEL_BINARY_OUT) &&
11577c478bd9Sstevel@tonic-gate 		    (tmip->flags & TEL_CRSND)) {
11587c478bd9Sstevel@tonic-gate 			if (*(mp->b_rptr) != '\n')
1159c86160f0SToomas Soome 				*tmp1++ = '\0';
11607c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_CRSND;
11617c478bd9Sstevel@tonic-gate 		}
11627c478bd9Sstevel@tonic-gate 		tmp = mp->b_rptr;
11637c478bd9Sstevel@tonic-gate 		while (tmp < mp->b_wptr) {
11647c478bd9Sstevel@tonic-gate 			if (!(tmip->flags & TEL_BINARY_OUT)) {
11657c478bd9Sstevel@tonic-gate 				*tmp1++ = *tmp;
11667c478bd9Sstevel@tonic-gate 				if ((tmp == (mp->b_wptr - 1)) &&
11677c478bd9Sstevel@tonic-gate 				    (tmp[0] == '\r')) {
11687c478bd9Sstevel@tonic-gate 						tmip->flags |= TEL_CRSND;
11697c478bd9Sstevel@tonic-gate 						break;
11707c478bd9Sstevel@tonic-gate 				}
11717c478bd9Sstevel@tonic-gate 				if ((tmp[0] == '\r') &&
11727c478bd9Sstevel@tonic-gate 				    (tmp1 == newmp->b_wptr)) {
11737c478bd9Sstevel@tonic-gate 					/* XXX.sparker: can't happen */
11747c478bd9Sstevel@tonic-gate 					tmip->flags |= TEL_CRSND;
11757c478bd9Sstevel@tonic-gate 					break;
11767c478bd9Sstevel@tonic-gate 				}
11777c478bd9Sstevel@tonic-gate 				if ((tmp[0] == '\r') && (tmp[1] != '\n')) {
1178c86160f0SToomas Soome 					*tmp1++ = '\0';
11797c478bd9Sstevel@tonic-gate 				}
11807c478bd9Sstevel@tonic-gate 			} else
11817c478bd9Sstevel@tonic-gate 				*tmp1++ = *tmp;
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate 			if (tmp[0] == IAC) {
11847c478bd9Sstevel@tonic-gate 				*tmp1++ = IAC;
11857c478bd9Sstevel@tonic-gate 			}
11867c478bd9Sstevel@tonic-gate 			tmp++;
11877c478bd9Sstevel@tonic-gate 		}
11887c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
11897c478bd9Sstevel@tonic-gate 	}
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 	newmp->b_wptr = tmp1;
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 	putnext(q, newmp);
11947c478bd9Sstevel@tonic-gate 	freemsg(savemp);
11957c478bd9Sstevel@tonic-gate 	return (1);
11967c478bd9Sstevel@tonic-gate }
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate static void
telmod_timer(void * arg)11997c478bd9Sstevel@tonic-gate telmod_timer(void *arg)
12007c478bd9Sstevel@tonic-gate {
12017c478bd9Sstevel@tonic-gate 	queue_t *q = arg;
12027c478bd9Sstevel@tonic-gate 	struct	telmod_info	*tmip = (struct telmod_info *)q->q_ptr;
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	ASSERT(tmip);
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
12077c478bd9Sstevel@tonic-gate 		ASSERT(tmip->rtimoutid);
12087c478bd9Sstevel@tonic-gate 		tmip->rtimoutid = 0;
12097c478bd9Sstevel@tonic-gate 	} else {
12107c478bd9Sstevel@tonic-gate 		ASSERT(tmip->wtimoutid);
12117c478bd9Sstevel@tonic-gate 		tmip->wtimoutid = 0;
12127c478bd9Sstevel@tonic-gate 	}
12137c478bd9Sstevel@tonic-gate 	enableok(q);
12147c478bd9Sstevel@tonic-gate 	qenable(q);
12157c478bd9Sstevel@tonic-gate }
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate static void
telmod_buffer(void * arg)12187c478bd9Sstevel@tonic-gate telmod_buffer(void *arg)
12197c478bd9Sstevel@tonic-gate {
12207c478bd9Sstevel@tonic-gate 	queue_t *q = arg;
12217c478bd9Sstevel@tonic-gate 	struct	telmod_info	*tmip = (struct telmod_info *)q->q_ptr;
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	ASSERT(tmip);
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
12267c478bd9Sstevel@tonic-gate 		ASSERT(tmip->rbufcid);
12277c478bd9Sstevel@tonic-gate 		tmip->rbufcid = 0;
12287c478bd9Sstevel@tonic-gate 	} else {
12297c478bd9Sstevel@tonic-gate 		ASSERT(tmip->wbufcid);
12307c478bd9Sstevel@tonic-gate 		tmip->wbufcid = 0;
12317c478bd9Sstevel@tonic-gate 	}
12327c478bd9Sstevel@tonic-gate 	enableok(q);
12337c478bd9Sstevel@tonic-gate 	qenable(q);
12347c478bd9Sstevel@tonic-gate }
12357c478bd9Sstevel@tonic-gate 
12367c478bd9Sstevel@tonic-gate static void
recover(queue_t * q,mblk_t * mp,size_t size)12377c478bd9Sstevel@tonic-gate recover(queue_t *q, mblk_t *mp, size_t size)
12387c478bd9Sstevel@tonic-gate {
12397c478bd9Sstevel@tonic-gate 	bufcall_id_t bid;
12407c478bd9Sstevel@tonic-gate 	timeout_id_t tid;
12417c478bd9Sstevel@tonic-gate 	struct	telmod_info	*tmip = (struct telmod_info *)q->q_ptr;
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type < QPCTL);
12447c478bd9Sstevel@tonic-gate 	noenable(q);
12457c478bd9Sstevel@tonic-gate 	(void) putbq(q, mp);
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate 	/*
12487c478bd9Sstevel@tonic-gate 	 * Make sure there is at most one outstanding request per queue.
12497c478bd9Sstevel@tonic-gate 	 */
12507c478bd9Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
12517c478bd9Sstevel@tonic-gate 		if (tmip->rtimoutid || tmip->rbufcid) {
12527c478bd9Sstevel@tonic-gate 			return;
12537c478bd9Sstevel@tonic-gate 		}
12547c478bd9Sstevel@tonic-gate 	} else {
12557c478bd9Sstevel@tonic-gate 		if (tmip->wtimoutid || tmip->wbufcid) {
12567c478bd9Sstevel@tonic-gate 			return;
12577c478bd9Sstevel@tonic-gate 		}
12587c478bd9Sstevel@tonic-gate 	}
12597c478bd9Sstevel@tonic-gate 	if (!(bid = qbufcall(RD(q), size, BPRI_MED, telmod_buffer, q))) {
12607c478bd9Sstevel@tonic-gate 		tid = qtimeout(RD(q), telmod_timer, q, SIMWAIT);
12617c478bd9Sstevel@tonic-gate 		if (q->q_flag & QREADR)
12627c478bd9Sstevel@tonic-gate 			tmip->rtimoutid = tid;
12637c478bd9Sstevel@tonic-gate 		else
12647c478bd9Sstevel@tonic-gate 			tmip->wtimoutid = tid;
12657c478bd9Sstevel@tonic-gate 	} else	{
12667c478bd9Sstevel@tonic-gate 		if (q->q_flag & QREADR)
12677c478bd9Sstevel@tonic-gate 			tmip->rbufcid = bid;
12687c478bd9Sstevel@tonic-gate 		else
12697c478bd9Sstevel@tonic-gate 			tmip->wbufcid = bid;
12707c478bd9Sstevel@tonic-gate 	}
12717c478bd9Sstevel@tonic-gate }
1272