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