📄 telnetd.c
字号:
/* * Copyright (c) 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */char copyright[] = "@(#) Copyright (c) 1989 Regents of the University of California.\n" "All rights reserved.\n";/* * From: @(#)telnetd.c 5.48 (Berkeley) 3/1/91 */char telnetd_rcsid[] = "$Id: telnetd.c,v 1.24 2000/04/12 21:36:12 dholland Exp $";#include "../version.h"#include <netdb.h>#include <termcap.h>#include <netinet/in.h>/* #include <netinet/ip.h> */ /* Don't think this is used at all here */#include <arpa/inet.h>#include <assert.h>#include "telnetd.h"#include "pathnames.h"#include "setproctitle.h"#if defined(AUTHENTICATE)#include <libtelnet/auth.h>#include <libtelnet/auth-proto.h>#include <libtelnet/misc-proto.h>int auth_level = 0;#endif#if defined(SecurID)int require_SecurID = 0;#endif/* In Linux, this is an enum */#if defined(__linux__) || defined(IPPROTO_IP)#define HAS_IPPROTO_IP#endifstatic void doit(struct sockaddr_in *who);static int terminaltypeok(const char *s);/* * I/O data buffers, * pointers, and counters. */char ptyibuf[BUFSIZ], *ptyip = ptyibuf;char ptyibuf2[BUFSIZ];int hostinfo = 1; /* do we print login banner? */int debug = 0;int keepalive = 1;char *loginprg = _PATH_LOGIN;char *progname;extern void usage(void);intmain(int argc, char *argv[], char *env[]){ struct sockaddr_in from; int on = 1; socklen_t fromlen; register int ch;#if defined(HAS_IPPROTO_IP) && defined(IP_TOS) int tos = -1;#endif initsetproctitle(argc, argv, env); pfrontp = pbackp = ptyobuf; netip = netibuf; nfrontp = nbackp = netobuf;#if defined(ENCRYPT) nclearto = 0;#endif progname = *argv; while ((ch = getopt(argc, argv, "d:a:e:lhnr:I:D:B:sS:a:X:L:")) != EOF) { switch(ch) {#ifdef AUTHENTICATE case 'a': /* * Check for required authentication level */ if (strcmp(optarg, "debug") == 0) { extern int auth_debug_mode; auth_debug_mode = 1; } else if (strcasecmp(optarg, "none") == 0) { auth_level = 0; } else if (strcasecmp(optarg, "other") == 0) { auth_level = AUTH_OTHER; } else if (strcasecmp(optarg, "user") == 0) { auth_level = AUTH_USER; } else if (strcasecmp(optarg, "valid") == 0) { auth_level = AUTH_VALID; } else if (strcasecmp(optarg, "off") == 0) { /* * This hack turns off authentication */ auth_level = -1; } else { fprintf(stderr, "telnetd: unknown authorization level for -a\n"); } break;#endif /* AUTHENTICATE */#ifdef BFTPDAEMON case 'B': bftpd++; break;#endif /* BFTPDAEMON */ case 'd': if (strcmp(optarg, "ebug") == 0) { debug++; break; } usage(); /* NOTREACHED */ break;#ifdef DIAGNOSTICS case 'D': /* * Check for desired diagnostics capabilities. */ if (!strcmp(optarg, "report")) { diagnostic |= TD_REPORT|TD_OPTIONS; } else if (!strcmp(optarg, "exercise")) { diagnostic |= TD_EXERCISE; } else if (!strcmp(optarg, "netdata")) { diagnostic |= TD_NETDATA; } else if (!strcmp(optarg, "ptydata")) { diagnostic |= TD_PTYDATA; } else if (!strcmp(optarg, "options")) { diagnostic |= TD_OPTIONS; } else { usage(); /* NOT REACHED */ } break;#endif /* DIAGNOSTICS */#ifdef AUTHENTICATE case 'e': if (strcmp(optarg, "debug") == 0) { extern int auth_debug_mode; auth_debug_mode = 1; break; } usage(); /* NOTREACHED */ break;#endif /* AUTHENTICATE */ case 'h': hostinfo = 0; break;#ifdef LINEMODE case 'l': alwayslinemode = 1; break;#endif /* LINEMODE */ case 'L': loginprg = strdup(optarg); /* XXX what if strdup fails? */ break; case 'n': keepalive = 0; break;#ifdef SecurID case 's': /* SecurID required */ require_SecurID = 1; break;#endif /* SecurID */ case 'S':#ifdef HAS_GETTOS if ((tos = parsetos(optarg, "tcp")) < 0) fprintf(stderr, "%s%s%s\n", "telnetd: Bad TOS argument '", optarg, "'; will try to use default TOS");#else fprintf(stderr, "%s%s\n", "TOS option unavailable; ", "-S flag not supported\n");#endif break;#ifdef AUTHENTICATE case 'X': /* * Check for invalid authentication types */ auth_disable_name(optarg); break;#endif /* AUTHENTICATE */ default: fprintf(stderr, "telnetd: %c: unknown option\n", ch); /* FALLTHROUGH */ case '?': usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (debug) { int s, ns; socklen_t foo; struct servent *sp; struct sockaddr_in sn; memset(&sn, 0, sizeof(sn)); sn.sin_family = AF_INET; if (argc > 1) { usage(); /* NOTREACHED */ } else if (argc == 1) { if ((sp = getservbyname(*argv, "tcp"))!=NULL) { sn.sin_port = sp->s_port; } else { int pt = atoi(*argv); if (pt <= 0) { fprintf(stderr, "telnetd: %s: bad port number\n", *argv); usage(); /* NOTREACHED */ } sn.sin_port = htons(pt); } } else { sp = getservbyname("telnet", "tcp"); if (sp == 0) { fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); exit(1); } sn.sin_port = sp->s_port; } s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("telnetd: socket");; exit(1); } (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (bind(s, (struct sockaddr *)&sn, sizeof(sn)) < 0) { perror("bind"); exit(1); } if (listen(s, 1) < 0) { perror("listen"); exit(1); } foo = sizeof(sn); ns = accept(s, (struct sockaddr *)&sn, &foo); if (ns < 0) { perror("accept"); exit(1); } (void) dup2(ns, 0); (void) close(ns); (void) close(s); } else if (argc > 0) { usage(); /* NOT REACHED */ } openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); fromlen = sizeof (from); if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { fprintf(stderr, "%s: ", progname); perror("getpeername"); _exit(1); } if (keepalive && setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); }#if defined(HAS_IPPROTO_IP) && defined(IP_TOS) {# if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) tos = tp->t_tos;# endif if (tos < 0) tos = 020; /* Low Delay bit */ if (tos && (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) && (errno != ENOPROTOOPT) ) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); }#endif /* defined(HAS_IPPROTO_IP) && defined(IP_TOS) */ net = 0; doit(&from); /* NOTREACHED */ return 0;} /* end of main */voidusage(void){ fprintf(stderr, "Usage: telnetd");#ifdef AUTHENTICATE fprintf(stderr, " [-a (debug|other|user|valid|off)]\n\t");#endif#ifdef BFTPDAEMON fprintf(stderr, " [-B]");#endif fprintf(stderr, " [-debug]");#ifdef DIAGNOSTICS fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");#endif#ifdef AUTHENTICATE fprintf(stderr, " [-edebug]");#endif fprintf(stderr, " [-h]");#ifdef LINEMODE fprintf(stderr, " [-l]");#endif fprintf(stderr, " [-L login_program]"); fprintf(stderr, " [-n]");#ifdef SecurID fprintf(stderr, " [-s]");#endif#ifdef AUTHENTICATE fprintf(stderr, " [-X auth-type]");#endif fprintf(stderr, " [port]\n"); exit(1);}/* * getterminaltype * * Ask the other end to send along its terminal type and speed. * Output is the variable terminaltype filled in. */static void _gettermname(void);staticintgetterminaltype(char *name){ int retval = -1; (void)name; settimer(baseline);#if defined(AUTHENTICATE) /* * Handle the Authentication option before we do anything else. */ send_do(TELOPT_AUTHENTICATION, 1); while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) ttloop(); if (his_state_is_will(TELOPT_AUTHENTICATION)) { retval = auth_wait(name); }#endif#if defined(ENCRYPT) send_will(TELOPT_ENCRYPT, 1);#endif send_do(TELOPT_TTYPE, 1); send_do(TELOPT_TSPEED, 1); send_do(TELOPT_XDISPLOC, 1); send_do(TELOPT_ENVIRON, 1); while (#if defined(ENCRYPT) his_do_dont_is_changing(TELOPT_ENCRYPT) ||#endif his_will_wont_is_changing(TELOPT_TTYPE) || his_will_wont_is_changing(TELOPT_TSPEED) || his_will_wont_is_changing(TELOPT_XDISPLOC) || his_will_wont_is_changing(TELOPT_ENVIRON)) { ttloop(); }#if defined(ENCRYPT) /* * Wait for the negotiation of what type of encryption we can * send with. If autoencrypt is not set, this will just return. */ if (his_state_is_will(TELOPT_ENCRYPT)) { encrypt_wait(); }#endif if (his_state_is_will(TELOPT_TSPEED)) { netoprintf("%c%c%c%c%c%c", IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE); } if (his_state_is_will(TELOPT_XDISPLOC)) { netoprintf("%c%c%c%c%c%c", IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE); } if (his_state_is_will(TELOPT_ENVIRON)) { netoprintf("%c%c%c%c%c%c", IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE); } if (his_state_is_will(TELOPT_TTYPE)) { netoprintf("%c%c%c%c%c%c", IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE); } if (his_state_is_will(TELOPT_TSPEED)) { while (sequenceIs(tspeedsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_XDISPLOC)) { while (sequenceIs(xdisplocsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_ENVIRON)) { while (sequenceIs(environsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_TTYPE)) { char first[256], last[256]; while (sequenceIs(ttypesubopt, baseline)) ttloop(); /* * If the other side has already disabled the option, then * we have to just go with what we (might) have already gotten. */ if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { /* * Due to state.c, terminaltype points to a static char[41]. * Therefore, this assert cannot fail, and therefore, strings * arising from "terminaltype" can be safely strcpy'd into * first[] or last[]. */ assert(strlen(terminaltype) < sizeof(first)); strcpy(first, terminaltype); for(;;) { /* * Save the unknown name, and request the next name. */ strcpy(last, terminaltype); _gettermname(); assert(strlen(terminaltype) < sizeof(first)); if (terminaltypeok(terminaltype)) break; if (!strcmp(last, terminaltype) || his_state_is_wont(TELOPT_TTYPE)) { /* * We've hit the end. If this is the same as * the first name, just go with it. */ if (!strcmp(first, terminaltype)) break; /* * Get the terminal name one more time, so that * RFC1091 compliant telnets will cycle back to * the start of the list. */ _gettermname(); assert(strlen(terminaltype) < sizeof(first)); if (strcmp(first, terminaltype)) { /* * first[] came from terminaltype, so it must fit * back in. */ strcpy(terminaltype, first); } break; } } } } return(retval);} /* end of getterminaltype */staticvoid_gettermname(void){ /* * If the client turned off the option, * we can't send another request, so we * just return. */ if (his_state_is_wont(TELOPT_TTYPE)) return; settimer(baseline); netoprintf("%c%c%c%c%c%c", IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE); while (sequenceIs(ttypesubopt, baseline)) ttloop();}static intterminaltypeok(const char *s){ /* char buf[2048]; */ if (terminaltype == NULL) return(1); /* * Fix from Chris Evans: if it has a / in it, termcap will * treat it as a filename. Oops. */ if (strchr(s, '/')) { return 0; } /* * If it's absurdly long, accept it without asking termcap. * * This means that it won't get seen again until after login, * at which point exploiting buffer problems in termcap doesn't * gain one anything. * * It's possible this limit ought to be raised to 128, but nothing * in my termcap is more than 64, 64 is _plenty_ for most, and while * buffers aren't likely to be smaller than 64, they might be 80 and * thus less than 128. */ if (strlen(s) > 63) { return 0; } /* * tgetent() will return 1 if the type is known, and * 0 if it is not known. If it returns -1, it couldn't * open the database. But if we can't open the database, * it won't help to say we failed, because we won't be * able to verify anything else. So, we treat -1 like 1. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -