Commit | Line | Data |
---|---|---|
22e155fc DF |
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 | ||
f18adf63 | 13 | #ifndef lint |
22e155fc DF |
14 | static char sccsid[] = "@(#)telnet.c 5.1 (Berkeley) %G%"; |
15 | #endif not lint | |
f18adf63 | 16 | |
a19db822 BJ |
17 | /* |
18 | * User telnet program. | |
19 | */ | |
de3b21e8 SL |
20 | #include <sys/types.h> |
21 | #include <sys/socket.h> | |
fb8e28da | 22 | #include <sys/ioctl.h> |
de3b21e8 SL |
23 | |
24 | #include <netinet/in.h> | |
25 | ||
9c1dab9e SL |
26 | #define TELOPTS |
27 | #include <arpa/telnet.h> | |
28 | ||
a19db822 BJ |
29 | #include <stdio.h> |
30 | #include <ctype.h> | |
31 | #include <errno.h> | |
32 | #include <signal.h> | |
a19db822 | 33 | #include <setjmp.h> |
9f005877 | 34 | #include <netdb.h> |
de3b21e8 | 35 | |
a19db822 | 36 | #define strip(x) ((x)&0177) |
a19db822 BJ |
37 | |
38 | char ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf; | |
a50d5753 | 39 | char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; |
a19db822 BJ |
40 | |
41 | char hisopts[256]; | |
42 | char myopts[256]; | |
43 | ||
44 | char doopt[] = { IAC, DO, '%', 'c', 0 }; | |
45 | char dont[] = { IAC, DONT, '%', 'c', 0 }; | |
46 | char will[] = { IAC, WILL, '%', 'c', 0 }; | |
47 | char wont[] = { IAC, WONT, '%', 'c', 0 }; | |
48 | ||
49 | int connected; | |
50 | int net; | |
fb8e28da | 51 | int showoptions = 0; |
19baf46e | 52 | int options; |
4971ba8c | 53 | int debug = 0; |
fb8e28da | 54 | int crmod = 0; |
a19db822 | 55 | char *prompt; |
fb8e28da | 56 | char escape = CTRL(]); |
a19db822 BJ |
57 | |
58 | char line[200]; | |
59 | int margc; | |
60 | char *margv[20]; | |
61 | ||
62 | jmp_buf toplevel; | |
63 | jmp_buf peerdied; | |
64 | ||
65 | extern int errno; | |
66 | ||
67 | int tn(), quit(), suspend(), bye(), help(); | |
de372207 | 68 | int setescape(), status(), toggle(), setoptions(); |
40bf00b7 | 69 | int setcrmod(), setdebug(), sendesc(), ayt(), intp(); |
a19db822 | 70 | |
a50d5753 | 71 | #define HELPINDENT (sizeof ("connect")) |
a19db822 BJ |
72 | |
73 | struct cmd { | |
f18adf63 SL |
74 | char *name; /* command name */ |
75 | char *help; /* help string */ | |
76 | int (*handler)(); /* routine which executes command */ | |
a19db822 BJ |
77 | }; |
78 | ||
f18adf63 SL |
79 | char openhelp[] = "connect to a site"; |
80 | char closehelp[] = "close current connection"; | |
81 | char quithelp[] = "exit telnet"; | |
82 | char zhelp[] = "suspend telnet"; | |
83 | char debughelp[] = "toggle debugging"; | |
84 | char escapehelp[] = "set escape character"; | |
85 | char statushelp[] = "print status information"; | |
86 | char helphelp[] = "print help information"; | |
87 | char optionshelp[] = "toggle viewing of options processing"; | |
88 | char crmodhelp[] = "toggle mapping of received carriage returns"; | |
40bf00b7 RC |
89 | char sendeschelp[] = "send escape character"; |
90 | char aythelp[] = "send Are You There"; | |
91 | char intphelp[] = "send Interrupt Process"; | |
a19db822 BJ |
92 | |
93 | struct cmd cmdtab[] = { | |
f18adf63 SL |
94 | { "open", openhelp, tn }, |
95 | { "close", closehelp, bye }, | |
96 | { "quit", quithelp, quit }, | |
a19db822 | 97 | { "z", zhelp, suspend }, |
f18adf63 SL |
98 | { "escape", escapehelp, setescape }, |
99 | { "status", statushelp, status }, | |
100 | { "options", optionshelp, setoptions }, | |
101 | { "crmod", crmodhelp, setcrmod }, | |
102 | { "debug", debughelp, setdebug }, | |
40bf00b7 RC |
103 | { "ayt", aythelp, ayt }, |
104 | { "interrupt", intphelp, intp }, | |
105 | { "passthru", sendeschelp, sendesc }, | |
f18adf63 | 106 | { "?", helphelp, help }, |
a19db822 BJ |
107 | 0 |
108 | }; | |
109 | ||
fb8e28da | 110 | struct sockaddr_in sin; |
a19db822 BJ |
111 | |
112 | int intr(), deadpeer(); | |
113 | char *control(); | |
114 | struct cmd *getcmd(); | |
9f005877 | 115 | struct servent *sp; |
a19db822 | 116 | |
ce292c83 SL |
117 | struct tchars otc; |
118 | struct ltchars oltc; | |
119 | struct sgttyb ottyb; | |
a50d5753 | 120 | |
a19db822 BJ |
121 | main(argc, argv) |
122 | int argc; | |
123 | char *argv[]; | |
124 | { | |
9f005877 SL |
125 | sp = getservbyname("telnet", "tcp"); |
126 | if (sp == 0) { | |
127 | fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); | |
128 | exit(1); | |
129 | } | |
ce292c83 SL |
130 | ioctl(0, TIOCGETP, (char *)&ottyb); |
131 | ioctl(0, TIOCGETC, (char *)&otc); | |
132 | ioctl(0, TIOCGLTC, (char *)&oltc); | |
a19db822 BJ |
133 | setbuf(stdin, 0); |
134 | setbuf(stdout, 0); | |
135 | prompt = argv[0]; | |
87e91975 CL |
136 | if (argc > 1 && !strcmp(argv[1], "-d")) { |
137 | debug = 1; | |
138 | argv++; | |
139 | argc--; | |
140 | } | |
a19db822 BJ |
141 | if (argc != 1) { |
142 | if (setjmp(toplevel) != 0) | |
143 | exit(0); | |
144 | tn(argc, argv); | |
145 | } | |
146 | setjmp(toplevel); | |
147 | for (;;) | |
148 | command(1); | |
149 | } | |
150 | ||
150ad7e5 SL |
151 | char *hostname; |
152 | char hnamebuf[32]; | |
a19db822 BJ |
153 | |
154 | tn(argc, argv) | |
155 | int argc; | |
156 | char *argv[]; | |
157 | { | |
158 | register int c; | |
150ad7e5 | 159 | register struct hostent *host; |
a19db822 BJ |
160 | |
161 | if (connected) { | |
150ad7e5 | 162 | printf("?Already connected to %s\n", hostname); |
a19db822 BJ |
163 | return; |
164 | } | |
165 | if (argc < 2) { | |
166 | strcpy(line, "Connect "); | |
167 | printf("(to) "); | |
168 | gets(&line[strlen(line)]); | |
169 | makeargv(); | |
170 | argc = margc; | |
171 | argv = margv; | |
172 | } | |
173 | if (argc > 3) { | |
174 | printf("usage: %s host-name [port]\n", argv[0]); | |
175 | return; | |
176 | } | |
9f005877 | 177 | host = gethostbyname(argv[1]); |
150ad7e5 | 178 | if (host) { |
de3b21e8 SL |
179 | sin.sin_family = host->h_addrtype; |
180 | bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length); | |
150ad7e5 SL |
181 | hostname = host->h_name; |
182 | } else { | |
de3b21e8 | 183 | sin.sin_family = AF_INET; |
150ad7e5 SL |
184 | sin.sin_addr.s_addr = inet_addr(argv[1]); |
185 | if (sin.sin_addr.s_addr == -1) { | |
186 | printf("%s: unknown host\n", argv[1]); | |
187 | return; | |
188 | } | |
189 | strcpy(hnamebuf, argv[1]); | |
190 | hostname = hnamebuf; | |
a19db822 | 191 | } |
9f005877 | 192 | sin.sin_port = sp->s_port; |
de372207 SL |
193 | if (argc == 3) { |
194 | sin.sin_port = atoi(argv[2]); | |
195 | if (sin.sin_port < 0) { | |
196 | printf("%s: bad port number\n", argv[2]); | |
197 | return; | |
198 | } | |
5eba30a3 | 199 | sin.sin_port = htons(sin.sin_port); |
de372207 | 200 | } |
40bf00b7 | 201 | net = socket(AF_INET, SOCK_STREAM, 0); |
de3b21e8 SL |
202 | if (net < 0) { |
203 | perror("telnet: socket"); | |
a19db822 BJ |
204 | return; |
205 | } | |
87e91975 CL |
206 | if (debug && |
207 | setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0) | |
f18adf63 | 208 | perror("setsockopt (SO_DEBUG)"); |
4ca10280 SL |
209 | signal(SIGINT, intr); |
210 | signal(SIGPIPE, deadpeer); | |
a19db822 | 211 | printf("Trying...\n"); |
40bf00b7 | 212 | if (connect(net, (caddr_t)&sin, sizeof (sin)) < 0) { |
de3b21e8 | 213 | perror("telnet: connect"); |
4ca10280 | 214 | signal(SIGINT, SIG_DFL); |
a19db822 BJ |
215 | return; |
216 | } | |
a19db822 BJ |
217 | connected++; |
218 | call(status, "status", 0); | |
219 | if (setjmp(peerdied) == 0) | |
220 | telnet(net); | |
221 | fprintf(stderr, "Connection closed by foreign host.\n"); | |
222 | exit(1); | |
223 | } | |
224 | ||
225 | /* | |
226 | * Print status about the connection. | |
227 | */ | |
228 | /*VARARGS*/ | |
229 | status() | |
230 | { | |
231 | if (connected) | |
150ad7e5 | 232 | printf("Connected to %s.\n", hostname); |
a19db822 BJ |
233 | else |
234 | printf("No connection.\n"); | |
235 | printf("Escape character is '%s'.\n", control(escape)); | |
fb8e28da | 236 | fflush(stdout); |
a19db822 BJ |
237 | } |
238 | ||
239 | makeargv() | |
240 | { | |
241 | register char *cp; | |
242 | register char **argp = margv; | |
243 | ||
244 | margc = 0; | |
245 | for (cp = line; *cp;) { | |
246 | while (isspace(*cp)) | |
247 | cp++; | |
248 | if (*cp == '\0') | |
249 | break; | |
250 | *argp++ = cp; | |
251 | margc += 1; | |
252 | while (*cp != '\0' && !isspace(*cp)) | |
253 | cp++; | |
254 | if (*cp == '\0') | |
255 | break; | |
256 | *cp++ = '\0'; | |
257 | } | |
258 | *argp++ = 0; | |
259 | } | |
260 | ||
261 | /*VARARGS*/ | |
262 | suspend() | |
263 | { | |
264 | register int save; | |
265 | ||
266 | save = mode(0); | |
a50d5753 SL |
267 | kill(0, SIGTSTP); |
268 | /* reget parameters in case they were changed */ | |
ce292c83 SL |
269 | ioctl(0, TIOCGETP, (char *)&ottyb); |
270 | ioctl(0, TIOCGETC, (char *)&otc); | |
271 | ioctl(0, TIOCGLTC, (char *)&oltc); | |
a50d5753 | 272 | (void) mode(save); |
a19db822 BJ |
273 | } |
274 | ||
275 | /*VARARGS*/ | |
276 | bye() | |
277 | { | |
a50d5753 | 278 | register char *op; |
a19db822 | 279 | |
a50d5753 | 280 | (void) mode(0); |
a19db822 | 281 | if (connected) { |
f18adf63 | 282 | shutdown(net, 2); |
a19db822 BJ |
283 | printf("Connection closed.\n"); |
284 | close(net); | |
285 | connected = 0; | |
a50d5753 SL |
286 | /* reset his options */ |
287 | for (op = hisopts; op < &hisopts[256]; op++) | |
288 | *op = 0; | |
a19db822 BJ |
289 | } |
290 | } | |
291 | ||
292 | /*VARARGS*/ | |
293 | quit() | |
294 | { | |
295 | call(bye, "bye", 0); | |
296 | exit(0); | |
297 | } | |
298 | ||
299 | /* | |
300 | * Help command. | |
a19db822 BJ |
301 | */ |
302 | help(argc, argv) | |
303 | int argc; | |
304 | char *argv[]; | |
305 | { | |
306 | register struct cmd *c; | |
307 | ||
308 | if (argc == 1) { | |
309 | printf("Commands may be abbreviated. Commands are:\n\n"); | |
310 | for (c = cmdtab; c->name; c++) | |
311 | printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); | |
312 | return; | |
313 | } | |
314 | while (--argc > 0) { | |
315 | register char *arg; | |
316 | arg = *++argv; | |
317 | c = getcmd(arg); | |
318 | if (c == (struct cmd *)-1) | |
319 | printf("?Ambiguous help command %s\n", arg); | |
320 | else if (c == (struct cmd *)0) | |
321 | printf("?Invalid help command %s\n", arg); | |
322 | else | |
323 | printf("%s\n", c->help); | |
324 | } | |
325 | } | |
326 | ||
327 | /* | |
328 | * Call routine with argc, argv set from args (terminated by 0). | |
329 | * VARARGS2 | |
330 | */ | |
331 | call(routine, args) | |
332 | int (*routine)(); | |
333 | int args; | |
334 | { | |
335 | register int *argp; | |
336 | register int argc; | |
337 | ||
338 | for (argc = 0, argp = &args; *argp++ != 0; argc++) | |
339 | ; | |
340 | (*routine)(argc, &args); | |
341 | } | |
342 | ||
ce292c83 SL |
343 | struct tchars notc = { -1, -1, -1, -1, -1, -1 }; |
344 | struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; | |
fb8e28da | 345 | |
a19db822 BJ |
346 | mode(f) |
347 | register int f; | |
348 | { | |
a50d5753 | 349 | static int prevmode = 0; |
ce292c83 SL |
350 | struct tchars *tc; |
351 | struct ltchars *ltc; | |
352 | struct sgttyb sb; | |
353 | int onoff, old; | |
a50d5753 SL |
354 | |
355 | if (prevmode == f) | |
356 | return (f); | |
357 | old = prevmode; | |
358 | prevmode = f; | |
ce292c83 | 359 | sb = ottyb; |
a19db822 | 360 | switch (f) { |
a50d5753 | 361 | |
a19db822 | 362 | case 0: |
a19db822 | 363 | onoff = 0; |
fb8e28da | 364 | tc = &otc; |
ce292c83 | 365 | ltc = &oltc; |
a19db822 BJ |
366 | break; |
367 | ||
368 | case 1: | |
a19db822 | 369 | case 2: |
ce292c83 | 370 | sb.sg_flags |= CBREAK; |
a50d5753 | 371 | if (f == 1) |
ce292c83 | 372 | sb.sg_flags &= ~(ECHO|CRMOD); |
a50d5753 | 373 | else |
ce292c83 SL |
374 | sb.sg_flags |= ECHO|CRMOD; |
375 | sb.sg_erase = sb.sg_kill = -1; | |
fb8e28da | 376 | tc = ¬c; |
ce292c83 | 377 | ltc = &noltc; |
a19db822 | 378 | onoff = 1; |
fb8e28da SL |
379 | break; |
380 | ||
381 | default: | |
382 | return; | |
a19db822 | 383 | } |
ce292c83 SL |
384 | ioctl(fileno(stdin), TIOCSLTC, (char *)ltc); |
385 | ioctl(fileno(stdin), TIOCSETC, (char *)tc); | |
386 | ioctl(fileno(stdin), TIOCSETP, (char *)&sb); | |
a19db822 BJ |
387 | ioctl(fileno(stdin), FIONBIO, &onoff); |
388 | ioctl(fileno(stdout), FIONBIO, &onoff); | |
389 | return (old); | |
390 | } | |
391 | ||
392 | char sibuf[BUFSIZ], *sbp; | |
393 | char tibuf[BUFSIZ], *tbp; | |
394 | int scc, tcc; | |
395 | ||
396 | /* | |
397 | * Select from tty and network... | |
398 | */ | |
399 | telnet(s) | |
400 | int s; | |
401 | { | |
402 | register int c; | |
403 | int tin = fileno(stdin), tout = fileno(stdout); | |
404 | int on = 1; | |
405 | ||
a50d5753 | 406 | (void) mode(2); |
a19db822 | 407 | ioctl(s, FIONBIO, &on); |
40bf00b7 RC |
408 | if (!hisopts[TELOPT_SGA]) |
409 | willoption(TELOPT_SGA); | |
a19db822 BJ |
410 | for (;;) { |
411 | int ibits = 0, obits = 0; | |
412 | ||
413 | if (nfrontp - nbackp) | |
414 | obits |= (1 << s); | |
415 | else | |
416 | ibits |= (1 << tin); | |
417 | if (tfrontp - tbackp) | |
418 | obits |= (1 << tout); | |
419 | else | |
420 | ibits |= (1 << s); | |
421 | if (scc < 0 && tcc < 0) | |
422 | break; | |
de3b21e8 | 423 | select(16, &ibits, &obits, 0, 0); |
a19db822 BJ |
424 | if (ibits == 0 && obits == 0) { |
425 | sleep(5); | |
426 | continue; | |
427 | } | |
428 | ||
429 | /* | |
430 | * Something to read from the network... | |
431 | */ | |
432 | if (ibits & (1 << s)) { | |
a50d5753 | 433 | scc = read(s, sibuf, sizeof (sibuf)); |
a19db822 BJ |
434 | if (scc < 0 && errno == EWOULDBLOCK) |
435 | scc = 0; | |
436 | else { | |
437 | if (scc <= 0) | |
438 | break; | |
439 | sbp = sibuf; | |
440 | } | |
441 | } | |
442 | ||
443 | /* | |
444 | * Something to read from the tty... | |
445 | */ | |
446 | if (ibits & (1 << tin)) { | |
a50d5753 | 447 | tcc = read(tin, tibuf, sizeof (tibuf)); |
a19db822 BJ |
448 | if (tcc < 0 && errno == EWOULDBLOCK) |
449 | tcc = 0; | |
450 | else { | |
451 | if (tcc <= 0) | |
452 | break; | |
453 | tbp = tibuf; | |
454 | } | |
455 | } | |
456 | ||
457 | while (tcc > 0) { | |
458 | register int c; | |
459 | ||
460 | if ((&netobuf[BUFSIZ] - nfrontp) < 2) | |
461 | break; | |
462 | c = *tbp++ & 0377, tcc--; | |
463 | if (strip(c) == escape) { | |
464 | command(0); | |
465 | tcc = 0; | |
466 | break; | |
467 | } | |
40bf00b7 RC |
468 | switch (c) { |
469 | case '\n': | |
470 | if (!hisopts[TELOPT_ECHO]) | |
471 | *nfrontp++ = '\r'; | |
1698259c | 472 | *nfrontp++ = '\n'; |
40bf00b7 RC |
473 | break; |
474 | case '\r': | |
475 | *nfrontp++ = '\r'; | |
476 | if (hisopts[TELOPT_ECHO]) | |
477 | *nfrontp++ = '\n'; | |
478 | else | |
479 | *nfrontp++ = '\0'; | |
480 | break; | |
481 | case IAC: | |
482 | *nfrontp++ = IAC; | |
483 | /* fall into ... */ | |
484 | default: | |
485 | *nfrontp++ = c; | |
486 | break; | |
487 | } | |
a19db822 BJ |
488 | } |
489 | if ((obits & (1 << s)) && (nfrontp - nbackp) > 0) | |
490 | netflush(s); | |
491 | if (scc > 0) | |
492 | telrcv(); | |
493 | if ((obits & (1 << tout)) && (tfrontp - tbackp) > 0) | |
494 | ttyflush(tout); | |
495 | } | |
a50d5753 | 496 | (void) mode(0); |
a19db822 BJ |
497 | } |
498 | ||
499 | command(top) | |
500 | int top; | |
501 | { | |
502 | register struct cmd *c; | |
503 | int oldmode, wasopen; | |
504 | ||
505 | oldmode = mode(0); | |
506 | if (!top) | |
507 | putchar('\n'); | |
508 | else | |
4ca10280 | 509 | signal(SIGINT, SIG_DFL); |
a19db822 BJ |
510 | for (;;) { |
511 | printf("%s> ", prompt); | |
baba6eb5 SL |
512 | if (gets(line) == 0) { |
513 | if (feof(stdin)) { | |
514 | clearerr(stdin); | |
515 | putchar('\n'); | |
516 | } | |
a19db822 | 517 | break; |
baba6eb5 | 518 | } |
a19db822 BJ |
519 | if (line[0] == 0) |
520 | break; | |
521 | makeargv(); | |
522 | c = getcmd(margv[0]); | |
523 | if (c == (struct cmd *)-1) { | |
524 | printf("?Ambiguous command\n"); | |
525 | continue; | |
526 | } | |
527 | if (c == 0) { | |
528 | printf("?Invalid command\n"); | |
529 | continue; | |
530 | } | |
531 | (*c->handler)(margc, margv); | |
532 | if (c->handler != help) | |
533 | break; | |
534 | } | |
535 | if (!top) { | |
536 | if (!connected) | |
537 | longjmp(toplevel, 1); | |
a50d5753 | 538 | (void) mode(oldmode); |
a19db822 BJ |
539 | } |
540 | } | |
541 | ||
542 | /* | |
543 | * Telnet receiver states for fsm | |
544 | */ | |
545 | #define TS_DATA 0 | |
546 | #define TS_IAC 1 | |
547 | #define TS_WILL 2 | |
548 | #define TS_WONT 3 | |
549 | #define TS_DO 4 | |
550 | #define TS_DONT 5 | |
551 | ||
552 | telrcv() | |
553 | { | |
554 | register int c; | |
555 | static int state = TS_DATA; | |
556 | ||
557 | while (scc > 0) { | |
558 | c = *sbp++ & 0377, scc--; | |
559 | switch (state) { | |
560 | ||
561 | case TS_DATA: | |
fb8e28da | 562 | if (c == IAC) { |
a19db822 | 563 | state = TS_IAC; |
fb8e28da SL |
564 | continue; |
565 | } | |
566 | *tfrontp++ = c; | |
567 | /* | |
568 | * This hack is needed since we can't set | |
569 | * CRMOD on output only. Machines like MULTICS | |
570 | * like to send \r without \n; since we must | |
571 | * turn off CRMOD to get proper input, the mapping | |
572 | * is done here (sigh). | |
573 | */ | |
574 | if (c == '\r' && crmod) | |
575 | *tfrontp++ = '\n'; | |
a19db822 BJ |
576 | continue; |
577 | ||
578 | case TS_IAC: | |
579 | switch (c) { | |
580 | ||
581 | case WILL: | |
582 | state = TS_WILL; | |
583 | continue; | |
584 | ||
585 | case WONT: | |
586 | state = TS_WONT; | |
587 | continue; | |
588 | ||
589 | case DO: | |
590 | state = TS_DO; | |
591 | continue; | |
592 | ||
593 | case DONT: | |
594 | state = TS_DONT; | |
595 | continue; | |
596 | ||
597 | case DM: | |
598 | ioctl(fileno(stdout), TIOCFLUSH, 0); | |
599 | break; | |
600 | ||
601 | case NOP: | |
602 | case GA: | |
603 | break; | |
604 | ||
605 | default: | |
606 | break; | |
607 | } | |
608 | state = TS_DATA; | |
609 | continue; | |
610 | ||
611 | case TS_WILL: | |
9f005877 | 612 | printoption("RCVD", will, c, !hisopts[c]); |
a19db822 BJ |
613 | if (!hisopts[c]) |
614 | willoption(c); | |
615 | state = TS_DATA; | |
616 | continue; | |
617 | ||
618 | case TS_WONT: | |
9f005877 | 619 | printoption("RCVD", wont, c, hisopts[c]); |
a19db822 BJ |
620 | if (hisopts[c]) |
621 | wontoption(c); | |
622 | state = TS_DATA; | |
623 | continue; | |
624 | ||
625 | case TS_DO: | |
9f005877 | 626 | printoption("RCVD", doopt, c, !myopts[c]); |
a19db822 BJ |
627 | if (!myopts[c]) |
628 | dooption(c); | |
629 | state = TS_DATA; | |
630 | continue; | |
631 | ||
632 | case TS_DONT: | |
9f005877 | 633 | printoption("RCVD", dont, c, myopts[c]); |
a19db822 BJ |
634 | if (myopts[c]) { |
635 | myopts[c] = 0; | |
636 | sprintf(nfrontp, wont, c); | |
a50d5753 | 637 | nfrontp += sizeof (wont) - 2; |
9f005877 | 638 | printoption("SENT", wont, c); |
a19db822 BJ |
639 | } |
640 | state = TS_DATA; | |
641 | continue; | |
642 | } | |
643 | } | |
644 | } | |
645 | ||
646 | willoption(option) | |
647 | int option; | |
648 | { | |
649 | char *fmt; | |
650 | ||
651 | switch (option) { | |
652 | ||
653 | case TELOPT_ECHO: | |
a50d5753 | 654 | (void) mode(1); |
a19db822 BJ |
655 | |
656 | case TELOPT_SGA: | |
657 | hisopts[option] = 1; | |
658 | fmt = doopt; | |
659 | break; | |
660 | ||
661 | case TELOPT_TM: | |
662 | fmt = dont; | |
663 | break; | |
664 | ||
665 | default: | |
666 | fmt = dont; | |
667 | break; | |
668 | } | |
de372207 | 669 | sprintf(nfrontp, fmt, option); |
a50d5753 | 670 | nfrontp += sizeof (dont) - 2; |
9f005877 | 671 | printoption("SENT", fmt, option); |
a19db822 BJ |
672 | } |
673 | ||
674 | wontoption(option) | |
675 | int option; | |
676 | { | |
677 | char *fmt; | |
678 | ||
679 | switch (option) { | |
680 | ||
681 | case TELOPT_ECHO: | |
a50d5753 | 682 | (void) mode(2); |
a19db822 BJ |
683 | |
684 | case TELOPT_SGA: | |
685 | hisopts[option] = 0; | |
686 | fmt = dont; | |
687 | break; | |
688 | ||
689 | default: | |
690 | fmt = dont; | |
691 | } | |
692 | sprintf(nfrontp, fmt, option); | |
a50d5753 | 693 | nfrontp += sizeof (doopt) - 2; |
9f005877 | 694 | printoption("SENT", fmt, option); |
a19db822 BJ |
695 | } |
696 | ||
697 | dooption(option) | |
698 | int option; | |
699 | { | |
700 | char *fmt; | |
701 | ||
702 | switch (option) { | |
703 | ||
704 | case TELOPT_TM: | |
705 | fmt = wont; | |
706 | break; | |
680e328e SL |
707 | |
708 | case TELOPT_ECHO: | |
709 | (void) mode(2); | |
710 | fmt = will; | |
711 | hisopts[option] = 0; | |
712 | break; | |
a19db822 BJ |
713 | |
714 | case TELOPT_SGA: | |
715 | fmt = will; | |
716 | break; | |
717 | ||
718 | default: | |
719 | fmt = wont; | |
720 | break; | |
721 | } | |
722 | sprintf(nfrontp, fmt, option); | |
a50d5753 | 723 | nfrontp += sizeof (doopt) - 2; |
9f005877 | 724 | printoption("SENT", fmt, option); |
a19db822 BJ |
725 | } |
726 | ||
727 | /* | |
728 | * Set the escape character. | |
729 | */ | |
730 | setescape(argc, argv) | |
731 | int argc; | |
732 | char *argv[]; | |
733 | { | |
734 | register char *arg; | |
735 | char buf[50]; | |
736 | ||
737 | if (argc > 2) | |
738 | arg = argv[1]; | |
739 | else { | |
740 | printf("new escape character: "); | |
741 | gets(buf); | |
742 | arg = buf; | |
743 | } | |
744 | if (arg[0] != '\0') | |
745 | escape = arg[0]; | |
746 | printf("Escape character is '%s'.\n", control(escape)); | |
fb8e28da | 747 | fflush(stdout); |
a19db822 BJ |
748 | } |
749 | ||
de372207 SL |
750 | /*VARARGS*/ |
751 | setoptions() | |
752 | { | |
fb8e28da | 753 | |
de372207 SL |
754 | showoptions = !showoptions; |
755 | printf("%s show option processing.\n", showoptions ? "Will" : "Wont"); | |
fb8e28da SL |
756 | fflush(stdout); |
757 | } | |
758 | ||
759 | /*VARARGS*/ | |
760 | setcrmod() | |
761 | { | |
762 | ||
763 | crmod = !crmod; | |
764 | printf("%s map carriage return on output.\n", crmod ? "Will" : "Wont"); | |
765 | fflush(stdout); | |
de372207 SL |
766 | } |
767 | ||
4971ba8c SL |
768 | /*VARARGS*/ |
769 | setdebug() | |
770 | { | |
771 | ||
87e91975 | 772 | debug = debug ? 0 : 1; |
4971ba8c SL |
773 | printf("%s turn on socket level debugging.\n", |
774 | debug ? "Will" : "Wont"); | |
775 | fflush(stdout); | |
87e91975 CL |
776 | if (net > 0 && |
777 | setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0) | |
f18adf63 | 778 | perror("setsockopt (SO_DEBUG)"); |
4971ba8c SL |
779 | } |
780 | ||
40bf00b7 RC |
781 | sendesc() |
782 | { | |
783 | *nfrontp++ = escape; | |
784 | } | |
785 | ||
786 | ayt() | |
787 | { | |
788 | *nfrontp++ = IAC; | |
789 | *nfrontp++ = AYT; | |
790 | } | |
791 | ||
792 | intp() | |
793 | { | |
794 | *nfrontp++ = IAC; | |
795 | *nfrontp++ = IP; | |
796 | } | |
797 | ||
a19db822 BJ |
798 | /* |
799 | * Construct a control character sequence | |
800 | * for a special character. | |
801 | */ | |
802 | char * | |
803 | control(c) | |
804 | register int c; | |
805 | { | |
806 | static char buf[3]; | |
807 | ||
808 | if (c == 0177) | |
809 | return ("^?"); | |
810 | if (c >= 040) { | |
811 | buf[0] = c; | |
812 | buf[1] = 0; | |
813 | } else { | |
814 | buf[0] = '^'; | |
815 | buf[1] = '@'+c; | |
816 | buf[2] = 0; | |
817 | } | |
818 | return (buf); | |
819 | } | |
820 | ||
821 | struct cmd * | |
822 | getcmd(name) | |
823 | register char *name; | |
824 | { | |
825 | register char *p, *q; | |
826 | register struct cmd *c, *found; | |
827 | register int nmatches, longest; | |
828 | ||
829 | longest = 0; | |
830 | nmatches = 0; | |
831 | found = 0; | |
832 | for (c = cmdtab; p = c->name; c++) { | |
833 | for (q = name; *q == *p++; q++) | |
834 | if (*q == 0) /* exact match? */ | |
835 | return (c); | |
836 | if (!*q) { /* the name was a prefix */ | |
837 | if (q - name > longest) { | |
838 | longest = q - name; | |
839 | nmatches = 1; | |
840 | found = c; | |
841 | } else if (q - name == longest) | |
842 | nmatches++; | |
843 | } | |
844 | } | |
845 | if (nmatches > 1) | |
846 | return ((struct cmd *)-1); | |
847 | return (found); | |
848 | } | |
849 | ||
850 | deadpeer() | |
851 | { | |
a50d5753 | 852 | (void) mode(0); |
a19db822 BJ |
853 | longjmp(peerdied, -1); |
854 | } | |
855 | ||
856 | intr() | |
857 | { | |
a50d5753 | 858 | (void) mode(0); |
a19db822 BJ |
859 | longjmp(toplevel, -1); |
860 | } | |
861 | ||
862 | ttyflush(fd) | |
863 | { | |
864 | int n; | |
865 | ||
866 | if ((n = tfrontp - tbackp) > 0) | |
867 | n = write(fd, tbackp, n); | |
9f005877 SL |
868 | if (n < 0) |
869 | return; | |
a19db822 BJ |
870 | tbackp += n; |
871 | if (tbackp == tfrontp) | |
872 | tbackp = tfrontp = ttyobuf; | |
873 | } | |
874 | ||
875 | netflush(fd) | |
876 | { | |
877 | int n; | |
878 | ||
879 | if ((n = nfrontp - nbackp) > 0) | |
880 | n = write(fd, nbackp, n); | |
f103d8a9 SL |
881 | if (n < 0) { |
882 | if (errno != ENOBUFS && errno != EWOULDBLOCK) { | |
a50d5753 | 883 | (void) mode(0); |
150ad7e5 | 884 | perror(hostname); |
f103d8a9 SL |
885 | close(fd); |
886 | longjmp(peerdied, -1); | |
887 | /*NOTREACHED*/ | |
888 | } | |
a19db822 | 889 | n = 0; |
f103d8a9 | 890 | } |
a19db822 BJ |
891 | nbackp += n; |
892 | if (nbackp == nfrontp) | |
893 | nbackp = nfrontp = netobuf; | |
894 | } | |
de372207 | 895 | |
6cebba9f BJ |
896 | /*VARARGS*/ |
897 | printoption(direction, fmt, option, what) | |
de372207 | 898 | char *direction, *fmt; |
6cebba9f | 899 | int option, what; |
de372207 | 900 | { |
9f005877 SL |
901 | if (!showoptions) |
902 | return; | |
de372207 SL |
903 | printf("%s ", direction); |
904 | if (fmt == doopt) | |
905 | fmt = "do"; | |
906 | else if (fmt == dont) | |
907 | fmt = "dont"; | |
908 | else if (fmt == will) | |
909 | fmt = "will"; | |
910 | else if (fmt == wont) | |
911 | fmt = "wont"; | |
912 | else | |
913 | fmt = "???"; | |
914 | if (option < TELOPT_SUPDUP) | |
6cebba9f | 915 | printf("%s %s", fmt, telopts[option]); |
de372207 | 916 | else |
6cebba9f BJ |
917 | printf("%s %d", fmt, option); |
918 | if (*direction == '<') { | |
919 | printf("\r\n"); | |
920 | return; | |
921 | } | |
922 | printf(" (%s)\r\n", what ? "reply" : "don't reply"); | |
de372207 | 923 | } |