|
|
1.1 root 1: /*
2: * Copyright (c) 1983 Regents of the University of California.
3: * All rights reserved. The Berkeley software License Agreement
4: * specifies the terms and conditions for redistribution.
5: */
6:
7: #ifndef lint
8: char copyright[] =
9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10: All rights reserved.\n";
11: #endif not lint
12:
13: #ifndef lint
14: static char sccsid[] = "@(#)telnet.c 5.16 (Berkeley) 5/27/86";
15: #endif not lint
16:
17: /*
18: * User telnet program.
19: *
20: * Many of the FUNCTIONAL changes in this newest version of telnet
21: * were suggested by Dave Borman of Cray Research, Inc.
22: */
23:
24: #include <sys/types.h>
25: #include <sys/socket.h>
26: #include <sys/ioctl.h>
27: #include <sys/time.h>
28:
29: #include <netinet/in.h>
30:
31: #define TELOPTS
32: #include <arpa/telnet.h>
33: #include <arpa/inet.h>
34:
35: #include <stdio.h>
36: #include <ctype.h>
37: #include <errno.h>
38: #include <signal.h>
39: #include <setjmp.h>
40: #include <netdb.h>
41: #include <strings.h>
42:
43:
44:
45: #ifndef FD_SETSIZE
46: /*
47: * The following is defined just in case someone should want to run
48: * this telnet on a 4.2 system.
49: *
50: */
51:
52: #define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
53: #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
54: #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
55: #define FD_ZERO(p) ((p)->fds_bits[0] = 0)
56:
57: #endif
58:
59: #define strip(x) ((x)&0x7f)
60:
61: char ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
62: #define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
63: #define TTYLOC() (tfrontp)
64: #define TTYMAX() (ttyobuf+sizeof ttyobuf-1)
65: #define TTYMIN() (netobuf)
66: #define TTYBYTES() (tfrontp-tbackp)
67: #define TTYROOM() (TTYMAX()-TTYLOC()+1)
68:
69: char netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
70: #define NETADD(c) { *nfrontp++ = c; }
71: #define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); }
72: #define NETLOC() (nfrontp)
73: #define NETMAX() (netobuf+sizeof netobuf-1)
74: #define NETBYTES() (nfrontp-nbackp)
75: #define NETROOM() (NETMAX()-NETLOC()+1)
76: char *neturg = 0; /* one past last byte of urgent data */
77:
78: char subbuffer[100], *subpointer, *subend; /* buffer for sub-options */
79: #define SB_CLEAR() subpointer = subbuffer;
80: #define SB_TERM() subend = subpointer;
81: #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
82: *subpointer++ = (c); \
83: }
84:
85: char hisopts[256];
86: char myopts[256];
87:
88: char doopt[] = { IAC, DO, '%', 'c', 0 };
89: char dont[] = { IAC, DONT, '%', 'c', 0 };
90: char will[] = { IAC, WILL, '%', 'c', 0 };
91: char wont[] = { IAC, WONT, '%', 'c', 0 };
92:
93: struct cmd {
94: char *name; /* command name */
95: char *help; /* help string */
96: int (*handler)(); /* routine which executes command */
97: int dohelp; /* Should we give general help information? */
98: int needconnect; /* Do we need to be connected to execute? */
99: };
100:
101: int connected;
102: int net;
103: int tout;
104: int showoptions = 0;
105: int debug = 0;
106: int crmod = 0;
107: int netdata = 0;
108: static FILE *NetTrace;
109: int telnetport = 1;
110:
111:
112: char *prompt;
113: char escape = CTRL(]);
114: char echoc = CTRL(E);
115:
116: int SYNCHing = 0; /* we are in TELNET SYNCH mode */
117: int flushout = 0; /* flush output */
118: int autoflush = 0; /* flush output when interrupting? */
119: int autosynch = 0; /* send interrupt characters with SYNCH? */
120: int localchars = 0; /* we recognize interrupt/quit */
121: int donelclchars = 0; /* the user has set "localchars" */
122: int dontlecho = 0; /* do we suppress local echoing right now? */
123:
124: char line[200];
125: int margc;
126: char *margv[20];
127:
128: jmp_buf toplevel;
129: jmp_buf peerdied;
130:
131: extern int errno;
132:
133:
134: struct sockaddr_in sin;
135:
136: struct cmd *getcmd();
137: struct servent *sp;
138:
139: struct tchars otc, ntc;
140: struct ltchars oltc, nltc;
141: struct sgttyb ottyb, nttyb;
142: int globalmode = 0;
143: int flushline = 1;
144:
145: char *hostname;
146: char hnamebuf[32];
147:
148: /*
149: * The following are some clocks used to decide how to interpret
150: * the relationship between various variables.
151: */
152:
153: struct {
154: int
155: system, /* what the current time is */
156: echotoggle, /* last time user entered echo character */
157: modenegotiated, /* last time operating mode negotiated */
158: didnetreceive, /* last time we read data from network */
159: gotDM; /* when did we last see a data mark */
160: } clocks;
161:
162: #define settimer(x) clocks.x = clocks.system++
163:
164: /*
165: * Various utility routines.
166: */
167:
168: char *ambiguous; /* special return value */
169: #define Ambiguous(t) ((t)&ambiguous)
170:
171:
172: char **
173: genget(name, table, next)
174: char *name; /* name to match */
175: char **table; /* name entry in table */
176: char **(*next)(); /* routine to return next entry in table */
177: {
178: register char *p, *q;
179: register char **c, **found;
180: register int nmatches, longest;
181:
182: longest = 0;
183: nmatches = 0;
184: found = 0;
185: for (c = table; p = *c; c = (*next)(c)) {
186: for (q = name; *q == *p++; q++)
187: if (*q == 0) /* exact match? */
188: return (c);
189: if (!*q) { /* the name was a prefix */
190: if (q - name > longest) {
191: longest = q - name;
192: nmatches = 1;
193: found = c;
194: } else if (q - name == longest)
195: nmatches++;
196: }
197: }
198: if (nmatches > 1)
199: return Ambiguous(char **);
200: return (found);
201: }
202:
203: /*
204: * Make a character string into a number.
205: *
206: * Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
207: */
208:
209: special(s)
210: register char *s;
211: {
212: register char c;
213: char b;
214:
215: switch (*s) {
216: case '^':
217: b = *++s;
218: if (b == '?') {
219: c = b | 0x40; /* DEL */
220: } else {
221: c = b & 0x1f;
222: }
223: break;
224: default:
225: c = *s;
226: break;
227: }
228: return c;
229: }
230:
231: /*
232: * Construct a control character sequence
233: * for a special character.
234: */
235: char *
236: control(c)
237: register int c;
238: {
239: static char buf[3];
240:
241: if (c == 0x7f)
242: return ("^?");
243: if (c == '\377') {
244: return "off";
245: }
246: if (c >= 0x20) {
247: buf[0] = c;
248: buf[1] = 0;
249: } else {
250: buf[0] = '^';
251: buf[1] = '@'+c;
252: buf[2] = 0;
253: }
254: return (buf);
255: }
256:
257:
258: /*
259: * upcase()
260: *
261: * Upcase (in place) the argument.
262: */
263:
264: void
265: upcase(argument)
266: register char *argument;
267: {
268: register int c;
269:
270: while (c = *argument) {
271: if (islower(c)) {
272: *argument = toupper(c);
273: }
274: argument++;
275: }
276: }
277:
278: /*
279: * Check to see if any out-of-band data exists on a socket (for
280: * Telnet "synch" processing).
281: */
282:
283: int
284: stilloob(s)
285: int s; /* socket number */
286: {
287: static struct timeval timeout = { 0 };
288: fd_set excepts;
289: int value;
290:
291: do {
292: FD_ZERO(&excepts);
293: FD_SET(s, &excepts);
294: value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
295: } while ((value == -1) && (errno = EINTR));
296:
297: if (value < 0) {
298: perror("select");
299: quit();
300: }
301: if (FD_ISSET(s, &excepts)) {
302: return 1;
303: } else {
304: return 0;
305: }
306: }
307:
308:
309: /*
310: * netflush
311: * Send as much data as possible to the network,
312: * handling requests for urgent data.
313: */
314:
315:
316: netflush(fd)
317: {
318: int n;
319:
320: if ((n = nfrontp - nbackp) > 0) {
321: if (!neturg) {
322: n = write(fd, nbackp, n); /* normal write */
323: } else {
324: n = neturg - nbackp;
325: /*
326: * In 4.2 (and 4.3) systems, there is some question about
327: * what byte in a sendOOB operation is the "OOB" data.
328: * To make ourselves compatible, we only send ONE byte
329: * out of band, the one WE THINK should be OOB (though
330: * we really have more the TCP philosophy of urgent data
331: * rather than the Unix philosophy of OOB data).
332: */
333: if (n > 1) {
334: n = send(fd, nbackp, n-1, 0); /* send URGENT all by itself */
335: } else {
336: n = send(fd, nbackp, n, MSG_OOB); /* URGENT data */
337: }
338: }
339: }
340: if (n < 0) {
341: if (errno != ENOBUFS && errno != EWOULDBLOCK) {
342: setcommandmode();
343: perror(hostname);
344: close(fd);
345: neturg = 0;
346: longjmp(peerdied, -1);
347: /*NOTREACHED*/
348: }
349: n = 0;
350: }
351: if (netdata && n) {
352: Dump('>', nbackp, n);
353: }
354: nbackp += n;
355: if (nbackp >= neturg) {
356: neturg = 0;
357: }
358: if (nbackp == nfrontp) {
359: nbackp = nfrontp = netobuf;
360: }
361: }
362:
363: /*
364: * nextitem()
365: *
366: * Return the address of the next "item" in the TELNET data
367: * stream. This will be the address of the next character if
368: * the current address is a user data character, or it will
369: * be the address of the character following the TELNET command
370: * if the current address is a TELNET IAC ("I Am a Command")
371: * character.
372: */
373:
374: char *
375: nextitem(current)
376: char *current;
377: {
378: if ((*current&0xff) != IAC) {
379: return current+1;
380: }
381: switch (*(current+1)&0xff) {
382: case DO:
383: case DONT:
384: case WILL:
385: case WONT:
386: return current+3;
387: case SB: /* loop forever looking for the SE */
388: {
389: register char *look = current+2;
390:
391: for (;;) {
392: if ((*look++&0xff) == IAC) {
393: if ((*look++&0xff) == SE) {
394: return look;
395: }
396: }
397: }
398: }
399: default:
400: return current+2;
401: }
402: }
403: /*
404: * netclear()
405: *
406: * We are about to do a TELNET SYNCH operation. Clear
407: * the path to the network.
408: *
409: * Things are a bit tricky since we may have sent the first
410: * byte or so of a previous TELNET command into the network.
411: * So, we have to scan the network buffer from the beginning
412: * until we are up to where we want to be.
413: *
414: * A side effect of what we do, just to keep things
415: * simple, is to clear the urgent data pointer. The principal
416: * caller should be setting the urgent data pointer AFTER calling
417: * us in any case.
418: */
419:
420: netclear()
421: {
422: register char *thisitem, *next;
423: char *good;
424: #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
425: ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
426:
427: thisitem = netobuf;
428:
429: while ((next = nextitem(thisitem)) <= nbackp) {
430: thisitem = next;
431: }
432:
433: /* Now, thisitem is first before/at boundary. */
434:
435: good = netobuf; /* where the good bytes go */
436:
437: while (nfrontp > thisitem) {
438: if (wewant(thisitem)) {
439: int length;
440:
441: next = thisitem;
442: do {
443: next = nextitem(next);
444: } while (wewant(next) && (nfrontp > next));
445: length = next-thisitem;
446: bcopy(thisitem, good, length);
447: good += length;
448: thisitem = next;
449: } else {
450: thisitem = nextitem(thisitem);
451: }
452: }
453:
454: nbackp = netobuf;
455: nfrontp = good; /* next byte to be sent */
456: neturg = 0;
457: }
458:
459: /*
460: * Send as much data as possible to the terminal.
461: */
462:
463:
464: ttyflush()
465: {
466: int n;
467:
468: if ((n = tfrontp - tbackp) > 0) {
469: if (!(SYNCHing||flushout)) {
470: n = write(tout, tbackp, n);
471: } else {
472: ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
473: /* we leave 'n' alone! */
474: }
475: }
476: if (n < 0) {
477: return;
478: }
479: tbackp += n;
480: if (tbackp == tfrontp) {
481: tbackp = tfrontp = ttyobuf;
482: }
483: }
484:
485: /*
486: * Various signal handling routines.
487: */
488:
489: deadpeer()
490: {
491: setcommandmode();
492: longjmp(peerdied, -1);
493: }
494:
495: intr()
496: {
497: if (localchars) {
498: intp();
499: return;
500: }
501: setcommandmode();
502: longjmp(toplevel, -1);
503: }
504:
505: intr2()
506: {
507: if (localchars) {
508: sendbrk();
509: return;
510: }
511: }
512:
513: doescape()
514: {
515: command(0);
516: }
517:
518: /*
519: * The following are routines used to print out debugging information.
520: */
521:
522:
523: static
524: Dump(direction, buffer, length)
525: char direction;
526: char *buffer;
527: int length;
528: {
529: # define BYTES_PER_LINE 32
530: # define min(x,y) ((x<y)? x:y)
531: char *pThis;
532: int offset;
533:
534: offset = 0;
535:
536: while (length) {
537: /* print one line */
538: fprintf(NetTrace, "%c 0x%x\t", direction, offset);
539: pThis = buffer;
540: buffer = buffer+min(length, BYTES_PER_LINE);
541: while (pThis < buffer) {
542: fprintf(NetTrace, "%.2x", (*pThis)&0xff);
543: pThis++;
544: }
545: fprintf(NetTrace, "\n");
546: length -= BYTES_PER_LINE;
547: offset += BYTES_PER_LINE;
548: if (length < 0) {
549: return;
550: }
551: /* find next unique line */
552: }
553: }
554:
555:
556: /*VARARGS*/
557: printoption(direction, fmt, option, what)
558: char *direction, *fmt;
559: int option, what;
560: {
561: if (!showoptions)
562: return;
563: printf("%s ", direction+1);
564: if (fmt == doopt)
565: fmt = "do";
566: else if (fmt == dont)
567: fmt = "dont";
568: else if (fmt == will)
569: fmt = "will";
570: else if (fmt == wont)
571: fmt = "wont";
572: else
573: fmt = "???";
574: if (option < (sizeof telopts/sizeof telopts[0]))
575: printf("%s %s", fmt, telopts[option]);
576: else
577: printf("%s %d", fmt, option);
578: if (*direction == '<') {
579: printf("\r\n");
580: return;
581: }
582: printf(" (%s)\r\n", what ? "reply" : "don't reply");
583: }
584:
585: /*
586: * Mode - set up terminal to a specific mode.
587: */
588:
589:
590: mode(f)
591: register int f;
592: {
593: static int prevmode = 0;
594: struct tchars *tc;
595: struct ltchars *ltc;
596: struct sgttyb sb;
597: int onoff, old;
598: struct tchars notc2;
599: struct ltchars noltc2;
600: static struct tchars notc = { -1, -1, -1, -1, -1, -1 };
601: static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
602:
603: globalmode = f;
604: if (prevmode == f)
605: return;
606: old = prevmode;
607: prevmode = f;
608: sb = nttyb;
609: switch (f) {
610:
611: case 0:
612: onoff = 0;
613: tc = &otc;
614: ltc = &oltc;
615: break;
616:
617: case 1: /* remote character processing, remote echo */
618: case 2: /* remote character processing, local echo */
619: sb.sg_flags |= CBREAK;
620: if (f == 1)
621: sb.sg_flags &= ~(ECHO|CRMOD);
622: else
623: sb.sg_flags |= ECHO|CRMOD;
624: sb.sg_erase = sb.sg_kill = -1;
625: tc = ¬c;
626: /*
627: * If user hasn't specified one way or the other,
628: * then default to not trapping signals.
629: */
630: if (!donelclchars) {
631: localchars = 0;
632: }
633: if (localchars) {
634: notc2 = notc;
635: notc2.t_intrc = ntc.t_intrc;
636: notc2.t_quitc = ntc.t_quitc;
637: tc = ¬c2;
638: } else
639: tc = ¬c;
640: ltc = &noltc;
641: onoff = 1;
642: break;
643: case 3: /* local character processing, remote echo */
644: case 4: /* local character processing, local echo */
645: case 5: /* local character processing, no echo */
646: sb.sg_flags &= ~CBREAK;
647: sb.sg_flags |= CRMOD;
648: if (f == 4)
649: sb.sg_flags |= ECHO;
650: else
651: sb.sg_flags &= ~ECHO;
652: notc2 = ntc;
653: tc = ¬c2;
654: noltc2 = oltc;
655: ltc = &noltc2;
656: /*
657: * If user hasn't specified one way or the other,
658: * then default to trapping signals.
659: */
660: if (!donelclchars) {
661: localchars = 1;
662: }
663: if (localchars) {
664: notc2.t_brkc = nltc.t_flushc;
665: noltc2.t_flushc = -1;
666: } else {
667: notc2.t_intrc = notc2.t_quitc = -1;
668: }
669: noltc2.t_suspc = escape;
670: noltc2.t_dsuspc = -1;
671: onoff = 1;
672: break;
673:
674: default:
675: return;
676: }
677: ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
678: ioctl(fileno(stdin), TIOCSETC, (char *)tc);
679: ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
680: ioctl(fileno(stdin), FIONBIO, (char *)&onoff);
681: ioctl(fileno(stdout), FIONBIO, (char *)&onoff);
682: if (f >= 3)
683: signal(SIGTSTP, doescape);
684: else if (old >= 3) {
685: signal(SIGTSTP, SIG_DFL);
686: sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
687: }
688: }
689:
690: /*
691: * These routines decides on what the mode should be (based on the values
692: * of various global variables).
693: */
694:
695: char *modedescriptions[] = {
696: "telnet command mode", /* 0 */
697: "character-at-a-time mode", /* 1 */
698: "character-at-a-time mode (local echo)", /* 2 */
699: "line-by-line mode (remote echo)", /* 3 */
700: "line-by-line mode", /* 4 */
701: "line-by-line mode (local echoing suppressed)", /* 5 */
702: };
703:
704: getconnmode()
705: {
706: static char newmode[8] = { 4, 5, 3, 3, 2, 2, 1, 1 };
707: int modeindex = 0;
708:
709: if (hisopts[TELOPT_ECHO]) {
710: modeindex += 2;
711: }
712: if (hisopts[TELOPT_SGA]) {
713: modeindex += 4;
714: }
715: if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
716: modeindex += 1;
717: }
718: return newmode[modeindex];
719: }
720:
721: setconnmode()
722: {
723: mode(getconnmode());
724: }
725:
726:
727: setcommandmode()
728: {
729: mode(0);
730: }
731:
732: char sibuf[BUFSIZ], *sbp;
733: char tibuf[BUFSIZ], *tbp;
734: int scc, tcc;
735:
736:
737: /*
738: * Select from tty and network...
739: */
740: telnet()
741: {
742: register int c;
743: int tin = fileno(stdin);
744: int on = 1;
745: fd_set ibits, obits, xbits;
746:
747: tout = fileno(stdout);
748: setconnmode();
749: scc = 0;
750: tcc = 0;
751: FD_ZERO(&ibits);
752: FD_ZERO(&obits);
753: FD_ZERO(&xbits);
754:
755: ioctl(net, FIONBIO, (char *)&on);
756: #if defined(SO_OOBINLINE)
757: setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
758: #endif /* defined(SO_OOBINLINE) */
759: if (telnetport) {
760: if (!hisopts[TELOPT_SGA]) {
761: willoption(TELOPT_SGA, 0);
762: }
763: if (!myopts[TELOPT_TTYPE]) {
764: dooption(TELOPT_TTYPE, 0);
765: }
766: }
767: for (;;) {
768: if (scc < 0 && tcc < 0) {
769: break;
770: }
771:
772: if (((globalmode < 4) || flushline) && NETBYTES()) {
773: FD_SET(net, &obits);
774: } else {
775: FD_SET(tin, &ibits);
776: }
777: if (TTYBYTES()) {
778: FD_SET(tout, &obits);
779: } else {
780: FD_SET(net, &ibits);
781: }
782: if (!SYNCHing) {
783: FD_SET(net, &xbits);
784: }
785: if ((c = select(16, &ibits, &obits, &xbits,
786: (struct timeval *)0)) < 1) {
787: if (c == -1) {
788: /*
789: * we can get EINTR if we are in line mode,
790: * and the user does an escape (TSTP), or
791: * some other signal generator.
792: */
793: if (errno == EINTR) {
794: continue;
795: }
796: }
797: sleep(5);
798: continue;
799: }
800:
801: /*
802: * Any urgent data?
803: */
804: if (FD_ISSET(net, &xbits)) {
805: FD_CLR(net, &xbits);
806: SYNCHing = 1;
807: ttyflush(); /* flush already enqueued data */
808: }
809:
810: /*
811: * Something to read from the network...
812: */
813: if (FD_ISSET(net, &ibits)) {
814: int canread;
815:
816: FD_CLR(net, &ibits);
817: if (scc == 0) {
818: sbp = sibuf;
819: }
820: canread = sibuf + sizeof sibuf - sbp;
821: #if !defined(SO_OOBINLINE)
822: /*
823: * In 4.2 (and some early 4.3) systems, the
824: * OOB indication and data handling in the kernel
825: * is such that if two separate TCP Urgent requests
826: * come in, one byte of TCP data will be overlaid.
827: * This is fatal for Telnet, but we try to live
828: * with it.
829: *
830: * In addition, in 4.2 (and...), a special protocol
831: * is needed to pick up the TCP Urgent data in
832: * the correct sequence.
833: *
834: * What we do is: if we think we are in urgent
835: * mode, we look to see if we are "at the mark".
836: * If we are, we do an OOB receive. If we run
837: * this twice, we will do the OOB receive twice,
838: * but the second will fail, since the second
839: * time we were "at the mark", but there wasn't
840: * any data there (the kernel doesn't reset
841: * "at the mark" until we do a normal read).
842: * Once we've read the OOB data, we go ahead
843: * and do normal reads.
844: *
845: * There is also another problem, which is that
846: * since the OOB byte we read doesn't put us
847: * out of OOB state, and since that byte is most
848: * likely the TELNET DM (data mark), we would
849: * stay in the TELNET SYNCH (SYNCHing) state.
850: * So, clocks to the rescue. If we've "just"
851: * received a DM, then we test for the
852: * presence of OOB data when the receive OOB
853: * fails (and AFTER we did the normal mode read
854: * to clear "at the mark").
855: */
856: if (SYNCHing) {
857: int atmark;
858:
859: ioctl(net, SIOCATMARK, (char *)&atmark);
860: if (atmark) {
861: c = recv(net, sibuf, canread, MSG_OOB);
862: if ((c == -1) && (errno == EINVAL)) {
863: c = read(net, sibuf, canread);
864: if (clocks.didnetreceive < clocks.gotDM) {
865: SYNCHing = stilloob(net);
866: }
867: }
868: } else {
869: c = read(net, sibuf, canread);
870: }
871: } else {
872: c = read(net, sibuf, canread);
873: }
874: settimer(didnetreceive);
875: #else /* !defined(SO_OOBINLINE) */
876: c = read(net, sbp, canread);
877: #endif /* !defined(SO_OOBINLINE) */
878: if (c < 0 && errno == EWOULDBLOCK) {
879: c = 0;
880: } else if (c <= 0) {
881: break;
882: }
883: if (netdata) {
884: Dump('<', sbp, c);
885: }
886: scc += c;
887: }
888:
889: /*
890: * Something to read from the tty...
891: */
892: if (FD_ISSET(tin, &ibits)) {
893: FD_CLR(tin, &ibits);
894: if (tcc == 0) {
895: tbp = tibuf; /* nothing left, reset */
896: }
897: c = read(tin, tbp, tibuf+sizeof tibuf - tbp);
898: if (c < 0 && errno == EWOULDBLOCK) {
899: c = 0;
900: } else {
901: /* EOF detection for line mode!!!! */
902: if (c == 0 && globalmode >= 3) {
903: /* must be an EOF... */
904: *tbp = ntc.t_eofc;
905: c = 1;
906: }
907: if (c <= 0) {
908: tcc = c;
909: break;
910: }
911: }
912: tcc += c;
913: }
914:
915: while (tcc > 0) {
916: register int sc;
917:
918: if (NETROOM() < 2) {
919: flushline = 1;
920: break;
921: }
922: c = *tbp++ & 0xff, sc = strip(c), tcc--;
923: if (sc == escape) {
924: command(0);
925: tcc = 0;
926: flushline = 1;
927: break;
928: } else if ((globalmode >= 4) && (sc == echoc)) {
929: if (tcc > 0 && strip(*tbp) == echoc) {
930: tbp++;
931: tcc--;
932: } else {
933: dontlecho = !dontlecho;
934: settimer(echotoggle);
935: setconnmode();
936: tcc = 0;
937: flushline = 1;
938: break;
939: }
940: }
941: if (localchars) {
942: if (sc == ntc.t_intrc) {
943: intp();
944: break;
945: } else if (sc == ntc.t_quitc) {
946: sendbrk();
947: break;
948: } else if (sc == nltc.t_flushc) {
949: NET2ADD(IAC, AO);
950: if (autoflush) {
951: doflush();
952: }
953: break;
954: } else if (globalmode > 2) {
955: ;
956: } else if (sc == nttyb.sg_kill) {
957: NET2ADD(IAC, EL);
958: break;
959: } else if (sc == nttyb.sg_erase) {
960: NET2ADD(IAC, EC);
961: break;
962: }
963: }
964: switch (c) {
965: case '\n':
966: /*
967: * If we are in CRMOD mode (\r ==> \n)
968: * on our local machine, then probably
969: * a newline (unix) is CRLF (TELNET).
970: */
971: if (globalmode >= 3) {
972: NETADD('\r');
973: }
974: NETADD('\n');
975: flushline = 1;
976: break;
977: case '\r':
978: NET2ADD('\r', '\0');
979: flushline = 1;
980: break;
981: case IAC:
982: NET2ADD(IAC, IAC);
983: break;
984: default:
985: NETADD(c);
986: break;
987: }
988: }
989: if (((globalmode < 4) || flushline) &&
990: FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
991: FD_CLR(net, &obits);
992: netflush(net);
993: }
994: if (scc > 0)
995: telrcv();
996: if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) {
997: FD_CLR(tout, &obits);
998: ttyflush();
999: }
1000: }
1001: setcommandmode();
1002: }
1003:
1004: /*
1005: * Telnet receiver states for fsm
1006: */
1007: #define TS_DATA 0
1008: #define TS_IAC 1
1009: #define TS_WILL 2
1010: #define TS_WONT 3
1011: #define TS_DO 4
1012: #define TS_DONT 5
1013: #define TS_CR 6
1014: #define TS_SB 7 /* sub-option collection */
1015: #define TS_SE 8 /* looking for sub-option end */
1016:
1017: telrcv()
1018: {
1019: register int c;
1020: static int state = TS_DATA;
1021:
1022: while ((scc > 0) && (TTYROOM() > 2)) {
1023: c = *sbp++ & 0xff, scc--;
1024: switch (state) {
1025:
1026: case TS_CR:
1027: state = TS_DATA;
1028: if (c == '\0') {
1029: break; /* Ignore \0 after CR */
1030: } else if (c == '\n') {
1031: if (hisopts[TELOPT_ECHO] && !crmod) {
1032: TTYADD(c);
1033: }
1034: break;
1035: }
1036: /* Else, fall through */
1037:
1038: case TS_DATA:
1039: if (c == IAC) {
1040: state = TS_IAC;
1041: continue;
1042: }
1043: /*
1044: * The 'crmod' hack (see following) is needed
1045: * since we can't * set CRMOD on output only.
1046: * Machines like MULTICS like to send \r without
1047: * \n; since we must turn off CRMOD to get proper
1048: * input, the mapping is done here (sigh).
1049: */
1050: if (c == '\r') {
1051: if (scc > 0) {
1052: c = *sbp&0xff;
1053: if (c == 0) {
1054: sbp++, scc--;
1055: /* a "true" CR */
1056: TTYADD('\r');
1057: } else if (!hisopts[TELOPT_ECHO] &&
1058: (c == '\n')) {
1059: sbp++, scc--;
1060: TTYADD('\n');
1061: } else {
1062: TTYADD('\r');
1063: if (crmod) {
1064: TTYADD('\n');
1065: }
1066: }
1067: } else {
1068: state = TS_CR;
1069: TTYADD('\r');
1070: if (crmod) {
1071: TTYADD('\n');
1072: }
1073: }
1074: } else {
1075: TTYADD(c);
1076: }
1077: continue;
1078:
1079: case TS_IAC:
1080: switch (c) {
1081:
1082: case WILL:
1083: state = TS_WILL;
1084: continue;
1085:
1086: case WONT:
1087: state = TS_WONT;
1088: continue;
1089:
1090: case DO:
1091: state = TS_DO;
1092: continue;
1093:
1094: case DONT:
1095: state = TS_DONT;
1096: continue;
1097:
1098: case DM:
1099: /*
1100: * We may have missed an urgent notification,
1101: * so make sure we flush whatever is in the
1102: * buffer currently.
1103: */
1104: SYNCHing = 1;
1105: ttyflush();
1106: SYNCHing = stilloob(net);
1107: settimer(gotDM);
1108: break;
1109:
1110: case NOP:
1111: case GA:
1112: break;
1113:
1114: case SB:
1115: SB_CLEAR();
1116: state = TS_SB;
1117: continue;
1118:
1119: default:
1120: break;
1121: }
1122: state = TS_DATA;
1123: continue;
1124:
1125: case TS_WILL:
1126: printoption(">RCVD", will, c, !hisopts[c]);
1127: if (c == TELOPT_TM) {
1128: if (flushout) {
1129: flushout = 0;
1130: }
1131: } else if (!hisopts[c]) {
1132: willoption(c, 1);
1133: }
1134: state = TS_DATA;
1135: continue;
1136:
1137: case TS_WONT:
1138: printoption(">RCVD", wont, c, hisopts[c]);
1139: if (c == TELOPT_TM) {
1140: if (flushout) {
1141: flushout = 0;
1142: }
1143: } else if (hisopts[c]) {
1144: wontoption(c, 1);
1145: }
1146: state = TS_DATA;
1147: continue;
1148:
1149: case TS_DO:
1150: printoption(">RCVD", doopt, c, !myopts[c]);
1151: if (!myopts[c])
1152: dooption(c);
1153: state = TS_DATA;
1154: continue;
1155:
1156: case TS_DONT:
1157: printoption(">RCVD", dont, c, myopts[c]);
1158: if (myopts[c]) {
1159: myopts[c] = 0;
1160: sprintf(nfrontp, wont, c);
1161: nfrontp += sizeof (wont) - 2;
1162: flushline = 1;
1163: setconnmode(); /* set new tty mode (maybe) */
1164: printoption(">SENT", wont, c);
1165: }
1166: state = TS_DATA;
1167: continue;
1168: case TS_SB:
1169: if (c == IAC) {
1170: state = TS_SE;
1171: } else {
1172: SB_ACCUM(c);
1173: }
1174: continue;
1175:
1176: case TS_SE:
1177: if (c != SE) {
1178: if (c != IAC) {
1179: SB_ACCUM(IAC);
1180: }
1181: SB_ACCUM(c);
1182: state = TS_SB;
1183: } else {
1184: SB_TERM();
1185: suboption(); /* handle sub-option */
1186: state = TS_DATA;
1187: }
1188: }
1189: }
1190: }
1191:
1192: willoption(option, reply)
1193: int option, reply;
1194: {
1195: char *fmt;
1196:
1197: switch (option) {
1198:
1199: case TELOPT_ECHO:
1200: case TELOPT_SGA:
1201: settimer(modenegotiated);
1202: hisopts[option] = 1;
1203: fmt = doopt;
1204: setconnmode(); /* possibly set new tty mode */
1205: break;
1206:
1207: case TELOPT_TM:
1208: return; /* Never reply to TM will's/wont's */
1209:
1210: default:
1211: fmt = dont;
1212: break;
1213: }
1214: sprintf(nfrontp, fmt, option);
1215: nfrontp += sizeof (dont) - 2;
1216: if (reply)
1217: printoption(">SENT", fmt, option);
1218: else
1219: printoption("<SENT", fmt, option);
1220: }
1221:
1222: wontoption(option, reply)
1223: int option, reply;
1224: {
1225: char *fmt;
1226:
1227: switch (option) {
1228:
1229: case TELOPT_ECHO:
1230: case TELOPT_SGA:
1231: settimer(modenegotiated);
1232: hisopts[option] = 0;
1233: fmt = dont;
1234: setconnmode(); /* Set new tty mode */
1235: break;
1236:
1237: case TELOPT_TM:
1238: return; /* Never reply to TM will's/wont's */
1239:
1240: default:
1241: fmt = dont;
1242: }
1243: sprintf(nfrontp, fmt, option);
1244: nfrontp += sizeof (doopt) - 2;
1245: if (reply)
1246: printoption(">SENT", fmt, option);
1247: else
1248: printoption("<SENT", fmt, option);
1249: }
1250:
1251: dooption(option)
1252: int option;
1253: {
1254: char *fmt;
1255:
1256: switch (option) {
1257:
1258: case TELOPT_TM:
1259: fmt = will;
1260: break;
1261:
1262: case TELOPT_TTYPE: /* terminal type option */
1263: case TELOPT_SGA: /* no big deal */
1264: fmt = will;
1265: myopts[option] = 1;
1266: break;
1267:
1268: case TELOPT_ECHO: /* We're never going to echo... */
1269: default:
1270: fmt = wont;
1271: break;
1272: }
1273: sprintf(nfrontp, fmt, option);
1274: nfrontp += sizeof (doopt) - 2;
1275: printoption(">SENT", fmt, option);
1276: }
1277:
1278: /*
1279: * suboption()
1280: *
1281: * Look at the sub-option buffer, and try to be helpful to the other
1282: * side.
1283: *
1284: * Currently we recognize:
1285: *
1286: * Terminal type, send request.
1287: */
1288:
1289: suboption()
1290: {
1291: switch (subbuffer[0]&0xff) {
1292: case TELOPT_TTYPE:
1293: if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
1294: ;
1295: } else {
1296: char *name;
1297: char namebuf[41];
1298: char *getenv();
1299: int len;
1300:
1301: name = getenv("TERM");
1302: if ((name == 0) || ((len = strlen(name)) > 40)) {
1303: name = "UNKNOWN";
1304: }
1305: if ((len + 4+2) < NETROOM()) {
1306: strcpy(namebuf, name);
1307: upcase(namebuf);
1308: sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
1309: TELQUAL_IS, namebuf, IAC, SE);
1310: nfrontp += 4+strlen(namebuf)+2;
1311: }
1312: }
1313:
1314: default:
1315: break;
1316: }
1317: }
1318:
1319: /*
1320: * The following are data structures and routines for
1321: * the "send" command.
1322: *
1323: */
1324:
1325: struct sendlist {
1326: char *name; /* How user refers to it (case independent) */
1327: int what; /* Character to be sent (<0 ==> special) */
1328: char *help; /* Help information (0 ==> no help) */
1329: int (*routine)(); /* Routine to perform (for special ops) */
1330: };
1331:
1332: /*ARGSUSED*/
1333: dosynch(s)
1334: struct sendlist *s;
1335: {
1336: netclear(); /* clear the path to the network */
1337: NET2ADD(IAC, DM);
1338: neturg = NETLOC()-1; /* Some systems are off by one XXX */
1339: }
1340:
1341: doflush()
1342: {
1343: NET2ADD(IAC, DO);
1344: NETADD(TELOPT_TM);
1345: flushline = 1;
1346: flushout = 1;
1347: ttyflush();
1348: /* do printoption AFTER flush, otherwise the output gets tossed... */
1349: printoption("<SENT", doopt, TELOPT_TM);
1350: }
1351:
1352: intp()
1353: {
1354: NET2ADD(IAC, IP);
1355: if (autoflush) {
1356: doflush();
1357: }
1358: if (autosynch) {
1359: dosynch();
1360: }
1361: }
1362:
1363: sendbrk()
1364: {
1365: NET2ADD(IAC, BREAK);
1366: if (autoflush) {
1367: doflush();
1368: }
1369: if (autosynch) {
1370: dosynch();
1371: }
1372: }
1373:
1374:
1375: #define SENDQUESTION -1
1376: #define SENDESCAPE -3
1377:
1378: struct sendlist Sendlist[] = {
1379: { "ao", AO, "Send Telnet Abort output" },
1380: { "ayt", AYT, "Send Telnet 'Are You There'" },
1381: { "brk", BREAK, "Send Telnet Break" },
1382: { "ec", EC, "Send Telnet Erase Character" },
1383: { "el", EL, "Send Telnet Erase Line" },
1384: { "escape", SENDESCAPE, "Send current escape character" },
1385: { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
1386: { "ip", IP, "Send Telnet Interrupt Process" },
1387: { "nop", NOP, "Send Telnet 'No operation'" },
1388: { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
1389: { "?", SENDQUESTION, "Display send options" },
1390: { 0 }
1391: };
1392:
1393: struct sendlist Sendlist2[] = { /* some synonyms */
1394: { "break", BREAK, 0 },
1395:
1396: { "intp", IP, 0 },
1397: { "interrupt", IP, 0 },
1398: { "intr", IP, 0 },
1399:
1400: { "help", SENDQUESTION, 0 },
1401:
1402: { 0 }
1403: };
1404:
1405: char **
1406: getnextsend(name)
1407: char *name;
1408: {
1409: struct sendlist *c = (struct sendlist *) name;
1410:
1411: return (char **) (c+1);
1412: }
1413:
1414: struct sendlist *
1415: getsend(name)
1416: char *name;
1417: {
1418: struct sendlist *sl;
1419:
1420: if (sl = (struct sendlist *)
1421: genget(name, (char **) Sendlist, getnextsend)) {
1422: return sl;
1423: } else {
1424: return (struct sendlist *)
1425: genget(name, (char **) Sendlist2, getnextsend);
1426: }
1427: }
1428:
1429: sendcmd(argc, argv)
1430: int argc;
1431: char **argv;
1432: {
1433: int what; /* what we are sending this time */
1434: int count; /* how many bytes we are going to need to send */
1435: int hadsynch; /* are we going to process a "synch"? */
1436: int i;
1437: int question = 0; /* was at least one argument a question */
1438: struct sendlist *s; /* pointer to current command */
1439:
1440: if (argc < 2) {
1441: printf("need at least one argument for 'send' command\n");
1442: printf("'send ?' for help\n");
1443: return 0;
1444: }
1445: /*
1446: * First, validate all the send arguments.
1447: * In addition, we see how much space we are going to need, and
1448: * whether or not we will be doing a "SYNCH" operation (which
1449: * flushes the network queue).
1450: */
1451: count = 0;
1452: hadsynch = 0;
1453: for (i = 1; i < argc; i++) {
1454: s = getsend(argv[i]);
1455: if (s == 0) {
1456: printf("Unknown send argument '%s'\n'send ?' for help.\n",
1457: argv[i]);
1458: return 0;
1459: } else if (s == Ambiguous(struct sendlist *)) {
1460: printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
1461: argv[i]);
1462: return 0;
1463: }
1464: switch (s->what) {
1465: case SENDQUESTION:
1466: break;
1467: case SENDESCAPE:
1468: count += 1;
1469: break;
1470: case SYNCH:
1471: hadsynch = 1;
1472: count += 2;
1473: break;
1474: default:
1475: count += 2;
1476: break;
1477: }
1478: }
1479: /* Now, do we have enough room? */
1480: if (NETROOM() < count) {
1481: printf("There is not enough room in the buffer TO the network\n");
1482: printf("to process your request. Nothing will be done.\n");
1483: printf("('send synch' will throw away most data in the network\n");
1484: printf("buffer, if this might help.)\n");
1485: return 0;
1486: }
1487: /* OK, they are all OK, now go through again and actually send */
1488: for (i = 1; i < argc; i++) {
1489: if (!(s = getsend(argv[i]))) {
1490: fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
1491: quit();
1492: /*NOTREACHED*/
1493: }
1494: if (s->routine) {
1495: (*s->routine)(s);
1496: } else {
1497: switch (what = s->what) {
1498: case SYNCH:
1499: dosynch();
1500: break;
1501: case SENDQUESTION:
1502: for (s = Sendlist; s->name; s++) {
1503: if (s->help) {
1504: printf(s->name);
1505: if (s->help) {
1506: printf("\t%s", s->help);
1507: }
1508: printf("\n");
1509: }
1510: }
1511: question = 1;
1512: break;
1513: case SENDESCAPE:
1514: NETADD(escape);
1515: break;
1516: default:
1517: NET2ADD(IAC, what);
1518: break;
1519: }
1520: }
1521: }
1522: return !question;
1523: }
1524:
1525: /*
1526: * The following are the routines and data structures referred
1527: * to by the arguments to the "toggle" command.
1528: */
1529:
1530: lclchars()
1531: {
1532: donelclchars = 1;
1533: return 1;
1534: }
1535:
1536: togdebug()
1537: {
1538: #ifndef NOT43
1539: if (net > 0 &&
1540: setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
1541: < 0) {
1542: perror("setsockopt (SO_DEBUG)");
1543: }
1544: #else NOT43
1545: if (debug) {
1546: if (net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
1547: perror("setsockopt (SO_DEBUG)");
1548: } else
1549: printf("Cannot turn off socket debugging\n");
1550: #endif NOT43
1551: return 1;
1552: }
1553:
1554:
1555:
1556: int togglehelp();
1557:
1558: struct togglelist {
1559: char *name; /* name of toggle */
1560: char *help; /* help message */
1561: int (*handler)(); /* routine to do actual setting */
1562: int dohelp; /* should we display help information */
1563: int *variable;
1564: char *actionexplanation;
1565: };
1566:
1567: struct togglelist Togglelist[] = {
1568: { "autoflush",
1569: "toggle flushing of output when sending interrupt characters",
1570: 0,
1571: 1,
1572: &autoflush,
1573: "flush output when sending interrupt characters" },
1574: { "autosynch",
1575: "toggle automatic sending of interrupt characters in urgent mode",
1576: 0,
1577: 1,
1578: &autosynch,
1579: "send interrupt characters in urgent mode" },
1580: { "crmod",
1581: "toggle mapping of received carriage returns",
1582: 0,
1583: 1,
1584: &crmod,
1585: "map carriage return on output" },
1586: { "localchars",
1587: "toggle local recognition of certain control characters",
1588: lclchars,
1589: 1,
1590: &localchars,
1591: "recognize certain control characters" },
1592: { " ", "", 0, 1 }, /* empty line */
1593: { "debug",
1594: "(debugging) toggle debugging",
1595: togdebug,
1596: 1,
1597: &debug,
1598: "turn on socket level debugging" },
1599: { "netdata",
1600: "(debugging) toggle printing of hexadecimal network data",
1601: 0,
1602: 1,
1603: &netdata,
1604: "print hexadecimal representation of network traffic" },
1605: { "options",
1606: "(debugging) toggle viewing of options processing",
1607: 0,
1608: 1,
1609: &showoptions,
1610: "show option processing" },
1611: { " ", "", 0, 1 }, /* empty line */
1612: { "?",
1613: "display help information",
1614: togglehelp,
1615: 1 },
1616: { "help",
1617: "display help information",
1618: togglehelp,
1619: 0 },
1620: { 0 }
1621: };
1622:
1623: togglehelp()
1624: {
1625: struct togglelist *c;
1626:
1627: for (c = Togglelist; c->name; c++) {
1628: if (c->dohelp) {
1629: printf("%s\t%s\n", c->name, c->help);
1630: }
1631: }
1632: return 0;
1633: }
1634:
1635: char **
1636: getnexttoggle(name)
1637: char *name;
1638: {
1639: struct togglelist *c = (struct togglelist *) name;
1640:
1641: return (char **) (c+1);
1642: }
1643:
1644: struct togglelist *
1645: gettoggle(name)
1646: char *name;
1647: {
1648: return (struct togglelist *)
1649: genget(name, (char **) Togglelist, getnexttoggle);
1650: }
1651:
1652: toggle(argc, argv)
1653: int argc;
1654: char *argv[];
1655: {
1656: int retval = 1;
1657: char *name;
1658: struct togglelist *c;
1659:
1660: if (argc < 2) {
1661: fprintf(stderr,
1662: "Need an argument to 'toggle' command. 'toggle ?' for help.\n");
1663: return 0;
1664: }
1665: argc--;
1666: argv++;
1667: while (argc--) {
1668: name = *argv++;
1669: c = gettoggle(name);
1670: if (c == Ambiguous(struct togglelist *)) {
1671: fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
1672: name);
1673: return 0;
1674: } else if (c == 0) {
1675: fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
1676: name);
1677: return 0;
1678: } else {
1679: if (c->variable) {
1680: *c->variable = !*c->variable; /* invert it */
1681: printf("%s %s.\n", *c->variable? "Will" : "Won't",
1682: c->actionexplanation);
1683: }
1684: if (c->handler) {
1685: retval &= (*c->handler)(c);
1686: }
1687: }
1688: }
1689: return retval;
1690: }
1691:
1692: /*
1693: * The following perform the "set" command.
1694: */
1695:
1696: struct setlist {
1697: char *name; /* name */
1698: char *help; /* help information */
1699: char *charp; /* where it is located at */
1700: };
1701:
1702: struct setlist Setlist[] = {
1703: { "echo", "character to toggle local echoing on/off", &echoc },
1704: { "escape", "character to escape back to telnet command mode", &escape },
1705: { " ", "" },
1706: { " ", "The following need 'localchars' to be toggled true", 0 },
1707: { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
1708: { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
1709: { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
1710: { "kill", "character to cause an Erase Line", &nttyb.sg_kill },
1711: { "quit", "character to cause a Break", &ntc.t_quitc },
1712: { "eof", "character to cause an EOF ", &ntc.t_eofc },
1713: { 0 }
1714: };
1715:
1716: char **
1717: getnextset(name)
1718: char *name;
1719: {
1720: struct setlist *c = (struct setlist *)name;
1721:
1722: return (char **) (c+1);
1723: }
1724:
1725: struct setlist *
1726: getset(name)
1727: char *name;
1728: {
1729: return (struct setlist *) genget(name, (char **) Setlist, getnextset);
1730: }
1731:
1732: setcmd(argc, argv)
1733: int argc;
1734: char *argv[];
1735: {
1736: int value;
1737: struct setlist *ct;
1738:
1739: /* XXX back we go... sigh */
1740: if (argc != 3) {
1741: if ((argc == 2) &&
1742: ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
1743: for (ct = Setlist; ct->name; ct++) {
1744: printf("%s\t%s\n", ct->name, ct->help);
1745: }
1746: printf("?\tdisplay help information\n");
1747: } else {
1748: printf("Format is 'set Name Value'\n'set ?' for help.\n");
1749: }
1750: return 0;
1751: }
1752:
1753: ct = getset(argv[1]);
1754: if (ct == 0) {
1755: fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
1756: argv[1]);
1757: return 0;
1758: } else if (ct == Ambiguous(struct setlist *)) {
1759: fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
1760: argv[1]);
1761: return 0;
1762: } else {
1763: if (strcmp("off", argv[2])) {
1764: value = special(argv[2]);
1765: } else {
1766: value = -1;
1767: }
1768: *(ct->charp) = value;
1769: printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
1770: }
1771: return 1;
1772: }
1773:
1774: /*
1775: * The following are the data structures and routines for the
1776: * 'mode' command.
1777: */
1778:
1779: dolinemode()
1780: {
1781: if (hisopts[TELOPT_SGA]) {
1782: wontoption(TELOPT_SGA, 0);
1783: }
1784: if (hisopts[TELOPT_ECHO]) {
1785: wontoption(TELOPT_ECHO, 0);
1786: }
1787: }
1788:
1789: docharmode()
1790: {
1791: if (!hisopts[TELOPT_SGA]) {
1792: willoption(TELOPT_SGA, 0);
1793: }
1794: if (!hisopts[TELOPT_ECHO]) {
1795: willoption(TELOPT_ECHO, 0);
1796: }
1797: }
1798:
1799: struct cmd Modelist[] = {
1800: { "character", "character-at-a-time mode", docharmode, 1, 1 },
1801: { "line", "line-by-line mode", dolinemode, 1, 1 },
1802: { 0 },
1803: };
1804:
1805: char **
1806: getnextmode(name)
1807: char *name;
1808: {
1809: struct cmd *c = (struct cmd *) name;
1810:
1811: return (char **) (c+1);
1812: }
1813:
1814: struct cmd *
1815: getmodecmd(name)
1816: char *name;
1817: {
1818: return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
1819: }
1820:
1821: modecmd(argc, argv)
1822: int argc;
1823: char *argv[];
1824: {
1825: struct cmd *mt;
1826:
1827: if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
1828: printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
1829: for (mt = Modelist; mt->name; mt++) {
1830: printf("%s\t%s\n", mt->name, mt->help);
1831: }
1832: return 0;
1833: }
1834: mt = getmodecmd(argv[1]);
1835: if (mt == 0) {
1836: fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
1837: return 0;
1838: } else if (mt == Ambiguous(struct cmd *)) {
1839: fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
1840: return 0;
1841: } else {
1842: (*mt->handler)();
1843: }
1844: return 1;
1845: }
1846:
1847: /*
1848: * The following data structures and routines implement the
1849: * "display" command.
1850: */
1851:
1852: display(argc, argv)
1853: int argc;
1854: char *argv[];
1855: {
1856: #define dotog(tl) if (tl->variable && tl->actionexplanation) { \
1857: if (*tl->variable) { \
1858: printf("will"); \
1859: } else { \
1860: printf("won't"); \
1861: } \
1862: printf(" %s.\n", tl->actionexplanation); \
1863: }
1864:
1865: #define doset(sl) if (sl->name && *sl->name != ' ') { \
1866: printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
1867: }
1868:
1869: struct togglelist *tl;
1870: struct setlist *sl;
1871:
1872: if (argc == 1) {
1873: for (tl = Togglelist; tl->name; tl++) {
1874: dotog(tl);
1875: }
1876: printf("\n");
1877: for (sl = Setlist; sl->name; sl++) {
1878: doset(sl);
1879: }
1880: } else {
1881: int i;
1882:
1883: for (i = 1; i < argc; i++) {
1884: sl = getset(argv[i]);
1885: tl = gettoggle(argv[i]);
1886: if ((sl == Ambiguous(struct setlist *)) ||
1887: (tl == Ambiguous(struct togglelist *))) {
1888: printf("?Ambiguous argument '%s'.\n", argv[i]);
1889: return 0;
1890: } else if (!sl && !tl) {
1891: printf("?Unknown argument '%s'.\n", argv[i]);
1892: return 0;
1893: } else {
1894: if (tl) {
1895: dotog(tl);
1896: }
1897: if (sl) {
1898: doset(sl);
1899: }
1900: }
1901: }
1902: }
1903: return 1;
1904: #undef doset(sl)
1905: #undef dotog(tl)
1906: }
1907:
1908: /*
1909: * The following are the data structures, and many of the routines,
1910: * relating to command processing.
1911: */
1912:
1913: /*
1914: * Set the escape character.
1915: */
1916: setescape(argc, argv)
1917: int argc;
1918: char *argv[];
1919: {
1920: register char *arg;
1921: char buf[50];
1922:
1923: printf(
1924: "Deprecated usage - please use 'set escape%s%s' in the future.\n",
1925: (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
1926: if (argc > 2)
1927: arg = argv[1];
1928: else {
1929: printf("new escape character: ");
1930: gets(buf);
1931: arg = buf;
1932: }
1933: if (arg[0] != '\0')
1934: escape = arg[0];
1935: printf("Escape character is '%s'.\n", control(escape));
1936: fflush(stdout);
1937: return 1;
1938: }
1939:
1940: /*VARARGS*/
1941: togcrmod()
1942: {
1943: crmod = !crmod;
1944: printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
1945: printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
1946: fflush(stdout);
1947: return 1;
1948: }
1949:
1950: /*VARARGS*/
1951: suspend()
1952: {
1953: setcommandmode();
1954: kill(0, SIGTSTP);
1955: /* reget parameters in case they were changed */
1956: ioctl(0, TIOCGETP, (char *)&ottyb);
1957: ioctl(0, TIOCGETC, (char *)&otc);
1958: ioctl(0, TIOCGLTC, (char *)&oltc);
1959: return 1;
1960: }
1961:
1962: /*VARARGS*/
1963: bye()
1964: {
1965: register char *op;
1966:
1967: if (connected) {
1968: shutdown(net, 2);
1969: printf("Connection closed.\n");
1970: close(net);
1971: connected = 0;
1972: /* reset his options */
1973: for (op = hisopts; op < &hisopts[256]; op++)
1974: *op = 0;
1975: }
1976: return 1;
1977: }
1978:
1979: /*VARARGS*/
1980: quit()
1981: {
1982: (void) call(bye, "bye", 0);
1983: exit(0);
1984: /*NOTREACHED*/
1985: }
1986:
1987: /*
1988: * Print status about the connection.
1989: */
1990: /*ARGSUSED*/
1991: status(argc, argv)
1992: int argc;
1993: char *argv[];
1994: {
1995: if (connected) {
1996: printf("Connected to %s.\n", hostname);
1997: if (argc < 2) {
1998: printf("Operating in %s.\n", modedescriptions[getconnmode()]);
1999: if (localchars) {
2000: printf("Catching signals locally.\n");
2001: }
2002: }
2003: } else {
2004: printf("No connection.\n");
2005: }
2006: printf("Escape character is '%s'.\n", control(escape));
2007: fflush(stdout);
2008: return 1;
2009: }
2010:
2011: tn(argc, argv)
2012: int argc;
2013: char *argv[];
2014: {
2015: register struct hostent *host = 0;
2016:
2017: if (connected) {
2018: printf("?Already connected to %s\n", hostname);
2019: return 0;
2020: }
2021: if (argc < 2) {
2022: (void) strcpy(line, "Connect ");
2023: printf("(to) ");
2024: gets(&line[strlen(line)]);
2025: makeargv();
2026: argc = margc;
2027: argv = margv;
2028: }
2029: if (argc > 3) {
2030: printf("usage: %s host-name [port]\n", argv[0]);
2031: return 0;
2032: }
2033: sin.sin_addr.s_addr = inet_addr(argv[1]);
2034: if (sin.sin_addr.s_addr != -1) {
2035: sin.sin_family = AF_INET;
2036: (void) strcpy(hnamebuf, argv[1]);
2037: hostname = hnamebuf;
2038: } else {
2039: host = gethostbyname(argv[1]);
2040: if (host) {
2041: sin.sin_family = host->h_addrtype;
2042: #ifndef NOT43
2043: bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
2044: host->h_length);
2045: #else NOT43
2046: bcopy(host->h_addr, (caddr_t)&sin.sin_addr,
2047: host->h_length);
2048: #endif NOT43
2049: hostname = host->h_name;
2050: } else {
2051: printf("%s: unknown host\n", argv[1]);
2052: return 0;
2053: }
2054: }
2055: sin.sin_port = sp->s_port;
2056: if (argc == 3) {
2057: sin.sin_port = atoi(argv[2]);
2058: if (sin.sin_port == 0) {
2059: sp = getservbyname(argv[2], "tcp");
2060: if (sp)
2061: sin.sin_port = sp->s_port;
2062: else {
2063: printf("%s: bad port number\n", argv[2]);
2064: return 0;
2065: }
2066: } else {
2067: sin.sin_port = atoi(argv[2]);
2068: sin.sin_port = htons(sin.sin_port);
2069: }
2070: telnetport = 0;
2071: } else {
2072: telnetport = 1;
2073: }
2074: signal(SIGINT, intr);
2075: signal(SIGQUIT, intr2);
2076: signal(SIGPIPE, deadpeer);
2077: printf("Trying...\n");
2078: do {
2079: net = socket(AF_INET, SOCK_STREAM, 0);
2080: if (net < 0) {
2081: perror("telnet: socket");
2082: return 0;
2083: }
2084: #ifndef NOT43
2085: if (debug &&
2086: setsockopt(net, SOL_SOCKET, SO_DEBUG,
2087: (char *)&debug, sizeof(debug)) < 0)
2088: #else NOT43
2089: if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
2090: #endif NOT43
2091: perror("setsockopt (SO_DEBUG)");
2092:
2093: if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
2094: #ifndef NOT43
2095: if (host && host->h_addr_list[1]) {
2096: int oerrno = errno;
2097:
2098: fprintf(stderr,
2099: "telnet: connect to address %s: ",
2100: inet_ntoa(sin.sin_addr));
2101: errno = oerrno;
2102: perror((char *)0);
2103: host->h_addr_list++;
2104: bcopy(host->h_addr_list[0],
2105: (caddr_t)&sin.sin_addr, host->h_length);
2106: fprintf(stderr, "Trying %s...\n",
2107: inet_ntoa(sin.sin_addr));
2108: (void) close(net);
2109: continue;
2110: }
2111: #endif NOT43
2112: perror("telnet: connect");
2113: signal(SIGINT, SIG_DFL);
2114: signal(SIGQUIT, SIG_DFL);
2115: return 0;
2116: }
2117: connected++;
2118: } while (connected == 0);
2119: call(status, "status", "notmuch", 0);
2120: if (setjmp(peerdied) == 0)
2121: telnet();
2122: fprintf(stderr, "Connection closed by foreign host.\n");
2123: exit(1);
2124: /*NOTREACHED*/
2125: }
2126:
2127:
2128: #define HELPINDENT (sizeof ("connect"))
2129:
2130: char openhelp[] = "connect to a site";
2131: char closehelp[] = "close current connection";
2132: char quithelp[] = "exit telnet";
2133: char zhelp[] = "suspend telnet";
2134: char statushelp[] = "print status information";
2135: char helphelp[] = "print help information";
2136: char sendhelp[] = "transmit special characters ('send ?' for more)";
2137: char sethelp[] = "set operating parameters ('set ?' for more)";
2138: char togglestring[] ="toggle operating parameters ('toggle ?' for more)";
2139: char displayhelp[] = "display operating parameters";
2140: char modehelp[] =
2141: "try to enter line-by-line or character-at-a-time mode";
2142:
2143: int help();
2144:
2145: struct cmd cmdtab[] = {
2146: { "close", closehelp, bye, 1, 1 },
2147: { "display", displayhelp, display, 1, 0 },
2148: { "mode", modehelp, modecmd, 1, 1 },
2149: { "open", openhelp, tn, 1, 0 },
2150: { "quit", quithelp, quit, 1, 0 },
2151: { "send", sendhelp, sendcmd, 1, 1 },
2152: { "set", sethelp, setcmd, 1, 0 },
2153: { "status", statushelp, status, 1, 0 },
2154: { "toggle", togglestring, toggle, 1, 0 },
2155: { "z", zhelp, suspend, 1, 0 },
2156: { "?", helphelp, help, 1, 0 },
2157: 0
2158: };
2159:
2160: char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
2161: char escapehelp[] = "deprecated command -- use 'set escape' instead";
2162:
2163: struct cmd cmdtab2[] = {
2164: { "help", helphelp, help, 0, 0 },
2165: { "escape", escapehelp, setescape, 1, 0 },
2166: { "crmod", crmodhelp, togcrmod, 1, 0 },
2167: 0
2168: };
2169:
2170: /*
2171: * Help command.
2172: */
2173: help(argc, argv)
2174: int argc;
2175: char *argv[];
2176: {
2177: register struct cmd *c;
2178:
2179: if (argc == 1) {
2180: printf("Commands may be abbreviated. Commands are:\n\n");
2181: for (c = cmdtab; c->name; c++)
2182: if (c->dohelp) {
2183: printf("%-*s\t%s\n", HELPINDENT, c->name,
2184: c->help);
2185: }
2186: return 0;
2187: }
2188: while (--argc > 0) {
2189: register char *arg;
2190: arg = *++argv;
2191: c = getcmd(arg);
2192: if (c == Ambiguous(struct cmd *))
2193: printf("?Ambiguous help command %s\n", arg);
2194: else if (c == (struct cmd *)0)
2195: printf("?Invalid help command %s\n", arg);
2196: else
2197: printf("%s\n", c->help);
2198: }
2199: return 0;
2200: }
2201: /*
2202: * Call routine with argc, argv set from args (terminated by 0).
2203: * VARARGS2
2204: */
2205: call(routine, args)
2206: int (*routine)();
2207: char *args;
2208: {
2209: register char **argp;
2210: register int argc;
2211:
2212: for (argc = 0, argp = &args; *argp++ != 0; argc++)
2213: ;
2214: return (*routine)(argc, &args);
2215: }
2216:
2217: makeargv()
2218: {
2219: register char *cp;
2220: register char **argp = margv;
2221:
2222: margc = 0;
2223: for (cp = line; *cp;) {
2224: while (isspace(*cp))
2225: cp++;
2226: if (*cp == '\0')
2227: break;
2228: *argp++ = cp;
2229: margc += 1;
2230: while (*cp != '\0' && !isspace(*cp))
2231: cp++;
2232: if (*cp == '\0')
2233: break;
2234: *cp++ = '\0';
2235: }
2236: *argp++ = 0;
2237: }
2238:
2239: char **
2240: getnextcmd(name)
2241: char *name;
2242: {
2243: struct cmd *c = (struct cmd *) name;
2244:
2245: return (char **) (c+1);
2246: }
2247:
2248: struct cmd *
2249: getcmd(name)
2250: char *name;
2251: {
2252: struct cmd *cm;
2253:
2254: if (cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) {
2255: return cm;
2256: } else {
2257: return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
2258: }
2259: }
2260:
2261: command(top)
2262: int top;
2263: {
2264: register struct cmd *c;
2265:
2266: setcommandmode();
2267: if (!top) {
2268: putchar('\n');
2269: } else {
2270: signal(SIGINT, SIG_DFL);
2271: signal(SIGQUIT, SIG_DFL);
2272: }
2273: for (;;) {
2274: printf("%s> ", prompt);
2275: if (gets(line) == 0) {
2276: if (feof(stdin))
2277: quit();
2278: break;
2279: }
2280: if (line[0] == 0)
2281: break;
2282: makeargv();
2283: c = getcmd(margv[0]);
2284: if (c == Ambiguous(struct cmd *)) {
2285: printf("?Ambiguous command\n");
2286: continue;
2287: }
2288: if (c == 0) {
2289: printf("?Invalid command\n");
2290: continue;
2291: }
2292: if (c->needconnect && !connected) {
2293: printf("?Need to be connected first.\n");
2294: continue;
2295: }
2296: if ((*c->handler)(margc, margv)) {
2297: break;
2298: }
2299: }
2300: if (!top) {
2301: if (!connected) {
2302: longjmp(toplevel, 1);
2303: /*NOTREACHED*/
2304: }
2305: setconnmode();
2306: }
2307: }
2308:
2309: /*
2310: * main. Parse arguments, invoke the protocol or command parser.
2311: */
2312:
2313:
2314: main(argc, argv)
2315: int argc;
2316: char *argv[];
2317: {
2318: sp = getservbyname("telnet", "tcp");
2319: if (sp == 0) {
2320: fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
2321: exit(1);
2322: }
2323: NetTrace = stdout;
2324: ioctl(0, TIOCGETP, (char *)&ottyb);
2325: ioctl(0, TIOCGETC, (char *)&otc);
2326: ioctl(0, TIOCGLTC, (char *)&oltc);
2327: #if defined(LNOFLSH)
2328: ioctl(0, TIOCLGET, (char *)&autoflush);
2329: autoflush = !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */
2330: #else /* LNOFLSH */
2331: autoflush = 1;
2332: #endif /* LNOFLSH */
2333: ntc = otc;
2334: nltc = oltc;
2335: nttyb = ottyb;
2336: setbuf(stdin, (char *)0);
2337: setbuf(stdout, (char *)0);
2338: prompt = argv[0];
2339: if (argc > 1 && !strcmp(argv[1], "-d")) {
2340: debug = 1;
2341: argv++;
2342: argc--;
2343: }
2344: if (argc > 1 && !strcmp(argv[1], "-n")) {
2345: argv++;
2346: argc--;
2347: if (argc > 1) { /* get file name */
2348: NetTrace = fopen(argv[1], "w");
2349: argv++;
2350: argc--;
2351: if (NetTrace == NULL) {
2352: NetTrace = stdout;
2353: }
2354: }
2355: }
2356: if (argc != 1) {
2357: if (setjmp(toplevel) != 0)
2358: exit(0);
2359: tn(argc, argv);
2360: }
2361: setjmp(toplevel);
2362: for (;;)
2363: command(1);
2364: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.