GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/telnet/sys_bsd.c Lines: 0 236 0.0 %
Date: 2017-11-07 Branches: 0 160 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: sys_bsd.c,v 1.34 2017/07/19 12:25:52 deraadt Exp $	*/
2
/*	$NetBSD: sys_bsd.c,v 1.11 1996/02/28 21:04:10 thorpej Exp $	*/
3
4
/*
5
 * Copyright (c) 1988, 1990, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include "telnet_locl.h"
34
35
#include <sys/ioctl.h>
36
#include <sys/socket.h>
37
#include <arpa/telnet.h>
38
#include <errno.h>
39
#include <poll.h>
40
#include <string.h>
41
#include <unistd.h>
42
43
/*
44
 * The following routines try to encapsulate what is system dependent
45
 * (at least between 4.x and dos) which is used in telnet.c.
46
 */
47
48
int
49
	tout,			/* Output file descriptor */
50
	tin,			/* Input file descriptor */
51
	net;
52
53
#define TELNET_FD_TOUT	0
54
#define TELNET_FD_TIN	1
55
#define TELNET_FD_NET	2
56
#define TELNET_FD_NUM	3
57
58
struct	termios old_tc = { 0 };
59
60
void
61
init_sys(void)
62
{
63
    tout = fileno(stdout);
64
    tin = fileno(stdin);
65
66
    errno = 0;
67
}
68
69
70
/*
71
 * TerminalSpecialChars()
72
 *
73
 * Look at an input character to see if it is a special character
74
 * and decide what to do.
75
 *
76
 * Output:
77
 *
78
 *	0	Don't add this character.
79
 *	1	Do add this character
80
 */
81
82
int
83
TerminalSpecialChars(int c)
84
{
85
    if (c == termIntChar) {
86
	intp();
87
	return 0;
88
    } else if (c == termQuitChar) {
89
#ifdef	KLUDGELINEMODE
90
	if (kludgelinemode)
91
	    sendbrk();
92
	else
93
#endif
94
	    sendabort();
95
	return 0;
96
    } else if (c == termEofChar) {
97
	if (my_want_state_is_will(TELOPT_LINEMODE)) {
98
	    sendeof();
99
	    return 0;
100
	}
101
	return 1;
102
    } else if (c == termSuspChar) {
103
	sendsusp();
104
	return(0);
105
    } else if (c == termFlushChar) {
106
	xmitAO();		/* Transmit Abort Output */
107
	return 0;
108
    } else if (!MODE_LOCAL_CHARS(globalmode)) {
109
	if (c == termKillChar) {
110
	    xmitEL();
111
	    return 0;
112
	} else if (c == termEraseChar) {
113
	    xmitEC();		/* Transmit Erase Character */
114
	    return 0;
115
	}
116
    }
117
    return 1;
118
}
119
120
void
121
TerminalSaveState(void)
122
{
123
    tcgetattr(0, &old_tc);
124
125
    new_tc = old_tc;
126
}
127
128
cc_t *
129
tcval(int func)
130
{
131
    switch(func) {
132
    case SLC_IP:	return(&termIntChar);
133
    case SLC_ABORT:	return(&termQuitChar);
134
    case SLC_EOF:	return(&termEofChar);
135
    case SLC_EC:	return(&termEraseChar);
136
    case SLC_EL:	return(&termKillChar);
137
    case SLC_XON:	return(&termStartChar);
138
    case SLC_XOFF:	return(&termStopChar);
139
    case SLC_FORW1:	return(&termForw1Char);
140
    case SLC_FORW2:	return(&termForw2Char);
141
    case SLC_SUSP:	return(&termSuspChar);
142
    case SLC_AO:	return(&termFlushChar);
143
    case SLC_EW:	return(&termWerasChar);
144
    case SLC_RP:	return(&termRprntChar);
145
    case SLC_LNEXT:	return(&termLiteralNextChar);
146
    case SLC_AYT:	return(&termAytChar);
147
    case SLC_SYNCH:
148
    case SLC_BRK:
149
    case SLC_EOR:
150
    default:
151
	return(NULL);
152
    }
153
}
154
155
void
156
TerminalDefaultChars(void)
157
{
158
    memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
159
}
160
161
/*
162
 * TerminalNewMode - set up terminal to a specific mode.
163
 *	MODE_ECHO: do local terminal echo
164
 *	MODE_FLOW: do local flow control
165
 *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
166
 *	MODE_EDIT: do local line editing
167
 *
168
 *	Command mode:
169
 *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
170
 *		local echo
171
 *		local editing
172
 *		local xon/xoff
173
 *		local signal mapping
174
 *
175
 *	Linemode:
176
 *		local/no editing
177
 *	Both Linemode and Single Character mode:
178
 *		local/remote echo
179
 *		local/no xon/xoff
180
 *		local/no signal mapping
181
 */
182
183
static void susp();
184
static void ayt();
185
186
void
187
TerminalNewMode(int f)
188
{
189
    static int prevmode = 0;
190
    struct termios tmp_tc;
191
    int onoff;
192
    int old;
193
    cc_t esc;
194
195
    globalmode = f&~MODE_FORCE;
196
    if (prevmode == f)
197
	return;
198
199
    /*
200
     * Write any outstanding data before switching modes
201
     * ttyflush() returns 0 only when there is no more data
202
     * left to write out, it returns -1 if it couldn't do
203
     * anything at all, otherwise it returns 1 + the number
204
     * of characters left to write.
205
     */
206
    old = ttyflush(SYNCHing|flushout);
207
    if (old < 0 || old > 1) {
208
	tcgetattr(tin, &tmp_tc);
209
	do {
210
	    /*
211
	     * Wait for data to drain, then flush again.
212
	     */
213
	    if (isatty(tin))
214
	        tcsetattr(tin, TCSADRAIN, &tmp_tc);
215
	    old = ttyflush(SYNCHing|flushout);
216
	} while (old < 0 || old > 1);
217
    }
218
219
    old = prevmode;
220
    prevmode = f&~MODE_FORCE;
221
    tmp_tc = new_tc;
222
223
    if (f&MODE_ECHO) {
224
	tmp_tc.c_lflag |= ECHO;
225
	tmp_tc.c_oflag |= ONLCR;
226
	if (crlf)
227
		tmp_tc.c_iflag |= ICRNL;
228
    } else {
229
	tmp_tc.c_lflag &= ~ECHO;
230
	tmp_tc.c_oflag &= ~ONLCR;
231
    }
232
233
    if ((f&MODE_FLOW) == 0) {
234
	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
235
    } else {
236
	if (restartany < 0) {
237
		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
238
	} else if (restartany > 0) {
239
		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
240
	} else {
241
		tmp_tc.c_iflag |= IXOFF|IXON;
242
		tmp_tc.c_iflag &= ~IXANY;
243
	}
244
    }
245
246
    if ((f&MODE_TRAPSIG) == 0) {
247
	tmp_tc.c_lflag &= ~ISIG;
248
	localchars = 0;
249
    } else {
250
	tmp_tc.c_lflag |= ISIG;
251
	localchars = 1;
252
    }
253
254
    if (f&MODE_EDIT) {
255
	tmp_tc.c_lflag |= ICANON;
256
    } else {
257
	tmp_tc.c_lflag &= ~ICANON;
258
	tmp_tc.c_iflag &= ~ICRNL;
259
	tmp_tc.c_cc[VMIN] = 1;
260
	tmp_tc.c_cc[VTIME] = 0;
261
    }
262
263
    if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
264
	tmp_tc.c_lflag &= ~IEXTEN;
265
    }
266
267
    if (f&MODE_SOFT_TAB) {
268
# ifdef	OXTABS
269
	tmp_tc.c_oflag |= OXTABS;
270
# endif
271
# ifdef	TABDLY
272
	tmp_tc.c_oflag &= ~TABDLY;
273
	tmp_tc.c_oflag |= TAB3;
274
# endif
275
    } else {
276
# ifdef	OXTABS
277
	tmp_tc.c_oflag &= ~OXTABS;
278
# endif
279
# ifdef	TABDLY
280
	tmp_tc.c_oflag &= ~TABDLY;
281
# endif
282
    }
283
284
    if (f&MODE_LIT_ECHO) {
285
# ifdef	ECHOCTL
286
	tmp_tc.c_lflag &= ~ECHOCTL;
287
# endif
288
    } else {
289
# ifdef	ECHOCTL
290
	tmp_tc.c_lflag |= ECHOCTL;
291
# endif
292
    }
293
294
    if (f == -1) {
295
	onoff = 0;
296
    } else {
297
	if (f & MODE_INBIN)
298
		tmp_tc.c_iflag &= ~ISTRIP;
299
	else
300
		tmp_tc.c_iflag |= ISTRIP;
301
	if ((f & MODE_OUTBIN) || (f & MODE_OUT8)) {
302
		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
303
		tmp_tc.c_cflag |= CS8;
304
		if(f & MODE_OUTBIN)
305
			tmp_tc.c_oflag &= ~OPOST;
306
		else
307
			tmp_tc.c_oflag |= OPOST;
308
309
	} else {
310
		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
311
		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
312
		tmp_tc.c_oflag |= OPOST;
313
	}
314
	onoff = 1;
315
    }
316
317
    if (f != -1) {
318
	(void) signal(SIGTSTP, susp);
319
	(void) signal(SIGINFO, ayt);
320
#if	defined(NOKERNINFO)
321
	tmp_tc.c_lflag |= NOKERNINFO;
322
#endif
323
	/*
324
	 * We don't want to process ^Y here.  It's just another
325
	 * character that we'll pass on to the back end.  It has
326
	 * to process it because it will be processed when the
327
	 * user attempts to read it, not when we send it.
328
	 */
329
# ifdef	VDSUSP
330
	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
331
# endif
332
	/*
333
	 * If the VEOL character is already set, then use VEOL2,
334
	 * otherwise use VEOL.
335
	 */
336
	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
337
	if ((tmp_tc.c_cc[VEOL] != esc)
338
# ifdef	VEOL2
339
	    && (tmp_tc.c_cc[VEOL2] != esc)
340
# endif
341
	    ) {
342
		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
343
		    tmp_tc.c_cc[VEOL] = esc;
344
# ifdef	VEOL2
345
		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
346
		    tmp_tc.c_cc[VEOL2] = esc;
347
# endif
348
	}
349
    } else {
350
	sigset_t mask;
351
	(void) signal(SIGINFO, ayt_status);
352
	(void) signal(SIGTSTP, SIG_DFL);
353
	sigemptyset(&mask);
354
	sigaddset(&mask, SIGTSTP);
355
	sigprocmask(SIG_UNBLOCK, &mask, NULL);
356
	tmp_tc = old_tc;
357
    }
358
    if (isatty(tin) && tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
359
	tcsetattr(tin, TCSANOW, &tmp_tc);
360
361
    ioctl(tin, FIONBIO, &onoff);
362
    ioctl(tout, FIONBIO, &onoff);
363
}
364
365
void
366
TerminalSpeeds(long *ispeed, long *ospeed)
367
{
368
    long in, out;
369
370
    out = cfgetospeed(&old_tc);
371
    in = cfgetispeed(&old_tc);
372
    if (in == 0)
373
	in = out;
374
375
    *ispeed = in;
376
    *ospeed = out;
377
}
378
379
int
380
TerminalWindowSize(long *rows, long *cols)
381
{
382
    struct winsize ws;
383
384
    if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) {
385
	*rows = ws.ws_row;
386
	*cols = ws.ws_col;
387
	return 1;
388
    }
389
    return 0;
390
}
391
392
/*
393
 * Various signal handling routines.
394
 */
395
396
void
397
deadpeer(int sig)
398
{
399
	setcommandmode();
400
	longjmp(peerdied, -1);
401
}
402
403
void
404
intr(int sig)
405
{
406
    if (localchars) {
407
	intp();
408
	return;
409
    }
410
    setcommandmode();
411
    longjmp(toplevel, -1);
412
}
413
414
void
415
intr2(int sig)
416
{
417
    if (localchars) {
418
#ifdef	KLUDGELINEMODE
419
	if (kludgelinemode)
420
	    sendbrk();
421
	else
422
#endif
423
	    sendabort();
424
	return;
425
    }
426
}
427
428
void
429
susp(int sig)
430
{
431
    if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
432
	return;
433
    if (localchars)
434
	sendsusp();
435
}
436
437
void
438
sendwin(int sig)
439
{
440
    if (connected) {
441
	sendnaws();
442
    }
443
}
444
445
void
446
ayt(int sig)
447
{
448
    if (connected)
449
	sendayt();
450
    else
451
	ayt_status(sig);
452
}
453
454
455
void
456
sys_telnet_init(void)
457
{
458
    int one = 1;
459
460
    (void) signal(SIGINT, intr);
461
    (void) signal(SIGQUIT, intr2);
462
    (void) signal(SIGPIPE, deadpeer);
463
    (void) signal(SIGWINCH, sendwin);
464
    (void) signal(SIGTSTP, susp);
465
    (void) signal(SIGINFO, ayt);
466
467
    setconnmode(0);
468
469
    /*
470
     * Mark the socket as non-blocking and receive urgent data inline.
471
     * (The latter is required for correct telnet operation when a
472
     * second urgent is sent before telnet can process the first.)
473
     */
474
    ioctl(net, FIONBIO, &one);
475
    if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &one, sizeof(one)) == -1) {
476
	perror("setsockopt");
477
    }
478
}
479
480
/*
481
 * Process rings -
482
 *
483
 *	This routine tries to fill up/empty our various rings.
484
 *
485
 *	The parameter specifies whether this is a poll operation,
486
 *	or a block-until-something-happens operation.
487
 *
488
 *	The return value is 1 if something happened, 0 if not.
489
 */
490
491
int
492
process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
493
    int dopoll)		/* If 0, then block until something to do */
494
{
495
    int c;
496
		/* One wants to be a bit careful about setting returnValue
497
		 * to one, since a one implies we did some useful work,
498
		 * and therefore probably won't be called to block next
499
		 * time (TN3270 mode only).
500
		 */
501
    int returnValue = 0;
502
    struct pollfd pfd[TELNET_FD_NUM];
503
504
    if (ttyout) {
505
	pfd[TELNET_FD_TOUT].fd = tout;
506
	pfd[TELNET_FD_TOUT].events = POLLOUT;
507
    } else {
508
	pfd[TELNET_FD_TOUT].fd = -1;
509
    }
510
    if (ttyin) {
511
	pfd[TELNET_FD_TIN].fd = tin;
512
	pfd[TELNET_FD_TIN].events = POLLIN;
513
    } else {
514
	pfd[TELNET_FD_TIN].fd = -1;
515
    }
516
    if (netout || netin || netex) {
517
	pfd[TELNET_FD_NET].fd = net;
518
	pfd[TELNET_FD_NET].events = 0;
519
	if (netout)
520
	    pfd[TELNET_FD_NET].events |= POLLOUT;
521
	if (netin)
522
	    pfd[TELNET_FD_NET].events |= POLLIN;
523
	if (netex)
524
	    pfd[TELNET_FD_NET].events |= POLLRDBAND;
525
    } else {
526
	pfd[TELNET_FD_NET].fd = -1;
527
    }
528
529
    if ((c = poll(pfd, TELNET_FD_NUM, dopoll ? 0 : INFTIM)) < 0) {
530
	return 0;
531
    }
532
533
    /*
534
     * Any urgent data?
535
     */
536
    if (pfd[TELNET_FD_NET].revents & POLLRDBAND) {
537
	SYNCHing = 1;
538
	(void) ttyflush(1);	/* flush already enqueued data */
539
    }
540
541
    /*
542
     * Something to read from the network...
543
     */
544
    if (pfd[TELNET_FD_NET].revents & (POLLIN|POLLHUP)) {
545
	int canread;
546
547
	canread = ring_empty_consecutive(&netiring);
548
	c = recv(net, netiring.supply, canread, 0);
549
	if (c < 0 && errno == EWOULDBLOCK) {
550
	    c = 0;
551
	} else if (c <= 0) {
552
	    return -1;
553
	}
554
	if (netdata) {
555
	    Dump('<', netiring.supply, c);
556
	}
557
	if (c)
558
	    ring_supplied(&netiring, c);
559
	returnValue = 1;
560
    }
561
562
    /*
563
     * Something to read from the tty...
564
     */
565
    if (pfd[TELNET_FD_TIN].revents & (POLLIN|POLLHUP)) {
566
	c = read(tin, ttyiring.supply, ring_empty_consecutive(&ttyiring));
567
	if (c < 0 && errno == EIO)
568
	    c = 0;
569
	if (c < 0 && errno == EWOULDBLOCK) {
570
	    c = 0;
571
	} else {
572
	    /* EOF detection for line mode!!!! */
573
	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
574
		/* must be an EOF... */
575
		*ttyiring.supply = termEofChar;
576
		c = 1;
577
	    }
578
	    if (c <= 0) {
579
		return -1;
580
	    }
581
	    if (termdata) {
582
		Dump('<', ttyiring.supply, c);
583
	    }
584
	    ring_supplied(&ttyiring, c);
585
	}
586
	returnValue = 1;		/* did something useful */
587
    }
588
589
    if (pfd[TELNET_FD_NET].revents & POLLOUT) {
590
	returnValue |= netflush();
591
    }
592
    if (pfd[TELNET_FD_TOUT].revents & POLLOUT) {
593
	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
594
    }
595
596
    return returnValue;
597
}