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 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  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.
  39  */
  40 
  41 #include <sys/types.h>
  42 #include <sys/param.h>
  43 #include <sys/stream.h>
  44 #include <sys/stropts.h>
  45 #include <sys/strsun.h>
  46 #include <sys/kmem.h>
  47 #include <sys/errno.h>
  48 #include <sys/ddi.h>
  49 #include <sys/sunddi.h>
  50 #include <sys/tihdr.h>
  51 #include <sys/ptem.h>
  52 #include <sys/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>
  60 
  61 #define IAC     255
  62 
  63 extern struct streamtab telmodinfo;
  64 
  65 #define TELMOD_ID       105
  66 #define SIMWAIT         (1*hz)
  67 
  68 /*
  69  * Module state flags
  70  */
  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
  76 
  77 /*
  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.
  82  */
  83 
  84 
  85 
  86 /*
  87  * Per queue instances are single-threaded since the q_ptr
  88  * field of queues need to be shared among threads.
  89  */
  90 static struct fmodsw fsw = {
  91         "telmod",
  92         &telmodinfo,
  93         D_MTQPAIR | D_MP
  94 };
  95 
  96 /*
  97  * Module linkage information for the kernel.
  98  */
  99 
 100 static struct modlstrmod modlstrmod = {
 101         &mod_strmodops,
 102         "telnet module",
 103         &fsw
 104 };
 105 
 106 static struct modlinkage modlinkage = {
 107         MODREV_1, &modlstrmod, NULL
 108 };
 109 
 110 int
 111 _init()
 112 {
 113         return (mod_install(&modlinkage));
 114 }
 115 
 116 int
 117 _fini()
 118 {
 119         return (mod_remove(&modlinkage));
 120 }
 121 
 122 int
 123 _info(struct modinfo *modinfop)
 124 {
 125         return (mod_info(&modlinkage, modinfop));
 126 }
 127 
 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);
 139 
 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 */
 147 };
 148 
 149 static struct qinit telmodrinit = {
 150         (int (*)())telmodrput,
 151         (int (*)())telmodrsrv,
 152         telmodopen,
 153         telmodclose,
 154         nulldev,
 155         &telmodoinfo,
 156         NULL
 157 };
 158 
 159 static struct qinit telmodwinit = {
 160         (int (*)())telmodwput,
 161         (int (*)())telmodwsrv,
 162         NULL,
 163         NULL,
 164         nulldev,
 165         &telmodoinfo,
 166         NULL
 167 };
 168 
 169 struct streamtab telmodinfo = {
 170         &telmodrinit,
 171         &telmodwinit,
 172         NULL,
 173         NULL
 174 };
 175 
 176 /*
 177  * Per-instance state struct for the telnet module.
 178  */
 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;
 186 
 187 };
 188 
 189 /*ARGSUSED*/
 190 static void
 191 dummy_callback(void *arg)
 192 {}
 193 
 194 /*
 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.
 205  */
 206 /*ARGSUSED*/
 207 static int
 208 telmodopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
 209 {
 210         struct telmod_info      *tmip;
 211         mblk_t *bp;
 212         union T_primitives *tp;
 213         int     error;
 214 
 215         if (sflag != MODOPEN)
 216                 return (EINVAL);
 217 
 218         if (q->q_ptr != NULL) {
 219                 /* It's already attached. */
 220                 return (0);
 221         }
 222         /*
 223          * Allocate state structure.
 224          */
 225         tmip = kmem_zalloc(sizeof (*tmip), KM_SLEEP);
 226 
 227         /*
 228          * Cross-link.
 229          */
 230         q->q_ptr = tmip;
 231         WR(q)->q_ptr = tmip;
 232 
 233         noenable(q);
 234         tmip->flags |= TEL_STOPPED;
 235         qprocson(q);
 236 
 237         /*
 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.
 245          */
 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;
 254                 }
 255                 qunbufcall(q, id);
 256         }
 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;
 262         /*
 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
 267          */
 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;
 275                 }
 276                 qunbufcall(q, id);
 277         }
 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;
 283 
 284         putnext(q, bp);
 285         return (0);
 286 
 287 fail:
 288         qprocsoff(q);
 289         if (tmip->unbind_mp != NULL) {
 290                 freemsg(tmip->unbind_mp);
 291         }
 292         kmem_free(tmip, sizeof (struct telmod_info));
 293         q->q_ptr = NULL;
 294         WR(q)->q_ptr = NULL;
 295         return (error);
 296 }
 297 
 298 
 299 /*
 300  * telmodclose - just the normal streams clean-up is required.
 301  */
 302 
 303 /*ARGSUSED*/
 304 static int
 305 telmodclose(queue_t *q, int flag, cred_t *credp)
 306 {
 307         struct telmod_info   *tmip = (struct telmod_info *)q->q_ptr;
 308         mblk_t  *mp;
 309 
 310         /*
 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.
 315          */
 316         while (mp = getq(WR(q)))
 317                 putnext(WR(q), mp);
 318 
 319         /* Poison the stream head so that we can't be pushed again. */
 320         (void) putnextctl(q, M_HANGUP);
 321 
 322         qprocsoff(q);
 323         if (tmip->wbufcid) {
 324                 qunbufcall(q, tmip->wbufcid);
 325                 tmip->wbufcid = 0;
 326         }
 327         if (tmip->rbufcid) {
 328                 qunbufcall(q, tmip->rbufcid);
 329                 tmip->rbufcid = 0;
 330         }
 331         if (tmip->wtimoutid) {
 332                 (void) quntimeout(q, tmip->wtimoutid);
 333                 tmip->wtimoutid = 0;
 334         }
 335         if (tmip->rtimoutid) {
 336                 (void) quntimeout(q, tmip->rtimoutid);
 337                 tmip->rtimoutid = 0;
 338         }
 339         if (tmip->unbind_mp != NULL) {
 340                 freemsg(tmip->unbind_mp);
 341         }
 342 
 343         kmem_free(q->q_ptr, sizeof (struct telmod_info));
 344         q->q_ptr = WR(q)->q_ptr = NULL;
 345         return (0);
 346 }
 347 
 348 /*
 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.
 354  */
 355 static void
 356 telmodrput(queue_t *q, mblk_t *mp)
 357 {
 358         mblk_t  *newmp;
 359         struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
 360         union T_primitives *tip;
 361 
 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;
 367         }
 368 
 369         switch (mp->b_datap->db_type) {
 370         case M_DATA:
 371 
 372                 /*
 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.
 376                  */
 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;
 382                         }
 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;
 390 
 391                         putnext(q, newmp);
 392 
 393                         break;
 394                 }
 395                 /*
 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.
 402                  */
 403                 (void) rcv_parse(q, mp);
 404                 break;
 405 
 406         case M_FLUSH:
 407                 /*
 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.
 411                  */
 412                 mp->b_flag |= MSGMARK;
 413                 if (*mp->b_rptr & FLUSHR)
 414                         flushq(q, FLUSHALL);
 415                 putnext(q, mp);
 416                 break;
 417 
 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;
 428 
 429         case M_PROTO:
 430         case M_PCPROTO:
 431                 if (tmip->flags & TEL_GETBLK)
 432                         tmip->flags &= ~TEL_GETBLK;
 433 
 434                 tip = (union T_primitives *)mp->b_rptr;
 435                 switch (tip->type) {
 436 
 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;
 446                         }
 447                         /*
 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.
 451                          */
 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);
 459                         }
 460                         break;
 461 
 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;
 471                                 }
 472                                 freemsg(mp);
 473                         }
 474                         break;
 475 
 476                 /*
 477                  * We only get T_OK_ACK when we issue the unbind, and it can
 478                  * be ignored safely.
 479                  */
 480                 case T_OK_ACK:
 481                         ASSERT(tmip->unbind_mp == NULL);
 482                         freemsg(mp);
 483                         break;
 484 
 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);
 492                 }
 493                 break;
 494 
 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);
 502         }
 503 }
 504 
 505 /*
 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?
 509  */
 510 static void
 511 telmodrsrv(queue_t *q)
 512 {
 513         mblk_t  *mp, *newmp;
 514         struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
 515         union T_primitives *tip;
 516 
 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;
 522                 }
 523                 switch (mp->b_datap->db_type) {
 524 
 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;
 532                                 }
 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;
 540 
 541                                 putnext(q, newmp);
 542 
 543                                 break;
 544                         }
 545                         if (!rcv_parse(q, mp)) {
 546                                 return;
 547                         }
 548                         break;
 549 
 550                 case M_PROTO:
 551 
 552                         tip = (union T_primitives *)mp->b_rptr;
 553 
 554                         /*
 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.
 558                          */
 559                         if (tip->type != T_DATA_IND &&
 560                             tip->type != T_EXDATA_IND)
 561                                 tmip->flags &= ~TEL_GETBLK;
 562 
 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;
 573                                 }
 574                                 /*
 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.
 578                                  */
 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);
 586                                 }
 587                                 break;
 588 
 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;
 598                                         }
 599                                         freemsg(mp);
 600                                 }
 601                                 break;
 602 
 603                         /*
 604                          * We only get T_OK_ACK when we issue the unbind, and
 605                          * it can be ignored safely.
 606                          */
 607                         case T_OK_ACK:
 608                                 ASSERT(tmip->unbind_mp == NULL);
 609                                 freemsg(mp);
 610                                 break;
 611 
 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);
 619                         }
 620                         break;
 621 
 622                 case M_SETOPTS:
 623                         putnext(q, mp);
 624                         break;
 625 
 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);
 633                 }
 634         }
 635 }
 636 
 637 /*
 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.
 649  */
 650 static void
 651 telmodwput(
 652         queue_t *q,     /* Pointer to the read queue */
 653         mblk_t *mp)     /* Pointer to current message block */
 654 {
 655         struct telmod_info      *tmip;
 656         struct iocblk *ioc;
 657         mblk_t *savemp;
 658         int rw;
 659         int error;
 660 
 661         tmip = (struct telmod_info *)q->q_ptr;
 662 
 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;
 670                 }
 671                 /*
 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.
 676                  */
 677                 (void) snd_parse(q, mp);
 678                 break;
 679 
 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;
 686                 }
 687                 putnext(q, mp);
 688                 break;
 689 
 690         case M_IOCTL:
 691                 ioc = (struct iocblk *)mp->b_rptr;
 692                 switch (ioc->ioc_cmd) {
 693 
 694                 /*
 695                  * This ioctl is issued by user level daemon to
 696                  * request one more message block to process protocol
 697                  */
 698                 case TEL_IOC_GETBLK:
 699                         if (!(tmip->flags & TEL_STOPPED)) {
 700                                 miocnak(q, mp, 0, EINVAL);
 701                                 break;
 702                         }
 703                         tmip->flags |= TEL_GETBLK;
 704                         qenable(RD(q));
 705                         enableok(RD(q));
 706 
 707                         miocack(q, mp, 0, 0);
 708                         break;
 709 
 710                 /*
 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.
 716                  */
 717                 case TEL_IOC_ENABLE:
 718 
 719                         /*
 720                          * Send negative ack if TEL_STOPPED flag is not set
 721                          */
 722                         if (!(tmip->flags & TEL_STOPPED)) {
 723                                 miocnak(q, mp, 0, EINVAL);
 724                                 break;
 725                         }
 726                         tmip->flags &= ~TEL_STOPPED;
 727                         if (mp->b_cont) {
 728                                 (void) putbq(RD(q), mp->b_cont);
 729                                 mp->b_cont = 0;
 730                         }
 731 
 732                         qenable(RD(q));
 733                         enableok(RD(q));
 734                         qenable(q);
 735                         enableok(q);
 736 
 737                         miocack(q, mp, 0, 0);
 738                         break;
 739 
 740                 /*
 741                  * Set binary/normal mode for input and output
 742                  * according to the instructions from the daemon.
 743                  */
 744                 case TEL_IOC_MODE:
 745                         error = miocpullup(mp, sizeof (uchar_t));
 746                         if (error != 0) {
 747                                 miocnak(q, mp, 0, error);
 748                                 break;
 749                         }
 750                         tmip->flags |= *(mp->b_cont->b_rptr) &
 751                             (TEL_BINARY_IN|TEL_BINARY_OUT);
 752                         miocack(q, mp, 0, 0);
 753                         break;
 754 
 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;
 773                         }
 774                         if (*(mp->b_cont->b_rptr) == 0x01)
 775                                 tmip->flags |= TEL_IOCPASSTHRU;
 776                         else
 777                                 tmip->flags &= ~TEL_IOCPASSTHRU;
 778 
 779                         miocack(q, mp, 0, 0);
 780                         break;
 781 
 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);
 792                         }
 793                         break;
 794                 }
 795                 break;
 796 
 797         case M_FLUSH:
 798                 /*
 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.
 805                  */
 806                 rw = *mp->b_rptr;
 807                 if (rw & FLUSHR) {
 808                         /*
 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.
 815                          */
 816                         *mp->b_rptr = rw & ~FLUSHW;
 817                         qreply(q, mp);
 818                 } else {
 819                         freemsg(mp);
 820                 }
 821                 if (rw & FLUSHW) {
 822                         /*
 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.
 827                          */
 828                         flushq(q, FLUSHDATA);
 829                 }
 830                 break;
 831 
 832         case M_PCPROTO:
 833                 putnext(q, mp);
 834                 break;
 835 
 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;
 843 
 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;
 852         }
 853 }
 854 
 855 /*
 856  * telmodwsrv - module write service procedure
 857  */
 858 static void
 859 telmodwsrv(queue_t *q)
 860 {
 861         mblk_t  *mp, *savemp;
 862 
 863         struct  telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
 864 
 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;
 870                 }
 871                 switch (mp->b_datap->db_type) {
 872 
 873                 case M_DATA:
 874                         if (tmip->flags & TEL_STOPPED) {
 875                                 (void) putbq(q, mp);
 876                                 return;
 877                         }
 878                         /*
 879                          * Insert a null character if carraige return
 880                          * is not followed by line feed
 881                          */
 882                         if (!snd_parse(q, mp)) {
 883                                 return;
 884                         }
 885                         break;
 886 
 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;
 893                         }
 894                         putnext(q, mp);
 895                         break;
 896 
 897                 case M_PROTO:
 898                         putnext(q, mp);
 899                         break;
 900 
 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);
 908                 }
 909 
 910         }
 911 }
 912 
 913 /*
 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.
 919  *
 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.
 926  *
 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.
 938  */
 939 static int
 940 rcv_parse(queue_t *q, mblk_t *mp)
 941 {
 942         mblk_t  *protomp, *newmp, *datamp, *prevmp;
 943         unsigned char *tmp;
 944         size_t  msgsize;
 945 
 946         struct telmod_info    *tmip = (struct telmod_info *)q->q_ptr;
 947 
 948         datamp = mp;
 949         prevmp = protomp = 0;
 950 
 951         while (mp) {
 952                 /*
 953                  * If the mblk is empty, just continue scanning.
 954                  */
 955                 if (mp->b_rptr == mp->b_wptr) {
 956                         prevmp = mp;
 957                         mp = mp->b_cont;
 958                         continue;
 959                 }
 960                 /*
 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.
 965                  */
 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) {
 980                                                         /*
 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.
 988                                                          */
 989                                                         return (1);
 990                                                 } else {
 991                                                         mp = datamp;
 992                                                         continue;
 993                                                 }
 994                                         }
 995                                 }
 996                                 mp->b_rptr += 1;
 997                         }
 998                         tmip->flags &= ~TEL_CRRCV;
 999                 }
1000                 tmp = mp->b_rptr;
1001                 /*
1002                  * Now scan through the entire message block, for IACs
1003                  * and CR characters, which need processing.
1004                  */
1005                 while (tmp < mp->b_wptr) {
1006 
1007                         if (tmp[0] == IAC) {
1008                                 /*
1009                                  * Telnet protocol - parse it now
1010                                  * process data part of mblk
1011                                  * before sending the protocol.
1012                                  */
1013                                 if (tmp > mp->b_rptr) {
1014                                         if ((protomp = dupb(mp)) == NULL) {
1015                                                 msgsize = msgdsize(datamp);
1016                                                 recover(q, datamp, msgsize);
1017                                                 return (0);
1018                                         }
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;
1028 
1029                                         if (prevmp)
1030                                                 prevmp->b_cont = mp;
1031 
1032                                 } else {
1033                                         protomp = mp;
1034 
1035                                         if (prevmp)
1036                                                 prevmp->b_cont = 0;
1037                                         else
1038                                                 datamp = 0;
1039                                 }
1040                                 if (datamp) {
1041                                         putnext(q, datamp);
1042                                 }
1043                                 /*
1044                                  * create a 1 byte M_CTL message block with
1045                                  * protomp and send it down.
1046                                  */
1047 
1048                                 if ((newmp = allocb(sizeof (char),
1049                                         BPRI_MED)) == NULL) {
1050                                         /*
1051                                          * Save the dup'ed mp containing
1052                                          * the protocol information which
1053                                          * we couldn't get an M_CTL header
1054                                          * for.
1055                                          */
1056                                         msgsize = msgdsize(protomp);
1057                                         recover(q, protomp, msgsize);
1058                                         return (0);
1059                                 }
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);
1067 
1068                                 return (0);
1069                         }
1070                         if (!(tmip->flags & TEL_BINARY_IN)) {
1071                                 /*
1072                                  * Set TEL_CRRCV flag if last character is CR
1073                                  */
1074                                 if ((tmp == (mp->b_wptr - 1)) &&
1075                                         (tmp[0] == '\r')) {
1076                                         tmip->flags |= TEL_CRRCV;
1077                                         break;
1078                                 }
1079 
1080                                 /*
1081                                  * If CR is followed by LF/NULL, get rid of
1082                                  * LF/NULL and realign the message block.
1083                                  */
1084                                 if ((tmp[0] == '\r') && ((tmp[1] == '\n') ||
1085                                     (tmp[1] == NULL))) {
1086                                         /*
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.
1090                                          */
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;
1097                                         }
1098 
1099                                         if (prevmp)
1100                                                 prevmp->b_cont = mp;
1101                                 }
1102                         }
1103                         tmp++;
1104                 }
1105                 prevmp = mp;
1106                 mp = mp->b_cont;
1107         }
1108         putnext(q, datamp);
1109 
1110         return (1);
1111 }
1112 
1113 /*
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.
1120  *
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.
1125  */
1126 static int
1127 snd_parse(queue_t *q, mblk_t *mp)
1128 {
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);
1133 
1134         savemp = mp;
1135 
1136         if (size == 0) {
1137                 putnext(q, mp);
1138                 return (1);
1139         }
1140 
1141         /*
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.
1145          */
1146         if ((newmp = allocb((2 * size)+1, BPRI_MED)) == NULL) {
1147                 recover(q, mp, (2 * size)+1);
1148                 return (0);
1149         }
1150         newmp->b_datap->db_type = M_DATA;
1151 
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;
1159                 }
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;
1168                                 }
1169                                 if ((tmp[0] == '\r') &&
1170                                     (tmp1 == newmp->b_wptr)) {
1171                                         /* XXX.sparker: can't happen */
1172                                         tmip->flags |= TEL_CRSND;
1173                                         break;
1174                                 }
1175                                 if ((tmp[0] == '\r') && (tmp[1] != '\n')) {
1176                                         *tmp1++ = NULL;
1177                                 }
1178                         } else
1179                                 *tmp1++ = *tmp;
1180 
1181                         if (tmp[0] == IAC) {
1182                                 *tmp1++ = IAC;
1183                         }
1184                         tmp++;
1185                 }
1186                 mp = mp->b_cont;
1187         }
1188 
1189         newmp->b_wptr = tmp1;
1190 
1191         putnext(q, newmp);
1192         freemsg(savemp);
1193         return (1);
1194 }
1195 
1196 static void
1197 telmod_timer(void *arg)
1198 {
1199         queue_t *q = arg;
1200         struct  telmod_info     *tmip = (struct telmod_info *)q->q_ptr;
1201 
1202         ASSERT(tmip);
1203 
1204         if (q->q_flag & QREADR) {
1205                 ASSERT(tmip->rtimoutid);
1206                 tmip->rtimoutid = 0;
1207         } else {
1208                 ASSERT(tmip->wtimoutid);
1209                 tmip->wtimoutid = 0;
1210         }
1211         enableok(q);
1212         qenable(q);
1213 }
1214 
1215 static void
1216 telmod_buffer(void *arg)
1217 {
1218         queue_t *q = arg;
1219         struct  telmod_info     *tmip = (struct telmod_info *)q->q_ptr;
1220 
1221         ASSERT(tmip);
1222 
1223         if (q->q_flag & QREADR) {
1224                 ASSERT(tmip->rbufcid);
1225                 tmip->rbufcid = 0;
1226         } else {
1227                 ASSERT(tmip->wbufcid);
1228                 tmip->wbufcid = 0;
1229         }
1230         enableok(q);
1231         qenable(q);
1232 }
1233 
1234 static void
1235 recover(queue_t *q, mblk_t *mp, size_t size)
1236 {
1237         bufcall_id_t bid;
1238         timeout_id_t tid;
1239         struct  telmod_info     *tmip = (struct telmod_info *)q->q_ptr;
1240 
1241         ASSERT(mp->b_datap->db_type < QPCTL);
1242         noenable(q);
1243         (void) putbq(q, mp);
1244 
1245         /*
1246          * Make sure there is at most one outstanding request per queue.
1247          */
1248         if (q->q_flag & QREADR) {
1249                 if (tmip->rtimoutid || tmip->rbufcid) {
1250                         return;
1251                 }
1252         } else {
1253                 if (tmip->wtimoutid || tmip->wbufcid) {
1254                         return;
1255                 }
1256         }
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;
1268         }
1269 }