| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * Copyright (c) 1989, 1993 | |
| 3 | * The Regents of the University of California. All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * 3. All advertising materials mentioning features or use of this software | |
| 14 | * must display the following acknowledgement: | |
| 15 | * This product includes software developed by the University of | |
| 16 | * California, Berkeley and its contributors. | |
| 17 | * 4. Neither the name of the University nor the names of its contributors | |
| 18 | * may be used to endorse or promote products derived from this software | |
| 19 | * without specific prior written permission. | |
| 20 | * | |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 31 | * SUCH DAMAGE. | |
| 1de703da MD |
32 | * |
| 33 | * @(#)telnetd.c 8.4 (Berkeley) 5/30/95 | |
| 068e7061 | 34 | * $FreeBSD: src/crypto/telnet/telnetd/telnetd.c,v 1.11.2.5 2002/04/13 10:59:09 markm Exp $ |
| 984263bc MD |
35 | */ |
| 36 | ||
| 984263bc MD |
37 | #include "telnetd.h" |
| 38 | #include "pathnames.h" | |
| 39 | ||
| 40 | #include <sys/mman.h> | |
| 41 | #include <err.h> | |
| 42 | #include <libutil.h> | |
| 43 | #include <paths.h> | |
| 44 | #include <termcap.h> | |
| 45 | #include <utmp.h> | |
| 46 | ||
| 47 | #include <arpa/inet.h> | |
| 48 | ||
| 068e7061 PA |
49 | #ifdef AUTHENTICATION |
| 50 | #include <libtelnet/auth.h> | |
| 51 | int auth_level = 0; | |
| 52 | #endif | |
| 53 | #ifdef ENCRYPTION | |
| 54 | #include <libtelnet/encrypt.h> | |
| 55 | #endif | |
| 984263bc MD |
56 | #include <libtelnet/misc.h> |
| 57 | ||
| 58 | char remote_hostname[MAXHOSTNAMELEN]; | |
| 59 | size_t utmp_len = sizeof(remote_hostname) - 1; | |
| 60 | int registerd_host_only = 0; | |
| 61 | ||
| 62 | ||
| 63 | /* | |
| 64 | * I/O data buffers, | |
| 65 | * pointers, and counters. | |
| 66 | */ | |
| 67 | char ptyibuf[BUFSIZ], *ptyip = ptyibuf; | |
| 68 | char ptyibuf2[BUFSIZ]; | |
| 69 | ||
| 70 | int readstream(int, char *, int); | |
| 71 | void doit(struct sockaddr *); | |
| 72 | int terminaltypeok(char *); | |
| 73 | ||
| 74 | int hostinfo = 1; /* do we print login banner? */ | |
| 75 | ||
| 76 | int debug = 0; | |
| 77 | int keepalive = 1; | |
| 78 | const char *altlogin; | |
| 79 | ||
| 80 | void doit(struct sockaddr *); | |
| 81 | int terminaltypeok(char *); | |
| 82 | void startslave(char *, int, char *); | |
| 83 | extern void usage(void); | |
| 84 | static void _gettermname(void); | |
| 85 | ||
| 86 | /* | |
| 87 | * The string to pass to getopt(). We do it this way so | |
| 88 | * that only the actual options that we support will be | |
| 89 | * passed off to getopt(). | |
| 90 | */ | |
| 91 | char valid_opts[] = { | |
| 92 | 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', | |
| 93 | '4', '6', | |
| 068e7061 PA |
94 | #ifdef AUTHENTICATION |
| 95 | 'a', ':', 'X', ':', | |
| 96 | #endif | |
| 984263bc MD |
97 | #ifdef BFTPDAEMON |
| 98 | 'B', | |
| 99 | #endif | |
| 100 | #ifdef DIAGNOSTICS | |
| 101 | 'D', ':', | |
| 102 | #endif | |
| 068e7061 PA |
103 | #ifdef ENCRYPTION |
| 104 | 'e', ':', | |
| 105 | #endif | |
| 984263bc MD |
106 | #ifdef LINEMODE |
| 107 | 'l', | |
| 108 | #endif | |
| 109 | '\0' | |
| 110 | }; | |
| 111 | ||
| 112 | int family = AF_INET; | |
| 113 | ||
| 114 | #ifndef MAXHOSTNAMELEN | |
| 115 | #define MAXHOSTNAMELEN 256 | |
| 116 | #endif /* MAXHOSTNAMELEN */ | |
| 117 | ||
| 118 | char *hostname; | |
| 119 | char host_name[MAXHOSTNAMELEN]; | |
| 120 | ||
| 121 | extern void telnet(int, int, char *); | |
| 122 | ||
| 123 | int level; | |
| 124 | char user_name[256]; | |
| 125 | ||
| 126 | int | |
| 127 | main(int argc, char *argv[]) | |
| 128 | { | |
| 129 | struct sockaddr_storage from; | |
| 130 | int on = 1, fromlen; | |
| 131 | int ch; | |
| 132 | #if defined(IPPROTO_IP) && defined(IP_TOS) | |
| 133 | int tos = -1; | |
| 134 | #endif | |
| 135 | ||
| 136 | pfrontp = pbackp = ptyobuf; | |
| 137 | netip = netibuf; | |
| 138 | nfrontp = nbackp = netobuf; | |
| 068e7061 PA |
139 | #ifdef ENCRYPTION |
| 140 | nclearto = 0; | |
| 141 | #endif /* ENCRYPTION */ | |
| 984263bc MD |
142 | |
| 143 | /* | |
| 144 | * This initialization causes linemode to default to a configuration | |
| 145 | * that works on all telnet clients, including the FreeBSD client. | |
| 146 | * This is not quite the same as the telnet client issuing a "mode | |
| 147 | * character" command, but has most of the same benefits, and is | |
| 148 | * preferable since some clients (like usofts) don't have the | |
| 149 | * mode character command anyway and linemode breaks things. | |
| 150 | * The most notable symptom of fix is that csh "set filec" operations | |
| 151 | * like <ESC> (filename completion) and ^D (choices) keys now work | |
| 152 | * in telnet sessions and can be used more than once on the same line. | |
| 153 | * CR/LF handling is also corrected in some termio modes. This | |
| 154 | * change resolves problem reports bin/771 and bin/1037. | |
| 155 | */ | |
| 156 | ||
| 157 | linemode=1; /*Default to mode that works on bulk of clients*/ | |
| 158 | ||
| 159 | while ((ch = getopt(argc, argv, valid_opts)) != -1) { | |
| 160 | switch(ch) { | |
| 161 | ||
| 068e7061 PA |
162 | #ifdef AUTHENTICATION |
| 163 | case 'a': | |
| 164 | /* | |
| 165 | * Check for required authentication level | |
| 166 | */ | |
| 167 | if (strcmp(optarg, "debug") == 0) { | |
| 168 | extern int auth_debug_mode; | |
| 169 | auth_debug_mode = 1; | |
| 170 | } else if (strcasecmp(optarg, "none") == 0) { | |
| 171 | auth_level = 0; | |
| 172 | } else if (strcasecmp(optarg, "other") == 0) { | |
| 173 | auth_level = AUTH_OTHER; | |
| 174 | } else if (strcasecmp(optarg, "user") == 0) { | |
| 175 | auth_level = AUTH_USER; | |
| 176 | } else if (strcasecmp(optarg, "valid") == 0) { | |
| 177 | auth_level = AUTH_VALID; | |
| 178 | } else if (strcasecmp(optarg, "off") == 0) { | |
| 179 | /* | |
| 180 | * This hack turns off authentication | |
| 181 | */ | |
| 182 | auth_level = -1; | |
| 183 | } else { | |
| 184 | warnx("unknown authorization level for -a"); | |
| 185 | } | |
| 186 | break; | |
| 187 | #endif /* AUTHENTICATION */ | |
| 984263bc MD |
188 | |
| 189 | #ifdef BFTPDAEMON | |
| 190 | case 'B': | |
| 191 | bftpd++; | |
| 192 | break; | |
| 193 | #endif /* BFTPDAEMON */ | |
| 194 | ||
| 195 | case 'd': | |
| 196 | if (strcmp(optarg, "ebug") == 0) { | |
| 197 | debug++; | |
| 198 | break; | |
| 199 | } | |
| 200 | usage(); | |
| 201 | /* NOTREACHED */ | |
| 202 | break; | |
| 203 | ||
| 204 | #ifdef DIAGNOSTICS | |
| 205 | case 'D': | |
| 206 | /* | |
| 207 | * Check for desired diagnostics capabilities. | |
| 208 | */ | |
| 209 | if (!strcmp(optarg, "report")) { | |
| 210 | diagnostic |= TD_REPORT|TD_OPTIONS; | |
| 211 | } else if (!strcmp(optarg, "exercise")) { | |
| 212 | diagnostic |= TD_EXERCISE; | |
| 213 | } else if (!strcmp(optarg, "netdata")) { | |
| 214 | diagnostic |= TD_NETDATA; | |
| 215 | } else if (!strcmp(optarg, "ptydata")) { | |
| 216 | diagnostic |= TD_PTYDATA; | |
| 217 | } else if (!strcmp(optarg, "options")) { | |
| 218 | diagnostic |= TD_OPTIONS; | |
| 219 | } else { | |
| 220 | usage(); | |
| 221 | /* NOT REACHED */ | |
| 222 | } | |
| 223 | break; | |
| 224 | #endif /* DIAGNOSTICS */ | |
| 225 | ||
| 068e7061 PA |
226 | #ifdef ENCRYPTION |
| 227 | case 'e': | |
| 228 | if (strcmp(optarg, "debug") == 0) { | |
| 229 | extern int encrypt_debug_mode; | |
| 230 | encrypt_debug_mode = 1; | |
| 231 | break; | |
| 232 | } | |
| 233 | usage(); | |
| 234 | /* NOTREACHED */ | |
| 235 | break; | |
| 236 | #endif /* ENCRYPTION */ | |
| 984263bc MD |
237 | |
| 238 | case 'h': | |
| 239 | hostinfo = 0; | |
| 240 | break; | |
| 241 | ||
| 242 | #ifdef LINEMODE | |
| 243 | case 'l': | |
| 244 | alwayslinemode = 1; | |
| 245 | break; | |
| 246 | #endif /* LINEMODE */ | |
| 247 | ||
| 248 | case 'k': | |
| 249 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
| 250 | lmodetype = NO_AUTOKLUDGE; | |
| 251 | #else | |
| 252 | /* ignore -k option if built without kludge linemode */ | |
| 253 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
| 254 | break; | |
| 255 | ||
| 256 | case 'n': | |
| 257 | keepalive = 0; | |
| 258 | break; | |
| 259 | ||
| 260 | case 'p': | |
| 261 | altlogin = optarg; | |
| 262 | break; | |
| 263 | ||
| 264 | case 'S': | |
| 265 | #ifdef HAS_GETTOS | |
| 266 | if ((tos = parsetos(optarg, "tcp")) < 0) | |
| 267 | warnx("%s%s%s", | |
| 268 | "bad TOS argument '", optarg, | |
| 269 | "'; will try to use default TOS"); | |
| 270 | #else | |
| 271 | warnx("TOS option unavailable; -S flag not supported"); | |
| 272 | #endif | |
| 273 | break; | |
| 274 | ||
| 275 | case 'u': | |
| 276 | utmp_len = (size_t)atoi(optarg); | |
| 277 | if (utmp_len >= sizeof(remote_hostname)) | |
| 278 | utmp_len = sizeof(remote_hostname) - 1; | |
| 279 | break; | |
| 280 | ||
| 281 | case 'U': | |
| 282 | registerd_host_only = 1; | |
| 283 | break; | |
| 284 | ||
| 068e7061 PA |
285 | #ifdef AUTHENTICATION |
| 286 | case 'X': | |
| 287 | /* | |
| 288 | * Check for invalid authentication types | |
| 289 | */ | |
| 290 | auth_disable_name(optarg); | |
| 291 | break; | |
| 292 | #endif /* AUTHENTICATION */ | |
| 984263bc MD |
293 | |
| 294 | case '4': | |
| 295 | family = AF_INET; | |
| 296 | break; | |
| 297 | ||
| 298 | #ifdef INET6 | |
| 299 | case '6': | |
| 300 | family = AF_INET6; | |
| 301 | break; | |
| 302 | #endif | |
| 303 | ||
| 304 | default: | |
| 305 | warnx("%c: unknown option", ch); | |
| 306 | /* FALLTHROUGH */ | |
| 307 | case '?': | |
| 308 | usage(); | |
| 309 | /* NOTREACHED */ | |
| 310 | } | |
| 311 | } | |
| 312 | ||
| 313 | argc -= optind; | |
| 314 | argv += optind; | |
| 315 | ||
| 316 | if (debug) { | |
| 317 | int s, ns, foo, error; | |
| 318 | const char *service = "telnet"; | |
| 319 | struct addrinfo hints, *res; | |
| 320 | ||
| 321 | if (argc > 1) { | |
| 322 | usage(); | |
| 323 | /* NOT REACHED */ | |
| 324 | } else if (argc == 1) | |
| 325 | service = *argv; | |
| 326 | ||
| 327 | memset(&hints, 0, sizeof(hints)); | |
| 328 | hints.ai_flags = AI_PASSIVE; | |
| 329 | hints.ai_family = family; | |
| 330 | hints.ai_socktype = SOCK_STREAM; | |
| 331 | hints.ai_protocol = 0; | |
| 332 | error = getaddrinfo(NULL, service, &hints, &res); | |
| 333 | ||
| 334 | if (error) { | |
| 335 | errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); | |
| 336 | if (error == EAI_SYSTEM) | |
| 337 | errx(1, "tcp/%s: %s\n", service, strerror(errno)); | |
| 338 | usage(); | |
| 339 | } | |
| 340 | ||
| 341 | s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | |
| 342 | if (s < 0) | |
| 343 | err(1, "socket"); | |
| 344 | (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, | |
| 345 | (char *)&on, sizeof(on)); | |
| 346 | if (bind(s, res->ai_addr, res->ai_addrlen) < 0) | |
| 347 | err(1, "bind"); | |
| 348 | if (listen(s, 1) < 0) | |
| 349 | err(1, "listen"); | |
| 350 | foo = res->ai_addrlen; | |
| 351 | ns = accept(s, res->ai_addr, &foo); | |
| 352 | if (ns < 0) | |
| 353 | err(1, "accept"); | |
| 354 | (void) dup2(ns, 0); | |
| 355 | (void) close(ns); | |
| 356 | (void) close(s); | |
| 357 | #ifdef convex | |
| 358 | } else if (argc == 1) { | |
| 359 | ; /* VOID*/ /* Just ignore the host/port name */ | |
| 360 | #endif | |
| 361 | } else if (argc > 0) { | |
| 362 | usage(); | |
| 363 | /* NOT REACHED */ | |
| 364 | } | |
| 365 | ||
| 366 | openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); | |
| 367 | fromlen = sizeof (from); | |
| 368 | if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { | |
| 369 | warn("getpeername"); | |
| 370 | _exit(1); | |
| 371 | } | |
| 372 | if (keepalive && | |
| 373 | setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, | |
| 374 | (char *)&on, sizeof (on)) < 0) { | |
| 375 | syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); | |
| 376 | } | |
| 377 | ||
| 378 | #if defined(IPPROTO_IP) && defined(IP_TOS) | |
| 379 | if (from.ss_family == AF_INET) { | |
| 380 | # if defined(HAS_GETTOS) | |
| 381 | struct tosent *tp; | |
| 382 | if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) | |
| 383 | tos = tp->t_tos; | |
| 384 | # endif | |
| 385 | if (tos < 0) | |
| 386 | tos = 020; /* Low Delay bit */ | |
| 387 | if (tos | |
| 388 | && (setsockopt(0, IPPROTO_IP, IP_TOS, | |
| 389 | (char *)&tos, sizeof(tos)) < 0) | |
| 390 | && (errno != ENOPROTOOPT) ) | |
| 391 | syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); | |
| 392 | } | |
| 393 | #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ | |
| 394 | net = 0; | |
| 395 | doit((struct sockaddr *)&from); | |
| 396 | /* NOTREACHED */ | |
| 397 | return(0); | |
| 398 | } /* end of main */ | |
| 399 | ||
| 89a89091 SW |
400 | void |
| 401 | usage(void) | |
| 984263bc MD |
402 | { |
| 403 | fprintf(stderr, "usage: telnetd"); | |
| 068e7061 PA |
404 | #ifdef AUTHENTICATION |
| 405 | fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t"); | |
| 406 | #endif | |
| 984263bc MD |
407 | #ifdef BFTPDAEMON |
| 408 | fprintf(stderr, " [-B]"); | |
| 409 | #endif | |
| 410 | fprintf(stderr, " [-debug]"); | |
| 411 | #ifdef DIAGNOSTICS | |
| 412 | fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t"); | |
| 413 | #endif | |
| 068e7061 PA |
414 | #ifdef AUTHENTICATION |
| 415 | fprintf(stderr, " [-edebug]"); | |
| 416 | #endif | |
| 984263bc MD |
417 | fprintf(stderr, " [-h]"); |
| 418 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
| 419 | fprintf(stderr, " [-k]"); | |
| 420 | #endif | |
| 421 | #ifdef LINEMODE | |
| 422 | fprintf(stderr, " [-l]"); | |
| 423 | #endif | |
| 424 | fprintf(stderr, " [-n]"); | |
| 425 | fprintf(stderr, "\n\t"); | |
| 426 | #ifdef HAS_GETTOS | |
| 427 | fprintf(stderr, " [-S tos]"); | |
| 428 | #endif | |
| 068e7061 PA |
429 | #ifdef AUTHENTICATION |
| 430 | fprintf(stderr, " [-X auth-type]"); | |
| 431 | #endif | |
| 984263bc MD |
432 | fprintf(stderr, " [-u utmp_hostname_length] [-U]"); |
| 433 | fprintf(stderr, " [port]\n"); | |
| 434 | exit(1); | |
| 435 | } | |
| 436 | ||
| 437 | /* | |
| 438 | * getterminaltype | |
| 439 | * | |
| 440 | * Ask the other end to send along its terminal type and speed. | |
| 441 | * Output is the variable terminaltype filled in. | |
| 442 | */ | |
| 443 | static unsigned char ttytype_sbbuf[] = { | |
| 444 | IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE | |
| 445 | }; | |
| 446 | ||
| 447 | ||
| 068e7061 | 448 | #ifndef AUTHENTICATION |
| 984263bc | 449 | #define undef2 __unused |
| 068e7061 PA |
450 | #else |
| 451 | #define undef2 | |
| 452 | #endif | |
| 984263bc MD |
453 | |
| 454 | static int | |
| 455 | getterminaltype(char *name undef2) | |
| 456 | { | |
| 457 | int retval = -1; | |
| 458 | ||
| 459 | settimer(baseline); | |
| 068e7061 PA |
460 | #ifdef AUTHENTICATION |
| 461 | /* | |
| 462 | * Handle the Authentication option before we do anything else. | |
| 463 | */ | |
| 464 | send_do(TELOPT_AUTHENTICATION, 1); | |
| 465 | while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) | |
| 466 | ttloop(); | |
| 467 | if (his_state_is_will(TELOPT_AUTHENTICATION)) { | |
| 468 | retval = auth_wait(name); | |
| 469 | } | |
| 470 | #endif | |
| 984263bc | 471 | |
| 068e7061 PA |
472 | #ifdef ENCRYPTION |
| 473 | send_will(TELOPT_ENCRYPT, 1); | |
| 474 | #endif /* ENCRYPTION */ | |
| 984263bc MD |
475 | send_do(TELOPT_TTYPE, 1); |
| 476 | send_do(TELOPT_TSPEED, 1); | |
| 477 | send_do(TELOPT_XDISPLOC, 1); | |
| 478 | send_do(TELOPT_NEW_ENVIRON, 1); | |
| 479 | send_do(TELOPT_OLD_ENVIRON, 1); | |
| 480 | while ( | |
| 068e7061 PA |
481 | #ifdef ENCRYPTION |
| 482 | his_do_dont_is_changing(TELOPT_ENCRYPT) || | |
| 483 | #endif /* ENCRYPTION */ | |
| 984263bc MD |
484 | his_will_wont_is_changing(TELOPT_TTYPE) || |
| 485 | his_will_wont_is_changing(TELOPT_TSPEED) || | |
| 486 | his_will_wont_is_changing(TELOPT_XDISPLOC) || | |
| 487 | his_will_wont_is_changing(TELOPT_NEW_ENVIRON) || | |
| 488 | his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) { | |
| 489 | ttloop(); | |
| 490 | } | |
| 068e7061 PA |
491 | #ifdef ENCRYPTION |
| 492 | /* | |
| 493 | * Wait for the negotiation of what type of encryption we can | |
| 494 | * send with. If autoencrypt is not set, this will just return. | |
| 495 | */ | |
| 496 | if (his_state_is_will(TELOPT_ENCRYPT)) { | |
| 497 | encrypt_wait(); | |
| 498 | } | |
| 499 | #endif /* ENCRYPTION */ | |
| 984263bc MD |
500 | if (his_state_is_will(TELOPT_TSPEED)) { |
| 501 | static unsigned char sb[] = | |
| 502 | { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; | |
| 503 | ||
| 504 | output_datalen(sb, sizeof sb); | |
| 505 | DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); | |
| 506 | } | |
| 507 | if (his_state_is_will(TELOPT_XDISPLOC)) { | |
| 508 | static unsigned char sb[] = | |
| 509 | { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; | |
| 510 | ||
| 511 | output_datalen(sb, sizeof sb); | |
| 512 | DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); | |
| 513 | } | |
| 514 | if (his_state_is_will(TELOPT_NEW_ENVIRON)) { | |
| 515 | static unsigned char sb[] = | |
| 516 | { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE }; | |
| 517 | ||
| 518 | output_datalen(sb, sizeof sb); | |
| 519 | DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); | |
| 520 | } | |
| 521 | else if (his_state_is_will(TELOPT_OLD_ENVIRON)) { | |
| 522 | static unsigned char sb[] = | |
| 523 | { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE }; | |
| 524 | ||
| 525 | output_datalen(sb, sizeof sb); | |
| 526 | DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); | |
| 527 | } | |
| 528 | if (his_state_is_will(TELOPT_TTYPE)) { | |
| 529 | ||
| 530 | output_datalen(ttytype_sbbuf, sizeof ttytype_sbbuf); | |
| 531 | DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, | |
| 532 | sizeof ttytype_sbbuf - 2);); | |
| 533 | } | |
| 534 | if (his_state_is_will(TELOPT_TSPEED)) { | |
| 535 | while (sequenceIs(tspeedsubopt, baseline)) | |
| 536 | ttloop(); | |
| 537 | } | |
| 538 | if (his_state_is_will(TELOPT_XDISPLOC)) { | |
| 539 | while (sequenceIs(xdisplocsubopt, baseline)) | |
| 540 | ttloop(); | |
| 541 | } | |
| 542 | if (his_state_is_will(TELOPT_NEW_ENVIRON)) { | |
| 543 | while (sequenceIs(environsubopt, baseline)) | |
| 544 | ttloop(); | |
| 545 | } | |
| 546 | if (his_state_is_will(TELOPT_OLD_ENVIRON)) { | |
| 547 | while (sequenceIs(oenvironsubopt, baseline)) | |
| 548 | ttloop(); | |
| 549 | } | |
| 550 | if (his_state_is_will(TELOPT_TTYPE)) { | |
| 551 | char first[256], last[256]; | |
| 552 | ||
| 553 | while (sequenceIs(ttypesubopt, baseline)) | |
| 554 | ttloop(); | |
| 555 | ||
| 556 | /* | |
| 557 | * If the other side has already disabled the option, then | |
| 558 | * we have to just go with what we (might) have already gotten. | |
| 559 | */ | |
| 560 | if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { | |
| 561 | (void) strncpy(first, terminaltype, sizeof(first)-1); | |
| 562 | first[sizeof(first)-1] = '\0'; | |
| 563 | for(;;) { | |
| 564 | /* | |
| 565 | * Save the unknown name, and request the next name. | |
| 566 | */ | |
| 567 | (void) strncpy(last, terminaltype, sizeof(last)-1); | |
| 568 | last[sizeof(last)-1] = '\0'; | |
| 569 | _gettermname(); | |
| 570 | if (terminaltypeok(terminaltype)) | |
| 571 | break; | |
| 572 | if ((strncmp(last, terminaltype, sizeof(last)) == 0) || | |
| 573 | his_state_is_wont(TELOPT_TTYPE)) { | |
| 574 | /* | |
| 575 | * We've hit the end. If this is the same as | |
| 576 | * the first name, just go with it. | |
| 577 | */ | |
| 578 | if (strncmp(first, terminaltype, sizeof(first)) == 0) | |
| 579 | break; | |
| 580 | /* | |
| 581 | * Get the terminal name one more time, so that | |
| 582 | * RFC1091 compliant telnets will cycle back to | |
| 583 | * the start of the list. | |
| 584 | */ | |
| 585 | _gettermname(); | |
| 586 | if (strncmp(first, terminaltype, sizeof(first)) != 0) { | |
| 068e7061 PA |
587 | (void) strncpy(terminaltype, first, TERMINAL_TYPE_SIZE-1); |
| 588 | terminaltype[TERMINAL_TYPE_SIZE-1] = '\0'; | |
| 984263bc MD |
589 | } |
| 590 | break; | |
| 591 | } | |
| 592 | } | |
| 593 | } | |
| 594 | } | |
| 595 | return(retval); | |
| 596 | } /* end of getterminaltype */ | |
| 597 | ||
| 598 | static void | |
| 599 | _gettermname(void) | |
| 600 | { | |
| 601 | /* | |
| 602 | * If the client turned off the option, | |
| 603 | * we can't send another request, so we | |
| 604 | * just return. | |
| 605 | */ | |
| 606 | if (his_state_is_wont(TELOPT_TTYPE)) | |
| 607 | return; | |
| 608 | settimer(baseline); | |
| 609 | output_datalen(ttytype_sbbuf, sizeof ttytype_sbbuf); | |
| 610 | DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, | |
| 611 | sizeof ttytype_sbbuf - 2);); | |
| 612 | while (sequenceIs(ttypesubopt, baseline)) | |
| 613 | ttloop(); | |
| 614 | } | |
| 615 | ||
| 616 | int | |
| 617 | terminaltypeok(char *s) | |
| 618 | { | |
| 619 | char buf[1024]; | |
| 620 | ||
| 621 | if (terminaltype == NULL) | |
| 622 | return(1); | |
| 623 | ||
| 624 | /* | |
| 625 | * tgetent() will return 1 if the type is known, and | |
| 626 | * 0 if it is not known. If it returns -1, it couldn't | |
| 627 | * open the database. But if we can't open the database, | |
| 628 | * it won't help to say we failed, because we won't be | |
| 629 | * able to verify anything else. So, we treat -1 like 1. | |
| 630 | */ | |
| 631 | if (tgetent(buf, s) == 0) | |
| 632 | return(0); | |
| 633 | return(1); | |
| 634 | } | |
| 635 | ||
| 636 | /* | |
| 637 | * Get a pty, scan input lines. | |
| 638 | */ | |
| 639 | void | |
| 640 | doit(struct sockaddr *who) | |
| 641 | { | |
| 642 | int err_; /* XXX */ | |
| 643 | int ptynum; | |
| 644 | ||
| 645 | /* | |
| 646 | * Find an available pty to use. | |
| 647 | */ | |
| 648 | #ifndef convex | |
| 649 | pty = getpty(&ptynum); | |
| 650 | if (pty < 0) | |
| 651 | fatal(net, "All network ports in use"); | |
| 652 | #else | |
| 653 | for (;;) { | |
| 654 | char *lp; | |
| 655 | ||
| 656 | if ((lp = getpty()) == NULL) | |
| 657 | fatal(net, "Out of ptys"); | |
| 658 | ||
| 659 | if ((pty = open(lp, 2)) >= 0) { | |
| 660 | strlcpy(line,lp,sizeof(line)); | |
| 661 | line[5] = 't'; | |
| 662 | break; | |
| 663 | } | |
| 664 | } | |
| 665 | #endif | |
| 666 | ||
| 667 | /* get name of connected client */ | |
| 668 | if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1, | |
| 669 | who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only) | |
| 670 | fatal(net, "Couldn't resolve your address into a host name.\r\n\ | |
| 671 | Please contact your net administrator"); | |
| 672 | remote_hostname[sizeof(remote_hostname) - 1] = '\0'; | |
| 673 | ||
| 674 | trimdomain(remote_hostname, UT_HOSTSIZE); | |
| 675 | if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len) | |
| 676 | err_ = getnameinfo(who, who->sa_len, remote_hostname, | |
| 677 | sizeof(remote_hostname), NULL, 0, | |
| 678 | NI_NUMERICHOST|NI_WITHSCOPEID); | |
| 679 | /* XXX: do 'err_' check */ | |
| 680 | ||
| 681 | (void) gethostname(host_name, sizeof(host_name) - 1); | |
| 682 | host_name[sizeof(host_name) - 1] = '\0'; | |
| 683 | hostname = host_name; | |
| 684 | ||
| 068e7061 PA |
685 | #ifdef AUTHENTICATION |
| 686 | #ifdef ENCRYPTION | |
| 687 | /* The above #ifdefs should actually be "or"'ed, not "and"'ed. | |
| 688 | * This is a byproduct of needing "#ifdef" and not "#if defined()" | |
| 689 | * for unifdef. XXX MarkM | |
| 690 | */ | |
| 691 | auth_encrypt_init(hostname, remote_hostname, "TELNETD", 1); | |
| 692 | #endif | |
| 693 | #endif | |
| 984263bc MD |
694 | |
| 695 | init_env(); | |
| 696 | /* | |
| 697 | * get terminal type. | |
| 698 | */ | |
| 699 | *user_name = 0; | |
| 700 | level = getterminaltype(user_name); | |
| 6fb88001 SS |
701 | if (setenv("TERM", terminaltype ? terminaltype : "network", 1) == -1) |
| 702 | syslog(LOG_ERR, "setenv: cannot set TERM=%s: %m", terminaltype ? terminaltype : "network"); | |
| 984263bc MD |
703 | |
| 704 | telnet(net, pty, remote_hostname); /* begin server process */ | |
| 705 | ||
| 706 | /*NOTREACHED*/ | |
| 707 | } /* end of doit */ | |
| 708 | ||
| 709 | /* | |
| 710 | * Main loop. Select from pty and network, and | |
| 711 | * hand data to telnet receiver finite state machine. | |
| 712 | */ | |
| 713 | void | |
| 714 | telnet(int f, int p, char *host) | |
| 715 | { | |
| 716 | int on = 1; | |
| 717 | #define TABBUFSIZ 512 | |
| 718 | char defent[TABBUFSIZ]; | |
| 719 | char defstrs[TABBUFSIZ]; | |
| 720 | #undef TABBUFSIZ | |
| 721 | char *HE; | |
| 722 | char *HN; | |
| 723 | char *IM; | |
| 724 | int nfd; | |
| 725 | ||
| 726 | /* | |
| 727 | * Initialize the slc mapping table. | |
| 728 | */ | |
| 729 | get_slc_defaults(); | |
| 730 | ||
| 731 | /* | |
| 732 | * Do some tests where it is desireable to wait for a response. | |
| 733 | * Rather than doing them slowly, one at a time, do them all | |
| 734 | * at once. | |
| 735 | */ | |
| 736 | if (my_state_is_wont(TELOPT_SGA)) | |
| 737 | send_will(TELOPT_SGA, 1); | |
| 738 | /* | |
| 739 | * Is the client side a 4.2 (NOT 4.3) system? We need to know this | |
| 740 | * because 4.2 clients are unable to deal with TCP urgent data. | |
| 741 | * | |
| 742 | * To find out, we send out a "DO ECHO". If the remote system | |
| 743 | * answers "WILL ECHO" it is probably a 4.2 client, and we note | |
| 744 | * that fact ("WILL ECHO" ==> that the client will echo what | |
| 745 | * WE, the server, sends it; it does NOT mean that the client will | |
| 746 | * echo the terminal input). | |
| 747 | */ | |
| 748 | send_do(TELOPT_ECHO, 1); | |
| 749 | ||
| 750 | #ifdef LINEMODE | |
| 751 | if (his_state_is_wont(TELOPT_LINEMODE)) { | |
| 752 | /* Query the peer for linemode support by trying to negotiate | |
| 753 | * the linemode option. | |
| 754 | */ | |
| 755 | linemode = 0; | |
| 756 | editmode = 0; | |
| 757 | send_do(TELOPT_LINEMODE, 1); /* send do linemode */ | |
| 758 | } | |
| 759 | #endif /* LINEMODE */ | |
| 760 | ||
| 761 | /* | |
| 762 | * Send along a couple of other options that we wish to negotiate. | |
| 763 | */ | |
| 764 | send_do(TELOPT_NAWS, 1); | |
| 765 | send_will(TELOPT_STATUS, 1); | |
| 766 | flowmode = 1; /* default flow control state */ | |
| 767 | restartany = -1; /* uninitialized... */ | |
| 768 | send_do(TELOPT_LFLOW, 1); | |
| 769 | ||
| 770 | /* | |
| 771 | * Spin, waiting for a response from the DO ECHO. However, | |
| 772 | * some REALLY DUMB telnets out there might not respond | |
| 773 | * to the DO ECHO. So, we spin looking for NAWS, (most dumb | |
| 774 | * telnets so far seem to respond with WONT for a DO that | |
| 775 | * they don't understand...) because by the time we get the | |
| 776 | * response, it will already have processed the DO ECHO. | |
| 777 | * Kludge upon kludge. | |
| 778 | */ | |
| 779 | while (his_will_wont_is_changing(TELOPT_NAWS)) | |
| 780 | ttloop(); | |
| 781 | ||
| 782 | /* | |
| 783 | * But... | |
| 784 | * The client might have sent a WILL NAWS as part of its | |
| 785 | * startup code; if so, we'll be here before we get the | |
| 786 | * response to the DO ECHO. We'll make the assumption | |
| 787 | * that any implementation that understands about NAWS | |
| 788 | * is a modern enough implementation that it will respond | |
| 789 | * to our DO ECHO request; hence we'll do another spin | |
| 790 | * waiting for the ECHO option to settle down, which is | |
| 791 | * what we wanted to do in the first place... | |
| 792 | */ | |
| 793 | if (his_want_state_is_will(TELOPT_ECHO) && | |
| 794 | his_state_is_will(TELOPT_NAWS)) { | |
| 795 | while (his_will_wont_is_changing(TELOPT_ECHO)) | |
| 796 | ttloop(); | |
| 797 | } | |
| 798 | /* | |
| 799 | * On the off chance that the telnet client is broken and does not | |
| 800 | * respond to the DO ECHO we sent, (after all, we did send the | |
| 801 | * DO NAWS negotiation after the DO ECHO, and we won't get here | |
| 802 | * until a response to the DO NAWS comes back) simulate the | |
| 803 | * receipt of a will echo. This will also send a WONT ECHO | |
| 804 | * to the client, since we assume that the client failed to | |
| 805 | * respond because it believes that it is already in DO ECHO | |
| 806 | * mode, which we do not want. | |
| 807 | */ | |
| 808 | if (his_want_state_is_will(TELOPT_ECHO)) { | |
| 809 | DIAG(TD_OPTIONS, output_data("td: simulating recv\r\n")); | |
| 810 | willoption(TELOPT_ECHO); | |
| 811 | } | |
| 812 | ||
| 813 | /* | |
| 814 | * Finally, to clean things up, we turn on our echo. This | |
| 815 | * will break stupid 4.2 telnets out of local terminal echo. | |
| 816 | */ | |
| 817 | ||
| 818 | if (my_state_is_wont(TELOPT_ECHO)) | |
| 819 | send_will(TELOPT_ECHO, 1); | |
| 820 | ||
| 821 | /* | |
| 822 | * Turn on packet mode | |
| 823 | */ | |
| 824 | (void) ioctl(p, TIOCPKT, (char *)&on); | |
| 825 | ||
| 826 | #if defined(LINEMODE) && defined(KLUDGELINEMODE) | |
| 827 | /* | |
| 828 | * Continuing line mode support. If client does not support | |
| 829 | * real linemode, attempt to negotiate kludge linemode by sending | |
| 830 | * the do timing mark sequence. | |
| 831 | */ | |
| 832 | if (lmodetype < REAL_LINEMODE) | |
| 833 | send_do(TELOPT_TM, 1); | |
| 834 | #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ | |
| 835 | ||
| 836 | /* | |
| 837 | * Call telrcv() once to pick up anything received during | |
| 838 | * terminal type negotiation, 4.2/4.3 determination, and | |
| 839 | * linemode negotiation. | |
| 840 | */ | |
| 841 | telrcv(); | |
| 842 | ||
| 843 | (void) ioctl(f, FIONBIO, (char *)&on); | |
| 844 | (void) ioctl(p, FIONBIO, (char *)&on); | |
| 845 | ||
| 846 | #if defined(SO_OOBINLINE) | |
| 847 | (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, | |
| 848 | (char *)&on, sizeof on); | |
| 849 | #endif /* defined(SO_OOBINLINE) */ | |
| 850 | ||
| 851 | #ifdef SIGTSTP | |
| 852 | (void) signal(SIGTSTP, SIG_IGN); | |
| 853 | #endif | |
| 854 | #ifdef SIGTTOU | |
| 855 | /* | |
| 856 | * Ignoring SIGTTOU keeps the kernel from blocking us | |
| 857 | * in ttioct() in /sys/tty.c. | |
| 858 | */ | |
| 859 | (void) signal(SIGTTOU, SIG_IGN); | |
| 860 | #endif | |
| 861 | ||
| 862 | (void) signal(SIGCHLD, cleanup); | |
| 863 | ||
| 864 | #ifdef TIOCNOTTY | |
| 865 | { | |
| 866 | int t; | |
| 867 | t = open(_PATH_TTY, O_RDWR); | |
| 868 | if (t >= 0) { | |
| 60233e58 | 869 | (void) ioctl(t, TIOCNOTTY, NULL); |
| 984263bc MD |
870 | (void) close(t); |
| 871 | } | |
| 872 | } | |
| 873 | #endif | |
| 874 | ||
| 875 | /* | |
| 876 | * Show banner that getty never gave. | |
| 877 | * | |
| 878 | * We put the banner in the pty input buffer. This way, it | |
| 879 | * gets carriage return null processing, etc., just like all | |
| 880 | * other pty --> client data. | |
| 881 | */ | |
| 882 | ||
| 883 | if (getent(defent, "default") == 1) { | |
| 884 | char *cp=defstrs; | |
| 885 | ||
| 886 | HE = Getstr("he", &cp); | |
| 887 | HN = Getstr("hn", &cp); | |
| 888 | IM = Getstr("im", &cp); | |
| 889 | if (HN && *HN) | |
| 890 | (void) strlcpy(host_name, HN, sizeof(host_name)); | |
| 678e8cc6 | 891 | if (IM == NULL) |
| 984263bc MD |
892 | IM = strdup(""); |
| 893 | } else { | |
| 894 | IM = strdup(DEFAULT_IM); | |
| 678e8cc6 | 895 | HE = NULL; |
| 984263bc MD |
896 | } |
| 897 | edithost(HE, host_name); | |
| 898 | if (hostinfo && *IM) | |
| 899 | putf(IM, ptyibuf2); | |
| 900 | ||
| 901 | if (pcc) | |
| 902 | (void) strncat(ptyibuf2, ptyip, pcc+1); | |
| 903 | ptyip = ptyibuf2; | |
| 904 | pcc = strlen(ptyip); | |
| 905 | #ifdef LINEMODE | |
| 906 | /* | |
| 907 | * Last check to make sure all our states are correct. | |
| 908 | */ | |
| 909 | init_termbuf(); | |
| 910 | localstat(); | |
| 911 | #endif /* LINEMODE */ | |
| 912 | ||
| 913 | DIAG(TD_REPORT, output_data("td: Entering processing loop\r\n")); | |
| 914 | ||
| 915 | /* | |
| 916 | * Startup the login process on the slave side of the terminal | |
| 917 | * now. We delay this until here to insure option negotiation | |
| 918 | * is complete. | |
| 919 | */ | |
| 920 | startslave(host, level, user_name); | |
| 921 | ||
| 922 | nfd = ((f > p) ? f : p) + 1; | |
| 923 | for (;;) { | |
| 924 | fd_set ibits, obits, xbits; | |
| 925 | int c; | |
| 926 | ||
| 927 | if (ncc < 0 && pcc < 0) | |
| 928 | break; | |
| 929 | ||
| 930 | FD_ZERO(&ibits); | |
| 931 | FD_ZERO(&obits); | |
| 932 | FD_ZERO(&xbits); | |
| 933 | /* | |
| 934 | * Never look for input if there's still | |
| 935 | * stuff in the corresponding output buffer | |
| 936 | */ | |
| 937 | if (nfrontp - nbackp || pcc > 0) { | |
| 938 | FD_SET(f, &obits); | |
| 939 | } else { | |
| 940 | FD_SET(p, &ibits); | |
| 941 | } | |
| 942 | if (pfrontp - pbackp || ncc > 0) { | |
| 943 | FD_SET(p, &obits); | |
| 944 | } else { | |
| 945 | FD_SET(f, &ibits); | |
| 946 | } | |
| 947 | if (!SYNCHing) { | |
| 948 | FD_SET(f, &xbits); | |
| 949 | } | |
| 60233e58 | 950 | if ((c = select(nfd, &ibits, &obits, &xbits, NULL)) < 1) { |
| 984263bc MD |
951 | if (c == -1) { |
| 952 | if (errno == EINTR) { | |
| 953 | continue; | |
| 954 | } | |
| 955 | } | |
| 956 | sleep(5); | |
| 957 | continue; | |
| 958 | } | |
| 959 | ||
| 960 | /* | |
| 961 | * Any urgent data? | |
| 962 | */ | |
| 963 | if (FD_ISSET(net, &xbits)) { | |
| 964 | SYNCHing = 1; | |
| 965 | } | |
| 966 | ||
| 967 | /* | |
| 968 | * Something to read from the network... | |
| 969 | */ | |
| 970 | if (FD_ISSET(net, &ibits)) { | |
| 971 | #if !defined(SO_OOBINLINE) | |
| 972 | /* | |
| 973 | * In 4.2 (and 4.3 beta) systems, the | |
| 974 | * OOB indication and data handling in the kernel | |
| 975 | * is such that if two separate TCP Urgent requests | |
| 976 | * come in, one byte of TCP data will be overlaid. | |
| 977 | * This is fatal for Telnet, but we try to live | |
| 978 | * with it. | |
| 979 | * | |
| 980 | * In addition, in 4.2 (and...), a special protocol | |
| 981 | * is needed to pick up the TCP Urgent data in | |
| 982 | * the correct sequence. | |
| 983 | * | |
| 984 | * What we do is: if we think we are in urgent | |
| 985 | * mode, we look to see if we are "at the mark". | |
| 986 | * If we are, we do an OOB receive. If we run | |
| 987 | * this twice, we will do the OOB receive twice, | |
| 988 | * but the second will fail, since the second | |
| 989 | * time we were "at the mark", but there wasn't | |
| 990 | * any data there (the kernel doesn't reset | |
| 991 | * "at the mark" until we do a normal read). | |
| 992 | * Once we've read the OOB data, we go ahead | |
| 993 | * and do normal reads. | |
| 994 | * | |
| 995 | * There is also another problem, which is that | |
| 996 | * since the OOB byte we read doesn't put us | |
| 997 | * out of OOB state, and since that byte is most | |
| 998 | * likely the TELNET DM (data mark), we would | |
| 999 | * stay in the TELNET SYNCH (SYNCHing) state. | |
| 1000 | * So, clocks to the rescue. If we've "just" | |
| 1001 | * received a DM, then we test for the | |
| 1002 | * presence of OOB data when the receive OOB | |
| 1003 | * fails (and AFTER we did the normal mode read | |
| 1004 | * to clear "at the mark"). | |
| 1005 | */ | |
| 1006 | if (SYNCHing) { | |
| 1007 | int atmark; | |
| 1008 | ||
| 1009 | (void) ioctl(net, SIOCATMARK, (char *)&atmark); | |
| 1010 | if (atmark) { | |
| 1011 | ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); | |
| 1012 | if ((ncc == -1) && (errno == EINVAL)) { | |
| 1013 | ncc = read(net, netibuf, sizeof (netibuf)); | |
| 1014 | if (sequenceIs(didnetreceive, gotDM)) { | |
| 1015 | SYNCHing = stilloob(net); | |
| 1016 | } | |
| 1017 | } | |
| 1018 | } else { | |
| 1019 | ncc = read(net, netibuf, sizeof (netibuf)); | |
| 1020 | } | |
| 1021 | } else { | |
| 1022 | ncc = read(net, netibuf, sizeof (netibuf)); | |
| 1023 | } | |
| 1024 | settimer(didnetreceive); | |
| 1025 | #else /* !defined(SO_OOBINLINE)) */ | |
| 1026 | ncc = read(net, netibuf, sizeof (netibuf)); | |
| 1027 | #endif /* !defined(SO_OOBINLINE)) */ | |
| 1028 | if (ncc < 0 && errno == EWOULDBLOCK) | |
| 1029 | ncc = 0; | |
| 1030 | else { | |
| 1031 | if (ncc <= 0) { | |
| 1032 | break; | |
| 1033 | } | |
| 1034 | netip = netibuf; | |
| 1035 | } | |
| 1036 | DIAG((TD_REPORT | TD_NETDATA), | |
| 1037 | output_data("td: netread %d chars\r\n", ncc)); | |
| 1038 | DIAG(TD_NETDATA, printdata("nd", netip, ncc)); | |
| 1039 | } | |
| 1040 | ||
| 1041 | /* | |
| 1042 | * Something to read from the pty... | |
| 1043 | */ | |
| 1044 | if (FD_ISSET(p, &ibits)) { | |
| 1045 | pcc = read(p, ptyibuf, BUFSIZ); | |
| 1046 | /* | |
| 1047 | * On some systems, if we try to read something | |
| 1048 | * off the master side before the slave side is | |
| 1049 | * opened, we get EIO. | |
| 1050 | */ | |
| 1051 | if (pcc < 0 && (errno == EWOULDBLOCK || | |
| 1052 | #ifdef EAGAIN | |
| 1053 | errno == EAGAIN || | |
| 1054 | #endif | |
| 1055 | errno == EIO)) { | |
| 1056 | pcc = 0; | |
| 1057 | } else { | |
| 1058 | if (pcc <= 0) | |
| 1059 | break; | |
| 1060 | #ifdef LINEMODE | |
| 1061 | /* | |
| 1062 | * If ioctl from pty, pass it through net | |
| 1063 | */ | |
| 1064 | if (ptyibuf[0] & TIOCPKT_IOCTL) { | |
| 1065 | copy_termbuf(ptyibuf+1, pcc-1); | |
| 1066 | localstat(); | |
| 1067 | pcc = 1; | |
| 1068 | } | |
| 1069 | #endif /* LINEMODE */ | |
| 1070 | if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { | |
| 1071 | netclear(); /* clear buffer back */ | |
| 1072 | #ifndef NO_URGENT | |
| 1073 | /* | |
| 1074 | * There are client telnets on some | |
| 1075 | * operating systems get screwed up | |
| 1076 | * royally if we send them urgent | |
| 1077 | * mode data. | |
| 1078 | */ | |
| 1079 | output_data("%c%c", IAC, DM); | |
| 1080 | neturg = nfrontp-1; /* off by one XXX */ | |
| 1081 | DIAG(TD_OPTIONS, | |
| 1082 | printoption("td: send IAC", DM)); | |
| 1083 | ||
| 1084 | #endif | |
| 1085 | } | |
| 1086 | if (his_state_is_will(TELOPT_LFLOW) && | |
| 1087 | (ptyibuf[0] & | |
| 1088 | (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { | |
| 1089 | int newflow = | |
| 1090 | ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0; | |
| 1091 | if (newflow != flowmode) { | |
| 1092 | flowmode = newflow; | |
| 1093 | output_data("%c%c%c%c%c%c", | |
| 1094 | IAC, SB, TELOPT_LFLOW, | |
| 1095 | flowmode ? LFLOW_ON | |
| 1096 | : LFLOW_OFF, | |
| 1097 | IAC, SE); | |
| 1098 | DIAG(TD_OPTIONS, printsub('>', | |
| 1099 | (unsigned char *)nfrontp-4, | |
| 1100 | 4);); | |
| 1101 | } | |
| 1102 | } | |
| 1103 | pcc--; | |
| 1104 | ptyip = ptyibuf+1; | |
| 1105 | } | |
| 1106 | } | |
| 1107 | ||
| 1108 | while (pcc > 0) { | |
| 1109 | if ((&netobuf[BUFSIZ] - nfrontp) < 2) | |
| 1110 | break; | |
| 1111 | c = *ptyip++ & 0377, pcc--; | |
| 1112 | if (c == IAC) | |
| 1113 | output_data("%c", c); | |
| 1114 | output_data("%c", c); | |
| 1115 | if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { | |
| 1116 | if (pcc > 0 && ((*ptyip & 0377) == '\n')) { | |
| 1117 | output_data("%c", *ptyip++ & 0377); | |
| 1118 | pcc--; | |
| 1119 | } else | |
| 1120 | output_data("%c", '\0'); | |
| 1121 | } | |
| 1122 | } | |
| 1123 | ||
| 1124 | if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) | |
| 1125 | netflush(); | |
| 1126 | if (ncc > 0) | |
| 1127 | telrcv(); | |
| 1128 | if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) | |
| 1129 | ptyflush(); | |
| 1130 | } | |
| 1131 | cleanup(0); | |
| 1132 | } /* end of telnet */ | |
| 1133 | ||
| 1134 | #ifndef TCSIG | |
| 1135 | # ifdef TIOCSIG | |
| 1136 | # define TCSIG TIOCSIG | |
| 1137 | # endif | |
| 1138 | #endif | |
| 1139 | ||
| 1140 | /* | |
| 1141 | * Send interrupt to process on other side of pty. | |
| 1142 | * If it is in raw mode, just write NULL; | |
| 1143 | * otherwise, write intr char. | |
| 1144 | */ | |
| 1145 | void | |
| 1146 | interrupt(void) | |
| 1147 | { | |
| 1148 | ptyflush(); /* half-hearted */ | |
| 1149 | ||
| 1150 | #ifdef TCSIG | |
| 1151 | (void) ioctl(pty, TCSIG, (char *)SIGINT); | |
| 1152 | #else /* TCSIG */ | |
| 1153 | init_termbuf(); | |
| 1154 | *pfrontp++ = slctab[SLC_IP].sptr ? | |
| 1155 | (unsigned char)*slctab[SLC_IP].sptr : '\177'; | |
| 1156 | #endif /* TCSIG */ | |
| 1157 | } | |
| 1158 | ||
| 1159 | /* | |
| 1160 | * Send quit to process on other side of pty. | |
| 1161 | * If it is in raw mode, just write NULL; | |
| 1162 | * otherwise, write quit char. | |
| 1163 | */ | |
| 1164 | void | |
| 1165 | sendbrk(void) | |
| 1166 | { | |
| 1167 | ptyflush(); /* half-hearted */ | |
| 1168 | #ifdef TCSIG | |
| 1169 | (void) ioctl(pty, TCSIG, (char *)SIGQUIT); | |
| 1170 | #else /* TCSIG */ | |
| 1171 | init_termbuf(); | |
| 1172 | *pfrontp++ = slctab[SLC_ABORT].sptr ? | |
| 1173 | (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; | |
| 1174 | #endif /* TCSIG */ | |
| 1175 | } | |
| 1176 | ||
| 1177 | void | |
| 1178 | sendsusp(void) | |
| 1179 | { | |
| 1180 | #ifdef SIGTSTP | |
| 1181 | ptyflush(); /* half-hearted */ | |
| 1182 | # ifdef TCSIG | |
| 1183 | (void) ioctl(pty, TCSIG, (char *)SIGTSTP); | |
| 1184 | # else /* TCSIG */ | |
| 1185 | *pfrontp++ = slctab[SLC_SUSP].sptr ? | |
| 1186 | (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; | |
| 1187 | # endif /* TCSIG */ | |
| 1188 | #endif /* SIGTSTP */ | |
| 1189 | } | |
| 1190 | ||
| 1191 | /* | |
| 1192 | * When we get an AYT, if ^T is enabled, use that. Otherwise, | |
| 1193 | * just send back "[Yes]". | |
| 1194 | */ | |
| 1195 | void | |
| 1196 | recv_ayt(void) | |
| 1197 | { | |
| 1198 | #if defined(SIGINFO) && defined(TCSIG) | |
| 1199 | if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { | |
| 1200 | (void) ioctl(pty, TCSIG, (char *)SIGINFO); | |
| 1201 | return; | |
| 1202 | } | |
| 1203 | #endif | |
| 1204 | output_data("\r\n[Yes]\r\n"); | |
| 1205 | } | |
| 1206 | ||
| 1207 | void | |
| 1208 | doeof(void) | |
| 1209 | { | |
| 1210 | init_termbuf(); | |
| 1211 | ||
| 1212 | #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) | |
| 1213 | if (!tty_isediting()) { | |
| 1214 | extern char oldeofc; | |
| 1215 | *pfrontp++ = oldeofc; | |
| 1216 | return; | |
| 1217 | } | |
| 1218 | #endif | |
| 1219 | *pfrontp++ = slctab[SLC_EOF].sptr ? | |
| 1220 | (unsigned char)*slctab[SLC_EOF].sptr : '\004'; | |
| 1221 | } |