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