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