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