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