1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * This module implements the services provided by the rlogin daemon
31 * after the connection is set up.  Mainly this means responding to
32 * interrupts and window size changes.  It begins operation in "disabled"
33 * state, and sends a T_DATA_REQ to the daemon to indicate that it is
34 * in place and ready to be enabled.  The daemon can then know when all
35 * data which sneaked passed rlmod (before it was pushed) has been received.
36 * The daemon may process this data, or send data back to be inserted in
37 * the read queue at the head with the RL_IOC_ENABLE ioctl.
38 */
39
40#include <sys/types.h>
41#include <sys/param.h>
42#include <sys/stream.h>
43#include <sys/stropts.h>
44#include <sys/strsun.h>
45#include <sys/kmem.h>
46#include <sys/errno.h>
47#include <sys/ddi.h>
48#include <sys/sunddi.h>
49#include <sys/tihdr.h>
50#include <sys/ptem.h>
51#include <sys/conf.h>
52#include <sys/debug.h>
53#include <sys/modctl.h>
54#include <sys/vtrace.h>
55#include <sys/rlioctl.h>
56#include <sys/termios.h>
57#include <sys/termio.h>
58#include <sys/byteorder.h>
59#include <sys/cmn_err.h>
60#include <sys/cryptmod.h>
61
62extern struct streamtab rloginmodinfo;
63
64static struct fmodsw fsw = {
65	"rlmod",
66	&rloginmodinfo,
67	D_MTQPAIR | D_MP
68};
69
70/*
71 * Module linkage information for the kernel.
72 */
73
74static struct modlstrmod modlstrmod = {
75	&mod_strmodops,
76	"rloginmod module",
77	&fsw
78};
79
80static struct modlinkage modlinkage = {
81	MODREV_1, &modlstrmod, NULL
82};
83
84
85int
86_init(void)
87{
88	return (mod_install(&modlinkage));
89}
90
91int
92_fini(void)
93{
94	return (mod_remove(&modlinkage));
95}
96
97int
98_info(struct modinfo *modinfop)
99{
100	return (mod_info(&modlinkage, modinfop));
101}
102
103struct rlmod_info; /* forward reference for function prototype */
104
105static int		rlmodopen(queue_t *, dev_t *, int, int, cred_t *);
106static int		rlmodclose(queue_t *, int, cred_t *);
107static int		rlmodrput(queue_t *, mblk_t *);
108static int		rlmodrsrv(queue_t *);
109static int		rlmodwput(queue_t *, mblk_t *);
110static int		rlmodwsrv(queue_t *);
111static int		rlmodrmsg(queue_t *, mblk_t *);
112static mblk_t		*make_expmblk(char);
113static int 		rlwinctl(queue_t *, mblk_t *);
114static mblk_t		*rlwinsetup(queue_t *, mblk_t *, unsigned char *);
115
116static void		rlmod_timer(void *);
117static void		rlmod_buffer(void *);
118static boolean_t	tty_flow(queue_t *, struct rlmod_info *, mblk_t *);
119static boolean_t	rlmodwioctl(queue_t *, mblk_t *);
120static void		recover(queue_t *, mblk_t *, size_t);
121static void		recover1(queue_t *, size_t);
122
123#define	RLMOD_ID	106
124#define	SIMWAIT		(1*hz)
125
126/*
127 * Stream module data structure definitions.
128 * generally pushed onto tcp by rlogin daemon
129 *
130 */
131static	struct	module_info	rloginmodiinfo = {
132	RLMOD_ID,				/* module id number */
133	"rlmod",				/* module name */
134	0,					/* minimum packet size */
135	INFPSZ,					/* maximum packet size */
136	512,					/* hi-water mark */
137	256					/* lo-water mark */
138};
139
140static	struct	qinit	rloginmodrinit = {
141	rlmodrput,
142	rlmodrsrv,
143	rlmodopen,
144	rlmodclose,
145	nulldev,
146	&rloginmodiinfo,
147	NULL
148};
149
150static	struct	qinit	rloginmodwinit = {
151	rlmodwput,
152	rlmodwsrv,
153	NULL,
154	NULL,
155	nulldev,
156	&rloginmodiinfo,
157	NULL
158};
159
160struct	streamtab	rloginmodinfo = {
161	&rloginmodrinit,
162	&rloginmodwinit,
163	NULL,
164	NULL
165};
166
167/*
168 * Per-instance state struct for the rloginmod module.
169 */
170struct rlmod_info
171{
172	int		flags;
173	bufcall_id_t	wbufcid;
174	bufcall_id_t	rbufcid;
175	timeout_id_t	wtimoutid;
176	timeout_id_t	rtimoutid;
177	int		rl_expdat;
178	int		stopmode;
179	mblk_t		*unbind_mp;
180	char		startc;
181	char		stopc;
182	char		oobdata[1];
183	mblk_t		*wndw_sz_hd_mp;
184};
185
186/*
187 * Flag used in flags
188 */
189#define	RL_DISABLED	0x1
190#define	RL_IOCPASSTHRU	0x2
191
192/*ARGSUSED*/
193static void
194dummy_callback(void *arg)
195{}
196
197/*
198 * rlmodopen - open routine gets called when the
199 *	    module gets pushed onto the stream.
200 */
201/*ARGSUSED*/
202static int
203rlmodopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *cred)
204{
205	struct rlmod_info	*rmip;
206	union T_primitives *tp;
207	mblk_t *bp;
208	int	error;
209
210	if (sflag != MODOPEN)
211		return (EINVAL);
212
213	if (q->q_ptr != NULL) {
214		/* It's already attached. */
215		return (0);
216	}
217
218	/*
219	 * Allocate state structure.
220	 */
221	rmip = kmem_zalloc(sizeof (*rmip), KM_SLEEP);
222
223	/*
224	 * Cross-link.
225	 */
226	q->q_ptr = rmip;
227	WR(q)->q_ptr = rmip;
228	rmip->rl_expdat = 0;
229	rmip->stopmode = TIOCPKT_DOSTOP;
230	rmip->startc = CTRL('q');
231	rmip->stopc = CTRL('s');
232	rmip->oobdata[0] = (char)TIOCPKT_WINDOW;
233	rmip->wndw_sz_hd_mp = NULL;
234	/*
235	 * Allow only non-M_DATA blocks to pass up to in.rlogind until
236	 * it is ready for M_DATA (indicated by RL_IOC_ENABLE).
237	 */
238	rmip->flags |= RL_DISABLED;
239
240	qprocson(q);
241
242	/*
243	 * Since TCP operates in the TLI-inspired brain-dead fashion,
244	 * the connection will revert to bound state if the connection
245	 * is reset by the client.  We must send a T_UNBIND_REQ in
246	 * that case so the port doesn't get "wedged" (preventing
247	 * inetd from being able to restart the listener).  Allocate
248	 * it here, so that we don't need to worry about allocb()
249	 * failures later.
250	 */
251	while ((rmip->unbind_mp = allocb(sizeof (union T_primitives),
252	    BPRI_HI)) == NULL) {
253		bufcall_id_t id = qbufcall(q, sizeof (union T_primitives),
254		    BPRI_HI, dummy_callback, NULL);
255		if (!qwait_sig(q)) {
256			qunbufcall(q, id);
257			error = EINTR;
258			goto fail;
259		}
260		qunbufcall(q, id);
261	}
262	rmip->unbind_mp->b_wptr = rmip->unbind_mp->b_rptr +
263	    sizeof (struct T_unbind_req);
264	rmip->unbind_mp->b_datap->db_type = M_PROTO;
265	tp = (union T_primitives *)rmip->unbind_mp->b_rptr;
266	tp->type = T_UNBIND_REQ;
267
268	/*
269	 * Send a M_PROTO msg of type T_DATA_REQ (this is unique for
270	 * read queue since only write queue can get T_DATA_REQ).
271	 * Readstream routine in the daemon will do a getmsg() till
272	 * it receives this proto message.
273	 */
274	while ((bp = allocb(sizeof (union T_primitives), BPRI_HI)) == NULL) {
275		bufcall_id_t id = qbufcall(q, sizeof (union T_primitives),
276		    BPRI_HI, dummy_callback, NULL);
277		if (!qwait_sig(q)) {
278			qunbufcall(q, id);
279			error = EINTR;
280			goto fail;
281		}
282		qunbufcall(q, id);
283	}
284	bp->b_datap->db_type = M_PROTO;
285	bp->b_wptr = bp->b_rptr + sizeof (union T_primitives);
286	tp = (union T_primitives *)bp->b_rptr;
287	tp->type = T_DATA_REQ;
288	tp->data_req.MORE_flag = 0;
289
290	putnext(q, bp);
291	return (0);
292fail:
293	qprocsoff(q);
294	if (rmip->unbind_mp != NULL) {
295		freemsg(rmip->unbind_mp);
296	}
297	kmem_free(rmip, sizeof (struct rlmod_info));
298	q->q_ptr = NULL;
299	WR(q)->q_ptr = NULL;
300	return (error);
301}
302
303
304/*
305 * rlmodclose - This routine gets called when the module
306 *	gets popped off of the stream.
307 */
308
309/*ARGSUSED*/
310static int
311rlmodclose(queue_t *q, int flag, cred_t *credp)
312{
313	struct rlmod_info   *rmip = (struct rlmod_info *)q->q_ptr;
314	mblk_t  *mp;
315
316	/*
317	 * Flush any write-side data downstream.  Ignoring flow
318	 * control at this point is known to be safe because the
319	 * M_HANGUP below poisons the stream such that no modules can
320	 * be pushed again.
321	 */
322	while (mp = getq(WR(q)))
323		putnext(WR(q), mp);
324
325	/* Poison the stream head so that we can't be pushed again. */
326	(void) putnextctl(q, M_HANGUP);
327
328	qprocsoff(q);
329	if (rmip->wbufcid) {
330		qunbufcall(q, rmip->wbufcid);
331		rmip->wbufcid = 0;
332	}
333	if (rmip->rbufcid) {
334		qunbufcall(q, rmip->rbufcid);
335		rmip->rbufcid = 0;
336	}
337	if (rmip->wtimoutid) {
338		(void) quntimeout(q, rmip->wtimoutid);
339		rmip->wtimoutid = 0;
340	}
341	if (rmip->rtimoutid) {
342		(void) quntimeout(q, rmip->rtimoutid);
343		rmip->rtimoutid = 0;
344	}
345
346	if (rmip->unbind_mp != NULL) {
347		freemsg(rmip->unbind_mp);
348	}
349
350	if (rmip->wndw_sz_hd_mp != NULL) {
351		freemsg(rmip->wndw_sz_hd_mp);
352	}
353
354	kmem_free(q->q_ptr, sizeof (struct rlmod_info));
355	q->q_ptr = WR(q)->q_ptr = NULL;
356	return (0);
357}
358
359/*
360 * rlmodrput - Module read queue put procedure.
361 *	This is called from the module or
362 *	driver downstream.
363 */
364
365static int
366rlmodrput(queue_t *q, mblk_t *mp)
367{
368	struct rlmod_info    *rmip = (struct rlmod_info *)q->q_ptr;
369	union T_primitives *tip;
370
371	TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_RPUT_IN, "rlmodrput start: "
372	    "q %p, mp %p", q, mp);
373
374
375	/* if low (normal) priority... */
376	if ((mp->b_datap->db_type < QPCTL) &&
377	    /* ...and data is already queued... */
378	    ((q->q_first) ||
379		/* ...or currently disabled and this is M_DATA... */
380		((rmip->flags & RL_DISABLED) &&
381		    (mp->b_datap->db_type == M_DATA)))) {
382		/* ...delay delivery of the message */
383		(void) putq(q, mp);
384		TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RPUT_OUT,
385		    "rlmodrput end: q %p, mp %p, %s", q, mp, "flow");
386		return (0);
387	}
388
389	switch (mp->b_datap->db_type) {
390
391	case M_PROTO:
392	case M_PCPROTO:
393		tip = (union T_primitives *)mp->b_rptr;
394		switch (tip->type) {
395
396		case T_ORDREL_IND:
397		case T_DISCON_IND:
398			/* Make into M_HANGUP and putnext */
399			mp->b_datap->db_type = M_HANGUP;
400			mp->b_wptr = mp->b_rptr;
401			if (mp->b_cont) {
402				freemsg(mp->b_cont);
403				mp->b_cont = NULL;
404			}
405			/*
406			 * If we haven't already, send T_UNBIND_REQ to prevent
407			 * TCP from going into "BOUND" state and locking up the
408			 * port.
409			 */
410			if (tip->type == T_DISCON_IND && rmip->unbind_mp !=
411			    NULL) {
412				putnext(q, mp);
413				qreply(q, rmip->unbind_mp);
414				rmip->unbind_mp = NULL;
415			} else {
416				putnext(q, mp);
417			}
418			break;
419
420		/*
421		 * We only get T_OK_ACK when we issue the unbind, and it can
422		 * be ignored safely.
423		 */
424		case T_OK_ACK:
425			ASSERT(rmip->unbind_mp == NULL);
426			freemsg(mp);
427			break;
428
429		default:
430			cmn_err(CE_NOTE,
431			    "rlmodrput: got 0x%x type M_PROTO/M_PCPROTO msg",
432			    tip->type);
433			freemsg(mp);
434		}
435		break;
436
437	case M_DATA:
438		if (canputnext(q) && q->q_first == NULL) {
439			(void) rlmodrmsg(q, mp);
440		} else {
441			(void) putq(q, mp);
442		}
443		break;
444
445	case M_FLUSH:
446		/*
447		 * Since M_FLUSH came from TCP, we mark it bound for
448		 * daemon, not tty.  This only happens when TCP expects
449		 * to do a connection reset.
450		 */
451		mp->b_flag |= MSGMARK;
452		if (*mp->b_rptr & FLUSHR)
453			flushq(q, FLUSHALL);
454
455		putnext(q, mp);
456		break;
457
458	case M_PCSIG:
459	case M_ERROR:
460	case M_IOCACK:
461	case M_IOCNAK:
462	case M_SETOPTS:
463		if (mp->b_datap->db_type <= QPCTL && !canputnext(q))
464			(void) putq(q, mp);
465		else
466			putnext(q, mp);
467		break;
468
469	default:
470#ifdef DEBUG
471		cmn_err(CE_NOTE, "rlmodrput: unexpected msg type 0x%x",
472		    mp->b_datap->db_type);
473#endif
474		freemsg(mp);
475	}
476	TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RPUT_OUT, "rlmodrput end: q %p, "
477		"mp %p, %s", q, mp, "done");
478	return (0);
479}
480
481/*
482 * rlmodrsrv - module read service procedure
483 */
484static int
485rlmodrsrv(queue_t *q)
486{
487	mblk_t	*mp;
488	struct rlmod_info    *rmip = (struct rlmod_info *)q->q_ptr;
489	union T_primitives *tip;
490
491	TRACE_1(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_IN, "rlmodrsrv start: "
492	    "q %p", q);
493	while ((mp = getq(q)) != NULL) {
494
495		switch (mp->b_datap->db_type) {
496		case M_DATA:
497			if (rmip->flags & RL_DISABLED) {
498				(void) putbq(q, mp);
499				TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT,
500					"rlmodrsrv end: q %p, mp %p, %s", q, mp,
501					"disabled");
502				return (0);
503			}
504			if (!canputnext(q)) {
505				(void) putbq(q, mp);
506				TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT,
507				    "rlmodrsrv end: q %p, mp %p, %s",
508				    q, mp, "!canputnext");
509				return (0);
510			}
511			if (!rlmodrmsg(q, mp)) {
512				TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT,
513				    "rlmodrsrv end: q %p, mp %p, %s",
514				    q, mp, "!rlmodrmsg");
515				return (0);
516			}
517			break;
518
519		case M_PROTO:
520			tip = (union T_primitives *)mp->b_rptr;
521			switch (tip->type) {
522
523			case T_ORDREL_IND:
524			case T_DISCON_IND:
525				/* Make into M_HANGUP and putnext */
526				mp->b_datap->db_type = M_HANGUP;
527				mp->b_wptr = mp->b_rptr;
528				if (mp->b_cont) {
529					freemsg(mp->b_cont);
530					mp->b_cont = NULL;
531				}
532				/*
533				 * If we haven't already, send T_UNBIND_REQ
534				 * to prevent TCP from going into "BOUND"
535				 * state and locking up the port.
536				 */
537				if (tip->type == T_DISCON_IND &&
538				    rmip->unbind_mp != NULL) {
539					putnext(q, mp);
540					qreply(q, rmip->unbind_mp);
541					rmip->unbind_mp = NULL;
542				} else {
543					putnext(q, mp);
544				}
545				break;
546
547			/*
548			 * We only get T_OK_ACK when we issue the unbind, and
549			 * it can be ignored safely.
550			 */
551			case T_OK_ACK:
552				ASSERT(rmip->unbind_mp == NULL);
553				freemsg(mp);
554				break;
555
556			default:
557				cmn_err(CE_NOTE,
558				    "rlmodrsrv: got 0x%x type PROTO msg",
559				    tip->type);
560				freemsg(mp);
561			}
562			break;
563
564		case M_SETOPTS:
565			if (!canputnext(q)) {
566				(void) putbq(q, mp);
567				TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT,
568				    "rlmodrsrv end: q %p, mp %p, %s",
569				    q, mp, "!canputnext M_SETOPTS");
570				return (0);
571			}
572			putnext(q, mp);
573			break;
574
575		default:
576#ifdef DEBUG
577			cmn_err(CE_NOTE,
578			    "rlmodrsrv: unexpected msg type 0x%x",
579			    mp->b_datap->db_type);
580#endif
581			freemsg(mp);
582		}
583	}
584
585	TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_RSRV_OUT, "rlmodrsrv end: q %p, "
586	    "mp %p, %s", q, mp, "empty");
587
588	return (0);
589}
590
591/*
592 * rlmodwput - Module write queue put procedure.
593 *	All non-zero messages are send downstream unchanged
594 */
595static int
596rlmodwput(queue_t *q, mblk_t *mp)
597{
598	char cntl;
599	struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
600	mblk_t *tmpmp;
601	int rw;
602
603	TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_IN, "rlmodwput start: "
604	    "q %p, mp %p", q, mp);
605
606	if (rmip->rl_expdat) {
607		/*
608		 * call make_expmblk to create an expedited
609		 * message block.
610		 */
611		cntl = rmip->oobdata[0] | TIOCPKT_FLUSHWRITE;
612
613		if (!canputnext(q)) {
614			(void) putq(q, mp);
615			TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT,
616			    "rlmodwput end: q %p, mp %p, %s",
617			    q, mp, "expdata && !canputnext");
618			return (0);
619		}
620		if ((tmpmp = make_expmblk(cntl))) {
621			putnext(q, tmpmp);
622			rmip->rl_expdat = 0;
623		} else {
624			recover1(q, sizeof (mblk_t)); /* XXX.sparker */
625		}
626	}
627
628	if ((q->q_first || rmip->rl_expdat) && mp->b_datap->db_type < QPCTL) {
629		(void) putq(q, mp);
630		TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT, "rlmodwput end: "
631		    "q %p, mp %p, %s", q, mp, "queued data");
632		return (0);
633	}
634	switch (mp->b_datap->db_type) {
635
636	case M_DATA:
637		if (!canputnext(q))
638			(void) putq(q, mp);
639		else
640			putnext(q, mp);
641		break;
642
643	case M_FLUSH:
644		/*
645		 * We must take care to create and forward out-of-band data
646		 * indicating the flush to the far side.
647		 */
648		rw = *mp->b_rptr;
649		*mp->b_rptr &= ~FLUSHW;
650		qreply(q, mp);
651		if (rw & FLUSHW) {
652			/*
653			 * Since all rlogin protocol data is sent in this
654			 * direction as urgent data, and TCP does not flush
655			 * urgent data, it is okay to actually forward this
656			 * flush.  (telmod cannot.)
657			 */
658			flushq(q, FLUSHDATA);
659			/*
660			 * The putnextctl1() call can only fail if we're
661			 * out of memory.  Ideally, we might set a state
662			 * bit and reschedule ourselves when memory
663			 * becomes available, so we make sure not to miss
664			 * sending the FLUSHW to TCP before the urgent
665			 * byte.  Not doing this just means in some cases
666			 * a bit more trash passes before the flush takes
667			 * hold.
668			 */
669			(void) putnextctl1(q, M_FLUSH, FLUSHW);
670			/*
671			 * Notify peer of the write flush request.
672			 */
673			cntl = rmip->oobdata[0] | TIOCPKT_FLUSHWRITE;
674			if (!canputnext(q)) {
675				TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT,
676				    "rlmodwput end: q %p, mp %p, %s",
677				    q, mp, "flushw && !canputnext");
678				return (0);
679			}
680			if ((mp = make_expmblk(cntl)) == NULL) {
681				rmip->rl_expdat = 1;
682				recover1(q, sizeof (mblk_t));
683				TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT,
684				    "rlmodwput end: q %p, mp %p, %s",
685				    q, mp, "!make_expmblk");
686				return (0);
687			}
688			putnext(q, mp);
689		}
690		break;
691
692	case M_IOCTL:
693		if (!rlmodwioctl(q, mp))
694			(void) putq(q, mp);
695		break;
696
697	case M_PROTO:
698		switch (((union T_primitives *)mp->b_rptr)->type) {
699		case T_EXDATA_REQ:
700		case T_ORDREL_REQ:
701		case T_DISCON_REQ:
702			putnext(q, mp);
703			break;
704
705		default:
706#ifdef DEBUG
707			cmn_err(CE_NOTE,
708			    "rlmodwput: unexpected TPI primitive 0x%x",
709			    ((union T_primitives *)mp->b_rptr)->type);
710#endif
711			freemsg(mp);
712		}
713		break;
714
715	case M_PCPROTO:
716		if (((struct T_exdata_req *)mp->b_rptr)->PRIM_type ==
717		    T_DISCON_REQ) {
718			putnext(q, mp);
719		} else {
720			/* XXX.sparker Log unexpected message */
721			freemsg(mp);
722		}
723		break;
724
725	default:
726#ifdef DEBUG
727		cmn_err(CE_NOTE,
728		    "rlmodwput: unexpected msg type 0x%x",
729		    mp->b_datap->db_type);
730#endif
731		freemsg(mp);
732		break;
733	}
734	TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT, "rlmodwput end: "
735	    "q %p, mp %p, %s", q, mp, "done");
736	return (0);
737}
738
739/*
740 * rlmodwsrv - module write service procedure
741 */
742static int
743rlmodwsrv(queue_t *q)
744{
745	mblk_t	*mp, *tmpmp;
746	char cntl;
747	struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
748
749	TRACE_1(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_IN, "rlmodwsrv "
750	    "start: q %p", q);
751	if (rmip->rl_expdat) {
752		/*
753		 * call make_expmblk to create an expedited
754		 * message block.
755		 */
756		cntl = rmip->oobdata[0] | TIOCPKT_FLUSHWRITE;
757		if (!canputnext(q)) {
758			TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT,
759			    "rlmodwsrv end: q %p, mp %p, %s",
760			    q, NULL, "!canputnext && expdat");
761			return (0);
762		}
763		if ((tmpmp = make_expmblk(cntl))) {
764			putnext(q, tmpmp);
765			rmip->rl_expdat = 0;
766		} else {
767			recover1(q, sizeof (mblk_t));
768			TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT,
769			    "rlmodwsrv end: q %p, mp %p, %s",
770			    q, NULL, "!make_expmblk");
771			return (0);
772		}
773	}
774	while ((mp = getq(q)) != NULL) {
775
776		if (!canputnext(q) || rmip->rl_expdat) {
777			(void) putbq(q, mp);
778			TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT,
779			    "rlmodwsrv end: q %p, mp %p, %s",
780			    q, mp, "!canputnext || expdat");
781			return (0);
782		}
783		if (mp->b_datap->db_type == M_IOCTL) {
784			if (!rlmodwioctl(q, mp)) {
785				TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT,
786				    "rlmodwsrv end: q %p, mp %p, %s",
787				    q, mp, "!rlmodwioctl");
788				(void) putbq(q, mp);
789				return (0);
790			}
791			continue;
792		}
793		putnext(q, mp);
794	}
795	TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WSRV_OUT, "rlmodwsrv end: q %p, "
796	    "mp %p, %s", q, mp, "done");
797	return (0);
798}
799
800/*
801 * This routine returns a message block with an expedited
802 * data request
803 */
804static mblk_t *
805make_expmblk(char cntl)
806{
807	mblk_t *mp;
808	mblk_t *bp;
809	struct T_exdata_req	*data_req;
810
811	bp = allocb(sizeof (struct T_exdata_req), BPRI_MED);
812	if (bp == NULL)
813		return (NULL);
814	if ((mp = allocb(sizeof (char), BPRI_MED)) == NULL) {
815		freeb(bp);
816		return (NULL);
817	}
818	bp->b_datap->db_type = M_PROTO;
819	data_req = (struct T_exdata_req *)bp->b_rptr;
820	data_req->PRIM_type = T_EXDATA_REQ;
821	data_req->MORE_flag = 0;
822
823	bp->b_wptr += sizeof (struct T_exdata_req);
824	/*
825	 * Send a 1 byte data message block with appropriate
826	 * control character.
827	 */
828	mp->b_datap->db_type = M_DATA;
829	mp->b_wptr = mp->b_rptr + 1;
830	(*(char *)(mp->b_rptr)) = cntl;
831	bp->b_cont = mp;
832	return (bp);
833}
834/*
835 * This routine parses M_DATA messages checking for window size protocol
836 * from a given message block.  It returns TRUE if no resource exhaustion
837 * conditions are found.  This is for use in the service procedure, which
838 * needs to know whether to continue, or stop processing the queue.
839 */
840static int
841rlmodrmsg(queue_t *q, mblk_t *mp)
842{
843	unsigned char *tmp, *tmp1;
844	mblk_t	*newmp;
845	size_t	sz;
846	ssize_t	count, newcount = 0;
847	struct	rlmod_info	*rmip = (struct rlmod_info *)q->q_ptr;
848
849	/*
850	 * Eliminate any zero length messages here, so we don't filter EOFs
851	 * accidentally.
852	 */
853	if (msgdsize(mp) == 0) {
854		ASSERT(rmip->wndw_sz_hd_mp == NULL);
855		goto out;
856	}
857	/*
858	 * Check if we have stored a previous message block because a window
859	 * update was split over TCP segments. If so, append the new one to
860	 * the stored one and process the stored one as if it just arrived.
861	 */
862	if (rmip->wndw_sz_hd_mp != NULL) {
863		linkb(rmip->wndw_sz_hd_mp, mp);
864		mp = rmip->wndw_sz_hd_mp;
865		rmip->wndw_sz_hd_mp = NULL;
866	}
867	newmp = mp;
868
869	while (mp) {
870		tmp = mp->b_rptr;
871		/*
872		 * scan through the entire message block
873		 */
874		while (tmp < mp->b_wptr) {
875			/*
876			 * check for FF (rlogin magic escape sequence)
877			 */
878			if (tmp[0] == RLOGIN_MAGIC) {
879				/*
880				 * Update bytes read so far.
881				 */
882				count = newcount + tmp - mp->b_rptr;
883				/*
884				 * Pull together message chain in case
885				 * window escape is split across blocks.
886				 */
887				if ((pullupmsg(newmp, -1)) == 0) {
888					sz = msgdsize(newmp);
889					recover(q, newmp, sz);
890					return (NULL);
891				}
892				/*
893				 * pullupmsg results in newmp consuming
894				 * all message blocks in this chain, and
895				 * therefor mp wants updating.
896				 */
897				mp = newmp;
898
899				/*
900				 * adjust tmp to where we
901				 * stopped - count keeps track
902				 * of bytes read so far.
903				 * reset newcount = 0.
904				 */
905				tmp = mp->b_rptr + count;
906				newcount = 0;
907
908				/*
909				 * Use the variable tmp1 to compute where
910				 * the end of the window escape (currently
911				 * the only rlogin protocol sequence), then
912				 * check to see if we got all those bytes.
913				 */
914				tmp1 = tmp + 4 + sizeof (struct winsize);
915
916				if (tmp1 > mp->b_wptr) {
917					/*
918					 * All the window escape bytes aren't
919					 * in this TCP segment. Store this
920					 * mblk to one side so we can append
921					 * the rest of the escape to it when
922					 * its segment arrives.
923					 */
924					rmip->wndw_sz_hd_mp = mp;
925					return (TRUE);
926				}
927				/*
928				 * check for FF FF s s pattern
929				 */
930				if ((tmp[1] == RLOGIN_MAGIC) &&
931				    (tmp[2] == 's') && (tmp[3] == 's')) {
932
933					/*
934					 * If rlwinsetup returns an error,
935					 * we do recover with newmp which
936					 * points to new chain of mblks after
937					 * doing window control ioctls.
938					 * rlwinsetup returns newmp which
939					 * contains only data part.
940					 * Note that buried inside rlwinsetup
941					 * is where we do the putnext.
942					 */
943					if (rlwinsetup(q, mp, tmp) == NULL) {
944						sz = msgdsize(mp);
945						recover(q, mp, sz);
946						return (NULL);
947					}
948					/*
949					 * We have successfully consumed the
950					 * window sequence, but rlwinsetup()
951					 * and its children have moved memory
952					 * up underneath us.  This means that
953					 * the byte underneath *tmp has not
954					 * been scanned now.  We will now need
955					 * to rescan it.
956					 */
957					continue;
958				}
959			}
960			tmp++;
961		}
962		/*
963		 * bump newcount to include size of this particular block.
964		 */
965		newcount += (mp->b_wptr - mp->b_rptr);
966		mp = mp->b_cont;
967	}
968	/*
969	 * If we trimmed the message down to nothing to forward, don't
970	 * send any M_DATA message.  (Don't want to send EOF!)
971	 */
972	if (msgdsize(newmp) == 0) {
973		freemsg(newmp);
974		newmp = NULL;
975	}
976out:
977	if (newmp) {
978		if (!canputnext(q)) {
979			(void) putbq(q, newmp);
980			return (NULL);
981		} else {
982			putnext(q, newmp);
983		}
984	}
985	return (TRUE);
986}
987
988
989/*
990 * This routine is called to handle window size changes.
991 * The routine returns 1 on success and 0 on error (allocb failure).
992 */
993static int
994rlwinctl(queue_t *q, mblk_t *mp)
995{
996	mblk_t	*rl_msgp;
997	struct	iocblk	*iocbp;
998	struct	rlmod_info	*rmip = (struct rlmod_info *)q->q_ptr;
999
1000	TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_WINCTL_IN, "rlwinctl start: q %p, "
1001	    "mp %p", q, mp);
1002
1003	rmip->oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */
1004
1005	if ((rl_msgp = mkiocb(TIOCSWINSZ)) == NULL) {
1006		TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_WINCTL_OUT, "rlwinctl end: "
1007		    "q %p, mp %p, allocb failed", q, mp);
1008		return (0);
1009	}
1010
1011	/*
1012	 * create an M_IOCTL message type.
1013	 */
1014	rl_msgp->b_cont = mp;
1015	iocbp = (struct iocblk *)rl_msgp->b_rptr;
1016	iocbp->ioc_count = msgdsize(mp);
1017
1018	putnext(q, rl_msgp);
1019	TRACE_2(TR_FAC_RLOGINP, TR_RLOGINP_WINCTL_OUT, "rlwinctl end: "
1020	    "q %p, mp %p, done", q, mp);
1021	return (1);
1022}
1023
1024/*
1025 * This routine sets up window size change protocol.
1026 * The routine returns the new mblk after issuing rlwinctl
1027 * for window size changes. New mblk contains only data part
1028 * of the message block. The routine returns 0 on error.
1029 */
1030static mblk_t *
1031rlwinsetup(queue_t *q, mblk_t *mp, unsigned char *blk)
1032{
1033	mblk_t		*mp1;
1034	unsigned char	*jmpmp;
1035	ssize_t		left = 0;
1036	struct winsize	win;
1037
1038	/*
1039	 * Set jmpmp to where to jump, to get just past the end of the
1040	 * window size protocol sequence.
1041	 */
1042	jmpmp = (blk + 4 + sizeof (struct winsize));
1043	left = mp->b_wptr - jmpmp;
1044
1045	if ((mp1 = allocb(sizeof (struct winsize), BPRI_MED)) == NULL)
1046		return (0);
1047	mp1->b_datap->db_type = M_DATA;
1048	mp1->b_wptr = mp1->b_rptr + sizeof (struct winsize);
1049	bcopy(blk + 4, &win, sizeof (struct winsize));
1050	win.ws_row = ntohs(win.ws_row);
1051	win.ws_col = ntohs(win.ws_col);
1052	win.ws_xpixel = ntohs(win.ws_xpixel);
1053	win.ws_ypixel = ntohs(win.ws_ypixel);
1054	bcopy(&win, mp1->b_rptr, sizeof (struct winsize));
1055
1056	if ((rlwinctl(q, mp1)) == NULL) {
1057		freeb(mp1);
1058		return (0);
1059	}
1060	if (left > 0) {
1061		/*
1062		 * Must delete the window size protocol sequence.  We do
1063		 * this by sliding all the stuff after the sequence (jmpmp)
1064		 * to where the sequence itself began (blk).
1065		 */
1066		bcopy(jmpmp, blk, left);
1067		mp->b_wptr = blk + left;
1068	} else
1069		mp->b_wptr = blk;
1070	return (mp);
1071}
1072
1073/*
1074 * When an ioctl changes software flow control on the tty, we must notify
1075 * the rlogin client, so it can adjust its behavior appropriately.  This
1076 * routine, called from either the put or service routine, determines if
1077 * the flow handling has changed.  If so, it tries to send the indication
1078 * to the client.  It returns true or false depending upon whether the
1079 * message was fully processed.  If it wasn't fully processed it queues
1080 * the message for retry later when resources
1081 * (allocb/canputnext) are available.
1082 */
1083static boolean_t
1084tty_flow(queue_t *q, struct rlmod_info *rmip, mblk_t *mp)
1085{
1086	struct iocblk *ioc;
1087	struct termios *tp;
1088	struct termio *ti;
1089	int stop, ixon;
1090	mblk_t *tmpmp;
1091	char cntl;
1092	int error;
1093
1094	ioc = (struct iocblk *)mp->b_rptr;
1095	switch (ioc->ioc_cmd) {
1096
1097	/*
1098	 * If it is a tty ioctl, save the output flow
1099	 * control flag and the start and stop flow control
1100	 * characters if they are available.
1101	 */
1102	case TCSETS:
1103	case TCSETSW:
1104	case TCSETSF:
1105		error = miocpullup(mp, sizeof (struct termios));
1106		if (error != 0) {
1107			miocnak(q, mp, 0, error);
1108			return (B_TRUE);
1109		}
1110		tp = (struct termios *)(mp->b_cont->b_rptr);
1111		rmip->stopc = tp->c_cc[VSTOP];
1112		rmip->startc = tp->c_cc[VSTART];
1113		ixon = tp->c_iflag & IXON;
1114		break;
1115
1116	case TCSETA:
1117	case TCSETAW:
1118	case TCSETAF:
1119		error = miocpullup(mp, sizeof (struct termio));
1120		if (error != 0) {
1121			miocnak(q, mp, 0, error);
1122			return (B_TRUE);
1123		}
1124		ti = (struct termio *)(mp->b_cont->b_rptr);
1125		ixon = ti->c_iflag & IXON;
1126		break;
1127
1128	default:
1129		/*
1130		 * This function must never be called for an M_IOCTL
1131		 * except the listed ones.
1132		 */
1133#ifdef DEBUG
1134		cmn_err(CE_PANIC,
1135		    "rloginmod: tty_flow: bad ioctl 0x%x", ioc->ioc_cmd);
1136#else
1137		miocnak(q, mp, 0, EINVAL);
1138		return (B_TRUE);
1139#endif
1140	}
1141	/*
1142	 * If tty ioctl processing is done, check for stopmode
1143	 */
1144	stop = (ixon && (rmip->stopc == CTRL('s')) &&
1145		(rmip->startc == CTRL('q')));
1146	if (rmip->stopmode == TIOCPKT_NOSTOP) {
1147		if (stop) {
1148			cntl = rmip->oobdata[0] | TIOCPKT_DOSTOP;
1149			if ((tmpmp = make_expmblk(cntl)) == NULL) {
1150				recover(q, mp, sizeof (mblk_t));
1151				return (B_FALSE);
1152			}
1153			if (!canputnext(q)) {
1154				freemsg(tmpmp);
1155				return (B_FALSE);
1156			}
1157			putnext(q, tmpmp);
1158			rmip->stopmode = TIOCPKT_DOSTOP;
1159		}
1160	} else {
1161		if (!stop) {
1162			cntl = rmip->oobdata[0] | TIOCPKT_NOSTOP;
1163			if ((tmpmp = make_expmblk(cntl)) == NULL) {
1164				recover(q, mp, sizeof (mblk_t));
1165				return (B_FALSE);
1166			}
1167			if (!canputnext(q)) {
1168				freemsg(tmpmp);
1169				return (B_FALSE);
1170			}
1171			putnext(q, tmpmp);
1172			rmip->stopmode = TIOCPKT_NOSTOP;
1173		}
1174	}
1175
1176	miocack(q, mp, 0, 0);
1177	return (B_TRUE);
1178}
1179
1180/* rlmodwioctl - handle M_IOCTL messages on the write queue. */
1181
1182static boolean_t
1183rlmodwioctl(queue_t *q, mblk_t *mp)
1184{
1185	struct iocblk *ioc;
1186	struct rlmod_info *rmip = (struct rlmod_info *)q->q_ptr;
1187	int error;
1188
1189	ioc = (struct iocblk *)mp->b_rptr;
1190	switch (ioc->ioc_cmd) {
1191
1192	/*
1193	 * This is a special ioctl to reenable the queue.
1194	 * The initial data read from the stream head is
1195	 * put back on the queue.
1196	 */
1197	case RL_IOC_ENABLE:
1198		/*
1199		 * Send negative ack if RL_DISABLED flag is not set
1200		 */
1201
1202		if (!(rmip->flags & RL_DISABLED)) {
1203			miocnak(q, mp, 0, EINVAL);
1204			break;
1205		}
1206		if (mp->b_cont) {
1207			(void) putbq(RD(q), mp->b_cont);
1208			mp->b_cont = NULL;
1209		}
1210
1211		if (rmip->flags & RL_DISABLED)
1212			rmip->flags &= ~RL_DISABLED;
1213		qenable(RD(q));
1214		miocack(q, mp, 0, 0);
1215		TRACE_3(TR_FAC_RLOGINP, TR_RLOGINP_WPUT_OUT,
1216		    "rlmodwput end: q %p, mp %p, %s",
1217		    q, mp, "IOCACK enable");
1218		return (B_TRUE);
1219
1220	/*
1221	 * If it is a tty ioctl, save the output flow
1222	 * control flag and the start and stop flow control
1223	 * characters if they are available.
1224	 */
1225	case TCSETS:
1226	case TCSETSW:
1227	case TCSETSF:
1228	case TCSETA:
1229	case TCSETAW:
1230	case TCSETAF:
1231		return (tty_flow(q, rmip, mp));
1232
1233#ifdef DEBUG
1234	case TIOCSWINSZ:
1235	case TIOCSTI:
1236	case TCSBRK:
1237		miocnak(q, mp, 0, EINVAL);
1238		break;
1239#endif
1240	case CRYPTPASSTHRU:
1241		error = miocpullup(mp, sizeof (uchar_t));
1242		if (error != 0) {
1243			miocnak(q, mp, 0, error);
1244			break;
1245		}
1246		if (*(mp->b_cont->b_rptr) == 0x01)
1247			rmip->flags |= RL_IOCPASSTHRU;
1248		else
1249			rmip->flags &= ~RL_IOCPASSTHRU;
1250
1251		miocack(q, mp, NULL, 0);
1252		break;
1253
1254	default:
1255		if (rmip->flags & RL_IOCPASSTHRU) {
1256			putnext(q, mp);
1257		} else {
1258#ifdef DEBUG
1259			cmn_err(CE_NOTE,
1260				"rlmodwioctl: unexpected ioctl type 0x%x",
1261				ioc->ioc_cmd);
1262#endif
1263			miocnak(q, mp, 0, EINVAL);
1264		}
1265	}
1266	return (B_TRUE);
1267}
1268
1269static void
1270rlmod_timer(void *arg)
1271{
1272	queue_t *q = arg;
1273	struct rlmod_info	*rmip = (struct rlmod_info *)q->q_ptr;
1274
1275	ASSERT(rmip);
1276	if (q->q_flag & QREADR) {
1277		ASSERT(rmip->rtimoutid);
1278		rmip->rtimoutid = 0;
1279	} else {
1280		ASSERT(rmip->wtimoutid);
1281		rmip->wtimoutid = 0;
1282	}
1283	enableok(q);
1284	qenable(q);
1285}
1286
1287static void
1288rlmod_buffer(void *arg)
1289{
1290	queue_t *q = arg;
1291	struct rlmod_info	*rmip = (struct rlmod_info *)q->q_ptr;
1292
1293	ASSERT(rmip);
1294	if (q->q_flag & QREADR) {
1295		ASSERT(rmip->rbufcid);
1296		rmip->rbufcid = 0;
1297	} else {
1298		ASSERT(rmip->wbufcid);
1299		rmip->wbufcid = 0;
1300	}
1301	enableok(q);
1302	qenable(q);
1303}
1304
1305static void
1306recover(queue_t *q, mblk_t *mp, size_t size)
1307{
1308	/*
1309	 * Avoid re-enabling the queue.
1310	 */
1311	ASSERT(mp->b_datap->db_type < QPCTL);
1312
1313	noenable(q);
1314	(void) putbq(q, mp);
1315	recover1(q, size);
1316}
1317
1318static void
1319recover1(queue_t *q, size_t size)
1320{
1321	struct rlmod_info	*rmip = (struct rlmod_info *)q->q_ptr;
1322	timeout_id_t	tid;
1323	bufcall_id_t	bid;
1324
1325	/*
1326	 * Make sure there is at most one outstanding request per queue.
1327	 */
1328	if (q->q_flag & QREADR) {
1329		if (rmip->rtimoutid || rmip->rbufcid)
1330			return;
1331	} else {
1332		if (rmip->wtimoutid || rmip->wbufcid)
1333			return;
1334	}
1335	if (!(bid = qbufcall(RD(q), size, BPRI_MED, rlmod_buffer, q))) {
1336		tid = qtimeout(RD(q), rlmod_timer, q, SIMWAIT);
1337		if (q->q_flag & QREADR)
1338			rmip->rtimoutid = tid;
1339		else
1340			rmip->wtimoutid = tid;
1341	} else	{
1342		if (q->q_flag & QREADR)
1343			rmip->rbufcid = bid;
1344		else
1345			rmip->wbufcid = bid;
1346	}
1347}
1348