telnetd.c
来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 1,646 行 · 第 1/3 页
C
1,646 行
#ifndef lintstatic char *sccsid = "@(#)telnetd.c 4.1 (ULTRIX) 7/2/90";#endif lint/************************************************************************ * * * Copyright (c) 1984,1988 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used and * * copied only in accordance with the terms of such license and * * with the inclusion of the above copyright notice. This * * software or any other copies thereof may not be provided or * * otherwise made available to any other person. No title to and * * ownership of the software is hereby transferred. * * * * This software is derived from software received from the * * University of California, Berkeley, and from Bell * * Laboratories. Use, duplication, or disclosure is subject to * * restrictions under license agreements with University of * * California and with AT&T. * * * * The information in this software is subject to change without * * notice and should not be construed as a commitment by Digital * * Equipment Corporation. * * * * Digital assumes no responsibility for the use or reliability * * of its software on equipment which is not supplied by Digital. * * * ************************************************************************//* * Copyright (c) 1983,1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. *//* * Modification History: * * 30-Aug-88 Michael G. Mc Menemy * Fixed hang in cleanup by closing master side of pty before * calling vhangup. * Flush any remaining output from slave pty to the master pty for * the telnet client. * * 09-Jun-88 Mark Parenti * Changed signal handlers to void. * *//*#ifndef lintchar copyright[] =" Copyright (c) 1983 Regents of the University of California.\n\ All rights reserved.\n";#endif not lint#ifndef lintstatic char sccsid[] = "telnetd.c 5.18 (Berkeley) 5/12/86";#endif not lint*//* * Telnet server. */#include <sys/types.h>#include <sys/param.h>#include <sys/socket.h>#include <sys/wait.h>#include <sys/file.h>#include <sys/stat.h>#include <sys/time.h>#include <netinet/in.h>#include <arpa/telnet.h>#include <stdio.h>#include <signal.h>#include <errno.h>#include <sgtty.h>#include <netdb.h>#include <syslog.h>#include <ctype.h>#define OPT_NO 0 /* won't do this option */#define OPT_YES 1 /* will do this option */#define OPT_YES_BUT_ALWAYS_LOOK 2#define OPT_NO_BUT_ALWAYS_LOOK 3char hisopts[256];char myopts[256];char doopt[] = { IAC, DO, '%', 'c', 0 };char dont[] = { IAC, DONT, '%', 'c', 0 };char will[] = { IAC, WILL, '%', 'c', 0 };char wont[] = { IAC, WONT, '%', 'c', 0 };/* * I/O data buffers, pointers, and counters. */char ptyibuf[BUFSIZ], *ptyip = ptyibuf;char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;char netibuf[BUFSIZ], *netip = netibuf;#define NIACCUM(c) { *netip++ = c; \ ncc++; \ }char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;char *neturg = 0; /* one past last bye of urgent data */ /* the remote system seems to NOT be an old 4.2 */int not42 = 1;#ifdef 43BSDchar BANNER1[] = "\r\n\r\n 4.3 BSD (", BANNER2[] = ")\r\n\r\0\r\n\r\0";#elsechar *BANNER1;#endif#define TABBUFSIZ 512 /* buffer for sub-options */char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;#define SB_CLEAR() subpointer = subbuffer;#define SB_TERM() { subend = subpointer; SB_CLEAR(); }#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ *subpointer++ = (c); \ }#define SB_GET() ((*subpointer++)&0xff)#define SB_EOF() (subpointer >= subend)int pcc, ncc;int pty, net;int inter;extern char **environ;extern int errno;char *line;int SYNCHing = 0; /* we are in TELNET SYNCH mode *//* * The following are some clocks used to decide how to interpret * the relationship between various variables. */struct { int system, /* what the current time is */ echotoggle, /* last time user entered echo character */ modenegotiated, /* last time operating mode negotiated */ didnetreceive, /* last time we read data from network */ ttypeopt, /* ttype will/won't received */ ttypesubopt, /* ttype subopt is received */ getterminal, /* time started to get terminal information */ gotDM; /* when did we last see a data mark */} clocks;#define settimer(x) (clocks.x = ++clocks.system)#define sequenceIs(x,y) (clocks.x < clocks.y)main(argc, argv) char *argv[];{ struct sockaddr_in from; int on = 1, fromlen;#if defined(DEBUG) { int s, ns, foo; struct servent *sp; static struct sockaddr_in sin = { AF_INET }; sp = getservbyname("telnet", "tcp"); if (sp == 0) { fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); exit(1); } sin.sin_port = sp->s_port; argc--, argv++; if (argc > 0) { sin.sin_port = atoi(*argv); sin.sin_port = htons((u_short)sin.sin_port); } s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("telnetd: socket");; exit(1); } if (bind(s, &sin, sizeof sin) < 0) { perror("bind"); exit(1); } if (listen(s, 1) < 0) { perror("listen"); exit(1); } foo = sizeof sin; ns = accept(s, &sin, &foo); if (ns < 0) { perror("accept"); exit(1); } dup2(ns, 0); close(s); }#endif /* defined(DEBUG) */#ifdef 43BSD openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);#else openlog("telnetd", LOG_PID );#endif fromlen = sizeof (from); if (getpeername(0, &from, &fromlen) < 0) { fprintf(stderr, "%s: ", argv[0]); perror("getpeername"); _exit(1); } if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); } doit(0, &from);}char *terminaltype = 0;char *envinit[2];void cleanup();/* * ttloop * * A small subroutine to flush the network output buffer, get some data * from the network, and pass it through the telnet state machine. We * also flush the pty input buffer (by dropping its data) if it becomes * too full. */voidttloop(){ if (nfrontp-nbackp) { netflush(); } ncc = read(net, netibuf, sizeof netibuf); if (ncc < 0) { syslog(LOG_INFO, "ttloop: read: %m\n"); exit(1); } else if (ncc == 0) { syslog(LOG_INFO, "ttloop: peer died: %m\n"); exit(1); } netip = netibuf; telrcv(); /* state machine */ if (ncc > 0) { pfrontp = pbackp = ptyobuf; telrcv(); }}/* * getterminaltype * * Ask the other end to send along its terminal type. * Output is the variable terminaltype filled in. */voidgetterminaltype(){ static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; settimer(getterminal); bcopy(sbuf, nfrontp, sizeof sbuf); nfrontp += sizeof sbuf; hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK; while (sequenceIs(ttypeopt, getterminal)) { ttloop(); } if (hisopts[TELOPT_TTYPE] == OPT_YES) { static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; bcopy(sbbuf, nfrontp, sizeof sbbuf); nfrontp += sizeof sbbuf; while (sequenceIs(ttypesubopt, getterminal)) { ttloop(); } }}/* * Get a pty, scan input lines. */doit(f, who) int f; struct sockaddr_in *who;{ char *host, *inet_ntoa(); int i, p, t; struct sgttyb b; struct hostent *hp; int c; for (c = 'p'; c <= 'z'; c++) { struct stat stb; line = "/dev/ptyXX"; line[strlen("/dev/pty")] = c; line[strlen("/dev/ptyp")] = '0'; if (stat(line, &stb) < 0) break; for (i = 0; i < 16; i++) { line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; p = open(line, 2); if (p > 0) goto gotpty; } } fatal(f, "All network ports in use"); /*NOTREACHED*/gotpty: dup2(f, 0); line[strlen("/dev/")] = 't'; t = open("/dev/tty", O_RDWR); if (t >= 0) { ioctl(t, TIOCNOTTY, 0); close(t); } t = open(line, O_RDWR); if (t < 0) fatalperror(f, line, errno); ioctl(t, TIOCGETP, &b); b.sg_flags = CRMOD|XTABS|ANYP; ioctl(t, TIOCSETP, &b); ioctl(p, TIOCGETP, &b); b.sg_flags &= ~ECHO; ioctl(p, TIOCSETP, &b); hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), who->sin_family); if (hp) host = hp->h_name; else host = inet_ntoa(who->sin_addr); net = f; pty = p; /* * get terminal type. */ getterminaltype(); if ((i = fork()) < 0) fatalperror(f, "fork", errno); if (i) telnet(f, p); close(f); close(p); dup2(t, 0); dup2(t, 1); dup2(t, 2); close(t); envinit[0] = terminaltype; envinit[1] = 0; environ = envinit; /* * -h : pass on name of host. * WARNING: -h is accepted by login if and only if * getuid() == 0. * -p : don't clobber the environment (so terminal type stays set). */ execl("/bin/login", "login", "-h", host, terminaltype ? "-p" : 0, 0); fatalperror(f, "/bin/login", errno); /*NOTREACHED*/}fatal(f, msg) int f; char *msg;{ char buf[BUFSIZ]; (void) sprintf(buf, "telnetd: %s.\r\n", msg); (void) write(f, buf, strlen(buf)); exit(1);}fatalperror(f, msg, errno) int f; char *msg; int errno;{ char buf[BUFSIZ]; extern char *sys_errlist[]; (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); fatal(f, buf);}/* * Check a descriptor to see if out of band data exists on it. */stilloob(s)int s; /* socket number */{ static struct timeval timeout = { 0 }; fd_set excepts; int value; do { FD_ZERO(&excepts); FD_SET(s, &excepts); value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); } while ((value == -1) && (errno == EINTR)); if (value < 0) { fatalperror(pty, "select", errno); } if (FD_ISSET(s, &excepts)) { return 1; } else { return 0; }}/* * Main loop. Select from pty and network, and * hand data to telnet receiver finite state machine. */char hostname[MAXHOSTNAMELEN];telnet(f, p){ int on = 1; ioctl(f, FIONBIO, &on); ioctl(p, FIONBIO, &on);#if defined(SO_OOBINLINE) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);#endif /* defined(SO_OOBINLINE) */ signal(SIGTSTP, SIG_IGN); /* * Ignoring SIGTTOU keeps the kernel from blocking us * in ttioctl() in /sys/tty.c. */ signal(SIGTTOU, SIG_IGN); signal(SIGCHLD, cleanup); setpgrp(0, 0); /* * Request to do remote echo and to suppress go ahead. */ if (!myopts[TELOPT_ECHO]) { dooption(TELOPT_ECHO); } if (!myopts[TELOPT_SGA]) { dooption(TELOPT_SGA); } /* * Is the client side a 4.2 (NOT 4.3) system? We need to know this * because 4.2 clients are unable to deal with TCP urgent data. * * To find out, we send out a "DO ECHO". If the remote system * answers "WILL ECHO" it is probably a 4.2 client, and we note * that fact ("WILL ECHO" ==> that the client will echo what * WE, the server, sends it; it does NOT mean that the client will * echo the terminal input). */ (void) sprintf(nfrontp, doopt, TELOPT_ECHO); nfrontp += sizeof doopt-2; hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; /* * Show banner that getty never gave. * * The banner includes some null's (for TELNET CR disambiguation), * so we have to be somewhat complicated. */ gethostname(hostname, sizeof (hostname));#ifdef 43bsd bcopy(BANNER1, nfrontp, sizeof BANNER1 -1); nfrontp += sizeof BANNER1 - 1; bcopy(hostname, nfrontp, strlen(hostname)); nfrontp += strlen(hostname); bcopy(BANNER2, nfrontp, sizeof BANNER2 -1); nfrontp += sizeof BANNER2 - 1;#else { char buffer[TABBUFSIZ], barea[TABBUFSIZ]; char *getstr(), *area = barea; if(getent(buffer, "default") >0) { BANNER1 = getstr("im", &area); bcopy(BANNER1, nfrontp, strlen(BANNER1)); nfrontp += strlen(BANNER1); } }#endif /* * Call telrcv() once to pick up anything received during * terminal type negotiation. */ telrcv(); for (;;) { fd_set ibits, obits, xbits; register int c; if (ncc < 0 && pcc < 0) break; FD_ZERO(&ibits); FD_ZERO(&obits); FD_ZERO(&xbits); /* * Never look for input if there's still * stuff in the corresponding output buffer */ if (nfrontp - nbackp || pcc > 0) { FD_SET(f, &obits); } else { FD_SET(p, &ibits); } if (pfrontp - pbackp || ncc > 0) { FD_SET(p, &obits); } else { FD_SET(f, &ibits); } if (!SYNCHing) { FD_SET(f, &xbits); } if ((c = select(16, &ibits, &obits, &xbits, (struct timeval *)0)) < 1) { if (c == -1) { if (errno == EINTR) { continue; } } sleep(1); continue; } /* * Any urgent data? */ if (FD_ISSET(net, &xbits)) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?