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