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