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