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