bmake-ify mega_sas
[unleashed.git] / usr / src / uts / common / io / telmod.c
blob85b0833f4271345acd218992879fd75400ce9675
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.
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.
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>
59 #define IAC 255
61 extern struct streamtab telmodinfo;
63 #define TELMOD_ID 105
64 #define SIMWAIT (1*hz)
67 * Module state flags
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
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.
85 * Per queue instances are single-threaded since the q_ptr
86 * field of queues need to be shared among threads.
88 static struct fmodsw fsw = {
89 "telmod",
90 &telmodinfo,
91 D_MTQPAIR | D_MP
95 * Module linkage information for the kernel.
98 static struct modlstrmod modlstrmod = {
99 &mod_strmodops,
100 "telnet module",
101 &fsw
104 static struct modlinkage modlinkage = {
105 MODREV_1, &modlstrmod, NULL
109 _init()
111 return (mod_install(&modlinkage));
115 _fini()
117 return (mod_remove(&modlinkage));
121 _info(struct modinfo *modinfop)
123 return (mod_info(&modlinkage, modinfop));
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);
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 */
147 static struct qinit telmodrinit = {
148 (int (*)())telmodrput,
149 (int (*)())telmodrsrv,
150 telmodopen,
151 telmodclose,
152 nulldev,
153 &telmodoinfo,
154 NULL
157 static struct qinit telmodwinit = {
158 (int (*)())telmodwput,
159 (int (*)())telmodwsrv,
160 NULL,
161 NULL,
162 nulldev,
163 &telmodoinfo,
164 NULL
167 struct streamtab telmodinfo = {
168 &telmodrinit,
169 &telmodwinit,
170 NULL,
171 NULL
175 * Per-instance state struct for the telnet module.
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;
187 /*ARGSUSED*/
188 static void
189 dummy_callback(void *arg)
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.
204 /*ARGSUSED*/
205 static int
206 telmodopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
208 struct telmod_info *tmip;
209 mblk_t *bp;
210 union T_primitives *tp;
211 int error;
213 if (sflag != MODOPEN)
214 return (EINVAL);
216 if (q->q_ptr != NULL) {
217 /* It's already attached. */
218 return (0);
221 * Allocate state structure.
223 tmip = kmem_zalloc(sizeof (*tmip), KM_SLEEP);
226 * Cross-link.
228 q->q_ptr = tmip;
229 WR(q)->q_ptr = tmip;
231 noenable(q);
232 tmip->flags |= TEL_STOPPED;
233 qprocson(q);
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.
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;
253 qunbufcall(q, id);
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;
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
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;
274 qunbufcall(q, id);
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;
282 putnext(q, bp);
283 return (0);
285 fail:
286 qprocsoff(q);
287 if (tmip->unbind_mp != NULL) {
288 freemsg(tmip->unbind_mp);
290 kmem_free(tmip, sizeof (struct telmod_info));
291 q->q_ptr = NULL;
292 WR(q)->q_ptr = NULL;
293 return (error);
298 * telmodclose - just the normal streams clean-up is required.
301 /*ARGSUSED*/
302 static int
303 telmodclose(queue_t *q, int flag, cred_t *credp)
305 struct telmod_info *tmip = (struct telmod_info *)q->q_ptr;
306 mblk_t *mp;
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.
314 while (mp = getq(WR(q)))
315 putnext(WR(q), mp);
317 /* Poison the stream head so that we can't be pushed again. */
318 (void) putnextctl(q, M_HANGUP);
320 qprocsoff(q);
321 if (tmip->wbufcid) {
322 qunbufcall(q, tmip->wbufcid);
323 tmip->wbufcid = 0;
325 if (tmip->rbufcid) {
326 qunbufcall(q, tmip->rbufcid);
327 tmip->rbufcid = 0;
329 if (tmip->wtimoutid) {
330 (void) quntimeout(q, tmip->wtimoutid);
331 tmip->wtimoutid = 0;
333 if (tmip->rtimoutid) {
334 (void) quntimeout(q, tmip->rtimoutid);
335 tmip->rtimoutid = 0;
337 if (tmip->unbind_mp != NULL) {
338 freemsg(tmip->unbind_mp);
341 kmem_free(q->q_ptr, sizeof (struct telmod_info));
342 q->q_ptr = WR(q)->q_ptr = NULL;
343 return (0);
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.
353 static void
354 telmodrput(queue_t *q, mblk_t *mp)
356 mblk_t *newmp;
357 struct telmod_info *tmip = (struct telmod_info *)q->q_ptr;
358 union T_primitives *tip;
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;
367 switch (mp->b_datap->db_type) {
368 case M_DATA:
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.
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;
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;
389 putnext(q, newmp);
391 break;
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.
401 (void) rcv_parse(q, mp);
402 break;
404 case M_FLUSH:
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.
410 mp->b_flag |= MSGMARK;
411 if (*mp->b_rptr & FLUSHR)
412 flushq(q, FLUSHALL);
413 putnext(q, mp);
414 break;
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;
427 case M_PROTO:
428 case M_PCPROTO:
429 if (tmip->flags & TEL_GETBLK)
430 tmip->flags &= ~TEL_GETBLK;
432 tip = (union T_primitives *)mp->b_rptr;
433 switch (tip->type) {
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;
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.
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);
458 break;
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;
470 freemsg(mp);
472 break;
475 * We only get T_OK_ACK when we issue the unbind, and it can
476 * be ignored safely.
478 case T_OK_ACK:
479 ASSERT(tmip->unbind_mp == NULL);
480 freemsg(mp);
481 break;
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);
491 break;
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);
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?
508 static void
509 telmodrsrv(queue_t *q)
511 mblk_t *mp, *newmp;
512 struct telmod_info *tmip = (struct telmod_info *)q->q_ptr;
513 union T_primitives *tip;
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;
521 switch (mp->b_datap->db_type) {
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;
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;
539 putnext(q, newmp);
541 break;
543 if (!rcv_parse(q, mp)) {
544 return;
546 break;
548 case M_PROTO:
550 tip = (union T_primitives *)mp->b_rptr;
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.
557 if (tip->type != T_DATA_IND &&
558 tip->type != T_EXDATA_IND)
559 tmip->flags &= ~TEL_GETBLK;
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;
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.
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);
585 break;
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;
597 freemsg(mp);
599 break;
602 * We only get T_OK_ACK when we issue the unbind, and
603 * it can be ignored safely.
605 case T_OK_ACK:
606 ASSERT(tmip->unbind_mp == NULL);
607 freemsg(mp);
608 break;
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);
618 break;
620 case M_SETOPTS:
621 putnext(q, mp);
622 break;
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);
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.
648 static void
649 telmodwput(
650 queue_t *q, /* Pointer to the read queue */
651 mblk_t *mp) /* Pointer to current message block */
653 struct telmod_info *tmip;
654 struct iocblk *ioc;
655 mblk_t *savemp;
656 int rw;
657 int error;
659 tmip = (struct telmod_info *)q->q_ptr;
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;
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.
675 (void) snd_parse(q, mp);
676 break;
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;
685 putnext(q, mp);
686 break;
688 case M_IOCTL:
689 ioc = (struct iocblk *)mp->b_rptr;
690 switch (ioc->ioc_cmd) {
693 * This ioctl is issued by user level daemon to
694 * request one more message block to process protocol
696 case TEL_IOC_GETBLK:
697 if (!(tmip->flags & TEL_STOPPED)) {
698 miocnak(q, mp, 0, EINVAL);
699 break;
701 tmip->flags |= TEL_GETBLK;
702 qenable(RD(q));
703 enableok(RD(q));
705 miocack(q, mp, 0, 0);
706 break;
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.
715 case TEL_IOC_ENABLE:
718 * Send negative ack if TEL_STOPPED flag is not set
720 if (!(tmip->flags & TEL_STOPPED)) {
721 miocnak(q, mp, 0, EINVAL);
722 break;
724 tmip->flags &= ~TEL_STOPPED;
725 if (mp->b_cont) {
726 (void) putbq(RD(q), mp->b_cont);
727 mp->b_cont = 0;
730 qenable(RD(q));
731 enableok(RD(q));
732 qenable(q);
733 enableok(q);
735 miocack(q, mp, 0, 0);
736 break;
739 * Set binary/normal mode for input and output
740 * according to the instructions from the daemon.
742 case TEL_IOC_MODE:
743 error = miocpullup(mp, sizeof (uchar_t));
744 if (error != 0) {
745 miocnak(q, mp, 0, error);
746 break;
748 tmip->flags |= *(mp->b_cont->b_rptr) &
749 (TEL_BINARY_IN|TEL_BINARY_OUT);
750 miocack(q, mp, 0, 0);
751 break;
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;
772 if (*(mp->b_cont->b_rptr) == 0x01)
773 tmip->flags |= TEL_IOCPASSTHRU;
774 else
775 tmip->flags &= ~TEL_IOCPASSTHRU;
777 miocack(q, mp, 0, 0);
778 break;
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);
791 break;
793 break;
795 case M_FLUSH:
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.
804 rw = *mp->b_rptr;
805 if (rw & FLUSHR) {
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.
814 *mp->b_rptr = rw & ~FLUSHW;
815 qreply(q, mp);
816 } else {
817 freemsg(mp);
819 if (rw & FLUSHW) {
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.
826 flushq(q, FLUSHDATA);
828 break;
830 case M_PCPROTO:
831 putnext(q, mp);
832 break;
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;
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;
854 * telmodwsrv - module write service procedure
856 static void
857 telmodwsrv(queue_t *q)
859 mblk_t *mp, *savemp;
861 struct telmod_info *tmip = (struct telmod_info *)q->q_ptr;
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;
869 switch (mp->b_datap->db_type) {
871 case M_DATA:
872 if (tmip->flags & TEL_STOPPED) {
873 (void) putbq(q, mp);
874 return;
877 * Insert a null character if carraige return
878 * is not followed by line feed
880 if (!snd_parse(q, mp)) {
881 return;
883 break;
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;
892 putnext(q, mp);
893 break;
895 case M_PROTO:
896 putnext(q, mp);
897 break;
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);
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.
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.
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.
937 static int
938 rcv_parse(queue_t *q, mblk_t *mp)
940 mblk_t *protomp, *newmp, *datamp, *prevmp;
941 unsigned char *tmp;
942 size_t msgsize;
944 struct telmod_info *tmip = (struct telmod_info *)q->q_ptr;
946 datamp = mp;
947 prevmp = protomp = 0;
949 while (mp) {
951 * If the mblk is empty, just continue scanning.
953 if (mp->b_rptr == mp->b_wptr) {
954 prevmp = mp;
955 mp = mp->b_cont;
956 continue;
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.
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) {
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.
987 return (1);
988 } else {
989 mp = datamp;
990 continue;
994 mp->b_rptr += 1;
996 tmip->flags &= ~TEL_CRRCV;
998 tmp = mp->b_rptr;
1000 * Now scan through the entire message block, for IACs
1001 * and CR characters, which need processing.
1003 while (tmp < mp->b_wptr) {
1005 if (tmp[0] == IAC) {
1007 * Telnet protocol - parse it now
1008 * process data part of mblk
1009 * before sending the protocol.
1011 if (tmp > mp->b_rptr) {
1012 if ((protomp = dupb(mp)) == NULL) {
1013 msgsize = msgdsize(datamp);
1014 recover(q, datamp, msgsize);
1015 return (0);
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;
1027 if (prevmp)
1028 prevmp->b_cont = mp;
1030 } else {
1031 protomp = mp;
1033 if (prevmp)
1034 prevmp->b_cont = 0;
1035 else
1036 datamp = 0;
1038 if (datamp) {
1039 putnext(q, datamp);
1042 * create a 1 byte M_CTL message block with
1043 * protomp and send it down.
1046 if ((newmp = allocb(sizeof (char),
1047 BPRI_MED)) == NULL) {
1049 * Save the dup'ed mp containing
1050 * the protocol information which
1051 * we couldn't get an M_CTL header
1052 * for.
1054 msgsize = msgdsize(protomp);
1055 recover(q, protomp, msgsize);
1056 return (0);
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);
1066 return (0);
1068 if (!(tmip->flags & TEL_BINARY_IN)) {
1070 * Set TEL_CRRCV flag if last character is CR
1072 if ((tmp == (mp->b_wptr - 1)) &&
1073 (tmp[0] == '\r')) {
1074 tmip->flags |= TEL_CRRCV;
1075 break;
1079 * If CR is followed by LF/NULL, get rid of
1080 * LF/NULL and realign the message block.
1082 if ((tmp[0] == '\r') && ((tmp[1] == '\n') ||
1083 (tmp[1] == '\0'))) {
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.
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;
1097 if (prevmp)
1098 prevmp->b_cont = mp;
1101 tmp++;
1103 prevmp = mp;
1104 mp = mp->b_cont;
1106 putnext(q, datamp);
1108 return (1);
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.
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.
1124 static int
1125 snd_parse(queue_t *q, mblk_t *mp)
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);
1132 savemp = mp;
1134 if (size == 0) {
1135 putnext(q, mp);
1136 return (1);
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.
1144 if ((newmp = allocb((2 * size)+1, BPRI_MED)) == NULL) {
1145 recover(q, mp, (2 * size)+1);
1146 return (0);
1148 newmp->b_datap->db_type = M_DATA;
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;
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;
1167 if ((tmp[0] == '\r') &&
1168 (tmp1 == newmp->b_wptr)) {
1169 /* XXX.sparker: can't happen */
1170 tmip->flags |= TEL_CRSND;
1171 break;
1173 if ((tmp[0] == '\r') && (tmp[1] != '\n')) {
1174 *tmp1++ = 0;
1176 } else
1177 *tmp1++ = *tmp;
1179 if (tmp[0] == IAC) {
1180 *tmp1++ = IAC;
1182 tmp++;
1184 mp = mp->b_cont;
1187 newmp->b_wptr = tmp1;
1189 putnext(q, newmp);
1190 freemsg(savemp);
1191 return (1);
1194 static void
1195 telmod_timer(void *arg)
1197 queue_t *q = arg;
1198 struct telmod_info *tmip = (struct telmod_info *)q->q_ptr;
1200 ASSERT(tmip);
1202 if (q->q_flag & QREADR) {
1203 ASSERT(tmip->rtimoutid);
1204 tmip->rtimoutid = 0;
1205 } else {
1206 ASSERT(tmip->wtimoutid);
1207 tmip->wtimoutid = 0;
1209 enableok(q);
1210 qenable(q);
1213 static void
1214 telmod_buffer(void *arg)
1216 queue_t *q = arg;
1217 struct telmod_info *tmip = (struct telmod_info *)q->q_ptr;
1219 ASSERT(tmip);
1221 if (q->q_flag & QREADR) {
1222 ASSERT(tmip->rbufcid);
1223 tmip->rbufcid = 0;
1224 } else {
1225 ASSERT(tmip->wbufcid);
1226 tmip->wbufcid = 0;
1228 enableok(q);
1229 qenable(q);
1232 static void
1233 recover(queue_t *q, mblk_t *mp, size_t size)
1235 bufcall_id_t bid;
1236 timeout_id_t tid;
1237 struct telmod_info *tmip = (struct telmod_info *)q->q_ptr;
1239 ASSERT(mp->b_datap->db_type < QPCTL);
1240 noenable(q);
1241 (void) putbq(q, mp);
1244 * Make sure there is at most one outstanding request per queue.
1246 if (q->q_flag & QREADR) {
1247 if (tmip->rtimoutid || tmip->rbufcid) {
1248 return;
1250 } else {
1251 if (tmip->wtimoutid || tmip->wbufcid) {
1252 return;
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;