xref: /original-bsd/usr.bin/telnet/sys_bsd.c (revision 5fd6b0d9)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)sys_bsd.c	1.14 (Berkeley) 08/28/88";
20 #endif /* not lint */
21 
22 /*
23  * The following routines try to encapsulate what is system dependent
24  * (at least between 4.x and dos) which is used in telnet.c.
25  */
26 
27 #if	defined(unix)
28 
29 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/socket.h>
33 #include <signal.h>
34 #include <errno.h>
35 
36 #include "ring.h"
37 
38 #include "fdset.h"
39 
40 #include "defines.h"
41 #include "externs.h"
42 #include "types.h"
43 
44 int
45 	tout,			/* Output file descriptor */
46 	tin,			/* Input file descriptor */
47 	net,
48 	HaveInput;		/* There is input available to scan */
49 
50 static struct	tchars otc = { 0 }, ntc = { 0 };
51 static struct	ltchars oltc = { 0 }, nltc = { 0 };
52 static struct	sgttyb ottyb = { 0 }, nttyb = { 0 };
53 
54 static fd_set ibits, obits, xbits;
55 
56 
57 init_sys()
58 {
59     tout = fileno(stdout);
60     tin = fileno(stdin);
61     FD_ZERO(&ibits);
62     FD_ZERO(&obits);
63     FD_ZERO(&xbits);
64 
65     errno = 0;
66 }
67 
68 
69 TerminalWrite(buf, n)
70 char	*buf;
71 int	n;
72 {
73     return write(tout, buf, n);
74 }
75 
76 TerminalRead(buf, n)
77 char	*buf;
78 int	n;
79 {
80     return read(tin, buf, n);
81 }
82 
83 /*
84  *
85  */
86 
87 int
88 TerminalAutoFlush()
89 {
90 #if	defined(LNOFLSH)
91     int flush;
92 
93     ioctl(0, TIOCLGET, (char *)&flush);
94     return !(flush&LNOFLSH);	/* if LNOFLSH, no autoflush */
95 #else	/* LNOFLSH */
96     return 1;
97 #endif	/* LNOFLSH */
98 }
99 
100 /*
101  * TerminalSpecialChars()
102  *
103  * Look at an input character to see if it is a special character
104  * and decide what to do.
105  *
106  * Output:
107  *
108  *	0	Don't add this character.
109  *	1	Do add this character
110  */
111 
112 int
113 TerminalSpecialChars(c)
114 int	c;
115 {
116     void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
117 
118     if (c == ntc.t_intrc) {
119 	intp();
120 	return 0;
121     } else if (c == ntc.t_quitc) {
122 	sendbrk();
123 	return 0;
124     } else if (c == nltc.t_flushc) {
125 	xmitAO();		/* Transmit Abort Output */
126 	return 0;
127     } else if (!MODE_LOCAL_CHARS(globalmode)) {
128 	if (c == nttyb.sg_kill) {
129 	    xmitEL();
130 	    return 0;
131 	} else if (c == nttyb.sg_erase) {
132 	    xmitEC();		/* Transmit Erase Character */
133 	    return 0;
134 	}
135     }
136     return 1;
137 }
138 
139 
140 /*
141  * Flush output to the terminal
142  */
143 
144 void
145 TerminalFlushOutput()
146 {
147     (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
148 }
149 
150 void
151 TerminalSaveState()
152 {
153     ioctl(0, TIOCGETP, (char *)&ottyb);
154     ioctl(0, TIOCGETC, (char *)&otc);
155     ioctl(0, TIOCGLTC, (char *)&oltc);
156 
157     ntc = otc;
158     nltc = oltc;
159     nttyb = ottyb;
160 
161     termEofChar = ntc.t_eofc;
162     termEraseChar = nttyb.sg_erase;
163     termFlushChar = nltc.t_flushc;
164     termIntChar = ntc.t_intrc;
165     termKillChar = nttyb.sg_kill;
166     termQuitChar = ntc.t_quitc;
167 }
168 
169 void
170 TerminalRestoreState()
171 {
172 }
173 
174 /*
175  * TerminalNewMode - set up terminal to a specific mode.
176  */
177 
178 
179 void
180 TerminalNewMode(f)
181 register int f;
182 {
183     static int prevmode = 0;
184     struct tchars *tc;
185     struct tchars tc3;
186     struct ltchars *ltc;
187     struct sgttyb sb;
188     int onoff;
189     int old;
190     struct	tchars notc2;
191     struct	ltchars noltc2;
192     static struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
193     static struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
194 
195     globalmode = f;
196     if (prevmode == f)
197 	return;
198     old = prevmode;
199     prevmode = f;
200     sb = nttyb;
201 
202     switch (f) {
203 
204     case 0:
205 	onoff = 0;
206 	tc = &otc;
207 	ltc = &oltc;
208 	break;
209 
210     case 1:		/* remote character processing, remote echo */
211     case 2:		/* remote character processing, local echo */
212     case 6:		/* 3270 mode - like 1, but with xon/xoff local */
213 		    /* (might be nice to have "6" in telnet also...) */
214 	    sb.sg_flags |= CBREAK;
215 	    if ((f == 1) || (f == 6)) {
216 		sb.sg_flags &= ~(ECHO|CRMOD);
217 	    } else {
218 		sb.sg_flags |= ECHO|CRMOD;
219 	    }
220 	    sb.sg_erase = sb.sg_kill = -1;
221 	    if (f == 6) {
222 		tc = &tc3;
223 		tc3 = notc;
224 		    /* get XON, XOFF characters */
225 		tc3.t_startc = otc.t_startc;
226 		tc3.t_stopc = otc.t_stopc;
227 	    } else {
228 		/*
229 		 * If user hasn't specified one way or the other,
230 		 * then default to not trapping signals.
231 		 */
232 		if (!donelclchars) {
233 		    localchars = 0;
234 		}
235 		if (localchars) {
236 		    notc2 = notc;
237 		    notc2.t_intrc = ntc.t_intrc;
238 		    notc2.t_quitc = ntc.t_quitc;
239 		    tc = &notc2;
240 		} else {
241 		    tc = &notc;
242 		}
243 	    }
244 	    ltc = &noltc;
245 	    onoff = 1;
246 	    break;
247     case 3:		/* local character processing, remote echo */
248     case 4:		/* local character processing, local echo */
249     case 5:		/* local character processing, no echo */
250 	    sb.sg_flags &= ~CBREAK;
251 	    sb.sg_flags |= CRMOD;
252 	    if (f == 4)
253 		sb.sg_flags |= ECHO;
254 	    else
255 		sb.sg_flags &= ~ECHO;
256 	    notc2 = ntc;
257 	    tc = &notc2;
258 	    noltc2 = oltc;
259 	    ltc = &noltc2;
260 	    /*
261 	     * If user hasn't specified one way or the other,
262 	     * then default to trapping signals.
263 	     */
264 	    if (!donelclchars) {
265 		localchars = 1;
266 	    }
267 	    if (localchars) {
268 		notc2.t_brkc = nltc.t_flushc;
269 		noltc2.t_flushc = -1;
270 	    } else {
271 		notc2.t_intrc = notc2.t_quitc = -1;
272 	    }
273 	    noltc2.t_suspc = escape;
274 	    noltc2.t_dsuspc = -1;
275 	    onoff = 1;
276 	    break;
277 
278     default:
279 	    return;
280     }
281     ioctl(tin, TIOCSLTC, (char *)ltc);
282     ioctl(tin, TIOCSETC, (char *)tc);
283     ioctl(tin, TIOCSETP, (char *)&sb);
284 #if	(!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
285     ioctl(tin, FIONBIO, (char *)&onoff);
286     ioctl(tout, FIONBIO, (char *)&onoff);
287 #endif	/* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
288 #if	defined(TN3270)
289     if (noasynch == 0) {
290 	ioctl(tin, FIOASYNC, (char *)&onoff);
291     }
292 #endif	/* defined(TN3270) */
293 
294     if (MODE_LINE(f)) {
295 	void doescape();
296 
297 	(void) signal(SIGTSTP, (int (*)())doescape);
298     } else if (MODE_LINE(old)) {
299 	(void) signal(SIGTSTP, SIG_DFL);
300 	sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
301     }
302 }
303 
304 
305 int
306 NetClose(fd)
307 int	fd;
308 {
309     return close(fd);
310 }
311 
312 
313 void
314 NetNonblockingIO(fd, onoff)
315 int
316 	fd,
317 	onoff;
318 {
319     ioctl(fd, FIONBIO, (char *)&onoff);
320 }
321 
322 #if	defined(TN3270)
323 void
324 NetSigIO(fd, onoff)
325 int
326 	fd,
327 	onoff;
328 {
329     ioctl(fd, FIOASYNC, (char *)&onoff);	/* hear about input */
330 }
331 
332 void
333 NetSetPgrp(fd)
334 int fd;
335 {
336     int myPid;
337 
338     myPid = getpid();
339 #if	defined(NOT43)
340     myPid = -myPid;
341 #endif	/* defined(NOT43) */
342     ioctl(fd, SIOCSPGRP, (char *)&myPid);	/* set my pid */
343 }
344 #endif	/*defined(TN3270)*/
345 
346 /*
347  * Various signal handling routines.
348  */
349 
350 static void
351 deadpeer()
352 {
353 	setcommandmode();
354 	longjmp(peerdied, -1);
355 }
356 
357 static void
358 intr()
359 {
360     if (localchars) {
361 	intp();
362 	return;
363     }
364     setcommandmode();
365     longjmp(toplevel, -1);
366 }
367 
368 static void
369 intr2()
370 {
371     if (localchars) {
372 	sendbrk();
373 	return;
374     }
375 }
376 
377 static void
378 doescape()
379 {
380     command(0);
381 }
382 
383 void
384 sys_telnet_init()
385 {
386     (void) signal(SIGINT, (int (*)())intr);
387     (void) signal(SIGQUIT, (int (*)())intr2);
388     (void) signal(SIGPIPE, (int (*)())deadpeer);
389 
390     setconnmode();
391 
392     NetNonblockingIO(net, 1);
393 
394 #if	defined(TN3270)
395     if (noasynch == 0) {			/* DBX can't handle! */
396 	NetSigIO(net, 1);
397 	NetSetPgrp(net);
398     }
399 #endif	/* defined(TN3270) */
400 
401 #if	defined(SO_OOBINLINE)
402     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
403 	perror("SetSockOpt");
404     }
405 #endif	/* defined(SO_OOBINLINE) */
406 }
407 
408 /*
409  * Process rings -
410  *
411  *	This routine tries to fill up/empty our various rings.
412  *
413  *	The parameter specifies whether this is a poll operation,
414  *	or a block-until-something-happens operation.
415  *
416  *	The return value is 1 if something happened, 0 if not.
417  */
418 
419 int
420 process_rings(netin, netout, netex, ttyin, ttyout, poll)
421 int poll;		/* If 0, then block until something to do */
422 {
423     register int c;
424 		/* One wants to be a bit careful about setting returnValue
425 		 * to one, since a one implies we did some useful work,
426 		 * and therefore probably won't be called to block next
427 		 * time (TN3270 mode only).
428 		 */
429     int returnValue = 0;
430     static struct timeval TimeValue = { 0 };
431 
432     if (netout) {
433 	FD_SET(net, &obits);
434     }
435     if (ttyout) {
436 	FD_SET(tout, &obits);
437     }
438 #if	defined(TN3270)
439     if (ttyin) {
440 	FD_SET(tin, &ibits);
441     }
442 #else	/* defined(TN3270) */
443     if (ttyin) {
444 	FD_SET(tin, &ibits);
445     }
446 #endif	/* defined(TN3270) */
447 #if	defined(TN3270)
448     if (netin) {
449 	FD_SET(net, &ibits);
450     }
451 #   else /* !defined(TN3270) */
452     if (netin) {
453 	FD_SET(net, &ibits);
454     }
455 #   endif /* !defined(TN3270) */
456     if (netex) {
457 	FD_SET(net, &xbits);
458     }
459     if ((c = select(16, &ibits, &obits, &xbits,
460 			(poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
461 	if (c == -1) {
462 		    /*
463 		     * we can get EINTR if we are in line mode,
464 		     * and the user does an escape (TSTP), or
465 		     * some other signal generator.
466 		     */
467 	    if (errno == EINTR) {
468 		return 0;
469 	    }
470 #	    if defined(TN3270)
471 		    /*
472 		     * we can get EBADF if we were in transparent
473 		     * mode, and the transcom process died.
474 		    */
475 	    if (errno == EBADF) {
476 			/*
477 			 * zero the bits (even though kernel does it)
478 			 * to make sure we are selecting on the right
479 			 * ones.
480 			*/
481 		FD_ZERO(&ibits);
482 		FD_ZERO(&obits);
483 		FD_ZERO(&xbits);
484 		return 0;
485 	    }
486 #	    endif /* defined(TN3270) */
487 		    /* I don't like this, does it ever happen? */
488 	    printf("sleep(5) from telnet, after select\r\n");
489 	    sleep(5);
490 	}
491 	return 0;
492     }
493 
494     /*
495      * Any urgent data?
496      */
497     if (FD_ISSET(net, &xbits)) {
498 	FD_CLR(net, &xbits);
499 	SYNCHing = 1;
500 	ttyflush(1);	/* flush already enqueued data */
501     }
502 
503     /*
504      * Something to read from the network...
505      */
506     if (FD_ISSET(net, &ibits)) {
507 	int canread;
508 
509 	FD_CLR(net, &ibits);
510 	canread = ring_empty_consecutive(&netiring);
511 #if	!defined(SO_OOBINLINE)
512 	    /*
513 	     * In 4.2 (and some early 4.3) systems, the
514 	     * OOB indication and data handling in the kernel
515 	     * is such that if two separate TCP Urgent requests
516 	     * come in, one byte of TCP data will be overlaid.
517 	     * This is fatal for Telnet, but we try to live
518 	     * with it.
519 	     *
520 	     * In addition, in 4.2 (and...), a special protocol
521 	     * is needed to pick up the TCP Urgent data in
522 	     * the correct sequence.
523 	     *
524 	     * What we do is:  if we think we are in urgent
525 	     * mode, we look to see if we are "at the mark".
526 	     * If we are, we do an OOB receive.  If we run
527 	     * this twice, we will do the OOB receive twice,
528 	     * but the second will fail, since the second
529 	     * time we were "at the mark", but there wasn't
530 	     * any data there (the kernel doesn't reset
531 	     * "at the mark" until we do a normal read).
532 	     * Once we've read the OOB data, we go ahead
533 	     * and do normal reads.
534 	     *
535 	     * There is also another problem, which is that
536 	     * since the OOB byte we read doesn't put us
537 	     * out of OOB state, and since that byte is most
538 	     * likely the TELNET DM (data mark), we would
539 	     * stay in the TELNET SYNCH (SYNCHing) state.
540 	     * So, clocks to the rescue.  If we've "just"
541 	     * received a DM, then we test for the
542 	     * presence of OOB data when the receive OOB
543 	     * fails (and AFTER we did the normal mode read
544 	     * to clear "at the mark").
545 	     */
546 	if (SYNCHing) {
547 	    int atmark;
548 
549 	    ioctl(net, SIOCATMARK, (char *)&atmark);
550 	    if (atmark) {
551 		c = recv(net, netiring.supply, canread, MSG_OOB);
552 		if ((c == -1) && (errno == EINVAL)) {
553 		    c = recv(net, netiring.supply, canread, 0);
554 		    if (clocks.didnetreceive < clocks.gotDM) {
555 			SYNCHing = stilloob(net);
556 		    }
557 		}
558 	    } else {
559 		c = recv(net, netiring.supply, canread, 0);
560 	    }
561 	} else {
562 	    c = recv(net, netiring.supply, canread, 0);
563 	}
564 	settimer(didnetreceive);
565 #else	/* !defined(SO_OOBINLINE) */
566 	c = recv(net, netiring.supply, canread, 0);
567 #endif	/* !defined(SO_OOBINLINE) */
568 	if (c < 0 && errno == EWOULDBLOCK) {
569 	    c = 0;
570 	} else if (c <= 0) {
571 	    return -1;
572 	}
573 	if (netdata) {
574 	    Dump('<', netiring.supply, c);
575 	}
576 	if (c)
577 	    ring_supplied(&netiring, c);
578 	returnValue = 1;
579     }
580 
581     /*
582      * Something to read from the tty...
583      */
584     if (FD_ISSET(tin, &ibits)) {
585 	FD_CLR(tin, &ibits);
586 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
587 	if (c < 0 && errno == EWOULDBLOCK) {
588 	    c = 0;
589 	} else {
590 	    /* EOF detection for line mode!!!! */
591 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
592 			/* must be an EOF... */
593 		*ttyiring.supply = termEofChar;
594 		c = 1;
595 	    }
596 	    if (c <= 0) {
597 		return -1;
598 	    }
599 	    ring_supplied(&ttyiring, c);
600 	}
601 	returnValue = 1;		/* did something useful */
602     }
603 
604     if (FD_ISSET(net, &obits)) {
605 	FD_CLR(net, &obits);
606 	returnValue |= netflush();
607     }
608     if (FD_ISSET(tout, &obits)) {
609 	FD_CLR(tout, &obits);
610 	returnValue |= ttyflush(SYNCHing|flushout);
611     }
612 
613     return returnValue;
614 }
615 #endif	/* defined(unix) */
616