1 /*
2 * Copyright (c) 1984-1987 by the Regents of the
3 * University of California and by Gregory Glenn Minshall.
4 *
5 * Permission to use, copy, modify, and distribute these
6 * programs and their documentation for any purpose and
7 * without fee is hereby granted, provided that this
8 * copyright and permission appear on all copies and
9 * supporting documentation, the name of the Regents of
10 * the University of California not be used in advertising
11 * or publicity pertaining to distribution of the programs
12 * without specific prior permission, and notice be given in
13 * supporting documentation that copying and distribution is
14 * by permission of the Regents of the University of California
15 * and by Gregory Glenn Minshall. Neither the Regents of the
16 * University of California nor Gregory Glenn Minshall make
17 * representations about the suitability of this software
18 * for any purpose. It is provided "as is" without
19 * express or implied warranty.
20 */
21
22 #ifndef lint
23 static char copyright[] =
24 "@(#) Copyright (c) 1984-1987 Regents of the University of California.\n\
25 All rights reserved.\n";
26 #endif /* not lint */
27
28 #ifndef lint
29 static char sccsid[] = "@(#)telnet.c 1.18.1.1 (Berkeley) 12/04/88";
30 #endif /* not lint */
31
32 /*
33 * User telnet program, modified for use by tn3270.c.
34 *
35 * Many of the FUNCTIONAL changes in this newest version of TELNET
36 * were suggested by Dave Borman of Cray Research, Inc.
37 *
38 * Other changes in the tn3270 side come from Alan Crosswell (Columbia),
39 * Bob Braden (ISI), Steve Jacobson (Berkeley), and Cliff Frost (Berkeley).
40 *
41 * This code is common between telnet(1c) and tn3270(1c). There are the
42 * following defines used to generate the various versions:
43 *
44 * TN3270 - This is to be linked with tn3270.
45 *
46 * NOT43 - Allows the program to compile and run on
47 * a 4.2BSD system.
48 *
49 * PUTCHAR - Within tn3270, on a NOT43 system,
50 * allows the use of the 4.3 curses
51 * (greater speed updating the screen).
52 * You need the 4.3 curses for this to work.
53 *
54 * FD_SETSIZE - On whichever system, if this isn't defined,
55 * we patch over the FD_SET, etc., macros with
56 * some homebrewed ones.
57 *
58 * SO_OOBINLINE - This is a socket option which we would like
59 * to set to allow TCP urgent data to come
60 * to us "inline". This is NECESSARY for
61 * CORRECT operation, and desireable for
62 * simpler operation.
63 *
64 * LNOFLSH - Detects the presence of the LNOFLSH bit
65 * in the tty structure.
66 *
67 * unix - Compiles in unix specific stuff.
68 *
69 * MSDOS - Compiles in MSDOS specific stuff.
70 *
71 */
72
73 #if !defined(TN3270)
74 #define ExitString(f,s,r) { fprintf(f, s); exit(r); }
75 #define Exit(x) exit(x)
76 #define SetIn3270()
77
78 void setcommandmode(), command(); /* forward declarations */
79 #endif /* !defined(TN3270) */
80
81 #include <sys/types.h>
82 #include <sys/socket.h>
83
84 #include <netinet/in.h>
85
86 #if defined(unix)
87 /* By the way, we need to include curses.h before telnet.h since,
88 * among other things, telnet.h #defines 'DO', which is a variable
89 * declared in curses.h.
90 */
91 #include <curses.h>
92 #endif /* defined(unix) */
93
94 #define TELOPTS
95 #include <arpa/telnet.h>
96
97 #if !defined(NOT43)
98 #include <arpa/inet.h>
99 #else /* !defined(NOT43) */
100 extern unsigned long inet_addr();
101 extern char *inet_ntoa();
102 #endif /* !defined(NOT43) */
103
104 #include <stdio.h>
105 #include <ctype.h>
106 #include <errno.h>
107 #include <setjmp.h>
108 #include <netdb.h>
109
110 #if defined(unix)
111 #include <strings.h>
112 #else /* defined(unix) */
113 #include <string.h>
114 #endif /* defined(unix) */
115
116 #if defined(TN3270)
117 #include "ascii/termin.ext"
118 #include "ctlr/screen.h"
119 #include "ctlr/oia.h"
120 #include "ctlr/options.ext"
121 #include "ctlr/outbound.ext"
122 #include "general/globals.h"
123 #include "telnet.ext"
124 #endif /* defined(TN3270) */
125
126 #include "general/general.h"
127
128
129
130 #ifndef FD_SETSIZE
131 /*
132 * The following is defined just in case someone should want to run
133 * this telnet on a 4.2 system.
134 *
135 */
136
137 #define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
138 #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
139 #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
140 #define FD_ZERO(p) ((p)->fds_bits[0] = 0)
141
142 #endif
143
144 #define strip(x) ((x)&0x7f)
145 #define min(x,y) ((x<y)? x:y)
146
147 #if defined(TN3270)
148 static char Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp;
149 #endif /* defined(TN3270) */
150
151 static char ttyobuf[2*BUFSIZ], *tfrontp, *tbackp;
152 #define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
153 #define TTYLOC() (tfrontp)
154 #define TTYMAX() (ttyobuf+sizeof ttyobuf-1)
155 #define TTYMIN() (netobuf)
156 #define TTYBYTES() (tfrontp-tbackp)
157 #define TTYROOM() (TTYMAX()-TTYLOC()+1)
158
159 static char netobuf[2*BUFSIZ], *nfrontp, *nbackp;
160 #define NETADD(c) { *nfrontp++ = c; }
161 #define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); }
162 #define NETLOC() (nfrontp)
163 #define NETMAX() (netobuf+sizeof netobuf-1)
164 #define NETBYTES() (nfrontp-nbackp)
165 #define NETROOM() (NETMAX()-NETLOC()+1)
166 static char *neturg; /* one past last byte of urgent data */
167
168 static char subbuffer[100],
169 *subpointer, *subend; /* buffer for sub-options */
170 #define SB_CLEAR() subpointer = subbuffer;
171 #define SB_TERM() subend = subpointer;
172 #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
173 *subpointer++ = (c); \
174 }
175
176 static char sb_terminal[] = { IAC, SB,
177 TELOPT_TTYPE, TELQUAL_IS,
178 'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
179 IAC, SE };
180 #define SBTERMMODEL 13
181
182
183 static char hisopts[256];
184 static char myopts[256];
185
186 static char doopt[] = { IAC, DO, '%', 'c', 0 };
187 static char dont[] = { IAC, DONT, '%', 'c', 0 };
188 static char will[] = { IAC, WILL, '%', 'c', 0 };
189 static char wont[] = { IAC, WONT, '%', 'c', 0 };
190
191 struct cmd {
192 char *name; /* command name */
193 char *help; /* help string */
194 int (*handler)(); /* routine which executes command */
195 int dohelp; /* Should we give general help information? */
196 int needconnect; /* Do we need to be connected to execute? */
197 };
198
199 static char sibuf[BUFSIZ], *sbp;
200 static char tibuf[BUFSIZ], *tbp;
201 static fd_set ibits, obits, xbits;
202
203
204 static int
205 connected,
206 net,
207 scc,
208 tcc,
209 showoptions,
210 In3270, /* Are we in 3270 mode? */
211 ISend, /* trying to send network data in */
212 debug = 0,
213 crmod,
214 netdata,
215 noasynch = 0, /* User specified "-noasynch" on command line */
216 askedSGA = 0, /* We have talked about suppress go ahead */
217 telnetport = 1;
218
219 static FILE *NetTrace = 0; /* Not in bss, since needs to stay */
220
221 #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
222
223 static char
224 *prompt = 0,
225 escape,
226 echoc;
227
228 static int
229 SYNCHing, /* we are in TELNET SYNCH mode */
230 flushout, /* flush output */
231 autoflush = 0, /* flush output when interrupting? */
232 autosynch, /* send interrupt characters with SYNCH? */
233 localchars, /* we recognize interrupt/quit */
234 donelclchars, /* the user has set "localchars" */
235 donebinarytoggle, /* the user has put us in binary */
236 dontlecho, /* do we suppress local echoing right now? */
237 globalmode;
238
239 /* The following are some tn3270 specific flags */
240 #if defined(TN3270)
241
242 static int
243 Sent3270TerminalType; /* Have we said we are a 3270? */
244
245 /* Some real, live, globals. */
246 int
247 tout, /* Output file descriptor */
248 tin; /* Input file descriptor */
249
250 #else /* defined(TN3270) */
251 static int tin, tout; /* file descriptors */
252 #endif /* defined(TN3270) */
253
254
255 /*
256 * Telnet receiver states for fsm
257 */
258 #define TS_DATA 0
259 #define TS_IAC 1
260 #define TS_WILL 2
261 #define TS_WONT 3
262 #define TS_DO 4
263 #define TS_DONT 5
264 #define TS_CR 6
265 #define TS_SB 7 /* sub-option collection */
266 #define TS_SE 8 /* looking for sub-option end */
267
268 static int telrcv_state = TS_DATA;
269
270 static char line[200];
271 static int margc;
272 static char *margv[20];
273
274 static jmp_buf toplevel = { 0 };
275 static jmp_buf peerdied;
276
277 extern int errno;
278
279
280 static struct sockaddr_in sin;
281
282 static struct servent *sp = 0;
283
284 static int flushline;
285
286 static char *hostname;
287 static char hnamebuf[32];
288
289 /*
290 * The following are some clocks used to decide how to interpret
291 * the relationship between various variables.
292 */
293
294 static struct {
295 int
296 system, /* what the current time is */
297 echotoggle, /* last time user entered echo character */
298 modenegotiated, /* last time operating mode negotiated */
299 didnetreceive, /* last time we read data from network */
300 gotDM; /* when did we last see a data mark */
301 } clocks;
302
303 #define settimer(x) clocks.x = clocks.system++
304
305 /* Various modes */
306 #define MODE_LINE(m) (modelist[m].modetype & LINE)
307 #define MODE_LOCAL_CHARS(m) (modelist[m].modetype & LOCAL_CHARS)
308 #define MODE_LOCAL_ECHO(m) (modelist[m].modetype & LOCAL_ECHO)
309 #define MODE_COMMAND_LINE(m) (modelist[m].modetype & COMMAND_LINE)
310
311 #define LOCAL_CHARS 0x01 /* Characters processed locally */
312 #define LINE 0x02 /* Line-by-line mode of operation */
313 #define LOCAL_ECHO 0x04 /* Echoing locally */
314 #define COMMAND_LINE 0x08 /* Command line mode */
315
316 static struct {
317 char *modedescriptions;
318 char modetype;
319 } modelist[] = {
320 { "telnet command mode", COMMAND_LINE },
321 { "character-at-a-time mode", 0 },
322 { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
323 { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
324 { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
325 { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
326 { "3270 mode", 0 },
327 };
328
329
330 /*
331 * The following routines try to encapsulate what is system dependent
332 * (at least between 4.x and dos) which is used in telnet.c.
333 */
334
335 #if defined(unix)
336 #include <sys/ioctl.h>
337 #include <sys/time.h>
338 #include <signal.h>
339
340 int
341 HaveInput; /* There is input available to scan */
342
343 #if defined(TN3270)
344 static char tline[200];
345 char *transcom = 0; /* transparent mode command (default: none) */
346 #endif /* defined(TN3270) */
347
348 static struct tchars otc = { 0 }, ntc = { 0 };
349 static struct ltchars oltc = { 0 }, nltc = { 0 };
350 static struct sgttyb ottyb = { 0 }, nttyb = { 0 };
351
352
353 #define TerminalWrite(fd,buf,n) write(fd,buf,n)
354 #define TerminalRead(fd,buf,n) read(fd,buf,n)
355
356 /*
357 *
358 */
359
360 static int
TerminalAutoFlush()361 TerminalAutoFlush() /* unix */
362 {
363 #if defined(LNOFLSH)
364 ioctl(0, TIOCLGET, (char *)&autoflush);
365 return !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */
366 #else /* LNOFLSH */
367 return 1;
368 #endif /* LNOFLSH */
369 }
370
371 /*
372 * TerminalSpecialChars()
373 *
374 * Look at an input character to see if it is a special character
375 * and decide what to do.
376 *
377 * Output:
378 *
379 * 0 Don't add this character.
380 * 1 Do add this character
381 */
382
383 int
TerminalSpecialChars(c)384 TerminalSpecialChars(c) /* unix */
385 int c;
386 {
387 void doflush(), intp(), sendbrk();
388
389 if (c == ntc.t_intrc) {
390 intp();
391 return 0;
392 } else if (c == ntc.t_quitc) {
393 sendbrk();
394 return 0;
395 } else if (c == nltc.t_flushc) {
396 NET2ADD(IAC, AO);
397 if (autoflush) {
398 doflush();
399 }
400 return 0;
401 } else if (!MODE_LOCAL_CHARS(globalmode)) {
402 if (c == nttyb.sg_kill) {
403 NET2ADD(IAC, EL);
404 return 0;
405 } else if (c == nttyb.sg_erase) {
406 NET2ADD(IAC, EC);
407 return 0;
408 }
409 }
410 return 1;
411 }
412
413
414 /*
415 * Flush output to the terminal
416 */
417
418 static void
TerminalFlushOutput()419 TerminalFlushOutput() /* unix */
420 {
421 (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
422 }
423
424 static void
TerminalSaveState()425 TerminalSaveState() /* unix */
426 {
427 ioctl(0, TIOCGETP, (char *)&ottyb);
428 ioctl(0, TIOCGETC, (char *)&otc);
429 ioctl(0, TIOCGLTC, (char *)&oltc);
430
431 ntc = otc;
432 nltc = oltc;
433 nttyb = ottyb;
434 }
435
436 static void
TerminalRestoreState()437 TerminalRestoreState() /* unix */
438 {
439 }
440
441 /*
442 * TerminalNewMode - set up terminal to a specific mode.
443 */
444
445
446 static void
TerminalNewMode(f)447 TerminalNewMode(f) /* unix */
448 register int f;
449 {
450 static int prevmode = 0;
451 struct tchars *tc;
452 struct tchars tc3;
453 struct ltchars *ltc;
454 struct sgttyb sb;
455 int onoff;
456 int old;
457 struct tchars notc2;
458 struct ltchars noltc2;
459 static struct tchars notc = { -1, -1, -1, -1, -1, -1 };
460 static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
461
462 globalmode = f;
463 if (prevmode == f)
464 return;
465 old = prevmode;
466 prevmode = f;
467 sb = nttyb;
468
469 switch (f) {
470
471 case 0:
472 onoff = 0;
473 tc = &otc;
474 ltc = &oltc;
475 break;
476
477 case 1: /* remote character processing, remote echo */
478 case 2: /* remote character processing, local echo */
479 case 6: /* 3270 mode - like 1, but with xon/xoff local */
480 /* (might be nice to have "6" in telnet also...) */
481 sb.sg_flags |= CBREAK;
482 if ((f == 1) || (f == 6)) {
483 sb.sg_flags &= ~(ECHO|CRMOD);
484 } else {
485 sb.sg_flags |= ECHO|CRMOD;
486 }
487 sb.sg_erase = sb.sg_kill = -1;
488 if (f == 6) {
489 tc = &tc3;
490 tc3 = notc;
491 /* get XON, XOFF characters */
492 tc3.t_startc = otc.t_startc;
493 tc3.t_stopc = otc.t_stopc;
494 } else {
495 /*
496 * If user hasn't specified one way or the other,
497 * then default to not trapping signals.
498 */
499 if (!donelclchars) {
500 localchars = 0;
501 }
502 if (localchars) {
503 notc2 = notc;
504 notc2.t_intrc = ntc.t_intrc;
505 notc2.t_quitc = ntc.t_quitc;
506 tc = ¬c2;
507 } else {
508 tc = ¬c;
509 }
510 }
511 ltc = &noltc;
512 onoff = 1;
513 break;
514 case 3: /* local character processing, remote echo */
515 case 4: /* local character processing, local echo */
516 case 5: /* local character processing, no echo */
517 sb.sg_flags &= ~CBREAK;
518 sb.sg_flags |= CRMOD;
519 if (f == 4)
520 sb.sg_flags |= ECHO;
521 else
522 sb.sg_flags &= ~ECHO;
523 notc2 = ntc;
524 tc = ¬c2;
525 noltc2 = oltc;
526 ltc = &noltc2;
527 /*
528 * If user hasn't specified one way or the other,
529 * then default to trapping signals.
530 */
531 if (!donelclchars) {
532 localchars = 1;
533 }
534 if (localchars) {
535 notc2.t_brkc = nltc.t_flushc;
536 noltc2.t_flushc = -1;
537 } else {
538 notc2.t_intrc = notc2.t_quitc = -1;
539 }
540 noltc2.t_suspc = escape;
541 noltc2.t_dsuspc = -1;
542 onoff = 1;
543 break;
544
545 default:
546 return;
547 }
548 ioctl(tin, TIOCSLTC, (char *)ltc);
549 ioctl(tin, TIOCSETC, (char *)tc);
550 ioctl(tin, TIOCSETP, (char *)&sb);
551 #if (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
552 ioctl(tin, FIONBIO, (char *)&onoff);
553 ioctl(tout, FIONBIO, (char *)&onoff);
554 #endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
555 #if defined(TN3270)
556 if (noasynch == 0) {
557 ioctl(tin, FIOASYNC, (char *)&onoff);
558 }
559 #endif /* defined(TN3270) */
560
561 if (MODE_LINE(f)) {
562 void doescape();
563
564 signal(SIGTSTP, doescape);
565 } else if (MODE_LINE(old)) {
566 signal(SIGTSTP, SIG_DFL);
567 sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
568 }
569 }
570
571
572 int
NetClose(net)573 NetClose(net)
574 int net;
575 {
576 return close(net);
577 }
578
579
580 static void
NetNonblockingIO(fd,onoff)581 NetNonblockingIO(fd, onoff) /* unix */
582 int
583 fd,
584 onoff;
585 {
586 ioctl(net, FIONBIO, (char *)&onoff);
587 }
588
589 static void
NetSigIO(fd,onoff)590 NetSigIO(fd, onoff) /* unix */
591 int
592 fd,
593 onoff;
594 {
595 ioctl(net, FIOASYNC, (char *)&onoff); /* hear about input */
596 }
597
598 static void
NetSetPgrp(fd)599 NetSetPgrp(fd) /* unix */
600 int fd;
601 {
602 int myPid;
603
604 myPid = getpid();
605 #if defined(NOT43)
606 myPid = -myPid;
607 #endif /* defined(NOT43) */
608 ioctl(net, SIOCSPGRP, (char *)&myPid); /* set my pid */
609 }
610
611
612 #endif /* defined(unix) */
613
614 #if defined(MSDOS)
615 #include <time.h>
616 #include <signal.h>
617 #include <process.h>
618 #include <fcntl.h>
619 #include <io.h>
620 #include <dos.h>
621
622 #if !defined(SO_OOBINLINE)
623 #define SO_OOBINLINE
624 #endif /* !defined(SO_OOBINLINE) */
625
626
627 static char
628 termEofChar,
629 termEraseChar,
630 termFlushChar,
631 termIntChar,
632 termKillChar,
633 termLiteralNextChar,
634 termQuitChar;
635
636
637 /*
638 * MSDOS doesn't have anyway of deciding whether a full-edited line
639 * is ready to be read in, so we need to do character-by-character
640 * reads, and then do the editing in the program (in the case where
641 * we are supporting line-by-line mode).
642 *
643 * The following routines, which are internal to the MSDOS-specific
644 * code, accomplish this miracle.
645 */
646
647 #define Hex(c) HEX[(c)&0xff]
648
649 static survivorSetup = 0; /* Do we have ^C hooks in? */
650
651 static int
652 lineend = 0, /* There is a line terminator */
653 ctrlCCount = 0;
654
655 static char linein[200], /* Where input line is assembled */
656 *nextin = linein, /* Next input character */
657 *nextout = linein; /* Next character to be consumed */
658
659 #define consumechar() \
660 if ((++nextout) >= nextin) { \
661 nextout = nextin = linein; \
662 lineend = 0; \
663 }
664
665 #define characteratatime() (!MODE_LINE(globalmode)) /* one by one */
666
667
668 /*
669 * killone()
670 *
671 * Erase the last character on the line.
672 */
673
674 static void
killone()675 killone()
676 {
677 if (lineend) {
678 return; /* ??? XXX */
679 }
680 if (nextin == linein) {
681 return; /* Nothing to do */
682 }
683 nextin--;
684 if (!(isspace(*nextin) || isprint(*nextin))) {
685 putchar('\b');
686 putchar(' ');
687 putchar('\b');
688 }
689 putchar('\b');
690 putchar(' ');
691 putchar('\b');
692 }
693
694
695 /*
696 * setlineend()
697 *
698 * Decide if it's time to send the current line up to the user
699 * process.
700 */
701
702 static void
setlineend()703 setlineend()
704 {
705 if (nextin == nextout) {
706 return;
707 }
708 if (characteratatime()) {
709 lineend = 1;
710 } else if (nextin >= (linein+sizeof linein)) {
711 lineend = 1;
712 } else {
713 int c = *(nextin-1);
714 if ((c == termIntChar)
715 || (c == termQuitChar)
716 || (c == termEofChar)) {
717 lineend = 1;
718 } else if (c == termFlushChar) {
719 lineend = 1;
720 } else if ((c == '\n') || (c == '\r')) {
721 lineend = 1;
722 }
723 }
724 /* Otherwise, leave it alone (reset by 'consumechar') */
725 }
726
727 /*
728 * OK, what we do here is:
729 *
730 * o If we are echoing, then
731 * o Look for character erase, line kill characters
732 * o Echo the character (using '^' if a control character)
733 * o Put the character in the input buffer
734 * o Set 'lineend' as necessary
735 */
736
737 static void
DoNextChar(c)738 DoNextChar(c)
739 int c; /* Character to process */
740 {
741 static char literalnextcharacter = 0;
742
743 if (nextin >= (linein+sizeof linein)) {
744 putchar('\7'); /* Ring bell */
745 setlineend();
746 return;
747 }
748 if (MODE_LOCAL_CHARS(globalmode)) {
749 /* Look for some special character */
750 if (!literalnextcharacter) {
751 if (c == termEraseChar) {
752 killone();
753 setlineend();
754 return;
755 } else if (c == termKillChar) {
756 while (nextin != linein) {
757 killone();
758 }
759 setlineend();
760 return;
761 } else if (c == termLiteralNextChar) {
762 literalnextcharacter = 1;
763 return;
764 }
765 }
766
767 if (MODE_LOCAL_ECHO(globalmode)) {
768 if ((literalnextcharacter == 0) && ((c == '\r') || (c == '\n'))) {
769 putchar('\r');
770 putchar('\n');
771 c = '\n';
772 } else if (!isprint(c) && !isspace(c)) {
773 putchar('^');
774 putchar(c^0x40);
775 } else {
776 putchar(c);
777 }
778 }
779 literalnextcharacter = 0;
780 }
781 *nextin++ = c;
782 setlineend();
783 }
784
785 static int
inputExists()786 inputExists()
787 {
788 int input;
789 static state = 0;
790
791 while (ctrlCCount) {
792 DoNextChar(0x03);
793 ctrlCCount--;
794 }
795 if (lineend) {
796 return 1;
797 }
798 #if 1 /* For BIOS variety of calls */
799 if (kbhit() == 0) {
800 return lineend;
801 }
802 input = getch(); /* MSC - get console character */
803 if ((input&0xff) == 0) {
804 DoNextChar(0x01); /* ^A */
805 } else {
806 DoNextChar(input&0xff);
807 }
808 #else /* 0 */
809 if ((input = dirconio()) == -1) {
810 return lineend;
811 }
812 if ((input&0xff) == 0) {
813 if ((input&0xff00) == 0x0300) { /* Null */
814 DoNextChar(0);
815 } else {
816 DoNextChar(0x01);
817 if (input&0x8000) {
818 DoNextChar(0x01);
819 DoNextChar((input>>8)&0x7f);
820 } else {
821 DoNextChar((input>>8)&0xff);
822 }
823 }
824 } else {
825 DoNextChar(input&0xff);
826 }
827 #endif /* 0 */
828 return lineend;
829 }
830
831
832 void
CtrlCInterrupt()833 CtrlCInterrupt()
834 {
835 if (!MODE_COMMAND_LINE(globalmode)) {
836 ctrlCCount++; /* XXX */
837 signal(SIGINT, CtrlCInterrupt);
838 } else {
839 closeallsockets();
840 exit(1);
841 }
842 }
843
844 /*
845 * The MSDOS routines, called from elsewhere.
846 */
847
848
849 static int
TerminalAutoFlush()850 TerminalAutoFlush() /* MSDOS */
851 {
852 return 1;
853 }
854
855 static int
TerminalCanRead()856 TerminalCanRead()
857 {
858 return inputExists();
859 }
860
861
862 /*
863 * Flush output to the terminal
864 */
865
866 static void
TerminalFlushOutput()867 TerminalFlushOutput() /* MSDOS */
868 {
869 }
870
871
872 static void
TerminalNewMode(f)873 TerminalNewMode(f) /* MSDOS */
874 register int f;
875 {
876 union REGS inregs;
877 struct SREGS segregs;
878 static old_1b_offset = 0, old_1b_segment = 0;
879
880 globalmode = f;
881 if (MODE_COMMAND_LINE(f)) {
882 signal(SIGINT, SIG_DFL);
883 if (old_1b_segment|old_1b_offset) {
884 inregs.h.ah = 0x25;
885 inregs.h.al = 0x1b;
886 inregs.x.dx = old_1b_offset;
887 segregs.ds = old_1b_segment;
888 intdosx(&inregs, &inregs, &segregs);
889 old_1b_segment = old_1b_offset = 0;
890 }
891 if (setmode(fileno(stdout), O_TEXT) == -1) {
892 ExitPerror("setmode (text)", 1);
893 }
894 if (setmode(fileno(stdin), O_TEXT) == -1) {
895 ExitPerror("setmode (text)", 1);
896 }
897 } else {
898 signal(SIGINT, CtrlCInterrupt);
899 if ((old_1b_segment|old_1b_offset) == 0) {
900 extern void iret_subr();
901 void (far *foo_subr)() = iret_subr;
902
903 inregs.h.ah = 0x35;
904 inregs.h.al = 0x1b;
905 intdosx(&inregs, &inregs, &segregs);
906 old_1b_segment = segregs.es;
907 old_1b_offset = inregs.x.bx;
908 inregs.h.ah = 0x25;
909 inregs.h.al = 0x1b;
910 inregs.x.dx = FP_OFF(foo_subr);
911 segregs.ds = FP_SEG(foo_subr);
912 intdosx(&inregs, &inregs, &segregs);
913 }
914 if (setmode(fileno(stdout), O_BINARY) == -1) {
915 ExitPerror("setmode (binary)", 1);
916 }
917 if (setmode(fileno(stdin), O_BINARY) == -1) {
918 ExitPerror("setmode (binary)", 1);
919 }
920 }
921 }
922
923
924 int
TerminalRead(fd,buffer,count)925 TerminalRead(fd, buffer, count)
926 int fd;
927 char *buffer;
928 int count;
929 {
930 int done = 0;
931
932 for (;;) {
933 while (inputExists() && (done < count)) {
934 *buffer++ = *nextout;
935 consumechar();
936 done++;
937 }
938 if (done) {
939 return(done);
940 } else {
941 return 0;
942 }
943 }
944 }
945
946
947 static void
TerminalSaveState()948 TerminalSaveState() /* MSDOS */
949 {
950 }
951
952 int
TerminalSpecialChars(c)953 TerminalSpecialChars(c) /* MSDOS */
954 {
955 return 1;
956 }
957
958
959 static void
TerminalRestoreState()960 TerminalRestoreState() /* MSDOS */
961 {
962 }
963
964
965 static int
TerminalWrite(fd,buffer,count)966 TerminalWrite(fd, buffer, count) /* MSDOS */
967 int fd;
968 char *buffer;
969 int count;
970 {
971 return fwrite(buffer, sizeof (char), count, stdout);
972 }
973
974
975 static int
NetClose(fd)976 NetClose(fd)
977 {
978 return closesocket(fd);
979 }
980
981 static void
NetNonblockingIO(fd,onoff)982 NetNonblockingIO(fd, onoff) /* MSDOS */
983 int
984 fd,
985 onoff;
986 {
987 if (SetSockOpt(net, SOL_SOCKET, SO_NONBLOCKING, onoff)) {
988 perror("setsockop (SO_NONBLOCKING) ");
989 ExitString(stderr, "exiting\n", 1);
990 }
991 }
992
993 static void
NetSigIO(fd)994 NetSigIO(fd) /* MSDOS */
995 int fd;
996 {
997 }
998
999 static void
NetSetPgrp(fd)1000 NetSetPgrp(fd) /* MSDOS */
1001 int fd;
1002 {
1003 }
1004
1005
1006 #endif /* defined(MSDOS) */
1007
1008 /*
1009 * Initialize variables.
1010 */
1011
1012 static void
tninit()1013 tninit()
1014 {
1015 #if defined(TN3270)
1016 Sent3270TerminalType = 0;
1017 Ifrontp = Ibackp = Ibuf;
1018 #endif /* defined(TN3270) */
1019
1020 tfrontp = tbackp = ttyobuf;
1021 nfrontp = nbackp = netobuf;
1022
1023 /* Don't change telnetport */
1024 SB_CLEAR();
1025 ClearArray(hisopts);
1026 ClearArray(myopts);
1027 sbp = sibuf;
1028 tbp = tibuf;
1029
1030 connected = net = scc = tcc = In3270 = ISend = donebinarytoggle = 0;
1031 telnetport = 0;
1032 #if defined(unix)
1033 HaveInput = 0;
1034 #endif /* defined(unix) */
1035
1036 SYNCHing = 0;
1037
1038 errno = 0;
1039
1040 flushline = 0;
1041
1042 /* Don't change NetTrace */
1043
1044 escape = CONTROL(']');
1045 echoc = CONTROL('E');
1046
1047 flushline = 1;
1048 sp = getservbyname("telnet", "tcp");
1049 if (sp == 0) {
1050 ExitString(stderr, "telnet: tcp/telnet: unknown service\n",1);
1051 /*NOTREACHED*/
1052 }
1053
1054 #if defined(TN3270)
1055 init_ctlr(); /* Initialize some things */
1056 init_keyboard();
1057 init_screen();
1058 init_system();
1059 #endif /* defined(TN3270) */
1060 }
1061
1062 /*
1063 * Various utility routines.
1064 */
1065
1066 static void
makeargv()1067 makeargv()
1068 {
1069 register char *cp;
1070 register char **argp = margv;
1071
1072 margc = 0;
1073 cp = line;
1074 if (*cp == '!') { /* Special case shell escape */
1075 *argp++ = "!"; /* No room in string to get this */
1076 margc++;
1077 cp++;
1078 }
1079 while (*cp) {
1080 while (isspace(*cp))
1081 cp++;
1082 if (*cp == '\0')
1083 break;
1084 *argp++ = cp;
1085 margc += 1;
1086 while (*cp != '\0' && !isspace(*cp))
1087 cp++;
1088 if (*cp == '\0')
1089 break;
1090 *cp++ = '\0';
1091 }
1092 *argp++ = 0;
1093 }
1094
1095 static char *ambiguous; /* special return value */
1096 #define Ambiguous(t) ((t)&ambiguous)
1097
1098
1099 static char **
genget(name,table,next)1100 genget(name, table, next)
1101 char *name; /* name to match */
1102 char **table; /* name entry in table */
1103 char **(*next)(); /* routine to return next entry in table */
1104 {
1105 register char *p, *q;
1106 register char **c, **found;
1107 register int nmatches, longest;
1108
1109 if (name == 0) {
1110 return 0;
1111 }
1112 longest = 0;
1113 nmatches = 0;
1114 found = 0;
1115 for (c = table; (p = *c) != 0; c = (*next)(c)) {
1116 for (q = name;
1117 (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++)
1118 if (*q == 0) /* exact match? */
1119 return (c);
1120 if (!*q) { /* the name was a prefix */
1121 if (q - name > longest) {
1122 longest = q - name;
1123 nmatches = 1;
1124 found = c;
1125 } else if (q - name == longest)
1126 nmatches++;
1127 }
1128 }
1129 if (nmatches > 1)
1130 return Ambiguous(char **);
1131 return (found);
1132 }
1133
1134 /*
1135 * Make a character string into a number.
1136 *
1137 * Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
1138 */
1139
1140 static
special(s)1141 special(s)
1142 register char *s;
1143 {
1144 register char c;
1145 char b;
1146
1147 switch (*s) {
1148 case '^':
1149 b = *++s;
1150 if (b == '?') {
1151 c = b | 0x40; /* DEL */
1152 } else {
1153 c = b & 0x1f;
1154 }
1155 break;
1156 default:
1157 c = *s;
1158 break;
1159 }
1160 return c;
1161 }
1162
1163 /*
1164 * Construct a control character sequence
1165 * for a special character.
1166 */
1167 static char *
control(c)1168 control(c)
1169 register int c;
1170 {
1171 static char buf[3];
1172
1173 if (c == 0x7f)
1174 return ("^?");
1175 if (c == '\377') {
1176 return "off";
1177 }
1178 if (c >= 0x20) {
1179 buf[0] = c;
1180 buf[1] = 0;
1181 } else {
1182 buf[0] = '^';
1183 buf[1] = '@'+c;
1184 buf[2] = 0;
1185 }
1186 return (buf);
1187 }
1188
1189
1190 /*
1191 * upcase()
1192 *
1193 * Upcase (in place) the argument.
1194 */
1195
1196 static void
upcase(argument)1197 upcase(argument)
1198 register char *argument;
1199 {
1200 register int c;
1201
1202 while ((c = *argument) != 0) {
1203 if (islower(c)) {
1204 *argument = toupper(c);
1205 }
1206 argument++;
1207 }
1208 }
1209
1210 /*
1211 * SetSockOpt()
1212 *
1213 * Compensate for differences in 4.2 and 4.3 systems.
1214 */
1215
1216 static int
SetSockOpt(fd,level,option,yesno)1217 SetSockOpt(fd, level, option, yesno)
1218 int
1219 fd,
1220 level,
1221 option,
1222 yesno;
1223 {
1224 #ifndef NOT43
1225 return setsockopt(fd, level, option,
1226 (char *)&yesno, sizeof yesno);
1227 #else /* NOT43 */
1228 if (yesno == 0) { /* Can't do that in 4.2! */
1229 fprintf(stderr, "Error: attempt to turn off an option 0x%x.\n",
1230 option);
1231 return -1;
1232 }
1233 return setsockopt(fd, level, option, 0, 0);
1234 #endif /* NOT43 */
1235 }
1236
1237 /*
1238 * The following are routines used to print out debugging information.
1239 */
1240
1241
1242 static void
Dump(direction,buffer,length)1243 Dump(direction, buffer, length)
1244 char direction;
1245 char *buffer;
1246 int length;
1247 {
1248 # define BYTES_PER_LINE 32
1249 # define min(x,y) ((x<y)? x:y)
1250 char *pThis;
1251 int offset;
1252
1253 offset = 0;
1254
1255 while (length) {
1256 /* print one line */
1257 fprintf(NetTrace, "%c 0x%x\t", direction, offset);
1258 pThis = buffer;
1259 buffer = buffer+min(length, BYTES_PER_LINE);
1260 while (pThis < buffer) {
1261 fprintf(NetTrace, "%.2x", (*pThis)&0xff);
1262 pThis++;
1263 }
1264 fprintf(NetTrace, "\n");
1265 length -= BYTES_PER_LINE;
1266 offset += BYTES_PER_LINE;
1267 if (length < 0) {
1268 return;
1269 }
1270 /* find next unique line */
1271 }
1272 }
1273
1274
1275 /*VARARGS*/
1276 static void
printoption(direction,fmt,option,what)1277 printoption(direction, fmt, option, what)
1278 char *direction, *fmt;
1279 int option, what;
1280 {
1281 if (!showoptions)
1282 return;
1283 fprintf(NetTrace, "%s ", direction+1);
1284 if (fmt == doopt)
1285 fmt = "do";
1286 else if (fmt == dont)
1287 fmt = "dont";
1288 else if (fmt == will)
1289 fmt = "will";
1290 else if (fmt == wont)
1291 fmt = "wont";
1292 else
1293 fmt = "???";
1294 if (option < (sizeof telopts/sizeof telopts[0]))
1295 fprintf(NetTrace, "%s %s", fmt, telopts[option]);
1296 else
1297 fprintf(NetTrace, "%s %d", fmt, option);
1298 if (*direction == '<') {
1299 fprintf(NetTrace, "\r\n");
1300 return;
1301 }
1302 fprintf(NetTrace, " (%s)\r\n", what ? "reply" : "don't reply");
1303 }
1304
1305 static void
printsub(direction,pointer,length)1306 printsub(direction, pointer, length)
1307 char *direction, /* "<" or ">" */
1308 *pointer; /* where suboption data sits */
1309 int length; /* length of suboption data */
1310 {
1311 if (showoptions) {
1312 fprintf(NetTrace, "%s suboption ",
1313 (direction[0] == '<')? "Received":"Sent");
1314 switch (pointer[0]) {
1315 case TELOPT_TTYPE:
1316 fprintf(NetTrace, "Terminal type ");
1317 switch (pointer[1]) {
1318 case TELQUAL_IS:
1319 {
1320 char tmpbuf[sizeof subbuffer];
1321 int minlen = min(length, sizeof tmpbuf);
1322
1323 memcpy(tmpbuf, pointer+2, minlen);
1324 tmpbuf[minlen-1] = 0;
1325 fprintf(NetTrace, "is %s.\n", tmpbuf);
1326 }
1327 break;
1328 case TELQUAL_SEND:
1329 fprintf(NetTrace, "- request to send.\n");
1330 break;
1331 default:
1332 fprintf(NetTrace,
1333 "- unknown qualifier %d (0x%x).\n", pointer[1]);
1334 }
1335 break;
1336 default:
1337 fprintf(NetTrace, "Unknown option %d (0x%x)\n",
1338 pointer[0], pointer[0]);
1339 }
1340 }
1341 }
1342
1343 /*
1344 * Check to see if any out-of-band data exists on a socket (for
1345 * Telnet "synch" processing).
1346 */
1347
1348 static int
stilloob(s)1349 stilloob(s)
1350 int s; /* socket number */
1351 {
1352 static struct timeval timeout = { 0 };
1353 fd_set excepts;
1354 int value;
1355
1356 do {
1357 FD_ZERO(&excepts);
1358 FD_SET(s, &excepts);
1359 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
1360 } while ((value == -1) && (errno == EINTR));
1361
1362 if (value < 0) {
1363 perror("select");
1364 quit();
1365 }
1366 if (FD_ISSET(s, &excepts)) {
1367 return 1;
1368 } else {
1369 return 0;
1370 }
1371 }
1372
1373
1374 /*
1375 * netflush
1376 * Send as much data as possible to the network,
1377 * handling requests for urgent data.
1378 *
1379 * The return value indicates whether we did any
1380 * useful work.
1381 */
1382
1383
1384 int
netflush()1385 netflush()
1386 {
1387 int n;
1388
1389 if ((n = nfrontp - nbackp) > 0) {
1390 if (!neturg) {
1391 n = send(net, nbackp, n, 0); /* normal write */
1392 } else {
1393 n = neturg - nbackp;
1394 /*
1395 * In 4.2 (and 4.3) systems, there is some question about
1396 * what byte in a sendOOB operation is the "OOB" data.
1397 * To make ourselves compatible, we only send ONE byte
1398 * out of band, the one WE THINK should be OOB (though
1399 * we really have more the TCP philosophy of urgent data
1400 * rather than the Unix philosophy of OOB data).
1401 */
1402 if (n > 1) {
1403 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
1404 } else {
1405 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
1406 }
1407 }
1408 }
1409 if (n < 0) {
1410 if (errno != ENOBUFS && errno != EWOULDBLOCK) {
1411 setcommandmode();
1412 perror(hostname);
1413 NetClose(net);
1414 neturg = 0;
1415 longjmp(peerdied, -1);
1416 /*NOTREACHED*/
1417 }
1418 n = 0;
1419 }
1420 if (netdata && n) {
1421 Dump('>', nbackp, n);
1422 }
1423 nbackp += n;
1424 if (nbackp >= neturg) {
1425 neturg = 0;
1426 }
1427 if (nbackp == nfrontp) {
1428 nbackp = nfrontp = netobuf;
1429 }
1430 return n > 0;
1431 }
1432
1433 /*
1434 * nextitem()
1435 *
1436 * Return the address of the next "item" in the TELNET data
1437 * stream. This will be the address of the next character if
1438 * the current address is a user data character, or it will
1439 * be the address of the character following the TELNET command
1440 * if the current address is a TELNET IAC ("I Am a Command")
1441 * character.
1442 */
1443
1444 static char *
nextitem(current)1445 nextitem(current)
1446 char *current;
1447 {
1448 if ((*current&0xff) != IAC) {
1449 return current+1;
1450 }
1451 switch (*(current+1)&0xff) {
1452 case DO:
1453 case DONT:
1454 case WILL:
1455 case WONT:
1456 return current+3;
1457 case SB: /* loop forever looking for the SE */
1458 {
1459 register char *look = current+2;
1460
1461 for (;;) {
1462 if ((*look++&0xff) == IAC) {
1463 if ((*look++&0xff) == SE) {
1464 return look;
1465 }
1466 }
1467 }
1468 }
1469 default:
1470 return current+2;
1471 }
1472 }
1473 /*
1474 * netclear()
1475 *
1476 * We are about to do a TELNET SYNCH operation. Clear
1477 * the path to the network.
1478 *
1479 * Things are a bit tricky since we may have sent the first
1480 * byte or so of a previous TELNET command into the network.
1481 * So, we have to scan the network buffer from the beginning
1482 * until we are up to where we want to be.
1483 *
1484 * A side effect of what we do, just to keep things
1485 * simple, is to clear the urgent data pointer. The principal
1486 * caller should be setting the urgent data pointer AFTER calling
1487 * us in any case.
1488 */
1489
1490 static void
netclear()1491 netclear()
1492 {
1493 register char *thisitem, *next;
1494 char *good;
1495 #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
1496 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1497
1498 thisitem = netobuf;
1499
1500 while ((next = nextitem(thisitem)) <= nbackp) {
1501 thisitem = next;
1502 }
1503
1504 /* Now, thisitem is first before/at boundary. */
1505
1506 good = netobuf; /* where the good bytes go */
1507
1508 while (nfrontp > thisitem) {
1509 if (wewant(thisitem)) {
1510 int length;
1511
1512 next = thisitem;
1513 do {
1514 next = nextitem(next);
1515 } while (wewant(next) && (nfrontp > next));
1516 length = next-thisitem;
1517 memcpy(good, thisitem, length);
1518 good += length;
1519 thisitem = next;
1520 } else {
1521 thisitem = nextitem(thisitem);
1522 }
1523 }
1524
1525 nbackp = netobuf;
1526 nfrontp = good; /* next byte to be sent */
1527 neturg = 0;
1528 }
1529
1530 /*
1531 * These routines add various telnet commands to the data stream.
1532 */
1533
1534 #if defined(NOT43)
1535 static int
1536 #else /* defined(NOT43) */
1537 static void
1538 #endif /* defined(NOT43) */
dosynch()1539 dosynch()
1540 {
1541 netclear(); /* clear the path to the network */
1542 NET2ADD(IAC, DM);
1543 neturg = NETLOC()-1; /* Some systems are off by one XXX */
1544
1545 #if defined(NOT43)
1546 return 0;
1547 #endif /* defined(NOT43) */
1548 }
1549
1550 static void
doflush()1551 doflush()
1552 {
1553 NET2ADD(IAC, DO);
1554 NETADD(TELOPT_TM);
1555 flushline = 1;
1556 flushout = 1;
1557 ttyflush();
1558 /* do printoption AFTER flush, otherwise the output gets tossed... */
1559 printoption("<SENT", doopt, TELOPT_TM, 0);
1560 }
1561
1562 static void
intp()1563 intp()
1564 {
1565 NET2ADD(IAC, IP);
1566 if (autoflush) {
1567 doflush();
1568 }
1569 if (autosynch) {
1570 dosynch();
1571 }
1572 }
1573
1574 static void
sendbrk()1575 sendbrk()
1576 {
1577 NET2ADD(IAC, BREAK);
1578 if (autoflush) {
1579 doflush();
1580 }
1581 if (autosynch) {
1582 dosynch();
1583 }
1584 }
1585
1586 /*
1587 * Send as much data as possible to the terminal.
1588 *
1589 * The return value indicates whether we did any
1590 * useful work.
1591 */
1592
1593
1594 static int
ttyflush()1595 ttyflush()
1596 {
1597 int n;
1598
1599 if ((n = tfrontp - tbackp) > 0) {
1600 if (!(SYNCHing||flushout)) {
1601 n = TerminalWrite(tout, tbackp, n);
1602 } else {
1603 TerminalFlushOutput();
1604 /* we leave 'n' alone! */
1605 }
1606 }
1607 if (n >= 0) {
1608 tbackp += n;
1609 if (tbackp == tfrontp) {
1610 tbackp = tfrontp = ttyobuf;
1611 }
1612 }
1613 return n > 0;
1614 }
1615
1616 #if defined(TN3270)
1617
1618 #if defined(unix)
1619 static void
inputAvailable()1620 inputAvailable()
1621 {
1622 HaveInput = 1;
1623 }
1624 #endif /* defined(unix) */
1625
1626 void
outputPurge()1627 outputPurge()
1628 {
1629 int tmp = flushout;
1630
1631 flushout = 1;
1632
1633 ttyflush();
1634
1635 flushout = tmp;
1636 }
1637
1638 #endif /* defined(TN3270) */
1639
1640 #if defined(unix)
1641 /*
1642 * Various signal handling routines.
1643 */
1644
1645 static void
deadpeer()1646 deadpeer()
1647 {
1648 setcommandmode();
1649 longjmp(peerdied, -1);
1650 }
1651
1652 static void
intr()1653 intr()
1654 {
1655 if (localchars) {
1656 intp();
1657 return;
1658 }
1659 setcommandmode();
1660 longjmp(toplevel, -1);
1661 }
1662
1663 static void
intr2()1664 intr2()
1665 {
1666 if (localchars) {
1667 sendbrk();
1668 return;
1669 }
1670 }
1671
1672 static void
doescape()1673 doescape()
1674 {
1675 command(0);
1676 }
1677 #endif /* defined(unix) */
1678
1679 /*
1680 * These routines decides on what the mode should be (based on the values
1681 * of various global variables).
1682 */
1683
1684
1685 static
getconnmode()1686 getconnmode()
1687 {
1688 static char newmode[16] =
1689 { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
1690 int modeindex = 0;
1691
1692 if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
1693 modeindex += 1;
1694 }
1695 if (hisopts[TELOPT_ECHO]) {
1696 modeindex += 2;
1697 }
1698 if (hisopts[TELOPT_SGA]) {
1699 modeindex += 4;
1700 }
1701 if (In3270) {
1702 modeindex += 8;
1703 }
1704 return newmode[modeindex];
1705 }
1706
1707 void
setconnmode()1708 setconnmode()
1709 {
1710 TerminalNewMode(getconnmode());
1711 }
1712
1713
1714 void
setcommandmode()1715 setcommandmode()
1716 {
1717 TerminalNewMode(0);
1718 }
1719
1720 static void
willoption(option,reply)1721 willoption(option, reply)
1722 int option, reply;
1723 {
1724 char *fmt;
1725
1726 switch (option) {
1727
1728 case TELOPT_ECHO:
1729 # if defined(TN3270)
1730 /*
1731 * The following is a pain in the rear-end.
1732 * Various IBM servers (some versions of Wiscnet,
1733 * possibly Fibronics/Spartacus, and who knows who
1734 * else) will NOT allow us to send "DO SGA" too early
1735 * in the setup proceedings. On the other hand,
1736 * 4.2 servers (telnetd) won't set SGA correctly.
1737 * So, we are stuck. Empirically (but, based on
1738 * a VERY small sample), the IBM servers don't send
1739 * out anything about ECHO, so we postpone our sending
1740 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
1741 * DO send).
1742 */
1743 {
1744 if (askedSGA == 0) {
1745 askedSGA = 1;
1746 if (!hisopts[TELOPT_SGA]) {
1747 willoption(TELOPT_SGA, 0);
1748 }
1749 }
1750 }
1751 /* Fall through */
1752 case TELOPT_EOR:
1753 case TELOPT_BINARY:
1754 #endif /* defined(TN3270) */
1755 case TELOPT_SGA:
1756 settimer(modenegotiated);
1757 hisopts[option] = 1;
1758 fmt = doopt;
1759 setconnmode(); /* possibly set new tty mode */
1760 break;
1761
1762 case TELOPT_TM:
1763 return; /* Never reply to TM will's/wont's */
1764
1765 default:
1766 fmt = dont;
1767 break;
1768 }
1769 sprintf(nfrontp, fmt, option);
1770 nfrontp += sizeof (dont) - 2;
1771 if (reply)
1772 printoption(">SENT", fmt, option, reply);
1773 else
1774 printoption("<SENT", fmt, option, reply);
1775 }
1776
1777 static void
wontoption(option,reply)1778 wontoption(option, reply)
1779 int option, reply;
1780 {
1781 char *fmt;
1782
1783 switch (option) {
1784
1785 case TELOPT_ECHO:
1786 case TELOPT_SGA:
1787 settimer(modenegotiated);
1788 hisopts[option] = 0;
1789 fmt = dont;
1790 setconnmode(); /* Set new tty mode */
1791 break;
1792
1793 case TELOPT_TM:
1794 return; /* Never reply to TM will's/wont's */
1795
1796 default:
1797 fmt = dont;
1798 }
1799 sprintf(nfrontp, fmt, option);
1800 nfrontp += sizeof (doopt) - 2;
1801 if (reply)
1802 printoption(">SENT", fmt, option, reply);
1803 else
1804 printoption("<SENT", fmt, option, reply);
1805 }
1806
1807 static void
dooption(option)1808 dooption(option)
1809 int option;
1810 {
1811 char *fmt;
1812
1813 switch (option) {
1814
1815 case TELOPT_TM:
1816 fmt = will;
1817 break;
1818
1819 # if defined(TN3270)
1820 case TELOPT_EOR:
1821 case TELOPT_BINARY:
1822 # endif /* defined(TN3270) */
1823 case TELOPT_TTYPE: /* terminal type option */
1824 case TELOPT_SGA: /* no big deal */
1825 fmt = will;
1826 myopts[option] = 1;
1827 break;
1828
1829 case TELOPT_ECHO: /* We're never going to echo... */
1830 default:
1831 fmt = wont;
1832 break;
1833 }
1834 sprintf(nfrontp, fmt, option);
1835 nfrontp += sizeof (doopt) - 2;
1836 printoption(">SENT", fmt, option, 0);
1837 }
1838
1839 /*
1840 * suboption()
1841 *
1842 * Look at the sub-option buffer, and try to be helpful to the other
1843 * side.
1844 *
1845 * Currently we recognize:
1846 *
1847 * Terminal type, send request.
1848 */
1849
1850 static void
suboption()1851 suboption()
1852 {
1853 printsub("<", subbuffer, subend-subbuffer+1);
1854 switch (subbuffer[0]&0xff) {
1855 case TELOPT_TTYPE:
1856 if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
1857 ;
1858 } else {
1859 char *name;
1860 char namebuf[41];
1861 extern char *getenv();
1862 int len;
1863
1864 #if defined(TN3270)
1865 /*
1866 * Try to send a 3270 type terminal name. Decide which one based
1867 * on the format of our screen, and (in the future) color
1868 * capaiblities.
1869 */
1870 #if defined(unix)
1871 if (initscr() != ERR) { /* Initialize curses to get line size */
1872 MaxNumberLines = LINES;
1873 MaxNumberColumns = COLS;
1874 }
1875 #else /* defined(unix) */
1876 InitTerminal();
1877 #endif /* defined(unix) */
1878 if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) {
1879 Sent3270TerminalType = 1;
1880 if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) {
1881 MaxNumberLines = 27;
1882 MaxNumberColumns = 132;
1883 sb_terminal[SBTERMMODEL] = '5';
1884 } else if (MaxNumberLines >= 43) {
1885 MaxNumberLines = 43;
1886 MaxNumberColumns = 80;
1887 sb_terminal[SBTERMMODEL] = '4';
1888 } else if (MaxNumberLines >= 32) {
1889 MaxNumberLines = 32;
1890 MaxNumberColumns = 80;
1891 sb_terminal[SBTERMMODEL] = '3';
1892 } else {
1893 MaxNumberLines = 24;
1894 MaxNumberColumns = 80;
1895 sb_terminal[SBTERMMODEL] = '2';
1896 }
1897 NumberLines = 24; /* before we start out... */
1898 NumberColumns = 80;
1899 ScreenSize = NumberLines*NumberColumns;
1900 if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
1901 ExitString(stderr,
1902 "Programming error: MAXSCREENSIZE too small.\n", 1);
1903 /*NOTREACHED*/
1904 }
1905 memcpy(nfrontp, sb_terminal, sizeof sb_terminal);
1906 printsub(">", nfrontp+2, sizeof sb_terminal-2);
1907 nfrontp += sizeof sb_terminal;
1908 return;
1909 }
1910 #endif /* defined(TN3270) */
1911
1912 name = getenv("TERM");
1913 if ((name == 0) || ((len = strlen(name)) > 40)) {
1914 name = "UNKNOWN";
1915 }
1916 if ((len + 4+2) < NETROOM()) {
1917 strcpy(namebuf, name);
1918 upcase(namebuf);
1919 sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
1920 TELQUAL_IS, namebuf, IAC, SE);
1921 printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2);
1922 nfrontp += 4+strlen(namebuf)+2;
1923 } else {
1924 ExitString(stderr, "No room in buffer for terminal type.\n",
1925 1);
1926 /*NOTREACHED*/
1927 }
1928 }
1929
1930 default:
1931 break;
1932 }
1933 }
1934
1935 #if defined(TN3270)
1936 static void
SetIn3270()1937 SetIn3270()
1938 {
1939 if (Sent3270TerminalType && myopts[TELOPT_BINARY]
1940 && hisopts[TELOPT_BINARY] && !donebinarytoggle) {
1941 if (!In3270) {
1942 In3270 = 1;
1943 Init3270(); /* Initialize 3270 functions */
1944 /* initialize terminal key mapping */
1945 InitTerminal(); /* Start terminal going */
1946 setconnmode();
1947 }
1948 } else {
1949 if (In3270) {
1950 StopScreen(1);
1951 In3270 = 0;
1952 Stop3270(); /* Tell 3270 we aren't here anymore */
1953 setconnmode();
1954 }
1955 }
1956 }
1957 #endif /* defined(TN3270) */
1958
1959
1960 static void
telrcv()1961 telrcv()
1962 {
1963 register int c;
1964 static int telrcv_state = TS_DATA;
1965 # if defined(TN3270)
1966 register int Scc;
1967 register char *Sbp;
1968 # endif /* defined(TN3270) */
1969
1970 while ((scc > 0) && (TTYROOM() > 2)) {
1971 c = *sbp++ & 0xff, scc--;
1972 switch (telrcv_state) {
1973
1974 case TS_CR:
1975 telrcv_state = TS_DATA;
1976 if (c == '\0') {
1977 break; /* Ignore \0 after CR */
1978 } else if (c == '\n') {
1979 if (hisopts[TELOPT_ECHO] && !crmod) {
1980 TTYADD(c);
1981 }
1982 break;
1983 }
1984 /* Else, fall through */
1985
1986 case TS_DATA:
1987 if (c == IAC) {
1988 telrcv_state = TS_IAC;
1989 continue;
1990 }
1991 # if defined(TN3270)
1992 if (In3270) {
1993 *Ifrontp++ = c;
1994 Sbp = sbp;
1995 Scc = scc;
1996 while (Scc > 0) {
1997 c = *Sbp++ & 0377, Scc--;
1998 if (c == IAC) {
1999 telrcv_state = TS_IAC;
2000 break;
2001 }
2002 *Ifrontp++ = c;
2003 }
2004 sbp = Sbp;
2005 scc = Scc;
2006 } else
2007 # endif /* defined(TN3270) */
2008 /*
2009 * The 'crmod' hack (see following) is needed
2010 * since we can't * set CRMOD on output only.
2011 * Machines like MULTICS like to send \r without
2012 * \n; since we must turn off CRMOD to get proper
2013 * input, the mapping is done here (sigh).
2014 */
2015 if (c == '\r') {
2016 if (scc > 0) {
2017 c = *sbp&0xff;
2018 if (c == 0) {
2019 sbp++, scc--;
2020 /* a "true" CR */
2021 TTYADD('\r');
2022 } else if (!hisopts[TELOPT_ECHO] &&
2023 (c == '\n')) {
2024 sbp++, scc--;
2025 TTYADD('\n');
2026 } else {
2027 TTYADD('\r');
2028 if (crmod) {
2029 TTYADD('\n');
2030 }
2031 }
2032 } else {
2033 telrcv_state = TS_CR;
2034 TTYADD('\r');
2035 if (crmod) {
2036 TTYADD('\n');
2037 }
2038 }
2039 } else {
2040 TTYADD(c);
2041 }
2042 continue;
2043
2044 case TS_IAC:
2045 switch (c) {
2046
2047 case WILL:
2048 telrcv_state = TS_WILL;
2049 continue;
2050
2051 case WONT:
2052 telrcv_state = TS_WONT;
2053 continue;
2054
2055 case DO:
2056 telrcv_state = TS_DO;
2057 continue;
2058
2059 case DONT:
2060 telrcv_state = TS_DONT;
2061 continue;
2062
2063 case DM:
2064 /*
2065 * We may have missed an urgent notification,
2066 * so make sure we flush whatever is in the
2067 * buffer currently.
2068 */
2069 SYNCHing = 1;
2070 ttyflush();
2071 SYNCHing = stilloob(net);
2072 settimer(gotDM);
2073 break;
2074
2075 case NOP:
2076 case GA:
2077 break;
2078
2079 case SB:
2080 SB_CLEAR();
2081 telrcv_state = TS_SB;
2082 continue;
2083
2084 # if defined(TN3270)
2085 case EOR:
2086 if (In3270) {
2087 Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
2088 if (Ibackp == Ifrontp) {
2089 Ibackp = Ifrontp = Ibuf;
2090 ISend = 0; /* should have been! */
2091 } else {
2092 ISend = 1;
2093 }
2094 }
2095 break;
2096 # endif /* defined(TN3270) */
2097
2098 case IAC:
2099 # if !defined(TN3270)
2100 TTYADD(IAC);
2101 # else /* !defined(TN3270) */
2102 if (In3270) {
2103 *Ifrontp++ = IAC;
2104 } else {
2105 TTYADD(IAC);
2106 }
2107 # endif /* !defined(TN3270) */
2108 break;
2109
2110 default:
2111 break;
2112 }
2113 telrcv_state = TS_DATA;
2114 continue;
2115
2116 case TS_WILL:
2117 printoption(">RCVD", will, c, !hisopts[c]);
2118 if (c == TELOPT_TM) {
2119 if (flushout) {
2120 flushout = 0;
2121 }
2122 } else if (!hisopts[c]) {
2123 willoption(c, 1);
2124 }
2125 SetIn3270();
2126 telrcv_state = TS_DATA;
2127 continue;
2128
2129 case TS_WONT:
2130 printoption(">RCVD", wont, c, hisopts[c]);
2131 if (c == TELOPT_TM) {
2132 if (flushout) {
2133 flushout = 0;
2134 }
2135 } else if (hisopts[c]) {
2136 wontoption(c, 1);
2137 }
2138 SetIn3270();
2139 telrcv_state = TS_DATA;
2140 continue;
2141
2142 case TS_DO:
2143 printoption(">RCVD", doopt, c, !myopts[c]);
2144 if (!myopts[c])
2145 dooption(c);
2146 SetIn3270();
2147 telrcv_state = TS_DATA;
2148 continue;
2149
2150 case TS_DONT:
2151 printoption(">RCVD", dont, c, myopts[c]);
2152 if (myopts[c]) {
2153 myopts[c] = 0;
2154 sprintf(nfrontp, wont, c);
2155 nfrontp += sizeof (wont) - 2;
2156 flushline = 1;
2157 setconnmode(); /* set new tty mode (maybe) */
2158 printoption(">SENT", wont, c, 0);
2159 }
2160 SetIn3270();
2161 telrcv_state = TS_DATA;
2162 continue;
2163
2164 case TS_SB:
2165 if (c == IAC) {
2166 telrcv_state = TS_SE;
2167 } else {
2168 SB_ACCUM(c);
2169 }
2170 continue;
2171
2172 case TS_SE:
2173 if (c != SE) {
2174 if (c != IAC) {
2175 SB_ACCUM(IAC);
2176 }
2177 SB_ACCUM(c);
2178 telrcv_state = TS_SB;
2179 } else {
2180 SB_TERM();
2181 suboption(); /* handle sub-option */
2182 SetIn3270();
2183 telrcv_state = TS_DATA;
2184 }
2185 }
2186 }
2187 }
2188
2189 #if defined(TN3270)
2190
2191 /*
2192 * The following routines are places where the various tn3270
2193 * routines make calls into telnet.c.
2194 */
2195
2196 /* TtyChars() - returns the number of characters in the TTY buffer */
TtyChars()2197 TtyChars()
2198 {
2199 return(tfrontp-tbackp);
2200 }
2201
2202 /*
2203 * DataToNetwork - queue up some data to go to network. If "done" is set,
2204 * then when last byte is queued, we add on an IAC EOR sequence (so,
2205 * don't call us with "done" until you want that done...)
2206 *
2207 * We actually do send all the data to the network buffer, since our
2208 * only client needs for us to do that.
2209 */
2210
2211 int
DataToNetwork(buffer,count,done)2212 DataToNetwork(buffer, count, done)
2213 register char *buffer; /* where the data is */
2214 register int count; /* how much to send */
2215 int done; /* is this the last of a logical block */
2216 {
2217 register int c;
2218 int origCount;
2219 fd_set o;
2220
2221 origCount = count;
2222 FD_ZERO(&o);
2223
2224 while (count) {
2225 if ((netobuf+sizeof netobuf - nfrontp) < 6) {
2226 netflush();
2227 while ((netobuf+sizeof netobuf - nfrontp) < 6) {
2228 FD_SET(net, &o);
2229 (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0,
2230 (struct timeval *) 0);
2231 netflush();
2232 }
2233 }
2234 c = *buffer++;
2235 count--;
2236 if (c == IAC) {
2237 *nfrontp++ = IAC;
2238 *nfrontp++ = IAC;
2239 } else {
2240 *nfrontp++ = c;
2241 }
2242 }
2243
2244 if (done && !count) {
2245 *nfrontp++ = IAC;
2246 *nfrontp++ = EOR;
2247 netflush(); /* try to move along as quickly as ... */
2248 }
2249 return(origCount - count);
2250 }
2251
2252 /* DataToTerminal - queue up some data to go to terminal. */
2253
2254 int
DataToTerminal(buffer,count)2255 DataToTerminal(buffer, count)
2256 register char *buffer; /* where the data is */
2257 register int count; /* how much to send */
2258 {
2259 int origCount;
2260 #if defined(unix)
2261 fd_set o;
2262
2263 FD_ZERO(&o);
2264 #endif /* defined(unix) */
2265 origCount = count;
2266
2267 while (count) {
2268 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
2269 ttyflush();
2270 while (tfrontp >= ttyobuf+sizeof ttyobuf) {
2271 #if defined(unix)
2272 FD_SET(tout, &o);
2273 (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
2274 (struct timeval *) 0);
2275 #endif /* defined(unix) */
2276 ttyflush();
2277 }
2278 }
2279 *tfrontp++ = *buffer++;
2280 count--;
2281 }
2282 return(origCount - count);
2283 }
2284
2285 /* EmptyTerminal - called to make sure that the terminal buffer is empty.
2286 * Note that we consider the buffer to run all the
2287 * way to the kernel (thus the select).
2288 */
2289
2290 void
EmptyTerminal()2291 EmptyTerminal()
2292 {
2293 #if defined(unix)
2294 fd_set o;
2295
2296 FD_ZERO(&o);
2297 #endif /* defined(unix) */
2298
2299 if (tfrontp == tbackp) {
2300 #if defined(unix)
2301 FD_SET(tout, &o);
2302 (void) select(tout+1, (int *) 0, &o, (int *) 0,
2303 (struct timeval *) 0); /* wait for TTLOWAT */
2304 #endif /* defined(unix) */
2305 } else {
2306 while (tfrontp != tbackp) {
2307 ttyflush();
2308 #if defined(unix)
2309 FD_SET(tout, &o);
2310 (void) select(tout+1, (int *) 0, &o, (int *) 0,
2311 (struct timeval *) 0); /* wait for TTLOWAT */
2312 #endif /* defined(unix) */
2313 }
2314 }
2315 }
2316
2317 /*
2318 * Push3270 - Try to send data along the 3270 output (to screen) direction.
2319 */
2320
2321 static int
Push3270()2322 Push3270()
2323 {
2324 int save = scc;
2325
2326 if (scc) {
2327 if (Ifrontp+scc > Ibuf+sizeof Ibuf) {
2328 if (Ibackp != Ibuf) {
2329 memcpy(Ibuf, Ibackp, Ifrontp-Ibackp);
2330 Ifrontp -= (Ibackp-Ibuf);
2331 Ibackp = Ibuf;
2332 }
2333 }
2334 if (Ifrontp+scc < Ibuf+sizeof Ibuf) {
2335 telrcv();
2336 }
2337 }
2338 return save != scc;
2339 }
2340
2341
2342 /*
2343 * Finish3270 - get the last dregs of 3270 data out to the terminal
2344 * before quitting.
2345 */
2346
2347 static void
Finish3270()2348 Finish3270()
2349 {
2350 while (Push3270() || !DoTerminalOutput()) {
2351 #if defined(unix)
2352 HaveInput = 0;
2353 #endif /* defined(unix) */
2354 ;
2355 }
2356 }
2357
2358
2359
2360 /* StringToTerminal - output a null terminated string to the terminal */
2361
2362 void
StringToTerminal(s)2363 StringToTerminal(s)
2364 char *s;
2365 {
2366 int count;
2367
2368 count = strlen(s);
2369 if (count) {
2370 (void) DataToTerminal(s, count); /* we know it always goes... */
2371 }
2372 }
2373
2374
2375 #if defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR))
2376 /* _putchar - output a single character to the terminal. This name is so that
2377 * curses(3x) can call us to send out data.
2378 */
2379
2380 void
_putchar(c)2381 _putchar(c)
2382 char c;
2383 {
2384 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
2385 (void) DataToTerminal(&c, 1);
2386 } else {
2387 *tfrontp++ = c; /* optimize if possible. */
2388 }
2389 }
2390 #endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */
2391
2392 static void
SetForExit()2393 SetForExit()
2394 {
2395 setconnmode();
2396 if (In3270) {
2397 Finish3270();
2398 }
2399 setcommandmode();
2400 fflush(stdout);
2401 fflush(stderr);
2402 if (In3270) {
2403 StopScreen(1);
2404 }
2405 setconnmode();
2406 setcommandmode();
2407 }
2408
2409 static void
Exit(returnCode)2410 Exit(returnCode)
2411 int returnCode;
2412 {
2413 SetForExit();
2414 exit(returnCode);
2415 }
2416
2417 void
ExitString(file,string,returnCode)2418 ExitString(file, string, returnCode)
2419 FILE *file;
2420 char *string;
2421 int returnCode;
2422 {
2423 SetForExit();
2424 fwrite(string, 1, strlen(string), file);
2425 exit(returnCode);
2426 }
2427
2428 void
ExitPerror(string,returnCode)2429 ExitPerror(string, returnCode)
2430 char *string;
2431 int returnCode;
2432 {
2433 SetForExit();
2434 perror(string);
2435 exit(returnCode);
2436 }
2437
2438 #endif /* defined(TN3270) */
2439
2440 /*
2441 * Scheduler()
2442 *
2443 * Try to do something.
2444 *
2445 * If we do something useful, return 1; else return 0.
2446 *
2447 */
2448
2449
2450 int
Scheduler(block)2451 Scheduler(block)
2452 int block; /* should we block in the select ? */
2453 {
2454 register int c;
2455 /* One wants to be a bit careful about setting returnValue
2456 * to one, since a one implies we did some useful work,
2457 * and therefore probably won't be called to block next
2458 * time (TN3270 mode only).
2459 */
2460 int returnValue = 0;
2461 static struct timeval TimeValue = { 0 };
2462
2463 if (scc < 0 && tcc < 0) {
2464 return -1;
2465 }
2466
2467 if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) {
2468 FD_SET(net, &obits);
2469 }
2470 #if !defined(MSDOS)
2471 if (TTYBYTES()) {
2472 FD_SET(tout, &obits);
2473 }
2474 if ((tcc == 0) && NETROOM() && (shell_active == 0)) {
2475 FD_SET(tin, &ibits);
2476 }
2477 #endif /* !defined(MSDOS) */
2478 # if !defined(TN3270)
2479 if (TTYROOM()) {
2480 FD_SET(net, &ibits);
2481 }
2482 # else /* !defined(TN3270) */
2483 if (!ISend && TTYROOM()) {
2484 FD_SET(net, &ibits);
2485 }
2486 # endif /* !defined(TN3270) */
2487 if (!SYNCHing) {
2488 FD_SET(net, &xbits);
2489 }
2490 # if defined(TN3270) && defined(unix)
2491 if (HaveInput) {
2492 HaveInput = 0;
2493 signal(SIGIO, inputAvailable);
2494 }
2495 #endif /* defined(TN3270) && defined(unix) */
2496 if ((c = select(16, &ibits, &obits, &xbits,
2497 block? (struct timeval *)0 : &TimeValue)) < 0) {
2498 if (c == -1) {
2499 /*
2500 * we can get EINTR if we are in line mode,
2501 * and the user does an escape (TSTP), or
2502 * some other signal generator.
2503 */
2504 if (errno == EINTR) {
2505 return 0;
2506 }
2507 # if defined(TN3270)
2508 /*
2509 * we can get EBADF if we were in transparent
2510 * mode, and the transcom process died.
2511 */
2512 if (errno == EBADF) {
2513 /*
2514 * zero the bits (even though kernel does it)
2515 * to make sure we are selecting on the right
2516 * ones.
2517 */
2518 FD_ZERO(&ibits);
2519 FD_ZERO(&obits);
2520 FD_ZERO(&xbits);
2521 return 0;
2522 }
2523 # endif /* defined(TN3270) */
2524 /* I don't like this, does it ever happen? */
2525 printf("sleep(5) from telnet, after select\r\n");
2526 #if defined(unix)
2527 sleep(5);
2528 #endif /* defined(unix) */
2529 }
2530 return 0;
2531 }
2532
2533 /*
2534 * Any urgent data?
2535 */
2536 if (FD_ISSET(net, &xbits)) {
2537 FD_CLR(net, &xbits);
2538 SYNCHing = 1;
2539 ttyflush(); /* flush already enqueued data */
2540 }
2541
2542 /*
2543 * Something to read from the network...
2544 */
2545 if (FD_ISSET(net, &ibits)) {
2546 int canread;
2547
2548 FD_CLR(net, &ibits);
2549 if (scc == 0) {
2550 sbp = sibuf;
2551 }
2552 canread = sibuf + sizeof sibuf - (sbp+scc);
2553 #if !defined(SO_OOBINLINE)
2554 /*
2555 * In 4.2 (and some early 4.3) systems, the
2556 * OOB indication and data handling in the kernel
2557 * is such that if two separate TCP Urgent requests
2558 * come in, one byte of TCP data will be overlaid.
2559 * This is fatal for Telnet, but we try to live
2560 * with it.
2561 *
2562 * In addition, in 4.2 (and...), a special protocol
2563 * is needed to pick up the TCP Urgent data in
2564 * the correct sequence.
2565 *
2566 * What we do is: if we think we are in urgent
2567 * mode, we look to see if we are "at the mark".
2568 * If we are, we do an OOB receive. If we run
2569 * this twice, we will do the OOB receive twice,
2570 * but the second will fail, since the second
2571 * time we were "at the mark", but there wasn't
2572 * any data there (the kernel doesn't reset
2573 * "at the mark" until we do a normal read).
2574 * Once we've read the OOB data, we go ahead
2575 * and do normal reads.
2576 *
2577 * There is also another problem, which is that
2578 * since the OOB byte we read doesn't put us
2579 * out of OOB state, and since that byte is most
2580 * likely the TELNET DM (data mark), we would
2581 * stay in the TELNET SYNCH (SYNCHing) state.
2582 * So, clocks to the rescue. If we've "just"
2583 * received a DM, then we test for the
2584 * presence of OOB data when the receive OOB
2585 * fails (and AFTER we did the normal mode read
2586 * to clear "at the mark").
2587 */
2588 if (SYNCHing) {
2589 int atmark;
2590
2591 ioctl(net, SIOCATMARK, (char *)&atmark);
2592 if (atmark) {
2593 c = recv(net, sbp+scc, canread, MSG_OOB);
2594 if ((c == -1) && (errno == EINVAL)) {
2595 c = recv(net, sbp+scc, canread, 0);
2596 if (clocks.didnetreceive < clocks.gotDM) {
2597 SYNCHing = stilloob(net);
2598 }
2599 }
2600 } else {
2601 c = recv(net, sbp+scc, canread, 0);
2602 }
2603 } else {
2604 c = recv(net, sbp+scc, canread, 0);
2605 }
2606 settimer(didnetreceive);
2607 #else /* !defined(SO_OOBINLINE) */
2608 c = recv(net, sbp+scc, canread, 0);
2609 #endif /* !defined(SO_OOBINLINE) */
2610 if (c < 0 && errno == EWOULDBLOCK) {
2611 c = 0;
2612 } else if (c <= 0) {
2613 return -1;
2614 }
2615 if (netdata) {
2616 Dump('<', sbp+scc, c);
2617 }
2618 scc += c;
2619 returnValue = 1;
2620 }
2621
2622 /*
2623 * Something to read from the tty...
2624 */
2625 #if defined(MSDOS)
2626 if ((tcc == 0) && NETROOM() && (shell_active == 0) && TerminalCanRead())
2627 #else /* defined(MSDOS) */
2628 if (FD_ISSET(tin, &ibits))
2629 #endif /* defined(MSDOS) */
2630 {
2631 FD_CLR(tin, &ibits);
2632 if (tcc == 0) {
2633 tbp = tibuf; /* nothing left, reset */
2634 }
2635 c = TerminalRead(tin, tbp, tibuf+sizeof tibuf - tbp);
2636 if (c < 0 && errno == EWOULDBLOCK) {
2637 c = 0;
2638 } else {
2639 #if defined(unix)
2640 /* EOF detection for line mode!!!! */
2641 if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
2642 /* must be an EOF... */
2643 *tbp = ntc.t_eofc;
2644 c = 1;
2645 }
2646 #endif /* defined(unix) */
2647 if (c <= 0) {
2648 tcc = c;
2649 return -1;
2650 }
2651 }
2652 tcc += c;
2653 returnValue = 1; /* did something useful */
2654 }
2655
2656 # if defined(TN3270)
2657 if (tcc > 0) {
2658 if (In3270) {
2659 c = DataFromTerminal(tbp, tcc);
2660 if (c) {
2661 returnValue = 1;
2662 }
2663 tcc -= c;
2664 tbp += c;
2665 } else {
2666 # endif /* defined(TN3270) */
2667 returnValue = 1;
2668 while (tcc > 0) {
2669 register int sc;
2670
2671 if (NETROOM() < 2) {
2672 flushline = 1;
2673 break;
2674 }
2675 c = *tbp++ & 0xff, sc = strip(c), tcc--;
2676 if (sc == escape) {
2677 command(0);
2678 tcc = 0;
2679 flushline = 1;
2680 break;
2681 } else if (MODE_LINE(globalmode) && (sc == echoc)) {
2682 if (tcc > 0 && strip(*tbp) == echoc) {
2683 tbp++;
2684 tcc--;
2685 } else {
2686 dontlecho = !dontlecho;
2687 settimer(echotoggle);
2688 setconnmode();
2689 tcc = 0;
2690 flushline = 1;
2691 break;
2692 }
2693 }
2694 if (localchars) {
2695 if (TerminalSpecialChars(sc) == 0) {
2696 break;
2697 }
2698 }
2699 switch (c) {
2700 case '\n':
2701 /*
2702 * If we are in CRMOD mode (\r ==> \n)
2703 * on our local machine, then probably
2704 * a newline (unix) is CRLF (TELNET).
2705 */
2706 if (MODE_LOCAL_CHARS(globalmode)) {
2707 NETADD('\r');
2708 }
2709 NETADD('\n');
2710 flushline = 1;
2711 break;
2712 case '\r':
2713 NET2ADD('\r', '\0');
2714 flushline = 1;
2715 break;
2716 case IAC:
2717 NET2ADD(IAC, IAC);
2718 break;
2719 default:
2720 NETADD(c);
2721 break;
2722 }
2723 }
2724 # if defined(TN3270)
2725 }
2726 }
2727 # endif /* defined(TN3270) */
2728
2729 if ((!MODE_LINE(globalmode) || flushline) &&
2730 FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
2731 FD_CLR(net, &obits);
2732 returnValue = netflush();
2733 }
2734 if (scc > 0) {
2735 # if !defined(TN3270)
2736 telrcv();
2737 returnValue = 1;
2738 # else /* !defined(TN3270) */
2739 returnValue = Push3270();
2740 # endif /* !defined(TN3270) */
2741 }
2742 #if defined(MSDOS)
2743 if (TTYBYTES())
2744 #else /* defined(MSDOS) */
2745 if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0))
2746 #endif /* defined(MSDOS) */
2747 {
2748 FD_CLR(tout, &obits);
2749 returnValue = ttyflush();
2750 }
2751 return returnValue;
2752 }
2753
2754 /*
2755 * Select from tty and network...
2756 */
2757 static void
telnet()2758 telnet()
2759 {
2760 #if defined(MSDOS)
2761 #define SCHED_BLOCK 0 /* Don't block in MSDOS */
2762 #else /* defined(MSDOS) */
2763 #define SCHED_BLOCK 1
2764 #endif /* defined(MSDOS) */
2765
2766 #if defined(TN3270) && defined(unix)
2767 int myPid;
2768 #endif /* defined(TN3270) */
2769
2770 tout = fileno(stdout);
2771 tin = fileno(stdin);
2772 setconnmode();
2773 scc = 0;
2774 tcc = 0;
2775 FD_ZERO(&ibits);
2776 FD_ZERO(&obits);
2777 FD_ZERO(&xbits);
2778
2779 NetNonblockingIO(net, 1);
2780
2781 #if defined(TN3270)
2782 if (noasynch == 0) { /* DBX can't handle! */
2783 NetSigIO(net, 1);
2784 }
2785 NetSetPgrp(net);
2786 #endif /* defined(TN3270) */
2787
2788
2789 #if defined(SO_OOBINLINE) && !defined(MSDOS)
2790 SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1);
2791 #endif /* defined(SO_OOBINLINE) && !defined(MSDOS) */
2792
2793 # if !defined(TN3270)
2794 if (telnetport) {
2795 if (!hisopts[TELOPT_SGA]) {
2796 willoption(TELOPT_SGA, 0);
2797 }
2798 if (!myopts[TELOPT_TTYPE]) {
2799 dooption(TELOPT_TTYPE, 0);
2800 }
2801 }
2802 # endif /* !defined(TN3270) */
2803
2804 # if !defined(TN3270)
2805 for (;;) {
2806 if (Scheduler(SCHED_BLOCK) == -1) {
2807 setcommandmode();
2808 return;
2809 }
2810 }
2811 # else /* !defined(TN3270) */
2812 for (;;) {
2813 int schedValue;
2814
2815 while (!In3270 && !shell_active) {
2816 if (Scheduler(SCHED_BLOCK) == -1) {
2817 setcommandmode();
2818 return;
2819 }
2820 }
2821
2822 while ((schedValue = Scheduler(0)) != 0) {
2823 if (schedValue == -1) {
2824 setcommandmode();
2825 return;
2826 }
2827 }
2828 /* If there is data waiting to go out to terminal, don't
2829 * schedule any more data for the terminal.
2830 */
2831 if (tfrontp-tbackp) {
2832 schedValue = 1;
2833 } else {
2834 if (shell_active) {
2835 if (shell_continue() == 0) {
2836 ConnectScreen();
2837 }
2838 } else if (In3270) {
2839 schedValue = DoTerminalOutput();
2840 }
2841 }
2842 if (schedValue && (shell_active == 0)) {
2843 if (Scheduler(SCHED_BLOCK) == -1) {
2844 setcommandmode();
2845 return;
2846 }
2847 }
2848 }
2849 # endif /* !defined(TN3270) */
2850 }
2851
2852 /*
2853 * The following are data structures and routines for
2854 * the "send" command.
2855 *
2856 */
2857
2858 struct sendlist {
2859 char *name; /* How user refers to it (case independent) */
2860 int what; /* Character to be sent (<0 ==> special) */
2861 char *help; /* Help information (0 ==> no help) */
2862 #if defined(NOT43)
2863 int (*routine)(); /* Routine to perform (for special ops) */
2864 #else /* defined(NOT43) */
2865 void (*routine)(); /* Routine to perform (for special ops) */
2866 #endif /* defined(NOT43) */
2867 };
2868
2869 #define SENDQUESTION -1
2870 #define SENDESCAPE -3
2871
2872 static struct sendlist Sendlist[] = {
2873 { "ao", AO, "Send Telnet Abort output" },
2874 { "ayt", AYT, "Send Telnet 'Are You There'" },
2875 { "brk", BREAK, "Send Telnet Break" },
2876 { "ec", EC, "Send Telnet Erase Character" },
2877 { "el", EL, "Send Telnet Erase Line" },
2878 { "escape", SENDESCAPE, "Send current escape character" },
2879 { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
2880 { "ip", IP, "Send Telnet Interrupt Process" },
2881 { "nop", NOP, "Send Telnet 'No operation'" },
2882 { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
2883 { "?", SENDQUESTION, "Display send options" },
2884 { 0 }
2885 };
2886
2887 static struct sendlist Sendlist2[] = { /* some synonyms */
2888 { "break", BREAK, 0 },
2889
2890 { "intp", IP, 0 },
2891 { "interrupt", IP, 0 },
2892 { "intr", IP, 0 },
2893
2894 { "help", SENDQUESTION, 0 },
2895
2896 { 0 }
2897 };
2898
2899 static char **
getnextsend(name)2900 getnextsend(name)
2901 char *name;
2902 {
2903 struct sendlist *c = (struct sendlist *) name;
2904
2905 return (char **) (c+1);
2906 }
2907
2908 static struct sendlist *
getsend(name)2909 getsend(name)
2910 char *name;
2911 {
2912 struct sendlist *sl;
2913
2914 if ((sl = (struct sendlist *)
2915 genget(name, (char **) Sendlist, getnextsend)) != 0) {
2916 return sl;
2917 } else {
2918 return (struct sendlist *)
2919 genget(name, (char **) Sendlist2, getnextsend);
2920 }
2921 }
2922
2923 static
sendcmd(argc,argv)2924 sendcmd(argc, argv)
2925 int argc;
2926 char **argv;
2927 {
2928 int what; /* what we are sending this time */
2929 int count; /* how many bytes we are going to need to send */
2930 int i;
2931 int question = 0; /* was at least one argument a question */
2932 struct sendlist *s; /* pointer to current command */
2933
2934 if (argc < 2) {
2935 printf("need at least one argument for 'send' command\n");
2936 printf("'send ?' for help\n");
2937 return 0;
2938 }
2939 /*
2940 * First, validate all the send arguments.
2941 * In addition, we see how much space we are going to need, and
2942 * whether or not we will be doing a "SYNCH" operation (which
2943 * flushes the network queue).
2944 */
2945 count = 0;
2946 for (i = 1; i < argc; i++) {
2947 s = getsend(argv[i]);
2948 if (s == 0) {
2949 printf("Unknown send argument '%s'\n'send ?' for help.\n",
2950 argv[i]);
2951 return 0;
2952 } else if (s == Ambiguous(struct sendlist *)) {
2953 printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
2954 argv[i]);
2955 return 0;
2956 }
2957 switch (s->what) {
2958 case SENDQUESTION:
2959 break;
2960 case SENDESCAPE:
2961 count += 1;
2962 break;
2963 case SYNCH:
2964 count += 2;
2965 break;
2966 default:
2967 count += 2;
2968 break;
2969 }
2970 }
2971 /* Now, do we have enough room? */
2972 if (NETROOM() < count) {
2973 printf("There is not enough room in the buffer TO the network\n");
2974 printf("to process your request. Nothing will be done.\n");
2975 printf("('send synch' will throw away most data in the network\n");
2976 printf("buffer, if this might help.)\n");
2977 return 0;
2978 }
2979 /* OK, they are all OK, now go through again and actually send */
2980 for (i = 1; i < argc; i++) {
2981 if ((s = getsend(argv[i])) == 0) {
2982 fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
2983 quit();
2984 /*NOTREACHED*/
2985 }
2986 if (s->routine) {
2987 (*s->routine)(s);
2988 } else {
2989 switch (what = s->what) {
2990 case SYNCH:
2991 dosynch();
2992 break;
2993 case SENDQUESTION:
2994 for (s = Sendlist; s->name; s++) {
2995 if (s->help) {
2996 printf(s->name);
2997 if (s->help) {
2998 printf("\t%s", s->help);
2999 }
3000 printf("\n");
3001 }
3002 }
3003 question = 1;
3004 break;
3005 case SENDESCAPE:
3006 NETADD(escape);
3007 break;
3008 default:
3009 NET2ADD(IAC, what);
3010 break;
3011 }
3012 }
3013 }
3014 return !question;
3015 }
3016
3017 /*
3018 * The following are the routines and data structures referred
3019 * to by the arguments to the "toggle" command.
3020 */
3021
3022 static
lclchars()3023 lclchars()
3024 {
3025 donelclchars = 1;
3026 return 1;
3027 }
3028
3029 static
togdebug()3030 togdebug()
3031 {
3032 #ifndef NOT43
3033 if (net > 0 &&
3034 (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
3035 perror("setsockopt (SO_DEBUG)");
3036 }
3037 #else /* NOT43 */
3038 if (debug) {
3039 if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
3040 perror("setsockopt (SO_DEBUG)");
3041 } else
3042 printf("Cannot turn off socket debugging\n");
3043 #endif /* NOT43 */
3044 return 1;
3045 }
3046
3047
3048 static int
togbinary()3049 togbinary()
3050 {
3051 donebinarytoggle = 1;
3052
3053 if (myopts[TELOPT_BINARY] == 0) { /* Go into binary mode */
3054 NET2ADD(IAC, DO);
3055 NETADD(TELOPT_BINARY);
3056 printoption("<SENT", doopt, TELOPT_BINARY, 0);
3057 NET2ADD(IAC, WILL);
3058 NETADD(TELOPT_BINARY);
3059 printoption("<SENT", doopt, TELOPT_BINARY, 0);
3060 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1;
3061 printf("Negotiating binary mode with remote host.\n");
3062 } else { /* Turn off binary mode */
3063 NET2ADD(IAC, DONT);
3064 NETADD(TELOPT_BINARY);
3065 printoption("<SENT", dont, TELOPT_BINARY, 0);
3066 NET2ADD(IAC, DONT);
3067 NETADD(TELOPT_BINARY);
3068 printoption("<SENT", dont, TELOPT_BINARY, 0);
3069 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0;
3070 printf("Negotiating network ascii mode with remote host.\n");
3071 }
3072 return 1;
3073 }
3074
3075
3076
3077 extern int togglehelp();
3078
3079 struct togglelist {
3080 char *name; /* name of toggle */
3081 char *help; /* help message */
3082 int (*handler)(); /* routine to do actual setting */
3083 int dohelp; /* should we display help information */
3084 int *variable;
3085 char *actionexplanation;
3086 };
3087
3088 static struct togglelist Togglelist[] = {
3089 { "autoflush",
3090 "toggle flushing of output when sending interrupt characters",
3091 0,
3092 1,
3093 &autoflush,
3094 "flush output when sending interrupt characters" },
3095 { "autosynch",
3096 "toggle automatic sending of interrupt characters in urgent mode",
3097 0,
3098 1,
3099 &autosynch,
3100 "send interrupt characters in urgent mode" },
3101 { "binary",
3102 "toggle sending and receiving of binary data",
3103 togbinary,
3104 1,
3105 0,
3106 "send and receive network data in binary mode" },
3107 { "crmod",
3108 "toggle mapping of received carriage returns",
3109 0,
3110 1,
3111 &crmod,
3112 "map carriage return on output" },
3113 { "localchars",
3114 "toggle local recognition of certain control characters",
3115 lclchars,
3116 1,
3117 &localchars,
3118 "recognize certain control characters" },
3119 { " ", "", 0, 1 }, /* empty line */
3120 { "debug",
3121 "(debugging) toggle debugging",
3122 togdebug,
3123 1,
3124 &debug,
3125 "turn on socket level debugging" },
3126 { "netdata",
3127 "(debugging) toggle printing of hexadecimal network data",
3128 0,
3129 1,
3130 &netdata,
3131 "print hexadecimal representation of network traffic" },
3132 { "options",
3133 "(debugging) toggle viewing of options processing",
3134 0,
3135 1,
3136 &showoptions,
3137 "show option processing" },
3138 { " ", "", 0, 1 }, /* empty line */
3139 { "?",
3140 "display help information",
3141 togglehelp,
3142 1 },
3143 { "help",
3144 "display help information",
3145 togglehelp,
3146 0 },
3147 { 0 }
3148 };
3149
3150 static
togglehelp()3151 togglehelp()
3152 {
3153 struct togglelist *c;
3154
3155 for (c = Togglelist; c->name; c++) {
3156 if (c->dohelp) {
3157 printf("%s\t%s\n", c->name, c->help);
3158 }
3159 }
3160 return 0;
3161 }
3162
3163 static char **
getnexttoggle(name)3164 getnexttoggle(name)
3165 char *name;
3166 {
3167 struct togglelist *c = (struct togglelist *) name;
3168
3169 return (char **) (c+1);
3170 }
3171
3172 static struct togglelist *
gettoggle(name)3173 gettoggle(name)
3174 char *name;
3175 {
3176 return (struct togglelist *)
3177 genget(name, (char **) Togglelist, getnexttoggle);
3178 }
3179
3180 static
toggle(argc,argv)3181 toggle(argc, argv)
3182 int argc;
3183 char *argv[];
3184 {
3185 int retval = 1;
3186 char *name;
3187 struct togglelist *c;
3188
3189 if (argc < 2) {
3190 fprintf(stderr,
3191 "Need an argument to 'toggle' command. 'toggle ?' for help.\n");
3192 return 0;
3193 }
3194 argc--;
3195 argv++;
3196 while (argc--) {
3197 name = *argv++;
3198 c = gettoggle(name);
3199 if (c == Ambiguous(struct togglelist *)) {
3200 fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
3201 name);
3202 return 0;
3203 } else if (c == 0) {
3204 fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
3205 name);
3206 return 0;
3207 } else {
3208 if (c->variable) {
3209 *c->variable = !*c->variable; /* invert it */
3210 printf("%s %s.\n", *c->variable? "Will" : "Won't",
3211 c->actionexplanation);
3212 }
3213 if (c->handler) {
3214 retval &= (*c->handler)(c);
3215 }
3216 }
3217 }
3218 return retval;
3219 }
3220
3221 /*
3222 * The following perform the "set" command.
3223 */
3224
3225 struct setlist {
3226 char *name; /* name */
3227 char *help; /* help information */
3228 char *charp; /* where it is located at */
3229 };
3230
3231 static struct setlist Setlist[] = {
3232 { "echo", "character to toggle local echoing on/off", &echoc },
3233 { "escape", "character to escape back to telnet command mode", &escape },
3234 { " ", "" },
3235 { " ", "The following need 'localchars' to be toggled true", 0 },
3236 #if defined(unix)
3237 { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
3238 { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
3239 { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
3240 { "kill", "character to cause an Erase Line", &nttyb.sg_kill },
3241 { "quit", "character to cause a Break", &ntc.t_quitc },
3242 { "eof", "character to cause an EOF ", &ntc.t_eofc },
3243 #endif /* defined(unix) */
3244 #if defined(MSDOS)
3245 { "erase", "character to cause an Erase Character", &termEraseChar },
3246 { "flushoutput", "character to cause an Abort Oubput", &termFlushChar },
3247 { "interrupt", "character to cause an Interrupt Process", &termIntChar },
3248 { "kill", "character to cause an Erase Line", &termKillChar },
3249 { "quit", "character to cause a Break", &termQuitChar },
3250 { "eof", "character to cause an EOF ", &termEofChar },
3251 #endif /* defined(MSDOS) */
3252 { 0 }
3253 };
3254
3255 static char **
getnextset(name)3256 getnextset(name)
3257 char *name;
3258 {
3259 struct setlist *c = (struct setlist *)name;
3260
3261 return (char **) (c+1);
3262 }
3263
3264 static struct setlist *
getset(name)3265 getset(name)
3266 char *name;
3267 {
3268 return (struct setlist *) genget(name, (char **) Setlist, getnextset);
3269 }
3270
3271 static
setcmd(argc,argv)3272 setcmd(argc, argv)
3273 int argc;
3274 char *argv[];
3275 {
3276 int value;
3277 struct setlist *ct;
3278
3279 /* XXX back we go... sigh */
3280 if (argc != 3) {
3281 if ((argc == 2) &&
3282 ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
3283 for (ct = Setlist; ct->name; ct++) {
3284 printf("%s\t%s\n", ct->name, ct->help);
3285 }
3286 printf("?\tdisplay help information\n");
3287 } else {
3288 printf("Format is 'set Name Value'\n'set ?' for help.\n");
3289 }
3290 return 0;
3291 }
3292
3293 ct = getset(argv[1]);
3294 if (ct == 0) {
3295 fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
3296 argv[1]);
3297 return 0;
3298 } else if (ct == Ambiguous(struct setlist *)) {
3299 fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
3300 argv[1]);
3301 return 0;
3302 } else {
3303 if (strcmp("off", argv[2])) {
3304 value = special(argv[2]);
3305 } else {
3306 value = -1;
3307 }
3308 *(ct->charp) = value;
3309 printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
3310 }
3311 return 1;
3312 }
3313
3314 /*
3315 * The following are the data structures and routines for the
3316 * 'mode' command.
3317 */
3318
3319 static
dolinemode()3320 dolinemode()
3321 {
3322 if (hisopts[TELOPT_SGA]) {
3323 wontoption(TELOPT_SGA, 0);
3324 }
3325 if (hisopts[TELOPT_ECHO]) {
3326 wontoption(TELOPT_ECHO, 0);
3327 }
3328 return 1;
3329 }
3330
3331 static
docharmode()3332 docharmode()
3333 {
3334 if (!hisopts[TELOPT_SGA]) {
3335 willoption(TELOPT_SGA, 0);
3336 }
3337 if (!hisopts[TELOPT_ECHO]) {
3338 willoption(TELOPT_ECHO, 0);
3339 }
3340 return 1;
3341 }
3342
3343 static struct cmd Modelist[] = {
3344 { "character", "character-at-a-time mode", docharmode, 1, 1 },
3345 { "line", "line-by-line mode", dolinemode, 1, 1 },
3346 { 0 },
3347 };
3348
3349 static char **
getnextmode(name)3350 getnextmode(name)
3351 char *name;
3352 {
3353 struct cmd *c = (struct cmd *) name;
3354
3355 return (char **) (c+1);
3356 }
3357
3358 static struct cmd *
getmodecmd(name)3359 getmodecmd(name)
3360 char *name;
3361 {
3362 return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
3363 }
3364
3365 static
modecmd(argc,argv)3366 modecmd(argc, argv)
3367 int argc;
3368 char *argv[];
3369 {
3370 struct cmd *mt;
3371
3372 if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
3373 printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
3374 for (mt = Modelist; mt->name; mt++) {
3375 printf("%s\t%s\n", mt->name, mt->help);
3376 }
3377 return 0;
3378 }
3379 mt = getmodecmd(argv[1]);
3380 if (mt == 0) {
3381 fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
3382 return 0;
3383 } else if (mt == Ambiguous(struct cmd *)) {
3384 fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
3385 return 0;
3386 } else {
3387 (*mt->handler)();
3388 }
3389 return 1;
3390 }
3391
3392 /*
3393 * The following data structures and routines implement the
3394 * "display" command.
3395 */
3396
3397 static
display(argc,argv)3398 display(argc, argv)
3399 int argc;
3400 char *argv[];
3401 {
3402 #define dotog(tl) if (tl->variable && tl->actionexplanation) { \
3403 if (*tl->variable) { \
3404 printf("will"); \
3405 } else { \
3406 printf("won't"); \
3407 } \
3408 printf(" %s.\n", tl->actionexplanation); \
3409 }
3410
3411 #define doset(sl) if (sl->name && *sl->name != ' ') { \
3412 printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
3413 }
3414
3415 struct togglelist *tl;
3416 struct setlist *sl;
3417
3418 if (argc == 1) {
3419 for (tl = Togglelist; tl->name; tl++) {
3420 dotog(tl);
3421 }
3422 printf("\n");
3423 for (sl = Setlist; sl->name; sl++) {
3424 doset(sl);
3425 }
3426 } else {
3427 int i;
3428
3429 for (i = 1; i < argc; i++) {
3430 sl = getset(argv[i]);
3431 tl = gettoggle(argv[i]);
3432 if ((sl == Ambiguous(struct setlist *)) ||
3433 (tl == Ambiguous(struct togglelist *))) {
3434 printf("?Ambiguous argument '%s'.\n", argv[i]);
3435 return 0;
3436 } else if (!sl && !tl) {
3437 printf("?Unknown argument '%s'.\n", argv[i]);
3438 return 0;
3439 } else {
3440 if (tl) {
3441 dotog(tl);
3442 }
3443 if (sl) {
3444 doset(sl);
3445 }
3446 }
3447 }
3448 }
3449 return 1;
3450 #undef doset
3451 #undef dotog
3452 }
3453
3454 /*
3455 * The following are the data structures, and many of the routines,
3456 * relating to command processing.
3457 */
3458
3459 /*
3460 * Set the escape character.
3461 */
3462 static
setescape(argc,argv)3463 setescape(argc, argv)
3464 int argc;
3465 char *argv[];
3466 {
3467 register char *arg;
3468 char buf[50];
3469
3470 printf(
3471 "Deprecated usage - please use 'set escape%s%s' in the future.\n",
3472 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
3473 if (argc > 2)
3474 arg = argv[1];
3475 else {
3476 printf("new escape character: ");
3477 gets(buf);
3478 arg = buf;
3479 }
3480 if (arg[0] != '\0')
3481 escape = arg[0];
3482 if (!In3270) {
3483 printf("Escape character is '%s'.\n", control(escape));
3484 }
3485 fflush(stdout);
3486 return 1;
3487 }
3488
3489 /*VARARGS*/
3490 static
togcrmod()3491 togcrmod()
3492 {
3493 crmod = !crmod;
3494 printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
3495 printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
3496 fflush(stdout);
3497 return 1;
3498 }
3499
3500 /*VARARGS*/
suspend()3501 suspend()
3502 {
3503 setcommandmode();
3504 #if defined(unix)
3505 kill(0, SIGTSTP);
3506 #endif /* defined(unix) */
3507 /* reget parameters in case they were changed */
3508 TerminalSaveState();
3509 setconnmode();
3510 return 1;
3511 }
3512
3513 /*VARARGS*/
3514 static
bye(argc,argv)3515 bye(argc, argv)
3516 int argc; /* Number of arguments */
3517 char *argv[]; /* arguments */
3518 {
3519 if (connected) {
3520 shutdown(net, 2);
3521 printf("Connection closed.\n");
3522 NetClose(net);
3523 connected = 0;
3524 /* reset options */
3525 tninit();
3526 #if defined(TN3270)
3527 SetIn3270(); /* Get out of 3270 mode */
3528 #endif /* defined(TN3270) */
3529 }
3530 if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
3531 longjmp(toplevel, 1);
3532 /* NOTREACHED */
3533 }
3534 return 1; /* Keep lint, etc., happy */
3535 }
3536
3537 /*VARARGS*/
quit()3538 quit()
3539 {
3540 (void) call(bye, "bye", "fromquit", 0);
3541 Exit(0);
3542 /*NOTREACHED*/
3543 return 1; /* just to keep lint happy */
3544 }
3545
3546 /*
3547 * Print status about the connection.
3548 */
3549 static
status(argc,argv)3550 status(argc, argv)
3551 int argc;
3552 char *argv[];
3553 {
3554 if (connected) {
3555 printf("Connected to %s.\n", hostname);
3556 if (argc < 2) {
3557 printf("Operating in %s.\n",
3558 modelist[getconnmode()].modedescriptions);
3559 if (localchars) {
3560 printf("Catching signals locally.\n");
3561 }
3562 }
3563 } else {
3564 printf("No connection.\n");
3565 }
3566 # if !defined(TN3270)
3567 printf("Escape character is '%s'.\n", control(escape));
3568 fflush(stdout);
3569 # else /* !defined(TN3270) */
3570 if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
3571 printf("Escape character is '%s'.\n", control(escape));
3572 }
3573 # if defined(unix)
3574 if (In3270 && transcom) {
3575 printf("Transparent mode command is '%s'.\n", transcom);
3576 }
3577 # endif /* defined(unix) */
3578 fflush(stdout);
3579 if (In3270) {
3580 return 0;
3581 }
3582 # endif /* defined(TN3270) */
3583 return 1;
3584 }
3585
3586 #if defined(TN3270) && defined(unix)
3587 static
settranscom(argc,argv)3588 settranscom(argc, argv)
3589 int argc;
3590 char *argv[];
3591 {
3592 int i, len = 0;
3593 char *strcpy(), *strcat();
3594
3595 if (argc == 1 && transcom) {
3596 transcom = 0;
3597 }
3598 if (argc == 1) {
3599 return;
3600 }
3601 for (i = 1; i < argc; ++i) {
3602 len += 1 + strlen(argv[1]);
3603 }
3604 transcom = tline;
3605 (void) strcpy(transcom, argv[1]);
3606 for (i = 2; i < argc; ++i) {
3607 (void) strcat(transcom, " ");
3608 (void) strcat(transcom, argv[i]);
3609 }
3610 }
3611 #endif /* defined(TN3270) && defined(unix) */
3612
3613
3614
3615 static
tn(argc,argv)3616 tn(argc, argv)
3617 int argc;
3618 char *argv[];
3619 {
3620 register struct hostent *host = 0;
3621 #if defined(MSDOS)
3622 char *cp;
3623 #endif /* defined(MSDOS) */
3624
3625 if (connected) {
3626 printf("?Already connected to %s\n", hostname);
3627 return 0;
3628 }
3629 if (argc < 2) {
3630 (void) strcpy(line, "Connect ");
3631 printf("(to) ");
3632 gets(&line[strlen(line)]);
3633 makeargv();
3634 argc = margc;
3635 argv = margv;
3636 }
3637 if ((argc < 2) || (argc > 3)) {
3638 printf("usage: %s host-name [port]\n", argv[0]);
3639 return 0;
3640 }
3641 #if defined(MSDOS)
3642 for (cp = argv[1]; *cp; cp++) {
3643 if (isupper(*cp)) {
3644 *cp = tolower(*cp);
3645 }
3646 }
3647 #endif /* defined(MSDOS) */
3648 sin.sin_addr.s_addr = inet_addr(argv[1]);
3649 if (sin.sin_addr.s_addr != -1) {
3650 sin.sin_family = AF_INET;
3651 (void) strcpy(hnamebuf, argv[1]);
3652 hostname = hnamebuf;
3653 } else {
3654 host = gethostbyname(argv[1]);
3655 if (host) {
3656 sin.sin_family = host->h_addrtype;
3657 #if defined(h_addr) /* In 4.3, this is a #define */
3658 memcpy((caddr_t)&sin.sin_addr,
3659 host->h_addr_list[0], host->h_length);
3660 #else /* defined(h_addr) */
3661 memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
3662 #endif /* defined(h_addr) */
3663 hostname = host->h_name;
3664 } else {
3665 printf("%s: unknown host\n", argv[1]);
3666 return 0;
3667 }
3668 }
3669 sin.sin_port = sp->s_port;
3670 if (argc == 3) {
3671 sin.sin_port = atoi(argv[2]);
3672 if (sin.sin_port == 0) {
3673 sp = getservbyname(argv[2], "tcp");
3674 if (sp)
3675 sin.sin_port = sp->s_port;
3676 else {
3677 printf("%s: bad port number\n", argv[2]);
3678 return 0;
3679 }
3680 } else {
3681 sin.sin_port = atoi(argv[2]);
3682 sin.sin_port = htons(sin.sin_port);
3683 }
3684 telnetport = 0;
3685 } else {
3686 telnetport = 1;
3687 }
3688 #if defined(unix)
3689 signal(SIGINT, intr);
3690 signal(SIGQUIT, intr2);
3691 signal(SIGPIPE, deadpeer);
3692 #endif /* defined(unix) */
3693 printf("Trying...\n");
3694 do {
3695 net = socket(AF_INET, SOCK_STREAM, 0);
3696 if (net < 0) {
3697 perror("telnet: socket");
3698 return 0;
3699 }
3700 if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
3701 perror("setsockopt (SO_DEBUG)");
3702 }
3703
3704 if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
3705 #if defined(h_addr) /* In 4.3, this is a #define */
3706 if (host && host->h_addr_list[1]) {
3707 int oerrno = errno;
3708
3709 fprintf(stderr, "telnet: connect to address %s: ",
3710 inet_ntoa(sin.sin_addr));
3711 errno = oerrno;
3712 perror((char *)0);
3713 host->h_addr_list++;
3714 memcpy((caddr_t)&sin.sin_addr,
3715 host->h_addr_list[0], host->h_length);
3716 fprintf(stderr, "Trying %s...\n",
3717 inet_ntoa(sin.sin_addr));
3718 (void) NetClose(net);
3719 continue;
3720 }
3721 #endif /* defined(h_addr) */
3722 perror("telnet: Unable to connect to remote host");
3723 #if defined(unix)
3724 signal(SIGINT, SIG_DFL);
3725 signal(SIGQUIT, SIG_DFL);
3726 #endif /* defined(unix) */
3727 return 0;
3728 }
3729 connected++;
3730 } while (connected == 0);
3731 call(status, "status", "notmuch", 0);
3732 if (setjmp(peerdied) == 0)
3733 telnet();
3734 NetClose(net);
3735 ExitString(stderr, "Connection closed by foreign host.\n",1);
3736 /*NOTREACHED*/
3737 }
3738
3739
3740 #define HELPINDENT (sizeof ("connect"))
3741
3742 static char
3743 openhelp[] = "connect to a site",
3744 closehelp[] = "close current connection",
3745 quithelp[] = "exit telnet",
3746 statushelp[] = "print status information",
3747 helphelp[] = "print help information",
3748 sendhelp[] = "transmit special characters ('send ?' for more)",
3749 sethelp[] = "set operating parameters ('set ?' for more)",
3750 togglestring[] ="toggle operating parameters ('toggle ?' for more)",
3751 displayhelp[] = "display operating parameters",
3752 #if defined(TN3270) && defined(unix)
3753 transcomhelp[] = "specify Unix command for transparent mode pipe",
3754 #endif /* defined(TN3270) && defined(unix) */
3755 #if defined(unix)
3756 zhelp[] = "suspend telnet",
3757 #endif /* defined(unix */
3758 #if defined(TN3270)
3759 shellhelp[] = "invoke a subshell",
3760 #endif /* defined(TN3270) */
3761 modehelp[] = "try to enter line-by-line or character-at-a-time mode";
3762
3763 extern int help(), shell();
3764
3765 static struct cmd cmdtab[] = {
3766 { "close", closehelp, bye, 1, 1 },
3767 { "display", displayhelp, display, 1, 0 },
3768 { "mode", modehelp, modecmd, 1, 1 },
3769 { "open", openhelp, tn, 1, 0 },
3770 { "quit", quithelp, quit, 1, 0 },
3771 { "send", sendhelp, sendcmd, 1, 1 },
3772 { "set", sethelp, setcmd, 1, 0 },
3773 { "status", statushelp, status, 1, 0 },
3774 { "toggle", togglestring, toggle, 1, 0 },
3775 #if defined(TN3270) && defined(unix)
3776 { "transcom", transcomhelp, settranscom, 1, 0 },
3777 #endif /* defined(TN3270) && defined(unix) */
3778 #if defined(unix)
3779 { "z", zhelp, suspend, 1, 0 },
3780 #endif /* defined(unix) */
3781 #if defined(TN3270)
3782 { "!", shellhelp, shell, 1, 1 },
3783 #endif /* defined(TN3270) */
3784 { "?", helphelp, help, 1, 0 },
3785 0
3786 };
3787
3788 static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
3789 static char escapehelp[] = "deprecated command -- use 'set escape' instead";
3790
3791 static struct cmd cmdtab2[] = {
3792 { "help", helphelp, help, 0, 0 },
3793 { "escape", escapehelp, setescape, 1, 0 },
3794 { "crmod", crmodhelp, togcrmod, 1, 0 },
3795 0
3796 };
3797
3798 /*
3799 * Call routine with argc, argv set from args (terminated by 0).
3800 * VARARGS2
3801 */
3802 static
3803 call(routine, args)
3804 int (*routine)();
3805 char *args;
3806 {
3807 register char **argp;
3808 register int argc;
3809
3810 for (argc = 0, argp = &args; *argp++ != 0; argc++)
3811 ;
3812 return (*routine)(argc, &args);
3813 }
3814
3815 static char **
getnextcmd(name)3816 getnextcmd(name)
3817 char *name;
3818 {
3819 struct cmd *c = (struct cmd *) name;
3820
3821 return (char **) (c+1);
3822 }
3823
3824 static struct cmd *
getcmd(name)3825 getcmd(name)
3826 char *name;
3827 {
3828 struct cmd *cm;
3829
3830 if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
3831 return cm;
3832 } else {
3833 return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
3834 }
3835 }
3836
3837 void
command(top)3838 command(top)
3839 int top;
3840 {
3841 register struct cmd *c;
3842
3843 setcommandmode();
3844 if (!top) {
3845 putchar('\n');
3846 } else {
3847 #if defined(unix)
3848 signal(SIGINT, SIG_DFL);
3849 signal(SIGQUIT, SIG_DFL);
3850 #endif /* defined(unix) */
3851 }
3852 for (;;) {
3853 printf("%s> ", prompt);
3854 if (gets(line) == NULL) {
3855 if (feof(stdin) || ferror(stdin))
3856 quit();
3857 break;
3858 }
3859 if (line[0] == 0)
3860 break;
3861 makeargv();
3862 c = getcmd(margv[0]);
3863 if (c == Ambiguous(struct cmd *)) {
3864 printf("?Ambiguous command\n");
3865 continue;
3866 }
3867 if (c == 0) {
3868 printf("?Invalid command\n");
3869 continue;
3870 }
3871 if (c->needconnect && !connected) {
3872 printf("?Need to be connected first.\n");
3873 continue;
3874 }
3875 if ((*c->handler)(margc, margv)) {
3876 break;
3877 }
3878 }
3879 if (!top) {
3880 if (!connected) {
3881 longjmp(toplevel, 1);
3882 /*NOTREACHED*/
3883 }
3884 if (shell_active == 0) {
3885 setconnmode();
3886 }
3887 }
3888 }
3889
3890 /*
3891 * Help command.
3892 */
3893 static
help(argc,argv)3894 help(argc, argv)
3895 int argc;
3896 char *argv[];
3897 {
3898 register struct cmd *c;
3899
3900 if (argc == 1) {
3901 printf("Commands may be abbreviated. Commands are:\n\n");
3902 for (c = cmdtab; c->name; c++)
3903 if (c->dohelp) {
3904 printf("%-*s\t%s\n", HELPINDENT, c->name,
3905 c->help);
3906 }
3907 return 0;
3908 }
3909 while (--argc > 0) {
3910 register char *arg;
3911 arg = *++argv;
3912 c = getcmd(arg);
3913 if (c == Ambiguous(struct cmd *))
3914 printf("?Ambiguous help command %s\n", arg);
3915 else if (c == (struct cmd *)0)
3916 printf("?Invalid help command %s\n", arg);
3917 else
3918 printf("%s\n", c->help);
3919 }
3920 return 0;
3921 }
3922
3923 /*
3924 * main. Parse arguments, invoke the protocol or command parser.
3925 */
3926
3927
3928 void
main(argc,argv)3929 main(argc, argv)
3930 int argc;
3931 char *argv[];
3932 {
3933 tninit(); /* Clear out things */
3934
3935 NetTrace = stdout;
3936 TerminalSaveState();
3937 autoflush = TerminalAutoFlush();
3938
3939 prompt = argv[0];
3940 while ((argc > 1) && (argv[1][0] == '-')) {
3941 if (!strcmp(argv[1], "-d")) {
3942 debug = 1;
3943 } else if (!strcmp(argv[1], "-n")) {
3944 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */
3945 NetTrace = fopen(argv[2], "w");
3946 argv++;
3947 argc--;
3948 if (NetTrace == NULL) {
3949 NetTrace = stdout;
3950 }
3951 }
3952 } else {
3953 #if defined(TN3270) && defined(unix)
3954 if (!strcmp(argv[1], "-t")) {
3955 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */
3956 transcom = tline;
3957 (void) strcpy(transcom, argv[1]);
3958 argv++;
3959 argc--;
3960 }
3961 } else if (!strcmp(argv[1], "-noasynch")) {
3962 noasynch = 1;
3963 } else
3964 #endif /* defined(TN3270) && defined(unix) */
3965 if (argv[1][1] != '\0') {
3966 fprintf(stderr, "Unknown option *%s*.\n", argv[1]);
3967 }
3968 }
3969 argc--;
3970 argv++;
3971 }
3972 if (argc != 1) {
3973 if (setjmp(toplevel) != 0)
3974 Exit(0);
3975 tn(argc, argv);
3976 }
3977 setjmp(toplevel);
3978 for (;;) {
3979 #if !defined(TN3270)
3980 command(1);
3981 #else /* !defined(TN3270) */
3982 if (!shell_active) {
3983 command(1);
3984 } else {
3985 #if defined(TN3270)
3986 shell_continue();
3987 #endif /* defined(TN3270) */
3988 }
3989 #endif /* !defined(TN3270) */
3990 }
3991 }
3992