xref: /titanic_50/usr/src/uts/common/io/rlmod.c (revision 0e42dee69ed771bf604dd1789fca9d77b5bbe302)
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 
62 extern struct streamtab rloginmodinfo;
63 
64 static struct fmodsw fsw = {
65 	"rlmod",
66 	&rloginmodinfo,
67 	D_MTQPAIR | D_MP
68 };
69 
70 /*
71  * Module linkage information for the kernel.
72  */
73 
74 static struct modlstrmod modlstrmod = {
75 	&mod_strmodops,
76 	"rloginmod module",
77 	&fsw
78 };
79 
80 static struct modlinkage modlinkage = {
81 	MODREV_1, &modlstrmod, NULL
82 };
83 
84 
85 int
86 _init(void)
87 {
88 	return (mod_install(&modlinkage));
89 }
90 
91 int
92 _fini(void)
93 {
94 	return (mod_remove(&modlinkage));
95 }
96 
97 int
98 _info(struct modinfo *modinfop)
99 {
100 	return (mod_info(&modlinkage, modinfop));
101 }
102 
103 struct rlmod_info; /* forward reference for function prototype */
104 
105 static int		rlmodopen(queue_t *, dev_t *, int, int, cred_t *);
106 static int		rlmodclose(queue_t *, int, cred_t *);
107 static int		rlmodrput(queue_t *, mblk_t *);
108 static int		rlmodrsrv(queue_t *);
109 static int		rlmodwput(queue_t *, mblk_t *);
110 static int		rlmodwsrv(queue_t *);
111 static int		rlmodrmsg(queue_t *, mblk_t *);
112 static mblk_t		*make_expmblk(char);
113 static int 		rlwinctl(queue_t *, mblk_t *);
114 static mblk_t		*rlwinsetup(queue_t *, mblk_t *, unsigned char *);
115 
116 static void		rlmod_timer(void *);
117 static void		rlmod_buffer(void *);
118 static boolean_t	tty_flow(queue_t *, struct rlmod_info *, mblk_t *);
119 static boolean_t	rlmodwioctl(queue_t *, mblk_t *);
120 static void		recover(queue_t *, mblk_t *, size_t);
121 static 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  */
131 static	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 
140 static	struct	qinit	rloginmodrinit = {
141 	rlmodrput,
142 	rlmodrsrv,
143 	rlmodopen,
144 	rlmodclose,
145 	nulldev,
146 	&rloginmodiinfo,
147 	NULL
148 };
149 
150 static	struct	qinit	rloginmodwinit = {
151 	rlmodwput,
152 	rlmodwsrv,
153 	NULL,
154 	NULL,
155 	nulldev,
156 	&rloginmodiinfo,
157 	NULL
158 };
159 
160 struct	streamtab	rloginmodinfo = {
161 	&rloginmodrinit,
162 	&rloginmodwinit,
163 	NULL,
164 	NULL
165 };
166 
167 /*
168  * Per-instance state struct for the rloginmod module.
169  */
170 struct 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*/
193 static void
194 dummy_callback(void *arg)
195 {}
196 
197 /*
198  * rlmodopen - open routine gets called when the
199  *	    module gets pushed onto the stream.
200  */
201 /*ARGSUSED*/
202 static int
203 rlmodopen(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);
292 fail:
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*/
310 static int
311 rlmodclose(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 
365 static int
366 rlmodrput(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  */
484 static int
485 rlmodrsrv(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  */
595 static int
596 rlmodwput(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  */
742 static int
743 rlmodwsrv(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  */
804 static mblk_t *
805 make_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  */
840 static int
841 rlmodrmsg(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 	}
976 out:
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  */
993 static int
994 rlwinctl(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  */
1030 static mblk_t *
1031 rlwinsetup(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  */
1083 static boolean_t
1084 tty_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 
1182 static boolean_t
1183 rlmodwioctl(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 
1269 static void
1270 rlmod_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 
1287 static void
1288 rlmod_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 
1305 static void
1306 recover(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 
1318 static void
1319 recover1(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