xref: /titanic_44/usr/src/uts/common/io/telmod.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * This module implements the "fast path" processing for the telnet protocol.
31*7c478bd9Sstevel@tonic-gate  * Since it only knows a very small number of the telnet protocol options,
32*7c478bd9Sstevel@tonic-gate  * the daemon is required to assist this module.  This module must be run
33*7c478bd9Sstevel@tonic-gate  * underneath logindmux, which handles switching messages between the
34*7c478bd9Sstevel@tonic-gate  * daemon and the pty master stream appropriately.  When an unknown telnet
35*7c478bd9Sstevel@tonic-gate  * option is received it is handled as a stop-and-wait operation.  The
36*7c478bd9Sstevel@tonic-gate  * module refuses to forward data in either direction, and waits for the
37*7c478bd9Sstevel@tonic-gate  * daemon to deal with the option, and forward any unprocessed data back
38*7c478bd9Sstevel@tonic-gate  * to the daemon.
39*7c478bd9Sstevel@tonic-gate  */
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/stream.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/ptem.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/logindmux.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/telioctl.h>
54*7c478bd9Sstevel@tonic-gate #include <sys/termios.h>
55*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
56*7c478bd9Sstevel@tonic-gate #include <sys/conf.h>
57*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
58*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
59*7c478bd9Sstevel@tonic-gate #include <sys/cryptmod.h>
60*7c478bd9Sstevel@tonic-gate 
61*7c478bd9Sstevel@tonic-gate #define	IAC	255
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate extern struct streamtab telmodinfo;
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate #define	TELMOD_ID	105
66*7c478bd9Sstevel@tonic-gate #define	SIMWAIT		(1*hz)
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate /*
69*7c478bd9Sstevel@tonic-gate  * Module state flags
70*7c478bd9Sstevel@tonic-gate  */
71*7c478bd9Sstevel@tonic-gate #define		TEL_IOCPASSTHRU	0x100
72*7c478bd9Sstevel@tonic-gate #define		TEL_STOPPED	0x80
73*7c478bd9Sstevel@tonic-gate #define		TEL_CRRCV	0x40
74*7c478bd9Sstevel@tonic-gate #define		TEL_CRSND	0x20
75*7c478bd9Sstevel@tonic-gate #define		TEL_GETBLK	0x10
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate /*
78*7c478bd9Sstevel@tonic-gate  * NOTE: values TEL_BINARY_IN and TEL_BINARY_OUT are defined in
79*7c478bd9Sstevel@tonic-gate  * telioctl.h, passed in the TEL_IOC_MODE ioctl and stored (bitwise)
80*7c478bd9Sstevel@tonic-gate  * in the module state flag.  So those values are not available
81*7c478bd9Sstevel@tonic-gate  * even though they are not defined here.
82*7c478bd9Sstevel@tonic-gate  */
83*7c478bd9Sstevel@tonic-gate 
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate 
86*7c478bd9Sstevel@tonic-gate /*
87*7c478bd9Sstevel@tonic-gate  * Per queue instances are single-threaded since the q_ptr
88*7c478bd9Sstevel@tonic-gate  * field of queues need to be shared among threads.
89*7c478bd9Sstevel@tonic-gate  */
90*7c478bd9Sstevel@tonic-gate static struct fmodsw fsw = {
91*7c478bd9Sstevel@tonic-gate 	"telmod",
92*7c478bd9Sstevel@tonic-gate 	&telmodinfo,
93*7c478bd9Sstevel@tonic-gate 	D_MTQPAIR | D_MP
94*7c478bd9Sstevel@tonic-gate };
95*7c478bd9Sstevel@tonic-gate 
96*7c478bd9Sstevel@tonic-gate /*
97*7c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
98*7c478bd9Sstevel@tonic-gate  */
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate static struct modlstrmod modlstrmod = {
101*7c478bd9Sstevel@tonic-gate 	&mod_strmodops,
102*7c478bd9Sstevel@tonic-gate 	"telnet module",
103*7c478bd9Sstevel@tonic-gate 	&fsw
104*7c478bd9Sstevel@tonic-gate };
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
107*7c478bd9Sstevel@tonic-gate 	MODREV_1, &modlstrmod, NULL
108*7c478bd9Sstevel@tonic-gate };
109*7c478bd9Sstevel@tonic-gate 
110*7c478bd9Sstevel@tonic-gate int
_init()111*7c478bd9Sstevel@tonic-gate _init()
112*7c478bd9Sstevel@tonic-gate {
113*7c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
114*7c478bd9Sstevel@tonic-gate }
115*7c478bd9Sstevel@tonic-gate 
116*7c478bd9Sstevel@tonic-gate int
_fini()117*7c478bd9Sstevel@tonic-gate _fini()
118*7c478bd9Sstevel@tonic-gate {
119*7c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
120*7c478bd9Sstevel@tonic-gate }
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)123*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
124*7c478bd9Sstevel@tonic-gate {
125*7c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
126*7c478bd9Sstevel@tonic-gate }
127*7c478bd9Sstevel@tonic-gate 
128*7c478bd9Sstevel@tonic-gate static int	telmodopen(queue_t *, dev_t *, int, int, cred_t *);
129*7c478bd9Sstevel@tonic-gate static int	telmodclose(queue_t *, int, cred_t *);
130*7c478bd9Sstevel@tonic-gate static void	telmodrput(queue_t *, mblk_t *);
131*7c478bd9Sstevel@tonic-gate static void	telmodrsrv(queue_t *);
132*7c478bd9Sstevel@tonic-gate static void	telmodwput(queue_t *, mblk_t *);
133*7c478bd9Sstevel@tonic-gate static void	telmodwsrv(queue_t *);
134*7c478bd9Sstevel@tonic-gate static int	rcv_parse(queue_t *q, mblk_t *mp);
135*7c478bd9Sstevel@tonic-gate static int	snd_parse(queue_t *q, mblk_t *mp);
136*7c478bd9Sstevel@tonic-gate static void	telmod_timer(void *);
137*7c478bd9Sstevel@tonic-gate static void	telmod_buffer(void *);
138*7c478bd9Sstevel@tonic-gate static void	recover(queue_t *, mblk_t *, size_t);
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate static struct module_info telmodoinfo = {
141*7c478bd9Sstevel@tonic-gate 	TELMOD_ID,				/* module id number */
142*7c478bd9Sstevel@tonic-gate 	"telmod",				/* module name */
143*7c478bd9Sstevel@tonic-gate 	0,					/* minimum packet size */
144*7c478bd9Sstevel@tonic-gate 	INFPSZ,					/* maximum packet size */
145*7c478bd9Sstevel@tonic-gate 	512,					/* hi-water mark */
146*7c478bd9Sstevel@tonic-gate 	256					/* lo-water mark */
147*7c478bd9Sstevel@tonic-gate };
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate static struct qinit telmodrinit = {
150*7c478bd9Sstevel@tonic-gate 	(int (*)())telmodrput,
151*7c478bd9Sstevel@tonic-gate 	(int (*)())telmodrsrv,
152*7c478bd9Sstevel@tonic-gate 	telmodopen,
153*7c478bd9Sstevel@tonic-gate 	telmodclose,
154*7c478bd9Sstevel@tonic-gate 	nulldev,
155*7c478bd9Sstevel@tonic-gate 	&telmodoinfo,
156*7c478bd9Sstevel@tonic-gate 	NULL
157*7c478bd9Sstevel@tonic-gate };
158*7c478bd9Sstevel@tonic-gate 
159*7c478bd9Sstevel@tonic-gate static struct qinit telmodwinit = {
160*7c478bd9Sstevel@tonic-gate 	(int (*)())telmodwput,
161*7c478bd9Sstevel@tonic-gate 	(int (*)())telmodwsrv,
162*7c478bd9Sstevel@tonic-gate 	NULL,
163*7c478bd9Sstevel@tonic-gate 	NULL,
164*7c478bd9Sstevel@tonic-gate 	nulldev,
165*7c478bd9Sstevel@tonic-gate 	&telmodoinfo,
166*7c478bd9Sstevel@tonic-gate 	NULL
167*7c478bd9Sstevel@tonic-gate };
168*7c478bd9Sstevel@tonic-gate 
169*7c478bd9Sstevel@tonic-gate struct streamtab telmodinfo = {
170*7c478bd9Sstevel@tonic-gate 	&telmodrinit,
171*7c478bd9Sstevel@tonic-gate 	&telmodwinit,
172*7c478bd9Sstevel@tonic-gate 	NULL,
173*7c478bd9Sstevel@tonic-gate 	NULL
174*7c478bd9Sstevel@tonic-gate };
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate /*
177*7c478bd9Sstevel@tonic-gate  * Per-instance state struct for the telnet module.
178*7c478bd9Sstevel@tonic-gate  */
179*7c478bd9Sstevel@tonic-gate struct telmod_info {
180*7c478bd9Sstevel@tonic-gate 	int		flags;
181*7c478bd9Sstevel@tonic-gate 	bufcall_id_t	wbufcid;
182*7c478bd9Sstevel@tonic-gate 	bufcall_id_t	rbufcid;
183*7c478bd9Sstevel@tonic-gate 	timeout_id_t	wtimoutid;
184*7c478bd9Sstevel@tonic-gate 	timeout_id_t	rtimoutid;
185*7c478bd9Sstevel@tonic-gate 	mblk_t		*unbind_mp;
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate };
188*7c478bd9Sstevel@tonic-gate 
189*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
190*7c478bd9Sstevel@tonic-gate static void
dummy_callback(void * arg)191*7c478bd9Sstevel@tonic-gate dummy_callback(void *arg)
192*7c478bd9Sstevel@tonic-gate {}
193*7c478bd9Sstevel@tonic-gate 
194*7c478bd9Sstevel@tonic-gate /*
195*7c478bd9Sstevel@tonic-gate  * telmodopen -
196*7c478bd9Sstevel@tonic-gate  *	A variety of telnet options can never really be processed in the
197*7c478bd9Sstevel@tonic-gate  *	kernel.  For example, TELOPT_TTYPE, must be based in the TERM
198*7c478bd9Sstevel@tonic-gate  *	environment variable to the login process.  Also, data may already
199*7c478bd9Sstevel@tonic-gate  *	have reached the stream head before telmod was pushed on the stream.
200*7c478bd9Sstevel@tonic-gate  *	So when telmod is opened, it begins in stopped state, preventing
201*7c478bd9Sstevel@tonic-gate  *	further data passing either direction through it.  It sends a
202*7c478bd9Sstevel@tonic-gate  *	T_DATA_REQ messages up toward the daemon.  This is so the daemon
203*7c478bd9Sstevel@tonic-gate  *	can be sure that all data which was not processed by telmod
204*7c478bd9Sstevel@tonic-gate  *	(because it wasn't yet pushed) has been received at the stream head.
205*7c478bd9Sstevel@tonic-gate  */
206*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
207*7c478bd9Sstevel@tonic-gate static int
telmodopen(queue_t * q,dev_t * devp,int oflag,int sflag,cred_t * credp)208*7c478bd9Sstevel@tonic-gate telmodopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
209*7c478bd9Sstevel@tonic-gate {
210*7c478bd9Sstevel@tonic-gate 	struct telmod_info	*tmip;
211*7c478bd9Sstevel@tonic-gate 	mblk_t *bp;
212*7c478bd9Sstevel@tonic-gate 	union T_primitives *tp;
213*7c478bd9Sstevel@tonic-gate 	int	error;
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate 	if (sflag != MODOPEN)
216*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
217*7c478bd9Sstevel@tonic-gate 
218*7c478bd9Sstevel@tonic-gate 	if (q->q_ptr != NULL) {
219*7c478bd9Sstevel@tonic-gate 		/* It's already attached. */
220*7c478bd9Sstevel@tonic-gate 		return (0);
221*7c478bd9Sstevel@tonic-gate 	}
222*7c478bd9Sstevel@tonic-gate 	/*
223*7c478bd9Sstevel@tonic-gate 	 * Allocate state structure.
224*7c478bd9Sstevel@tonic-gate 	 */
225*7c478bd9Sstevel@tonic-gate 	tmip = kmem_zalloc(sizeof (*tmip), KM_SLEEP);
226*7c478bd9Sstevel@tonic-gate 
227*7c478bd9Sstevel@tonic-gate 	/*
228*7c478bd9Sstevel@tonic-gate 	 * Cross-link.
229*7c478bd9Sstevel@tonic-gate 	 */
230*7c478bd9Sstevel@tonic-gate 	q->q_ptr = tmip;
231*7c478bd9Sstevel@tonic-gate 	WR(q)->q_ptr = tmip;
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate 	noenable(q);
234*7c478bd9Sstevel@tonic-gate 	tmip->flags |= TEL_STOPPED;
235*7c478bd9Sstevel@tonic-gate 	qprocson(q);
236*7c478bd9Sstevel@tonic-gate 
237*7c478bd9Sstevel@tonic-gate 	/*
238*7c478bd9Sstevel@tonic-gate 	 * Since TCP operates in the TLI-inspired brain-dead fashion,
239*7c478bd9Sstevel@tonic-gate 	 * the connection will revert to bound state if the connection
240*7c478bd9Sstevel@tonic-gate 	 * is reset by the client.  We must send a T_UNBIND_REQ in
241*7c478bd9Sstevel@tonic-gate 	 * that case so the port doesn't get "wedged" (preventing
242*7c478bd9Sstevel@tonic-gate 	 * inetd from being able to restart the listener).  Allocate
243*7c478bd9Sstevel@tonic-gate 	 * it here, so that we don't need to worry about allocb()
244*7c478bd9Sstevel@tonic-gate 	 * failures later.
245*7c478bd9Sstevel@tonic-gate 	 */
246*7c478bd9Sstevel@tonic-gate 	while ((tmip->unbind_mp = allocb(sizeof (union T_primitives),
247*7c478bd9Sstevel@tonic-gate 	    BPRI_HI)) == NULL) {
248*7c478bd9Sstevel@tonic-gate 		bufcall_id_t id = qbufcall(q, sizeof (union T_primitives),
249*7c478bd9Sstevel@tonic-gate 		    BPRI_HI, dummy_callback, NULL);
250*7c478bd9Sstevel@tonic-gate 		if (!qwait_sig(q)) {
251*7c478bd9Sstevel@tonic-gate 			qunbufcall(q, id);
252*7c478bd9Sstevel@tonic-gate 			error = EINTR;
253*7c478bd9Sstevel@tonic-gate 			goto fail;
254*7c478bd9Sstevel@tonic-gate 		}
255*7c478bd9Sstevel@tonic-gate 		qunbufcall(q, id);
256*7c478bd9Sstevel@tonic-gate 	}
257*7c478bd9Sstevel@tonic-gate 	tmip->unbind_mp->b_wptr = tmip->unbind_mp->b_rptr +
258*7c478bd9Sstevel@tonic-gate 	    sizeof (struct T_unbind_req);
259*7c478bd9Sstevel@tonic-gate 	tmip->unbind_mp->b_datap->db_type = M_PROTO;
260*7c478bd9Sstevel@tonic-gate 	tp = (union T_primitives *)tmip->unbind_mp->b_rptr;
261*7c478bd9Sstevel@tonic-gate 	tp->type = T_UNBIND_REQ;
262*7c478bd9Sstevel@tonic-gate 	/*
263*7c478bd9Sstevel@tonic-gate 	 * Send a M_PROTO msg of type T_DATA_REQ (this is unique for
264*7c478bd9Sstevel@tonic-gate 	 * read queue since only write queue can get T_DATA_REQ).
265*7c478bd9Sstevel@tonic-gate 	 * Readstream routine in telnet daemon will do a getmsg() till
266*7c478bd9Sstevel@tonic-gate 	 * it receives this proto message
267*7c478bd9Sstevel@tonic-gate 	 */
268*7c478bd9Sstevel@tonic-gate 	while ((bp = allocb(sizeof (union T_primitives), BPRI_HI)) == NULL) {
269*7c478bd9Sstevel@tonic-gate 		bufcall_id_t id = qbufcall(q, sizeof (union T_primitives),
270*7c478bd9Sstevel@tonic-gate 		    BPRI_HI, dummy_callback, NULL);
271*7c478bd9Sstevel@tonic-gate 		if (!qwait_sig(q)) {
272*7c478bd9Sstevel@tonic-gate 			qunbufcall(q, id);
273*7c478bd9Sstevel@tonic-gate 			error = EINTR;
274*7c478bd9Sstevel@tonic-gate 			goto fail;
275*7c478bd9Sstevel@tonic-gate 		}
276*7c478bd9Sstevel@tonic-gate 		qunbufcall(q, id);
277*7c478bd9Sstevel@tonic-gate 	}
278*7c478bd9Sstevel@tonic-gate 	bp->b_datap->db_type = M_PROTO;
279*7c478bd9Sstevel@tonic-gate 	bp->b_wptr = bp->b_rptr + sizeof (union T_primitives);
280*7c478bd9Sstevel@tonic-gate 	tp = (union T_primitives *)bp->b_rptr;
281*7c478bd9Sstevel@tonic-gate 	tp->type = T_DATA_REQ;
282*7c478bd9Sstevel@tonic-gate 	tp->data_req.MORE_flag = 0;
283*7c478bd9Sstevel@tonic-gate 
284*7c478bd9Sstevel@tonic-gate 	putnext(q, bp);
285*7c478bd9Sstevel@tonic-gate 	return (0);
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate fail:
288*7c478bd9Sstevel@tonic-gate 	qprocsoff(q);
289*7c478bd9Sstevel@tonic-gate 	if (tmip->unbind_mp != NULL) {
290*7c478bd9Sstevel@tonic-gate 		freemsg(tmip->unbind_mp);
291*7c478bd9Sstevel@tonic-gate 	}
292*7c478bd9Sstevel@tonic-gate 	kmem_free(tmip, sizeof (struct telmod_info));
293*7c478bd9Sstevel@tonic-gate 	q->q_ptr = NULL;
294*7c478bd9Sstevel@tonic-gate 	WR(q)->q_ptr = NULL;
295*7c478bd9Sstevel@tonic-gate 	return (error);
296*7c478bd9Sstevel@tonic-gate }
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate /*
300*7c478bd9Sstevel@tonic-gate  * telmodclose - just the normal streams clean-up is required.
301*7c478bd9Sstevel@tonic-gate  */
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
304*7c478bd9Sstevel@tonic-gate static int
telmodclose(queue_t * q,int flag,cred_t * credp)305*7c478bd9Sstevel@tonic-gate telmodclose(queue_t *q, int flag, cred_t *credp)
306*7c478bd9Sstevel@tonic-gate {
307*7c478bd9Sstevel@tonic-gate 	struct telmod_info   *tmip = (struct telmod_info *)q->q_ptr;
308*7c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
309*7c478bd9Sstevel@tonic-gate 
310*7c478bd9Sstevel@tonic-gate 	/*
311*7c478bd9Sstevel@tonic-gate 	 * Flush any write-side data downstream.  Ignoring flow
312*7c478bd9Sstevel@tonic-gate 	 * control at this point is known to be safe because the
313*7c478bd9Sstevel@tonic-gate 	 * M_HANGUP below poisons the stream such that no modules can
314*7c478bd9Sstevel@tonic-gate 	 * be pushed again.
315*7c478bd9Sstevel@tonic-gate 	 */
316*7c478bd9Sstevel@tonic-gate 	while (mp = getq(WR(q)))
317*7c478bd9Sstevel@tonic-gate 		putnext(WR(q), mp);
318*7c478bd9Sstevel@tonic-gate 
319*7c478bd9Sstevel@tonic-gate 	/* Poison the stream head so that we can't be pushed again. */
320*7c478bd9Sstevel@tonic-gate 	(void) putnextctl(q, M_HANGUP);
321*7c478bd9Sstevel@tonic-gate 
322*7c478bd9Sstevel@tonic-gate 	qprocsoff(q);
323*7c478bd9Sstevel@tonic-gate 	if (tmip->wbufcid) {
324*7c478bd9Sstevel@tonic-gate 		qunbufcall(q, tmip->wbufcid);
325*7c478bd9Sstevel@tonic-gate 		tmip->wbufcid = 0;
326*7c478bd9Sstevel@tonic-gate 	}
327*7c478bd9Sstevel@tonic-gate 	if (tmip->rbufcid) {
328*7c478bd9Sstevel@tonic-gate 		qunbufcall(q, tmip->rbufcid);
329*7c478bd9Sstevel@tonic-gate 		tmip->rbufcid = 0;
330*7c478bd9Sstevel@tonic-gate 	}
331*7c478bd9Sstevel@tonic-gate 	if (tmip->wtimoutid) {
332*7c478bd9Sstevel@tonic-gate 		(void) quntimeout(q, tmip->wtimoutid);
333*7c478bd9Sstevel@tonic-gate 		tmip->wtimoutid = 0;
334*7c478bd9Sstevel@tonic-gate 	}
335*7c478bd9Sstevel@tonic-gate 	if (tmip->rtimoutid) {
336*7c478bd9Sstevel@tonic-gate 		(void) quntimeout(q, tmip->rtimoutid);
337*7c478bd9Sstevel@tonic-gate 		tmip->rtimoutid = 0;
338*7c478bd9Sstevel@tonic-gate 	}
339*7c478bd9Sstevel@tonic-gate 	if (tmip->unbind_mp != NULL) {
340*7c478bd9Sstevel@tonic-gate 		freemsg(tmip->unbind_mp);
341*7c478bd9Sstevel@tonic-gate 	}
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate 	kmem_free(q->q_ptr, sizeof (struct telmod_info));
344*7c478bd9Sstevel@tonic-gate 	q->q_ptr = WR(q)->q_ptr = NULL;
345*7c478bd9Sstevel@tonic-gate 	return (0);
346*7c478bd9Sstevel@tonic-gate }
347*7c478bd9Sstevel@tonic-gate 
348*7c478bd9Sstevel@tonic-gate /*
349*7c478bd9Sstevel@tonic-gate  * telmodrput:
350*7c478bd9Sstevel@tonic-gate  * Be sure to preserve data order.  If the daemon is waiting for additional
351*7c478bd9Sstevel@tonic-gate  * data (TEL_GETBLK state) forward new data.  Otherwise, apply normal
352*7c478bd9Sstevel@tonic-gate  * telnet protocol processing to M_DATA.  Take notice of TLI messages
353*7c478bd9Sstevel@tonic-gate  * indicating connection tear-down, and change them into M_HANGUP's.
354*7c478bd9Sstevel@tonic-gate  */
355*7c478bd9Sstevel@tonic-gate static void
telmodrput(queue_t * q,mblk_t * mp)356*7c478bd9Sstevel@tonic-gate telmodrput(queue_t *q, mblk_t *mp)
357*7c478bd9Sstevel@tonic-gate {
358*7c478bd9Sstevel@tonic-gate 	mblk_t	*newmp;
359*7c478bd9Sstevel@tonic-gate 	struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
360*7c478bd9Sstevel@tonic-gate 	union T_primitives *tip;
361*7c478bd9Sstevel@tonic-gate 
362*7c478bd9Sstevel@tonic-gate 	if ((mp->b_datap->db_type < QPCTL) &&
363*7c478bd9Sstevel@tonic-gate 	    ((q->q_first) || ((tmip->flags & TEL_STOPPED) &&
364*7c478bd9Sstevel@tonic-gate 	    !(tmip->flags & TEL_GETBLK)) || !canputnext(q))) {
365*7c478bd9Sstevel@tonic-gate 		(void) putq(q, mp);
366*7c478bd9Sstevel@tonic-gate 		return;
367*7c478bd9Sstevel@tonic-gate 	}
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
370*7c478bd9Sstevel@tonic-gate 	case M_DATA:
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate 		/*
373*7c478bd9Sstevel@tonic-gate 		 * If the user level daemon requests for 1 more
374*7c478bd9Sstevel@tonic-gate 		 * block of data (needs more data for protocol processing)
375*7c478bd9Sstevel@tonic-gate 		 * create a M_CTL message block with the mp.
376*7c478bd9Sstevel@tonic-gate 		 */
377*7c478bd9Sstevel@tonic-gate is_mdata:
378*7c478bd9Sstevel@tonic-gate 		if (tmip->flags & TEL_GETBLK) {
379*7c478bd9Sstevel@tonic-gate 			if ((newmp = allocb(sizeof (char), BPRI_MED)) == NULL) {
380*7c478bd9Sstevel@tonic-gate 				recover(q, mp, msgdsize(mp));
381*7c478bd9Sstevel@tonic-gate 				return;
382*7c478bd9Sstevel@tonic-gate 			}
383*7c478bd9Sstevel@tonic-gate 			newmp->b_datap->db_type = M_CTL;
384*7c478bd9Sstevel@tonic-gate 			newmp->b_wptr = newmp->b_rptr + 1;
385*7c478bd9Sstevel@tonic-gate 			*(newmp->b_rptr) = M_CTL_MAGIC_NUMBER;
386*7c478bd9Sstevel@tonic-gate 			newmp->b_cont = mp;
387*7c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_GETBLK;
388*7c478bd9Sstevel@tonic-gate 			noenable(q);
389*7c478bd9Sstevel@tonic-gate 			tmip->flags |= TEL_STOPPED;
390*7c478bd9Sstevel@tonic-gate 
391*7c478bd9Sstevel@tonic-gate 			putnext(q, newmp);
392*7c478bd9Sstevel@tonic-gate 
393*7c478bd9Sstevel@tonic-gate 			break;
394*7c478bd9Sstevel@tonic-gate 		}
395*7c478bd9Sstevel@tonic-gate 		/*
396*7c478bd9Sstevel@tonic-gate 		 * call the protocol parsing routine which processes
397*7c478bd9Sstevel@tonic-gate 		 * the data part of the message block first. Then it
398*7c478bd9Sstevel@tonic-gate 		 * handles protocol and CR/LF processing.
399*7c478bd9Sstevel@tonic-gate 		 * If an error is found inside allocb/dupb, recover
400*7c478bd9Sstevel@tonic-gate 		 * routines inside rcv_parse will queue up the
401*7c478bd9Sstevel@tonic-gate 		 * original message block in its service queue.
402*7c478bd9Sstevel@tonic-gate 		 */
403*7c478bd9Sstevel@tonic-gate 		(void) rcv_parse(q, mp);
404*7c478bd9Sstevel@tonic-gate 		break;
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate 	case M_FLUSH:
407*7c478bd9Sstevel@tonic-gate 		/*
408*7c478bd9Sstevel@tonic-gate 		 * Since M_FLUSH came from TCP, we mark it bound for
409*7c478bd9Sstevel@tonic-gate 		 * daemon, not tty.  This only happens when TCP expects
410*7c478bd9Sstevel@tonic-gate 		 * to do a connection reset.
411*7c478bd9Sstevel@tonic-gate 		 */
412*7c478bd9Sstevel@tonic-gate 		mp->b_flag |= MSGMARK;
413*7c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR)
414*7c478bd9Sstevel@tonic-gate 			flushq(q, FLUSHALL);
415*7c478bd9Sstevel@tonic-gate 		putnext(q, mp);
416*7c478bd9Sstevel@tonic-gate 		break;
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 	case M_PCSIG:
419*7c478bd9Sstevel@tonic-gate 	case M_ERROR:
420*7c478bd9Sstevel@tonic-gate 		if (tmip->flags & TEL_GETBLK)
421*7c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_GETBLK;
422*7c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
423*7c478bd9Sstevel@tonic-gate 	case M_IOCACK:
424*7c478bd9Sstevel@tonic-gate 	case M_IOCNAK:
425*7c478bd9Sstevel@tonic-gate 	case M_SETOPTS:
426*7c478bd9Sstevel@tonic-gate 		putnext(q, mp);
427*7c478bd9Sstevel@tonic-gate 		break;
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 	case M_PROTO:
430*7c478bd9Sstevel@tonic-gate 	case M_PCPROTO:
431*7c478bd9Sstevel@tonic-gate 		if (tmip->flags & TEL_GETBLK)
432*7c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_GETBLK;
433*7c478bd9Sstevel@tonic-gate 
434*7c478bd9Sstevel@tonic-gate 		tip = (union T_primitives *)mp->b_rptr;
435*7c478bd9Sstevel@tonic-gate 		switch (tip->type) {
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate 		case T_ORDREL_IND:
438*7c478bd9Sstevel@tonic-gate 		case T_DISCON_IND:
439*7c478bd9Sstevel@tonic-gate 			/* Make into M_HANGUP and putnext */
440*7c478bd9Sstevel@tonic-gate 			ASSERT(mp->b_cont == NULL);
441*7c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_HANGUP;
442*7c478bd9Sstevel@tonic-gate 			mp->b_wptr = mp->b_rptr;
443*7c478bd9Sstevel@tonic-gate 			if (mp->b_cont) {
444*7c478bd9Sstevel@tonic-gate 				freemsg(mp->b_cont);
445*7c478bd9Sstevel@tonic-gate 				mp->b_cont = NULL;
446*7c478bd9Sstevel@tonic-gate 			}
447*7c478bd9Sstevel@tonic-gate 			/*
448*7c478bd9Sstevel@tonic-gate 			 * If we haven't already, send T_UNBIND_REQ to prevent
449*7c478bd9Sstevel@tonic-gate 			 * TCP from going into "BOUND" state and locking up the
450*7c478bd9Sstevel@tonic-gate 			 * port.
451*7c478bd9Sstevel@tonic-gate 			 */
452*7c478bd9Sstevel@tonic-gate 			if (tip->type == T_DISCON_IND && tmip->unbind_mp !=
453*7c478bd9Sstevel@tonic-gate 			    NULL) {
454*7c478bd9Sstevel@tonic-gate 				putnext(q, mp);
455*7c478bd9Sstevel@tonic-gate 				qreply(q, tmip->unbind_mp);
456*7c478bd9Sstevel@tonic-gate 				tmip->unbind_mp = NULL;
457*7c478bd9Sstevel@tonic-gate 			} else {
458*7c478bd9Sstevel@tonic-gate 				putnext(q, mp);
459*7c478bd9Sstevel@tonic-gate 			}
460*7c478bd9Sstevel@tonic-gate 			break;
461*7c478bd9Sstevel@tonic-gate 
462*7c478bd9Sstevel@tonic-gate 		case T_EXDATA_IND:
463*7c478bd9Sstevel@tonic-gate 		case T_DATA_IND:	/* conform to TPI, but never happens */
464*7c478bd9Sstevel@tonic-gate 			newmp = mp->b_cont;
465*7c478bd9Sstevel@tonic-gate 			freeb(mp);
466*7c478bd9Sstevel@tonic-gate 			mp = newmp;
467*7c478bd9Sstevel@tonic-gate 			if (mp) {
468*7c478bd9Sstevel@tonic-gate 				ASSERT(mp->b_datap->db_type == M_DATA);
469*7c478bd9Sstevel@tonic-gate 				if (msgdsize(mp) != 0) {
470*7c478bd9Sstevel@tonic-gate 					goto is_mdata;
471*7c478bd9Sstevel@tonic-gate 				}
472*7c478bd9Sstevel@tonic-gate 				freemsg(mp);
473*7c478bd9Sstevel@tonic-gate 			}
474*7c478bd9Sstevel@tonic-gate 			break;
475*7c478bd9Sstevel@tonic-gate 
476*7c478bd9Sstevel@tonic-gate 		/*
477*7c478bd9Sstevel@tonic-gate 		 * We only get T_OK_ACK when we issue the unbind, and it can
478*7c478bd9Sstevel@tonic-gate 		 * be ignored safely.
479*7c478bd9Sstevel@tonic-gate 		 */
480*7c478bd9Sstevel@tonic-gate 		case T_OK_ACK:
481*7c478bd9Sstevel@tonic-gate 			ASSERT(tmip->unbind_mp == NULL);
482*7c478bd9Sstevel@tonic-gate 			freemsg(mp);
483*7c478bd9Sstevel@tonic-gate 			break;
484*7c478bd9Sstevel@tonic-gate 
485*7c478bd9Sstevel@tonic-gate 		default:
486*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
487*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE,
488*7c478bd9Sstevel@tonic-gate 			    "telmodrput: unexpected TLI primitive msg "
489*7c478bd9Sstevel@tonic-gate 			    "type 0x%x", tip->type);
490*7c478bd9Sstevel@tonic-gate #endif
491*7c478bd9Sstevel@tonic-gate 			freemsg(mp);
492*7c478bd9Sstevel@tonic-gate 		}
493*7c478bd9Sstevel@tonic-gate 		break;
494*7c478bd9Sstevel@tonic-gate 
495*7c478bd9Sstevel@tonic-gate 	default:
496*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
497*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE,
498*7c478bd9Sstevel@tonic-gate 		    "telmodrput: unexpected msg type 0x%x",
499*7c478bd9Sstevel@tonic-gate 		    mp->b_datap->db_type);
500*7c478bd9Sstevel@tonic-gate #endif
501*7c478bd9Sstevel@tonic-gate 		freemsg(mp);
502*7c478bd9Sstevel@tonic-gate 	}
503*7c478bd9Sstevel@tonic-gate }
504*7c478bd9Sstevel@tonic-gate 
505*7c478bd9Sstevel@tonic-gate /*
506*7c478bd9Sstevel@tonic-gate  * telmodrsrv:
507*7c478bd9Sstevel@tonic-gate  * Mostly we end up here because of M_DATA processing delayed due to flow
508*7c478bd9Sstevel@tonic-gate  * control or lack of memory.  XXX.sparker: TLI primitives here?
509*7c478bd9Sstevel@tonic-gate  */
510*7c478bd9Sstevel@tonic-gate static void
telmodrsrv(queue_t * q)511*7c478bd9Sstevel@tonic-gate telmodrsrv(queue_t *q)
512*7c478bd9Sstevel@tonic-gate {
513*7c478bd9Sstevel@tonic-gate 	mblk_t	*mp, *newmp;
514*7c478bd9Sstevel@tonic-gate 	struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
515*7c478bd9Sstevel@tonic-gate 	union T_primitives *tip;
516*7c478bd9Sstevel@tonic-gate 
517*7c478bd9Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
518*7c478bd9Sstevel@tonic-gate 		if (((tmip->flags & TEL_STOPPED) &&
519*7c478bd9Sstevel@tonic-gate 		    !(tmip->flags & TEL_GETBLK)) || !canputnext(q)) {
520*7c478bd9Sstevel@tonic-gate 			(void) putbq(q, mp);
521*7c478bd9Sstevel@tonic-gate 			return;
522*7c478bd9Sstevel@tonic-gate 		}
523*7c478bd9Sstevel@tonic-gate 		switch (mp->b_datap->db_type) {
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 		case M_DATA:
526*7c478bd9Sstevel@tonic-gate is_mdata:
527*7c478bd9Sstevel@tonic-gate 			if (tmip->flags & TEL_GETBLK) {
528*7c478bd9Sstevel@tonic-gate 				if ((newmp = allocb(sizeof (char),
529*7c478bd9Sstevel@tonic-gate 				    BPRI_MED)) == NULL) {
530*7c478bd9Sstevel@tonic-gate 					recover(q, mp, msgdsize(mp));
531*7c478bd9Sstevel@tonic-gate 					return;
532*7c478bd9Sstevel@tonic-gate 				}
533*7c478bd9Sstevel@tonic-gate 				newmp->b_datap->db_type = M_CTL;
534*7c478bd9Sstevel@tonic-gate 				newmp->b_wptr = newmp->b_rptr + 1;
535*7c478bd9Sstevel@tonic-gate 				*(newmp->b_rptr) = M_CTL_MAGIC_NUMBER;
536*7c478bd9Sstevel@tonic-gate 				newmp->b_cont = mp;
537*7c478bd9Sstevel@tonic-gate 				tmip->flags &= ~TEL_GETBLK;
538*7c478bd9Sstevel@tonic-gate 				noenable(q);
539*7c478bd9Sstevel@tonic-gate 				tmip->flags |= TEL_STOPPED;
540*7c478bd9Sstevel@tonic-gate 
541*7c478bd9Sstevel@tonic-gate 				putnext(q, newmp);
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate 				break;
544*7c478bd9Sstevel@tonic-gate 			}
545*7c478bd9Sstevel@tonic-gate 			if (!rcv_parse(q, mp)) {
546*7c478bd9Sstevel@tonic-gate 				return;
547*7c478bd9Sstevel@tonic-gate 			}
548*7c478bd9Sstevel@tonic-gate 			break;
549*7c478bd9Sstevel@tonic-gate 
550*7c478bd9Sstevel@tonic-gate 		case M_PROTO:
551*7c478bd9Sstevel@tonic-gate 
552*7c478bd9Sstevel@tonic-gate 			tip = (union T_primitives *)mp->b_rptr;
553*7c478bd9Sstevel@tonic-gate 
554*7c478bd9Sstevel@tonic-gate 			/*
555*7c478bd9Sstevel@tonic-gate 			 * Unless the M_PROTO message indicates data, clear
556*7c478bd9Sstevel@tonic-gate 			 * TEL_GETBLK so that we stop passing our messages
557*7c478bd9Sstevel@tonic-gate 			 * up to the telnet daemon.
558*7c478bd9Sstevel@tonic-gate 			 */
559*7c478bd9Sstevel@tonic-gate 			if (tip->type != T_DATA_IND &&
560*7c478bd9Sstevel@tonic-gate 			    tip->type != T_EXDATA_IND)
561*7c478bd9Sstevel@tonic-gate 				tmip->flags &= ~TEL_GETBLK;
562*7c478bd9Sstevel@tonic-gate 
563*7c478bd9Sstevel@tonic-gate 			switch (tip->type) {
564*7c478bd9Sstevel@tonic-gate 			case T_ORDREL_IND:
565*7c478bd9Sstevel@tonic-gate 			case T_DISCON_IND:
566*7c478bd9Sstevel@tonic-gate 			/* Make into M_HANGUP and putnext */
567*7c478bd9Sstevel@tonic-gate 				ASSERT(mp->b_cont == NULL);
568*7c478bd9Sstevel@tonic-gate 				mp->b_datap->db_type = M_HANGUP;
569*7c478bd9Sstevel@tonic-gate 				mp->b_wptr = mp->b_rptr;
570*7c478bd9Sstevel@tonic-gate 				if (mp->b_cont) {
571*7c478bd9Sstevel@tonic-gate 					freemsg(mp->b_cont);
572*7c478bd9Sstevel@tonic-gate 					mp->b_cont = NULL;
573*7c478bd9Sstevel@tonic-gate 				}
574*7c478bd9Sstevel@tonic-gate 				/*
575*7c478bd9Sstevel@tonic-gate 				 * If we haven't already, send T_UNBIND_REQ
576*7c478bd9Sstevel@tonic-gate 				 * to prevent TCP from going into "BOUND"
577*7c478bd9Sstevel@tonic-gate 				 * state and locking up the port.
578*7c478bd9Sstevel@tonic-gate 				 */
579*7c478bd9Sstevel@tonic-gate 				if (tip->type == T_DISCON_IND &&
580*7c478bd9Sstevel@tonic-gate 				    tmip->unbind_mp != NULL) {
581*7c478bd9Sstevel@tonic-gate 					putnext(q, mp);
582*7c478bd9Sstevel@tonic-gate 					qreply(q, tmip->unbind_mp);
583*7c478bd9Sstevel@tonic-gate 					tmip->unbind_mp = NULL;
584*7c478bd9Sstevel@tonic-gate 				} else {
585*7c478bd9Sstevel@tonic-gate 					putnext(q, mp);
586*7c478bd9Sstevel@tonic-gate 				}
587*7c478bd9Sstevel@tonic-gate 				break;
588*7c478bd9Sstevel@tonic-gate 
589*7c478bd9Sstevel@tonic-gate 			case T_DATA_IND: /* conform to TPI, but never happens */
590*7c478bd9Sstevel@tonic-gate 			case T_EXDATA_IND:
591*7c478bd9Sstevel@tonic-gate 				newmp = mp->b_cont;
592*7c478bd9Sstevel@tonic-gate 				freeb(mp);
593*7c478bd9Sstevel@tonic-gate 				mp = newmp;
594*7c478bd9Sstevel@tonic-gate 				if (mp) {
595*7c478bd9Sstevel@tonic-gate 					ASSERT(mp->b_datap->db_type == M_DATA);
596*7c478bd9Sstevel@tonic-gate 					if (msgdsize(mp) != 0) {
597*7c478bd9Sstevel@tonic-gate 						goto is_mdata;
598*7c478bd9Sstevel@tonic-gate 					}
599*7c478bd9Sstevel@tonic-gate 					freemsg(mp);
600*7c478bd9Sstevel@tonic-gate 				}
601*7c478bd9Sstevel@tonic-gate 				break;
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate 			/*
604*7c478bd9Sstevel@tonic-gate 			 * We only get T_OK_ACK when we issue the unbind, and
605*7c478bd9Sstevel@tonic-gate 			 * it can be ignored safely.
606*7c478bd9Sstevel@tonic-gate 			 */
607*7c478bd9Sstevel@tonic-gate 			case T_OK_ACK:
608*7c478bd9Sstevel@tonic-gate 				ASSERT(tmip->unbind_mp == NULL);
609*7c478bd9Sstevel@tonic-gate 				freemsg(mp);
610*7c478bd9Sstevel@tonic-gate 				break;
611*7c478bd9Sstevel@tonic-gate 
612*7c478bd9Sstevel@tonic-gate 			default:
613*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
614*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE,
615*7c478bd9Sstevel@tonic-gate 				    "telmodrsrv: unexpected TLI primitive "
616*7c478bd9Sstevel@tonic-gate 				    "msg type 0x%x", tip->type);
617*7c478bd9Sstevel@tonic-gate #endif
618*7c478bd9Sstevel@tonic-gate 				freemsg(mp);
619*7c478bd9Sstevel@tonic-gate 			}
620*7c478bd9Sstevel@tonic-gate 			break;
621*7c478bd9Sstevel@tonic-gate 
622*7c478bd9Sstevel@tonic-gate 		case M_SETOPTS:
623*7c478bd9Sstevel@tonic-gate 			putnext(q, mp);
624*7c478bd9Sstevel@tonic-gate 			break;
625*7c478bd9Sstevel@tonic-gate 
626*7c478bd9Sstevel@tonic-gate 		default:
627*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
628*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE,
629*7c478bd9Sstevel@tonic-gate 			    "telmodrsrv: unexpected msg type 0x%x",
630*7c478bd9Sstevel@tonic-gate 			    mp->b_datap->db_type);
631*7c478bd9Sstevel@tonic-gate #endif
632*7c478bd9Sstevel@tonic-gate 			freemsg(mp);
633*7c478bd9Sstevel@tonic-gate 		}
634*7c478bd9Sstevel@tonic-gate 	}
635*7c478bd9Sstevel@tonic-gate }
636*7c478bd9Sstevel@tonic-gate 
637*7c478bd9Sstevel@tonic-gate /*
638*7c478bd9Sstevel@tonic-gate  * telmodwput:
639*7c478bd9Sstevel@tonic-gate  * M_DATA is processed and forwarded if we aren't stopped awaiting the daemon
640*7c478bd9Sstevel@tonic-gate  * to process something.  M_CTL's are data from the daemon bound for the
641*7c478bd9Sstevel@tonic-gate  * network.  We forward them immediately.  There are two classes of ioctl's
642*7c478bd9Sstevel@tonic-gate  * we must handle here also.  One is ioctl's forwarded by ptem which we
643*7c478bd9Sstevel@tonic-gate  * ignore.  The other is ioctl's issued by the daemon to control us.
644*7c478bd9Sstevel@tonic-gate  * Process them appropriately.  M_PROTO's we pass along, figuring they are
645*7c478bd9Sstevel@tonic-gate  * are TPI operations for TCP.  M_FLUSH requires careful processing, since
646*7c478bd9Sstevel@tonic-gate  * telnet cannot tolerate flushing its protocol requests.  Also the flushes
647*7c478bd9Sstevel@tonic-gate  * can be running either daemon<->TCP or application<->telmod.  We must
648*7c478bd9Sstevel@tonic-gate  * carefully deal with this.
649*7c478bd9Sstevel@tonic-gate  */
650*7c478bd9Sstevel@tonic-gate static void
telmodwput(queue_t * q,mblk_t * mp)651*7c478bd9Sstevel@tonic-gate telmodwput(
652*7c478bd9Sstevel@tonic-gate 	queue_t *q,	/* Pointer to the read queue */
653*7c478bd9Sstevel@tonic-gate 	mblk_t *mp)	/* Pointer to current message block */
654*7c478bd9Sstevel@tonic-gate {
655*7c478bd9Sstevel@tonic-gate 	struct telmod_info	*tmip;
656*7c478bd9Sstevel@tonic-gate 	struct iocblk *ioc;
657*7c478bd9Sstevel@tonic-gate 	mblk_t *savemp;
658*7c478bd9Sstevel@tonic-gate 	int rw;
659*7c478bd9Sstevel@tonic-gate 	int error;
660*7c478bd9Sstevel@tonic-gate 
661*7c478bd9Sstevel@tonic-gate 	tmip = (struct telmod_info *)q->q_ptr;
662*7c478bd9Sstevel@tonic-gate 
663*7c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
664*7c478bd9Sstevel@tonic-gate 	case M_DATA:
665*7c478bd9Sstevel@tonic-gate 		if (!canputnext(q) || (tmip->flags & TEL_STOPPED) ||
666*7c478bd9Sstevel@tonic-gate 			(q->q_first)) {
667*7c478bd9Sstevel@tonic-gate 			noenable(q);
668*7c478bd9Sstevel@tonic-gate 			(void) putq(q, mp);
669*7c478bd9Sstevel@tonic-gate 			break;
670*7c478bd9Sstevel@tonic-gate 		}
671*7c478bd9Sstevel@tonic-gate 		/*
672*7c478bd9Sstevel@tonic-gate 		 * This routine parses data generating from ptm side.
673*7c478bd9Sstevel@tonic-gate 		 * Insert a null character if carraige return
674*7c478bd9Sstevel@tonic-gate 		 * is not followed by line feed unless we are in binary mode.
675*7c478bd9Sstevel@tonic-gate 		 * Also, duplicate IAC if found in the data.
676*7c478bd9Sstevel@tonic-gate 		 */
677*7c478bd9Sstevel@tonic-gate 		(void) snd_parse(q, mp);
678*7c478bd9Sstevel@tonic-gate 		break;
679*7c478bd9Sstevel@tonic-gate 
680*7c478bd9Sstevel@tonic-gate 	case M_CTL:
681*7c478bd9Sstevel@tonic-gate 		if (((mp->b_wptr - mp->b_rptr) == 1) &&
682*7c478bd9Sstevel@tonic-gate 			(*(mp->b_rptr) == M_CTL_MAGIC_NUMBER)) {
683*7c478bd9Sstevel@tonic-gate 			savemp = mp->b_cont;
684*7c478bd9Sstevel@tonic-gate 			freeb(mp);
685*7c478bd9Sstevel@tonic-gate 			mp = savemp;
686*7c478bd9Sstevel@tonic-gate 		}
687*7c478bd9Sstevel@tonic-gate 		putnext(q, mp);
688*7c478bd9Sstevel@tonic-gate 		break;
689*7c478bd9Sstevel@tonic-gate 
690*7c478bd9Sstevel@tonic-gate 	case M_IOCTL:
691*7c478bd9Sstevel@tonic-gate 		ioc = (struct iocblk *)mp->b_rptr;
692*7c478bd9Sstevel@tonic-gate 		switch (ioc->ioc_cmd) {
693*7c478bd9Sstevel@tonic-gate 
694*7c478bd9Sstevel@tonic-gate 		/*
695*7c478bd9Sstevel@tonic-gate 		 * This ioctl is issued by user level daemon to
696*7c478bd9Sstevel@tonic-gate 		 * request one more message block to process protocol
697*7c478bd9Sstevel@tonic-gate 		 */
698*7c478bd9Sstevel@tonic-gate 		case TEL_IOC_GETBLK:
699*7c478bd9Sstevel@tonic-gate 			if (!(tmip->flags & TEL_STOPPED)) {
700*7c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
701*7c478bd9Sstevel@tonic-gate 				break;
702*7c478bd9Sstevel@tonic-gate 			}
703*7c478bd9Sstevel@tonic-gate 			tmip->flags |= TEL_GETBLK;
704*7c478bd9Sstevel@tonic-gate 			qenable(RD(q));
705*7c478bd9Sstevel@tonic-gate 			enableok(RD(q));
706*7c478bd9Sstevel@tonic-gate 
707*7c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
708*7c478bd9Sstevel@tonic-gate 			break;
709*7c478bd9Sstevel@tonic-gate 
710*7c478bd9Sstevel@tonic-gate 		/*
711*7c478bd9Sstevel@tonic-gate 		 * This ioctl is issued by user level daemon to reenable the
712*7c478bd9Sstevel@tonic-gate 		 * read and write queues. This is issued during startup time
713*7c478bd9Sstevel@tonic-gate 		 * after setting up the mux links and also after processing
714*7c478bd9Sstevel@tonic-gate 		 * the protocol.  It is also issued after each time an
715*7c478bd9Sstevel@tonic-gate 		 * an unrecognized telnet option is forwarded to the daemon.
716*7c478bd9Sstevel@tonic-gate 		 */
717*7c478bd9Sstevel@tonic-gate 		case TEL_IOC_ENABLE:
718*7c478bd9Sstevel@tonic-gate 
719*7c478bd9Sstevel@tonic-gate 			/*
720*7c478bd9Sstevel@tonic-gate 			 * Send negative ack if TEL_STOPPED flag is not set
721*7c478bd9Sstevel@tonic-gate 			 */
722*7c478bd9Sstevel@tonic-gate 			if (!(tmip->flags & TEL_STOPPED)) {
723*7c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
724*7c478bd9Sstevel@tonic-gate 				break;
725*7c478bd9Sstevel@tonic-gate 			}
726*7c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_STOPPED;
727*7c478bd9Sstevel@tonic-gate 			if (mp->b_cont) {
728*7c478bd9Sstevel@tonic-gate 				(void) putbq(RD(q), mp->b_cont);
729*7c478bd9Sstevel@tonic-gate 				mp->b_cont = 0;
730*7c478bd9Sstevel@tonic-gate 			}
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate 			qenable(RD(q));
733*7c478bd9Sstevel@tonic-gate 			enableok(RD(q));
734*7c478bd9Sstevel@tonic-gate 			qenable(q);
735*7c478bd9Sstevel@tonic-gate 			enableok(q);
736*7c478bd9Sstevel@tonic-gate 
737*7c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
738*7c478bd9Sstevel@tonic-gate 			break;
739*7c478bd9Sstevel@tonic-gate 
740*7c478bd9Sstevel@tonic-gate 		/*
741*7c478bd9Sstevel@tonic-gate 		 * Set binary/normal mode for input and output
742*7c478bd9Sstevel@tonic-gate 		 * according to the instructions from the daemon.
743*7c478bd9Sstevel@tonic-gate 		 */
744*7c478bd9Sstevel@tonic-gate 		case TEL_IOC_MODE:
745*7c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uchar_t));
746*7c478bd9Sstevel@tonic-gate 			if (error != 0) {
747*7c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
748*7c478bd9Sstevel@tonic-gate 				break;
749*7c478bd9Sstevel@tonic-gate 			}
750*7c478bd9Sstevel@tonic-gate 			tmip->flags |= *(mp->b_cont->b_rptr) &
751*7c478bd9Sstevel@tonic-gate 			    (TEL_BINARY_IN|TEL_BINARY_OUT);
752*7c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
753*7c478bd9Sstevel@tonic-gate 			break;
754*7c478bd9Sstevel@tonic-gate 
755*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
756*7c478bd9Sstevel@tonic-gate 		case TCSETAF:
757*7c478bd9Sstevel@tonic-gate 		case TCSETSF:
758*7c478bd9Sstevel@tonic-gate 		case TCSETA:
759*7c478bd9Sstevel@tonic-gate 		case TCSETAW:
760*7c478bd9Sstevel@tonic-gate 		case TCSETS:
761*7c478bd9Sstevel@tonic-gate 		case TCSETSW:
762*7c478bd9Sstevel@tonic-gate 		case TCSBRK:
763*7c478bd9Sstevel@tonic-gate 		case TIOCSTI:
764*7c478bd9Sstevel@tonic-gate 		case TIOCSWINSZ:
765*7c478bd9Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
766*7c478bd9Sstevel@tonic-gate 			break;
767*7c478bd9Sstevel@tonic-gate #endif
768*7c478bd9Sstevel@tonic-gate 		case CRYPTPASSTHRU:
769*7c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (uchar_t));
770*7c478bd9Sstevel@tonic-gate 			if (error != 0) {
771*7c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
772*7c478bd9Sstevel@tonic-gate 				break;
773*7c478bd9Sstevel@tonic-gate 			}
774*7c478bd9Sstevel@tonic-gate 			if (*(mp->b_cont->b_rptr) == 0x01)
775*7c478bd9Sstevel@tonic-gate 				tmip->flags |= TEL_IOCPASSTHRU;
776*7c478bd9Sstevel@tonic-gate 			else
777*7c478bd9Sstevel@tonic-gate 				tmip->flags &= ~TEL_IOCPASSTHRU;
778*7c478bd9Sstevel@tonic-gate 
779*7c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
780*7c478bd9Sstevel@tonic-gate 			break;
781*7c478bd9Sstevel@tonic-gate 
782*7c478bd9Sstevel@tonic-gate 		default:
783*7c478bd9Sstevel@tonic-gate 			if (tmip->flags & TEL_IOCPASSTHRU) {
784*7c478bd9Sstevel@tonic-gate 				putnext(q, mp);
785*7c478bd9Sstevel@tonic-gate 			} else {
786*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
787*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE,
788*7c478bd9Sstevel@tonic-gate 				"telmodwput: unexpected ioctl type 0x%x",
789*7c478bd9Sstevel@tonic-gate 					ioc->ioc_cmd);
790*7c478bd9Sstevel@tonic-gate #endif
791*7c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
792*7c478bd9Sstevel@tonic-gate 			}
793*7c478bd9Sstevel@tonic-gate 			break;
794*7c478bd9Sstevel@tonic-gate 		}
795*7c478bd9Sstevel@tonic-gate 		break;
796*7c478bd9Sstevel@tonic-gate 
797*7c478bd9Sstevel@tonic-gate 	case M_FLUSH:
798*7c478bd9Sstevel@tonic-gate 		/*
799*7c478bd9Sstevel@tonic-gate 		 * Flushing is tricky:  We try to flush all we can, but certain
800*7c478bd9Sstevel@tonic-gate 		 * data cannot be flushed.  Telnet protocol sequences cannot
801*7c478bd9Sstevel@tonic-gate 		 * be flushed.  So, TCP's queues cannot be flushed since we
802*7c478bd9Sstevel@tonic-gate 		 * cannot tell what might be telnet protocol data.  Then we
803*7c478bd9Sstevel@tonic-gate 		 * must take care to create and forward out-of-band data
804*7c478bd9Sstevel@tonic-gate 		 * indicating the flush to the far side.
805*7c478bd9Sstevel@tonic-gate 		 */
806*7c478bd9Sstevel@tonic-gate 		rw = *mp->b_rptr;
807*7c478bd9Sstevel@tonic-gate 		if (rw & FLUSHR) {
808*7c478bd9Sstevel@tonic-gate 			/*
809*7c478bd9Sstevel@tonic-gate 			 * We cannot flush our read queue, since there may
810*7c478bd9Sstevel@tonic-gate 			 * be telnet protocol bits in the queue, awaiting
811*7c478bd9Sstevel@tonic-gate 			 * processing.  However, once it leaves this module
812*7c478bd9Sstevel@tonic-gate 			 * it's guaranteed that all protocol data is in
813*7c478bd9Sstevel@tonic-gate 			 * M_CTL, so we do flush read data beyond us, expecting
814*7c478bd9Sstevel@tonic-gate 			 * them (actually logindmux) to do FLUSHDATAs also.
815*7c478bd9Sstevel@tonic-gate 			 */
816*7c478bd9Sstevel@tonic-gate 			*mp->b_rptr = rw & ~FLUSHW;
817*7c478bd9Sstevel@tonic-gate 			qreply(q, mp);
818*7c478bd9Sstevel@tonic-gate 		} else {
819*7c478bd9Sstevel@tonic-gate 			freemsg(mp);
820*7c478bd9Sstevel@tonic-gate 		}
821*7c478bd9Sstevel@tonic-gate 		if (rw & FLUSHW) {
822*7c478bd9Sstevel@tonic-gate 			/*
823*7c478bd9Sstevel@tonic-gate 			 * Since all telnet protocol data comes from the
824*7c478bd9Sstevel@tonic-gate 			 * daemon, stored as M_CTL messages, flushq will
825*7c478bd9Sstevel@tonic-gate 			 * do exactly what's needed:  Flush bytes which do
826*7c478bd9Sstevel@tonic-gate 			 * not have telnet protocol data.
827*7c478bd9Sstevel@tonic-gate 			 */
828*7c478bd9Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
829*7c478bd9Sstevel@tonic-gate 		}
830*7c478bd9Sstevel@tonic-gate 		break;
831*7c478bd9Sstevel@tonic-gate 
832*7c478bd9Sstevel@tonic-gate 	case M_PCPROTO:
833*7c478bd9Sstevel@tonic-gate 		putnext(q, mp);
834*7c478bd9Sstevel@tonic-gate 		break;
835*7c478bd9Sstevel@tonic-gate 
836*7c478bd9Sstevel@tonic-gate 	case M_PROTO:
837*7c478bd9Sstevel@tonic-gate 		/* We may receive T_DISCON_REQ from the mux */
838*7c478bd9Sstevel@tonic-gate 		if (!canputnext(q) || q->q_first != NULL)
839*7c478bd9Sstevel@tonic-gate 			(void) putq(q, mp);
840*7c478bd9Sstevel@tonic-gate 		else
841*7c478bd9Sstevel@tonic-gate 			putnext(q, mp);
842*7c478bd9Sstevel@tonic-gate 		break;
843*7c478bd9Sstevel@tonic-gate 
844*7c478bd9Sstevel@tonic-gate 	default:
845*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
846*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE,
847*7c478bd9Sstevel@tonic-gate 		    "telmodwput: unexpected msg type 0x%x",
848*7c478bd9Sstevel@tonic-gate 		    mp->b_datap->db_type);
849*7c478bd9Sstevel@tonic-gate #endif
850*7c478bd9Sstevel@tonic-gate 		freemsg(mp);
851*7c478bd9Sstevel@tonic-gate 		break;
852*7c478bd9Sstevel@tonic-gate 	}
853*7c478bd9Sstevel@tonic-gate }
854*7c478bd9Sstevel@tonic-gate 
855*7c478bd9Sstevel@tonic-gate /*
856*7c478bd9Sstevel@tonic-gate  * telmodwsrv - module write service procedure
857*7c478bd9Sstevel@tonic-gate  */
858*7c478bd9Sstevel@tonic-gate static void
telmodwsrv(queue_t * q)859*7c478bd9Sstevel@tonic-gate telmodwsrv(queue_t *q)
860*7c478bd9Sstevel@tonic-gate {
861*7c478bd9Sstevel@tonic-gate 	mblk_t	*mp, *savemp;
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate 	struct	telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
864*7c478bd9Sstevel@tonic-gate 
865*7c478bd9Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
866*7c478bd9Sstevel@tonic-gate 		if (!canputnext(q)) {
867*7c478bd9Sstevel@tonic-gate 			ASSERT(mp->b_datap->db_type < QPCTL);
868*7c478bd9Sstevel@tonic-gate 			(void) putbq(q, mp);
869*7c478bd9Sstevel@tonic-gate 			return;
870*7c478bd9Sstevel@tonic-gate 		}
871*7c478bd9Sstevel@tonic-gate 		switch (mp->b_datap->db_type) {
872*7c478bd9Sstevel@tonic-gate 
873*7c478bd9Sstevel@tonic-gate 		case M_DATA:
874*7c478bd9Sstevel@tonic-gate 			if (tmip->flags & TEL_STOPPED) {
875*7c478bd9Sstevel@tonic-gate 				(void) putbq(q, mp);
876*7c478bd9Sstevel@tonic-gate 				return;
877*7c478bd9Sstevel@tonic-gate 			}
878*7c478bd9Sstevel@tonic-gate 			/*
879*7c478bd9Sstevel@tonic-gate 			 * Insert a null character if carraige return
880*7c478bd9Sstevel@tonic-gate 			 * is not followed by line feed
881*7c478bd9Sstevel@tonic-gate 			 */
882*7c478bd9Sstevel@tonic-gate 			if (!snd_parse(q, mp)) {
883*7c478bd9Sstevel@tonic-gate 				return;
884*7c478bd9Sstevel@tonic-gate 			}
885*7c478bd9Sstevel@tonic-gate 			break;
886*7c478bd9Sstevel@tonic-gate 
887*7c478bd9Sstevel@tonic-gate 		case M_CTL:
888*7c478bd9Sstevel@tonic-gate 			if (((mp->b_wptr - mp->b_rptr) == 1) &&
889*7c478bd9Sstevel@tonic-gate 				(*(mp->b_rptr) == M_CTL_MAGIC_NUMBER)) {
890*7c478bd9Sstevel@tonic-gate 				savemp = mp->b_cont;
891*7c478bd9Sstevel@tonic-gate 				freeb(mp);
892*7c478bd9Sstevel@tonic-gate 				mp = savemp;
893*7c478bd9Sstevel@tonic-gate 			}
894*7c478bd9Sstevel@tonic-gate 			putnext(q, mp);
895*7c478bd9Sstevel@tonic-gate 			break;
896*7c478bd9Sstevel@tonic-gate 
897*7c478bd9Sstevel@tonic-gate 		case M_PROTO:
898*7c478bd9Sstevel@tonic-gate 			putnext(q, mp);
899*7c478bd9Sstevel@tonic-gate 			break;
900*7c478bd9Sstevel@tonic-gate 
901*7c478bd9Sstevel@tonic-gate 		default:
902*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
903*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE,
904*7c478bd9Sstevel@tonic-gate 			    "telmodwsrv: unexpected msg type 0x%x",
905*7c478bd9Sstevel@tonic-gate 			    mp->b_datap->db_type);
906*7c478bd9Sstevel@tonic-gate #endif
907*7c478bd9Sstevel@tonic-gate 			freemsg(mp);
908*7c478bd9Sstevel@tonic-gate 		}
909*7c478bd9Sstevel@tonic-gate 
910*7c478bd9Sstevel@tonic-gate 	}
911*7c478bd9Sstevel@tonic-gate }
912*7c478bd9Sstevel@tonic-gate 
913*7c478bd9Sstevel@tonic-gate /*
914*7c478bd9Sstevel@tonic-gate  * This routine is called from read put/service procedure and parses
915*7c478bd9Sstevel@tonic-gate  * message block to check for telnet protocol by detecting an IAC.
916*7c478bd9Sstevel@tonic-gate  * The routine processes the data part of the message block first and
917*7c478bd9Sstevel@tonic-gate  * then sends protocol followed after IAC to the telnet daemon. The
918*7c478bd9Sstevel@tonic-gate  * routine also processes CR/LF by eliminating LF/NULL followed after CR.
919*7c478bd9Sstevel@tonic-gate  *
920*7c478bd9Sstevel@tonic-gate  * Since the code to do this with streams mblks is complicated, some
921*7c478bd9Sstevel@tonic-gate  * explanations are in order.  If an IAC is found, a dupb() is done,
922*7c478bd9Sstevel@tonic-gate  * and the pointers are adjusted to create two streams message.  The
923*7c478bd9Sstevel@tonic-gate  * (possibly empty) first message contains preceeding data, and the
924*7c478bd9Sstevel@tonic-gate  * second begins with the IAC and contains the rest of the streams
925*7c478bd9Sstevel@tonic-gate  * message.
926*7c478bd9Sstevel@tonic-gate  *
927*7c478bd9Sstevel@tonic-gate  * The variables:
928*7c478bd9Sstevel@tonic-gate  * datamp:	Points to the head of a chain of mblks containing data
929*7c478bd9Sstevel@tonic-gate  *		which requires no expansion, and can be forwarded directly
930*7c478bd9Sstevel@tonic-gate  *		to the pty.
931*7c478bd9Sstevel@tonic-gate  * prevmp:	Points to the last mblk on the datamp chain, used to add
932*7c478bd9Sstevel@tonic-gate  *		to the chain headed by datamp.
933*7c478bd9Sstevel@tonic-gate  * newmp:	When an M_CTL header is required, this pointer references
934*7c478bd9Sstevel@tonic-gate  *		that "header" mblk.
935*7c478bd9Sstevel@tonic-gate  * protomp:	When an IAC is discovered, a dupb() is done on the first mblk
936*7c478bd9Sstevel@tonic-gate  *		containing an IAC.  protomp points to this dup'ed mblk.
937*7c478bd9Sstevel@tonic-gate  *		This mblk is eventually forwarded to the daemon.
938*7c478bd9Sstevel@tonic-gate  */
939*7c478bd9Sstevel@tonic-gate static int
rcv_parse(queue_t * q,mblk_t * mp)940*7c478bd9Sstevel@tonic-gate rcv_parse(queue_t *q, mblk_t *mp)
941*7c478bd9Sstevel@tonic-gate {
942*7c478bd9Sstevel@tonic-gate 	mblk_t	*protomp, *newmp, *datamp, *prevmp;
943*7c478bd9Sstevel@tonic-gate 	unsigned char *tmp;
944*7c478bd9Sstevel@tonic-gate 	size_t	msgsize;
945*7c478bd9Sstevel@tonic-gate 
946*7c478bd9Sstevel@tonic-gate 	struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
947*7c478bd9Sstevel@tonic-gate 
948*7c478bd9Sstevel@tonic-gate 	datamp = mp;
949*7c478bd9Sstevel@tonic-gate 	prevmp = protomp = 0;
950*7c478bd9Sstevel@tonic-gate 
951*7c478bd9Sstevel@tonic-gate 	while (mp) {
952*7c478bd9Sstevel@tonic-gate 		/*
953*7c478bd9Sstevel@tonic-gate 		 * If the mblk is empty, just continue scanning.
954*7c478bd9Sstevel@tonic-gate 		 */
955*7c478bd9Sstevel@tonic-gate 		if (mp->b_rptr == mp->b_wptr) {
956*7c478bd9Sstevel@tonic-gate 			prevmp = mp;
957*7c478bd9Sstevel@tonic-gate 			mp = mp->b_cont;
958*7c478bd9Sstevel@tonic-gate 			continue;
959*7c478bd9Sstevel@tonic-gate 		}
960*7c478bd9Sstevel@tonic-gate 		/*
961*7c478bd9Sstevel@tonic-gate 		 * First check to see if we have received CR and are checking
962*7c478bd9Sstevel@tonic-gate 		 * for a following LF/NULL.  If so, do what's necessary to
963*7c478bd9Sstevel@tonic-gate 		 * trim the LF/NULL.  This case is for when the LF/NULL is
964*7c478bd9Sstevel@tonic-gate 		 * at the beginning of a subsequent mblk.
965*7c478bd9Sstevel@tonic-gate 		 */
966*7c478bd9Sstevel@tonic-gate 		if (!(tmip->flags & TEL_BINARY_IN) &&
967*7c478bd9Sstevel@tonic-gate 		    (tmip->flags & TEL_CRRCV)) {
968*7c478bd9Sstevel@tonic-gate 			if ((*mp->b_rptr == '\n') || (*mp->b_rptr == NULL)) {
969*7c478bd9Sstevel@tonic-gate 				if (mp->b_wptr == (mp->b_rptr + 1)) {
970*7c478bd9Sstevel@tonic-gate 					tmip->flags &= ~TEL_CRRCV;
971*7c478bd9Sstevel@tonic-gate 					if (prevmp) {
972*7c478bd9Sstevel@tonic-gate 						prevmp->b_cont = mp->b_cont;
973*7c478bd9Sstevel@tonic-gate 						freeb(mp);
974*7c478bd9Sstevel@tonic-gate 						mp = prevmp->b_cont;
975*7c478bd9Sstevel@tonic-gate 						continue;
976*7c478bd9Sstevel@tonic-gate 					} else {
977*7c478bd9Sstevel@tonic-gate 						datamp = mp->b_cont;
978*7c478bd9Sstevel@tonic-gate 						freeb(mp);
979*7c478bd9Sstevel@tonic-gate 						if (datamp == NULL) {
980*7c478bd9Sstevel@tonic-gate 							/*
981*7c478bd9Sstevel@tonic-gate 							 * Message contained
982*7c478bd9Sstevel@tonic-gate 							 * only a '\0' after
983*7c478bd9Sstevel@tonic-gate 							 * a '\r' in a previous
984*7c478bd9Sstevel@tonic-gate 							 * message, so we can
985*7c478bd9Sstevel@tonic-gate 							 * read more, even
986*7c478bd9Sstevel@tonic-gate 							 * though we have
987*7c478bd9Sstevel@tonic-gate 							 * nothing to putnext.
988*7c478bd9Sstevel@tonic-gate 							 */
989*7c478bd9Sstevel@tonic-gate 							return (1);
990*7c478bd9Sstevel@tonic-gate 						} else {
991*7c478bd9Sstevel@tonic-gate 							mp = datamp;
992*7c478bd9Sstevel@tonic-gate 							continue;
993*7c478bd9Sstevel@tonic-gate 						}
994*7c478bd9Sstevel@tonic-gate 					}
995*7c478bd9Sstevel@tonic-gate 				}
996*7c478bd9Sstevel@tonic-gate 				mp->b_rptr += 1;
997*7c478bd9Sstevel@tonic-gate 			}
998*7c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_CRRCV;
999*7c478bd9Sstevel@tonic-gate 		}
1000*7c478bd9Sstevel@tonic-gate 		tmp = mp->b_rptr;
1001*7c478bd9Sstevel@tonic-gate 		/*
1002*7c478bd9Sstevel@tonic-gate 		 * Now scan through the entire message block, for IACs
1003*7c478bd9Sstevel@tonic-gate 		 * and CR characters, which need processing.
1004*7c478bd9Sstevel@tonic-gate 		 */
1005*7c478bd9Sstevel@tonic-gate 		while (tmp < mp->b_wptr) {
1006*7c478bd9Sstevel@tonic-gate 
1007*7c478bd9Sstevel@tonic-gate 			if (tmp[0] == IAC) {
1008*7c478bd9Sstevel@tonic-gate 				/*
1009*7c478bd9Sstevel@tonic-gate 				 * Telnet protocol - parse it now
1010*7c478bd9Sstevel@tonic-gate 				 * process data part of mblk
1011*7c478bd9Sstevel@tonic-gate 				 * before sending the protocol.
1012*7c478bd9Sstevel@tonic-gate 				 */
1013*7c478bd9Sstevel@tonic-gate 				if (tmp > mp->b_rptr) {
1014*7c478bd9Sstevel@tonic-gate 					if ((protomp = dupb(mp)) == NULL) {
1015*7c478bd9Sstevel@tonic-gate 						msgsize = msgdsize(datamp);
1016*7c478bd9Sstevel@tonic-gate 						recover(q, datamp, msgsize);
1017*7c478bd9Sstevel@tonic-gate 						return (0);
1018*7c478bd9Sstevel@tonic-gate 					}
1019*7c478bd9Sstevel@tonic-gate 					ASSERT(tmp >= mp->b_datap->db_base);
1020*7c478bd9Sstevel@tonic-gate 					ASSERT(tmp <= mp->b_datap->db_lim);
1021*7c478bd9Sstevel@tonic-gate 					ASSERT(tmp >=
1022*7c478bd9Sstevel@tonic-gate 					    protomp->b_datap->db_base);
1023*7c478bd9Sstevel@tonic-gate 					ASSERT(tmp <= protomp->b_datap->db_lim);
1024*7c478bd9Sstevel@tonic-gate 					mp->b_wptr = tmp;
1025*7c478bd9Sstevel@tonic-gate 					protomp->b_rptr = tmp;
1026*7c478bd9Sstevel@tonic-gate 					protomp->b_cont = mp->b_cont;
1027*7c478bd9Sstevel@tonic-gate 					mp->b_cont = 0;
1028*7c478bd9Sstevel@tonic-gate 
1029*7c478bd9Sstevel@tonic-gate 					if (prevmp)
1030*7c478bd9Sstevel@tonic-gate 						prevmp->b_cont = mp;
1031*7c478bd9Sstevel@tonic-gate 
1032*7c478bd9Sstevel@tonic-gate 				} else {
1033*7c478bd9Sstevel@tonic-gate 					protomp = mp;
1034*7c478bd9Sstevel@tonic-gate 
1035*7c478bd9Sstevel@tonic-gate 					if (prevmp)
1036*7c478bd9Sstevel@tonic-gate 						prevmp->b_cont = 0;
1037*7c478bd9Sstevel@tonic-gate 					else
1038*7c478bd9Sstevel@tonic-gate 						datamp = 0;
1039*7c478bd9Sstevel@tonic-gate 				}
1040*7c478bd9Sstevel@tonic-gate 				if (datamp) {
1041*7c478bd9Sstevel@tonic-gate 					putnext(q, datamp);
1042*7c478bd9Sstevel@tonic-gate 				}
1043*7c478bd9Sstevel@tonic-gate 				/*
1044*7c478bd9Sstevel@tonic-gate 				 * create a 1 byte M_CTL message block with
1045*7c478bd9Sstevel@tonic-gate 				 * protomp and send it down.
1046*7c478bd9Sstevel@tonic-gate 				 */
1047*7c478bd9Sstevel@tonic-gate 
1048*7c478bd9Sstevel@tonic-gate 				if ((newmp = allocb(sizeof (char),
1049*7c478bd9Sstevel@tonic-gate 					BPRI_MED)) == NULL) {
1050*7c478bd9Sstevel@tonic-gate 					/*
1051*7c478bd9Sstevel@tonic-gate 					 * Save the dup'ed mp containing
1052*7c478bd9Sstevel@tonic-gate 					 * the protocol information which
1053*7c478bd9Sstevel@tonic-gate 					 * we couldn't get an M_CTL header
1054*7c478bd9Sstevel@tonic-gate 					 * for.
1055*7c478bd9Sstevel@tonic-gate 					 */
1056*7c478bd9Sstevel@tonic-gate 					msgsize = msgdsize(protomp);
1057*7c478bd9Sstevel@tonic-gate 					recover(q, protomp, msgsize);
1058*7c478bd9Sstevel@tonic-gate 					return (0);
1059*7c478bd9Sstevel@tonic-gate 				}
1060*7c478bd9Sstevel@tonic-gate 				newmp->b_datap->db_type = M_CTL;
1061*7c478bd9Sstevel@tonic-gate 				newmp->b_wptr = newmp->b_rptr + 1;
1062*7c478bd9Sstevel@tonic-gate 				*(newmp->b_rptr) = M_CTL_MAGIC_NUMBER;
1063*7c478bd9Sstevel@tonic-gate 				newmp->b_cont = protomp;
1064*7c478bd9Sstevel@tonic-gate 				noenable(q);
1065*7c478bd9Sstevel@tonic-gate 				tmip->flags |= TEL_STOPPED;
1066*7c478bd9Sstevel@tonic-gate 				putnext(q, newmp);
1067*7c478bd9Sstevel@tonic-gate 
1068*7c478bd9Sstevel@tonic-gate 				return (0);
1069*7c478bd9Sstevel@tonic-gate 			}
1070*7c478bd9Sstevel@tonic-gate 			if (!(tmip->flags & TEL_BINARY_IN)) {
1071*7c478bd9Sstevel@tonic-gate 				/*
1072*7c478bd9Sstevel@tonic-gate 				 * Set TEL_CRRCV flag if last character is CR
1073*7c478bd9Sstevel@tonic-gate 				 */
1074*7c478bd9Sstevel@tonic-gate 				if ((tmp == (mp->b_wptr - 1)) &&
1075*7c478bd9Sstevel@tonic-gate 					(tmp[0] == '\r')) {
1076*7c478bd9Sstevel@tonic-gate 					tmip->flags |= TEL_CRRCV;
1077*7c478bd9Sstevel@tonic-gate 					break;
1078*7c478bd9Sstevel@tonic-gate 				}
1079*7c478bd9Sstevel@tonic-gate 
1080*7c478bd9Sstevel@tonic-gate 				/*
1081*7c478bd9Sstevel@tonic-gate 				 * If CR is followed by LF/NULL, get rid of
1082*7c478bd9Sstevel@tonic-gate 				 * LF/NULL and realign the message block.
1083*7c478bd9Sstevel@tonic-gate 				 */
1084*7c478bd9Sstevel@tonic-gate 				if ((tmp[0] == '\r') && ((tmp[1] == '\n') ||
1085*7c478bd9Sstevel@tonic-gate 				    (tmp[1] == NULL))) {
1086*7c478bd9Sstevel@tonic-gate 					/*
1087*7c478bd9Sstevel@tonic-gate 					 * If CR is in the middle of a block,
1088*7c478bd9Sstevel@tonic-gate 					 * we need to get rid of LF and join
1089*7c478bd9Sstevel@tonic-gate 					 * the two pieces together.
1090*7c478bd9Sstevel@tonic-gate 					 */
1091*7c478bd9Sstevel@tonic-gate 					if (mp->b_wptr > (tmp + 2)) {
1092*7c478bd9Sstevel@tonic-gate 						bcopy(tmp + 2, tmp + 1,
1093*7c478bd9Sstevel@tonic-gate 						    (mp->b_wptr - tmp - 2));
1094*7c478bd9Sstevel@tonic-gate 						mp->b_wptr -= 1;
1095*7c478bd9Sstevel@tonic-gate 					} else {
1096*7c478bd9Sstevel@tonic-gate 						mp->b_wptr = tmp + 1;
1097*7c478bd9Sstevel@tonic-gate 					}
1098*7c478bd9Sstevel@tonic-gate 
1099*7c478bd9Sstevel@tonic-gate 					if (prevmp)
1100*7c478bd9Sstevel@tonic-gate 						prevmp->b_cont = mp;
1101*7c478bd9Sstevel@tonic-gate 				}
1102*7c478bd9Sstevel@tonic-gate 			}
1103*7c478bd9Sstevel@tonic-gate 			tmp++;
1104*7c478bd9Sstevel@tonic-gate 		}
1105*7c478bd9Sstevel@tonic-gate 		prevmp = mp;
1106*7c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
1107*7c478bd9Sstevel@tonic-gate 	}
1108*7c478bd9Sstevel@tonic-gate 	putnext(q, datamp);
1109*7c478bd9Sstevel@tonic-gate 
1110*7c478bd9Sstevel@tonic-gate 	return (1);
1111*7c478bd9Sstevel@tonic-gate }
1112*7c478bd9Sstevel@tonic-gate 
1113*7c478bd9Sstevel@tonic-gate /*
1114*7c478bd9Sstevel@tonic-gate  * This routine is called from write put/service procedures and processes
1115*7c478bd9Sstevel@tonic-gate  * CR-LF. If CR is not followed by LF, it inserts a NULL character if we are
1116*7c478bd9Sstevel@tonic-gate  * in non binary mode. Also, duplicate IAC(0xFF) if found in the mblk.
1117*7c478bd9Sstevel@tonic-gate  * This routine is pessimistic:  It pre-allocates a buffer twice the size
1118*7c478bd9Sstevel@tonic-gate  * of the incoming message, which is the maximum size a message can become
1119*7c478bd9Sstevel@tonic-gate  * after IAC expansion.
1120*7c478bd9Sstevel@tonic-gate  *
1121*7c478bd9Sstevel@tonic-gate  * savemp:	Points at the original message, so it can be freed when
1122*7c478bd9Sstevel@tonic-gate  *		processing is complete.
1123*7c478bd9Sstevel@tonic-gate  * mp:		The current point of scanning the message.
1124*7c478bd9Sstevel@tonic-gate  * newmp:	New message being created with the processed output.
1125*7c478bd9Sstevel@tonic-gate  */
1126*7c478bd9Sstevel@tonic-gate static int
snd_parse(queue_t * q,mblk_t * mp)1127*7c478bd9Sstevel@tonic-gate snd_parse(queue_t *q, mblk_t *mp)
1128*7c478bd9Sstevel@tonic-gate {
1129*7c478bd9Sstevel@tonic-gate 	unsigned char *tmp, *tmp1;
1130*7c478bd9Sstevel@tonic-gate 	mblk_t	*newmp, *savemp;
1131*7c478bd9Sstevel@tonic-gate 	struct  telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
1132*7c478bd9Sstevel@tonic-gate 	size_t size = msgdsize(mp);
1133*7c478bd9Sstevel@tonic-gate 
1134*7c478bd9Sstevel@tonic-gate 	savemp = mp;
1135*7c478bd9Sstevel@tonic-gate 
1136*7c478bd9Sstevel@tonic-gate 	if (size == 0) {
1137*7c478bd9Sstevel@tonic-gate 		putnext(q, mp);
1138*7c478bd9Sstevel@tonic-gate 		return (1);
1139*7c478bd9Sstevel@tonic-gate 	}
1140*7c478bd9Sstevel@tonic-gate 
1141*7c478bd9Sstevel@tonic-gate 	/*
1142*7c478bd9Sstevel@tonic-gate 	 * Extra byte to allocb() takes care of the case when there was
1143*7c478bd9Sstevel@tonic-gate 	 * a '\r' at the end of the previous message and there's a '\r'
1144*7c478bd9Sstevel@tonic-gate 	 * at the beginning of the current message.
1145*7c478bd9Sstevel@tonic-gate 	 */
1146*7c478bd9Sstevel@tonic-gate 	if ((newmp = allocb((2 * size)+1, BPRI_MED)) == NULL) {
1147*7c478bd9Sstevel@tonic-gate 		recover(q, mp, (2 * size)+1);
1148*7c478bd9Sstevel@tonic-gate 		return (0);
1149*7c478bd9Sstevel@tonic-gate 	}
1150*7c478bd9Sstevel@tonic-gate 	newmp->b_datap->db_type = M_DATA;
1151*7c478bd9Sstevel@tonic-gate 
1152*7c478bd9Sstevel@tonic-gate 	tmp1 = newmp->b_rptr;
1153*7c478bd9Sstevel@tonic-gate 	while (mp) {
1154*7c478bd9Sstevel@tonic-gate 		if (!(tmip->flags & TEL_BINARY_OUT) &&
1155*7c478bd9Sstevel@tonic-gate 			(tmip->flags & TEL_CRSND)) {
1156*7c478bd9Sstevel@tonic-gate 			if (*(mp->b_rptr) != '\n')
1157*7c478bd9Sstevel@tonic-gate 				*tmp1++ = NULL;
1158*7c478bd9Sstevel@tonic-gate 			tmip->flags &= ~TEL_CRSND;
1159*7c478bd9Sstevel@tonic-gate 		}
1160*7c478bd9Sstevel@tonic-gate 		tmp = mp->b_rptr;
1161*7c478bd9Sstevel@tonic-gate 		while (tmp < mp->b_wptr) {
1162*7c478bd9Sstevel@tonic-gate 			if (!(tmip->flags & TEL_BINARY_OUT)) {
1163*7c478bd9Sstevel@tonic-gate 				*tmp1++ = *tmp;
1164*7c478bd9Sstevel@tonic-gate 				if ((tmp == (mp->b_wptr - 1)) &&
1165*7c478bd9Sstevel@tonic-gate 					(tmp[0] == '\r')) {
1166*7c478bd9Sstevel@tonic-gate 						tmip->flags |= TEL_CRSND;
1167*7c478bd9Sstevel@tonic-gate 						break;
1168*7c478bd9Sstevel@tonic-gate 				}
1169*7c478bd9Sstevel@tonic-gate 				if ((tmp[0] == '\r') &&
1170*7c478bd9Sstevel@tonic-gate 				    (tmp1 == newmp->b_wptr)) {
1171*7c478bd9Sstevel@tonic-gate 					/* XXX.sparker: can't happen */
1172*7c478bd9Sstevel@tonic-gate 					tmip->flags |= TEL_CRSND;
1173*7c478bd9Sstevel@tonic-gate 					break;
1174*7c478bd9Sstevel@tonic-gate 				}
1175*7c478bd9Sstevel@tonic-gate 				if ((tmp[0] == '\r') && (tmp[1] != '\n')) {
1176*7c478bd9Sstevel@tonic-gate 					*tmp1++ = NULL;
1177*7c478bd9Sstevel@tonic-gate 				}
1178*7c478bd9Sstevel@tonic-gate 			} else
1179*7c478bd9Sstevel@tonic-gate 				*tmp1++ = *tmp;
1180*7c478bd9Sstevel@tonic-gate 
1181*7c478bd9Sstevel@tonic-gate 			if (tmp[0] == IAC) {
1182*7c478bd9Sstevel@tonic-gate 				*tmp1++ = IAC;
1183*7c478bd9Sstevel@tonic-gate 			}
1184*7c478bd9Sstevel@tonic-gate 			tmp++;
1185*7c478bd9Sstevel@tonic-gate 		}
1186*7c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
1187*7c478bd9Sstevel@tonic-gate 	}
1188*7c478bd9Sstevel@tonic-gate 
1189*7c478bd9Sstevel@tonic-gate 	newmp->b_wptr = tmp1;
1190*7c478bd9Sstevel@tonic-gate 
1191*7c478bd9Sstevel@tonic-gate 	putnext(q, newmp);
1192*7c478bd9Sstevel@tonic-gate 	freemsg(savemp);
1193*7c478bd9Sstevel@tonic-gate 	return (1);
1194*7c478bd9Sstevel@tonic-gate }
1195*7c478bd9Sstevel@tonic-gate 
1196*7c478bd9Sstevel@tonic-gate static void
telmod_timer(void * arg)1197*7c478bd9Sstevel@tonic-gate telmod_timer(void *arg)
1198*7c478bd9Sstevel@tonic-gate {
1199*7c478bd9Sstevel@tonic-gate 	queue_t *q = arg;
1200*7c478bd9Sstevel@tonic-gate 	struct	telmod_info	*tmip = (struct telmod_info *)q->q_ptr;
1201*7c478bd9Sstevel@tonic-gate 
1202*7c478bd9Sstevel@tonic-gate 	ASSERT(tmip);
1203*7c478bd9Sstevel@tonic-gate 
1204*7c478bd9Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
1205*7c478bd9Sstevel@tonic-gate 		ASSERT(tmip->rtimoutid);
1206*7c478bd9Sstevel@tonic-gate 		tmip->rtimoutid = 0;
1207*7c478bd9Sstevel@tonic-gate 	} else {
1208*7c478bd9Sstevel@tonic-gate 		ASSERT(tmip->wtimoutid);
1209*7c478bd9Sstevel@tonic-gate 		tmip->wtimoutid = 0;
1210*7c478bd9Sstevel@tonic-gate 	}
1211*7c478bd9Sstevel@tonic-gate 	enableok(q);
1212*7c478bd9Sstevel@tonic-gate 	qenable(q);
1213*7c478bd9Sstevel@tonic-gate }
1214*7c478bd9Sstevel@tonic-gate 
1215*7c478bd9Sstevel@tonic-gate static void
telmod_buffer(void * arg)1216*7c478bd9Sstevel@tonic-gate telmod_buffer(void *arg)
1217*7c478bd9Sstevel@tonic-gate {
1218*7c478bd9Sstevel@tonic-gate 	queue_t *q = arg;
1219*7c478bd9Sstevel@tonic-gate 	struct	telmod_info	*tmip = (struct telmod_info *)q->q_ptr;
1220*7c478bd9Sstevel@tonic-gate 
1221*7c478bd9Sstevel@tonic-gate 	ASSERT(tmip);
1222*7c478bd9Sstevel@tonic-gate 
1223*7c478bd9Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
1224*7c478bd9Sstevel@tonic-gate 		ASSERT(tmip->rbufcid);
1225*7c478bd9Sstevel@tonic-gate 		tmip->rbufcid = 0;
1226*7c478bd9Sstevel@tonic-gate 	} else {
1227*7c478bd9Sstevel@tonic-gate 		ASSERT(tmip->wbufcid);
1228*7c478bd9Sstevel@tonic-gate 		tmip->wbufcid = 0;
1229*7c478bd9Sstevel@tonic-gate 	}
1230*7c478bd9Sstevel@tonic-gate 	enableok(q);
1231*7c478bd9Sstevel@tonic-gate 	qenable(q);
1232*7c478bd9Sstevel@tonic-gate }
1233*7c478bd9Sstevel@tonic-gate 
1234*7c478bd9Sstevel@tonic-gate static void
recover(queue_t * q,mblk_t * mp,size_t size)1235*7c478bd9Sstevel@tonic-gate recover(queue_t *q, mblk_t *mp, size_t size)
1236*7c478bd9Sstevel@tonic-gate {
1237*7c478bd9Sstevel@tonic-gate 	bufcall_id_t bid;
1238*7c478bd9Sstevel@tonic-gate 	timeout_id_t tid;
1239*7c478bd9Sstevel@tonic-gate 	struct	telmod_info	*tmip = (struct telmod_info *)q->q_ptr;
1240*7c478bd9Sstevel@tonic-gate 
1241*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type < QPCTL);
1242*7c478bd9Sstevel@tonic-gate 	noenable(q);
1243*7c478bd9Sstevel@tonic-gate 	(void) putbq(q, mp);
1244*7c478bd9Sstevel@tonic-gate 
1245*7c478bd9Sstevel@tonic-gate 	/*
1246*7c478bd9Sstevel@tonic-gate 	 * Make sure there is at most one outstanding request per queue.
1247*7c478bd9Sstevel@tonic-gate 	 */
1248*7c478bd9Sstevel@tonic-gate 	if (q->q_flag & QREADR) {
1249*7c478bd9Sstevel@tonic-gate 		if (tmip->rtimoutid || tmip->rbufcid) {
1250*7c478bd9Sstevel@tonic-gate 			return;
1251*7c478bd9Sstevel@tonic-gate 		}
1252*7c478bd9Sstevel@tonic-gate 	} else {
1253*7c478bd9Sstevel@tonic-gate 		if (tmip->wtimoutid || tmip->wbufcid) {
1254*7c478bd9Sstevel@tonic-gate 			return;
1255*7c478bd9Sstevel@tonic-gate 		}
1256*7c478bd9Sstevel@tonic-gate 	}
1257*7c478bd9Sstevel@tonic-gate 	if (!(bid = qbufcall(RD(q), size, BPRI_MED, telmod_buffer, q))) {
1258*7c478bd9Sstevel@tonic-gate 		tid = qtimeout(RD(q), telmod_timer, q, SIMWAIT);
1259*7c478bd9Sstevel@tonic-gate 		if (q->q_flag & QREADR)
1260*7c478bd9Sstevel@tonic-gate 			tmip->rtimoutid = tid;
1261*7c478bd9Sstevel@tonic-gate 		else
1262*7c478bd9Sstevel@tonic-gate 			tmip->wtimoutid = tid;
1263*7c478bd9Sstevel@tonic-gate 	} else	{
1264*7c478bd9Sstevel@tonic-gate 		if (q->q_flag & QREADR)
1265*7c478bd9Sstevel@tonic-gate 			tmip->rbufcid = bid;
1266*7c478bd9Sstevel@tonic-gate 		else
1267*7c478bd9Sstevel@tonic-gate 			tmip->wbufcid = bid;
1268*7c478bd9Sstevel@tonic-gate 	}
1269*7c478bd9Sstevel@tonic-gate }
1270