精華區beta SetupBBS 關於我們 聯絡資訊
/*-------------------------------------------------------*/ /* in.zbbsd.c ( NTHU CS MapleBBS Ver 2.39 ) */ /*-------------------------------------------------------*/ /* target : super telnet server for BBS */ /* create : 95/03/29 */ /* update : 96/10/10 (modified by woju) */ /*-------------------------------------------------------*/ /* syntax : (1) login as root */ /* (2) in.zbbsd {ports} [bbsuser] [bbs-public-user-without-passwd] */ /*-------------------------------------------------------*/ /* (1) standalone telnet daemon */ /* (2) CPU/system loading daemon */ /* (3) replace /bin/login */ /* (4) replace ~bbs/bin/bbsrf */ /* (5) speed up pty/tty search by shared memory */ /*-------------------------------------------------------*/ #define PASSWD "按Enter繼續..." #define BBSUSER "bbs" #define BBSUSER2 "bbs" #define BAD_HOST "/home/bbs/etc/bad_host" #include <pwd.h> char bbsprog[80]; char *bbsuser2, *bbsuser, *bbshome, *bbsshell; int bbsuid, bbsgid; #define HASHING #define HAVE_CHKLOAD #define NUMTTYS 256 #define QLEN 5 #define MSG_WORKING "\r\n...嘿咻嘿咻..." #define SYNC_PERIOD ( 8 * 60 + 17 ) #define BUSY_PERIOD 55 /* less than timeout = 75 */ #define TH_LOW 36 #define TH_HIGH 40 #define PTYSIZ 2048 /* 來自 pty 之 buffer size */ #define NETSIZ 2048 /* 送往 net 之 buffer size */ #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> #ifndef LINUX #include <ttyent.h> #endif #include <stdio.h> #include <signal.h> #include <errno.h> #include <termios.h> #include <netdb.h> #include <syslog.h> #include <ctype.h> #include <unistd.h> #include <stdlib.h> #ifdef STREAM_PTY #include <sys/stropts.h> #include "../lib/tiocpkt.h" extern char *ptsname(); #define PTY_PKT_READ pty_pkt_read #else #define PTY_PKT_READ read #endif #ifndef O_RDWR #include <sys/fcntl.h> #endif #ifndef FIONBIO #include <sys/filio.h> #endif #ifndef FIONBIO #include <sys/ioctl.h> #endif #if !defined(STREAM_PTY) && !defined(TIOCPKT) #include <sys/pty.h> #endif #include <string.h> #define bcopy(s,d,l) memcpy(d,s,l) #ifdef __STDC__ #define puts xputs /* prototype conflict */ #endif /* Ultrix syslog(3) has no facility stuff. */ #ifndef LOG_DAEMON #define LOG_DAEMON 0 #define LOG_ODELAY 0 #endif #ifdef ultrix #define setsid() setpgrp(0,0) #endif #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 3 char 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 maple[64]; char ptyibuf[PTYSIZ], *ptyip = ptyibuf; char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; char netibuf[BUFSIZ], *netip = netibuf; #define NIACCUM(c) { *netip++ = c; \ ncc++; \ } char netobuf[NETSIZ], *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 HAVE_CHKLOAD #define BANNER "\r\n【批踢踢實業坊】◎ 台大資訊附屬地下轉播站 ◎ (ptt.m8.ntu.edu.tw)\r\nLinux2.0.0 (系統負載 %s) [140.112.171.204:%s]\r\n\n" #else #define BANNER "\r\n【批踢踢實業坊】◎ 台大資訊附屬地下轉播站 ◎ (ptt.m8.ntu.edu.tw)\r\nLinux2.0.0 [140.112.171.204]\r\n\n" #endif /* buffer for sub-options */ char subbuffer[128], *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; #ifdef STREAM_PTY int pts; #endif int inter; #ifndef SYSV_ENV extern char **environ; #endif extern int errno; static char line[] = "/dev/ptyXX"; char *terminaltype = 0; int SYNCHing = 0; /* we are in TELNET SYNCH mode */ #ifdef HAVE_UTMP int utmp_slot; #endif gid_t mygid; /* * 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) time_t time(); /* ----------------------------------------------------- */ static int rfds; /* read file descriptor set */ static struct sockaddr_in xsin; #include <sys/ipc.h> #include <sys/shm.h> #define PPT_SHMKEY 889 struct PPT_SHM { time_t uptime; int visit; int busy; int load; pid_t ppt[NUMTTYS]; } *ppt_shm = NULL; int slot; static void ipc_err(name) char *name; { fprintf(stderr, "[%s] error\n", name); exit(1); } static void resolve_pptshm() { register int i; register struct PPT_SHM *shm; if (ppt_shm == NULL) { i = shmget(PPT_SHMKEY, sizeof(struct PPT_SHM), 0); if (i < 0) { i = shmget(PPT_SHMKEY, sizeof(struct PPT_SHM), IPC_CREAT | 0600); if (i < 0) ipc_err("shmget"); shm = (struct PPT_SHM *) shmat(i, NULL, 0); if (shm < 0) ipc_err("shmat"); memset(shm, 0, sizeof(struct PPT_SHM)); (void) time(&shm->uptime); } else { shm = (struct PPT_SHM *) shmat(i, NULL, 0); if (shm < 0) ipc_err("shmat"); } ppt_shm = shm; } } /* ----------------------------------------------------- */ /* FSA (finite state automata) for telnet protocol */ /* ----------------------------------------------------- */ void ptyflush(); void dooption(); void telrcv(); void cleanup() { char *p; int visit; p = line + sizeof("/dev/") - 1; #ifdef SYSV_UTMP UTMP_LOGOUT(p); (void) chown(line, 0, 0); (void) chmod(line, 0644); #else /* SYSV_UTMP */ #ifdef HAVE_UTMP logout(p); #endif (void) chmod(line, 0666); (void) chown(line, 0, 0); *p = 'p'; (void) chmod(line, 0666); (void) chown(line, 0, 0); #endif /* SYSV_UTMP */ resolve_pptshm(); ppt_shm->ppt[slot] = 0; ppt_shm->visit--; #if 0 visit = ppt_shm->visit - 1; if (visit >= 0) ppt_shm->visit = visit; #endif shutdown(net, 2); exit(0); } void fatal(f, msg) int f; char *msg; { char buf[256]; (void) sprintf(buf, "%s\r\n", msg); (void) write(f, buf, strlen(buf)); exit(1); } void fatalperror(f, msg) int f; char *msg; { char buf[256]; #ifndef BSD44 extern char *sys_errlist[]; #endif (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); fatal(f, buf); } #if 0 mode(on, off) int on, off; { struct sgttyb b; #ifdef STREAM_PTY int pty = pts; /* XXX apply ioctl()s at slave end */ #endif ptyflush(); ioctl(pty, TIOCGETP, &b); b.sg_flags |= on; b.sg_flags &= ~off; ioctl(pty, TIOCSETP, &b); } #else void telopt_echo(on) int on; { struct termios b; #ifdef STREAM_PTY int pty = pts; #endif ptyflush(); tcgetattr(pty, &b); if (on) { b.c_lflag |= ECHO; } else { b.c_lflag &= ~ECHO; } tcsetattr(pty, TCSANOW, &b); } void telopt_binary(on) int on; { struct termios b; #ifdef STREAM_PTY int pty = pts; #endif ptyflush(); tcgetattr(pty, &b); if (on) { b.c_oflag &= ~OPOST; } else { b.c_oflag |= OPOST; } tcsetattr(pty, TCSANOW, &b); } #endif /* * Send interrupt to process on other side of pty. If it is in raw mode, just * write NULL; otherwise, write intr char. */ void interrupt() { struct termios b; #ifdef STREAM_PTY int pty = pts; /* XXX apply ioctl()s at slave end */ #endif ptyflush(); /* half-hearted */ tcgetattr(pty, &b); if ((b.c_lflag & ICANON) == 0) { *pfrontp++ = '\0'; return; } if (b.c_cc[VINTR] != 0377) *pfrontp++ = b.c_cc[VINTR]; } /* * Send quit to process on other side of pty. If it is in raw mode, just * write NULL; otherwise, write quit char. */ void sendbrk() { struct termios b; #ifdef STREAM_PTY int pty = pts; /* XXX apply ioctl()s at slave end */ #endif ptyflush(); /* half-hearted */ tcgetattr(pty, &b); if ((b.c_lflag & ICANON) == 0) { *pfrontp++ = '\0'; return; } if (b.c_cc[VQUIT] != 0377) *pfrontp++ = b.c_cc[VQUIT]; } void ptyflush() { register int n; if ((n = pfrontp - pbackp) > 0) n = write(pty, pbackp, n); if (n >= 0) { pbackp += n; if (pbackp == pfrontp) pbackp = pfrontp = ptyobuf; } } /* * nextitem() * * Return the address of the next "item" in the TELNET data stream. This will * be the address of the next character if the current address is a user data * character, or it will be the address of the character following the TELNET * command if the current address is a TELNET IAC ("I Am a Command") * character. */ char * nextitem(current) char *current; { if ((*current & 0xff) != IAC) { return current + 1; } switch (*(current + 1) & 0xff) { case DO: case DONT: case WILL: case WONT: return current + 3; case SB: /* loop forever looking for the SE */ { register char *look = current + 2; for (;;) { if ((*look++ & 0xff) == IAC) { if ((*look++ & 0xff) == SE) { return look; } } } } default: return current + 2; } } /* * netclear() * * We are about to do a TELNET SYNCH operation. Clear the path to the network. * * Things are a bit tricky since we may have sent the first byte or so of a * previous TELNET command into the network. So, we have to scan the network * buffer from the beginning until we are up to where we want to be. * * A side effect of what we do, just to keep things simple, is to clear the * urgent data pointer. The principal caller should be setting the urgent * data pointer AFTER calling us in any case. */ void netclear() { register char *thisitem, *next; char *good; #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) thisitem = netobuf; while ((next = nextitem(thisitem)) <= nbackp) { thisitem = next; } /* Now, thisitem is first before/at boundary. */ good = netobuf; /* where the good bytes go */ while (nfrontp > thisitem) { if (wewant(thisitem)) { int length; next = thisitem; do { next = nextitem(next); } while (wewant(next) && (nfrontp > next)); length = next - thisitem; bcopy(thisitem, good, length); good += length; thisitem = next; } else { thisitem = nextitem(thisitem); } } nbackp = netobuf; nfrontp = good; /* next byte to be sent */ neturg = 0; } /* * netflush Send as much data as possible to the network, handling requests * for urgent data. */ void netflush() { register int n; if ((n = nfrontp - nbackp) > 0) { /* * if no urgent data, or if the other side appears to be an old 4.2 * client (and thus unable to survive TCP urgent data), write the entire * buffer in non-OOB mode. */ if ((neturg == 0) || (not42 == 0)) { n = write(net, nbackp, n);/* normal write */ } else { n = neturg - nbackp; /* * In 4.2 (and 4.3) systems, there is some question about what byte in * a sendOOB operation is the "OOB" data. To make ourselves compatible, * we only send ONE byte out of band, the one WE THINK should be OOB * (though we really have more the TCP philosophy of urgent data rather * than the Unix philosophy of OOB data). */ if (n > 1) { n = send(net, nbackp, n - 1, 0); /* send URGENT all by itself */ } else { n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ } } } if (n < 0) { #if 0 if (errno == EWOULDBLOCK) return; #endif /* should blow this guy away... */ return; } nbackp += n; if (nbackp >= neturg) { neturg = 0; } if (nbackp == nfrontp) { nbackp = nfrontp = netobuf; } } void willoption(option) int option; { char *fmt; switch (option) { case TELOPT_BINARY: telopt_binary(1); fmt = doopt; break; case TELOPT_ECHO: not42 = 0; /* looks like a 4.2 system */ /* * Now, in a 4.2 system, to break them out of ECHOing (to the terminal) * mode, we need to send a "WILL ECHO". Kludge upon kludge! */ if (myopts[TELOPT_ECHO] == OPT_YES) { dooption(TELOPT_ECHO); } fmt = dont; break; case TELOPT_TTYPE: settimer(ttypeopt); if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { hisopts[TELOPT_TTYPE] = OPT_YES; return; } fmt = doopt; break; case TELOPT_SGA: fmt = doopt; break; case TELOPT_TM: fmt = dont; break; default: fmt = dont; break; } if (fmt == doopt) { hisopts[option] = OPT_YES; } else { hisopts[option] = OPT_NO; } (void) sprintf(nfrontp, fmt, option); nfrontp += sizeof(dont) - 2; } void wontoption(option) int option; { char *fmt; switch (option) { case TELOPT_ECHO: not42 = 1; /* doesn't seem to be a 4.2 system */ break; case TELOPT_BINARY: telopt_binary(0); break; case TELOPT_TTYPE: settimer(ttypeopt); break; } fmt = dont; hisopts[option] = OPT_NO; (void) sprintf(nfrontp, fmt, option); nfrontp += sizeof(doopt) - 2; } void dooption(option) int option; { char *fmt; switch (option) { case TELOPT_ECHO: telopt_echo(1); fmt = will; break; case TELOPT_BINARY: telopt_binary(1); fmt = will; break; case TELOPT_SGA: fmt = will; break; case TELOPT_TM: default: fmt = wont; } if (fmt == will) { myopts[option] = OPT_YES; } else { myopts[option] = OPT_NO; } (void) sprintf(nfrontp, fmt, option); nfrontp += sizeof(doopt) - 2; } void dontoption(option) int option; { char *fmt; switch (option) { case TELOPT_ECHO: /* we should stop echoing */ telopt_echo(0); default: fmt = wont; } if (fmt = wont) { myopts[option] = OPT_NO; } else { myopts[option] = OPT_YES; } (void) sprintf(nfrontp, fmt, option); nfrontp += sizeof(wont) - 2; } /* * suboption() * * Look at the sub-option buffer, and try to be helpful to the other side. * * Currently we recognize: * * Terminal type is */ void suboption() { switch (SB_GET()) { case TELOPT_TTYPE: { /* Yaaaay! */ static char terminalname[5 + 41] = "TERM="; settimer(ttypesubopt); if (SB_GET() != TELQUAL_IS) { return; /* ??? XXX but, this is the most robust */ } terminaltype = terminalname + strlen(terminalname); while ((terminaltype < (terminalname + sizeof terminalname - 1)) && !SB_EOF()) { register int c; c = SB_GET(); if (isupper(c)) { c = tolower(c); } *terminaltype++ = c; /* accumulate name */ } *terminaltype = 0; terminaltype = terminalname; break; } } } /* * 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. */ void ttloop() { if (nfrontp - nbackp) { netflush(); } ncc = read(net, netibuf, sizeof netibuf); if (ncc < 0) { syslog(LOG_INFO, "ttloop: read: %m\n"); cleanup(); /* exit(1); */ } else if (ncc == 0) { syslog(LOG_INFO, "ttloop: peer died: %m\n"); cleanup(); /* exit(1); */ } netip = netibuf; telrcv(); /* state machine */ if (ncc > 0) { pfrontp = pbackp = ptyobuf; telrcv(); } } /* Check a descriptor to see if out of band data exists on it. */ int 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"); } return (FD_ISSET(s, &excepts)); } /* * Main loop. Select from pty and network, and hand data to telnet receiver * finite state machine. */ void telnet(f, p) { int on = 1; #ifdef VERBOSE char hostname[MAXHOSTNAMELEN]; #define TABBUFSIZ 512 char defent[TABBUFSIZ]; char defstrs[TABBUFSIZ]; #undef TABBUFSIZ char *HE; char *HN; char *IM; #endif ioctl(f, FIONBIO, &on); ioctl(p, FIONBIO, &on); #ifndef STREAM_PTY ioctl(p, TIOCPKT, &on); #endif #if defined(SO_OOBINLINE) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof on); #endif /* defined(SO_OOBINLINE) */ signal(SIGTSTP, SIG_IGN); signal(SIGTTIN, SIG_IGN); /* * Ignoring SIGTTOU keeps the kernel from blocking us in ttioctl() in * /sys/tty.c. */ signal(SIGTTOU, SIG_IGN); signal(SIGCHLD, cleanup); setsid(); /* 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. * * We put the banner in the pty input buffer. This way, it gets carriage * return null processing, etc., just like all other pty --> client data. */ #ifdef VERBOSE gethostname(hostname, sizeof(hostname)); if (getent(defent, "default") == 1) { char *getstr(); char *p = defstrs; HE = getstr("he", &p); HN = getstr("hn", &p); IM = getstr("im", &p); if (HN && *HN) strcpy(hostname, HN); edithost(HE, hostname); if (IM && *IM) putf(IM, ptyibuf + 1); } else #endif #ifdef HAVE_CHKLOAD sprintf(ptyibuf + 1, BANNER, maple, line + 5); #else sprintf(ptyibuf + 1, BANNER); #endif ptyip = ptyibuf + 1; /* Prime the pump */ pcc = strlen(ptyip); /* ditto */ /* Clear ptybuf[0] - where the packet information is received */ ptyibuf[0] = 0; /* * 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); FD_SET(p, &xbits); } 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(8, &ibits, &obits, &xbits, (struct timeval *) 0)) < 1) { if (c == -1) { if (errno == EINTR) { continue; } } sleep(3); continue; } /* Any urgent data? */ if (FD_ISSET(net, &xbits)) { SYNCHing = 1; } /* Something to read from the network... */ if (FD_ISSET(net, &ibits)) { #if !defined(SO_OOBINLINE) /* * In 4.2 (and 4.3 beta) systems, the OOB indication and data handling * in the kernel is such that if two separate TCP Urgent requests come * in, one byte of TCP data will be overlaid. This is fatal for Telnet, * but we try to live with it. * * In addition, in 4.2 (and...), a special protocol is needed to pick up * the TCP Urgent data in the correct sequence. * * What we do is: if we think we are in urgent mode, we look to see if we * are "at the mark". If we are, we do an OOB receive. If we run this * twice, we will do the OOB receive twice, but the second will fail, * since the second time we were "at the mark", but there wasn't any * data there (the kernel doesn't reset "at the mark" until we do a * normal read). Once we've read the OOB data, we go ahead and do * normal reads. * * There is also another problem, which is that since the OOB byte we read * doesn't put us out of OOB state, and since that byte is most likely * the TELNET DM (data mark), we would stay in the TELNET SYNCH * (SYNCHing) state. So, clocks to the rescue. If we've "just" * received a DM, then we test for the presence of OOB data when the * receive OOB fails (and AFTER we did the normal mode read to clear * "at the mark"). */ if (SYNCHing) { int atmark; ioctl(net, SIOCATMARK, (char *) &atmark); if (atmark) { ncc = recv(net, netibuf, sizeof(netibuf), MSG_OOB); if ((ncc == -1) && (errno == EINVAL)) { ncc = read(net, netibuf, sizeof(netibuf)); if (sequenceIs(didnetreceive, gotDM)) { SYNCHing = stilloob(net); } } } else { ncc = read(net, netibuf, sizeof(netibuf)); } } else { ncc = read(net, netibuf, sizeof(netibuf)); } settimer(didnetreceive); #else /* !defined(SO_OOBINLINE)) */ ncc = read(net, netibuf, sizeof(netibuf)); #endif /* !defined(SO_OOBINLINE)) */ if (ncc < 0 && errno == EWOULDBLOCK) ncc = 0; else { if (ncc <= 0) { break; } netip = netibuf; } } /* Something to read from the pty... */ if (FD_ISSET(p, &xbits)) { if (PTY_PKT_READ(p, ptyibuf, 1) != 1) { break; } } if (FD_ISSET(p, &ibits)) { pcc = PTY_PKT_READ(p, ptyibuf, PTYSIZ); if (pcc < 0 && errno == EWOULDBLOCK) pcc = 0; else { if (pcc <= 0) break; /* Skip past "packet" */ pcc--; ptyip = ptyibuf + 1; } } if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { netclear(); /* clear buffer back */ *nfrontp++ = IAC; *nfrontp++ = DM; neturg = nfrontp - 1; /* off by one XXX */ ptyibuf[0] = 0; } while (pcc > 0) { if ((&netobuf[NETSIZ] - nfrontp) < 2) break; c = *ptyip++ & 0377, pcc--; if (c == IAC) *nfrontp++ = c; *nfrontp++ = c; /* Don't do CR-NUL if we are in binary mode */ if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { if (pcc > 0 && ((*ptyip & 0377) == '\n')) { *nfrontp++ = *ptyip++ & 0377; pcc--; } else *nfrontp++ = '\0'; } } if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) netflush(); if (ncc > 0) telrcv(); if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) ptyflush(); } cleanup(); } /* State for recv fsm */ #define TS_DATA 0 /* base state */ #define TS_IAC 1 /* look for double IAC's */ #define TS_CR 2 /* CR-LF ->'s CR */ #define TS_SB 3 /* throw away begin's... */ #define TS_SE 4 /* ...end's (suboption negotiation) */ #define TS_WILL 5 /* will option negotiation */ #define TS_WONT 6 /* wont " */ #define TS_DO 7 /* do " */ #define TS_DONT 8 /* dont " */ void telrcv() { register int c; static int state = TS_DATA; #ifdef STREAM_PTY int pty = pts; /* XXX apply ioctl()s at slave end */ #endif while (ncc > 0) { if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) return; c = *netip++ & 0377, ncc--; switch (state) { case TS_CR: state = TS_DATA; /* Strip off \n or \0 after a \r */ if ((c == 0) || (c == '\n')) { break; } /* FALL THROUGH */ case TS_DATA: if (c == IAC) { state = TS_IAC; break; } if (inter > 0) break; /* * We now map \r\n ==> \r for pragmatic reasons. Many client * implementations send \r\n when the user hits the CarriageReturn key. * * We USED to map \r\n ==> \n, since \r\n says that we want to be in * column 1 of the next printable line, and \n is the standard unix way * of saying that (\r is only good if CRMOD is set, which it normally * is). */ if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { state = TS_CR; } *pfrontp++ = c; break; case TS_IAC: switch (c) { /* * Send the process on the pty side an interrupt. Do this with a * NULL or interrupt char; depending on the tty mode. */ case IP: interrupt(); break; case BREAK: sendbrk(); break; /* Are You There? */ case AYT: strcpy(nfrontp, "\r\n[Yes]\r\n"); nfrontp += 9; break; /* Abort Output */ case AO: { ptyflush(); /* half-hearted */ tcflush(pty, TCOFLUSH); netclear(); /* clear buffer back */ *nfrontp++ = IAC; *nfrontp++ = DM; neturg = nfrontp - 1; /* off by one XXX */ break; } /* Erase Character and Erase Line */ case EC: case EL: { struct termios b; char ch; ptyflush(); /* half-hearted */ tcgetattr(pty, &b); ch = (c == EC) ? b.c_cc[VERASE] : b.c_cc[VKILL]; if (ch != '\377') { *pfrontp++ = ch; } break; } /* Check for urgent data... */ case DM: SYNCHing = stilloob(net); settimer(gotDM); break; /* Begin option subnegotiation... */ case SB: state = TS_SB; continue; case WILL: state = TS_WILL; continue; case WONT: state = TS_WONT; continue; case DO: state = TS_DO; continue; case DONT: state = TS_DONT; continue; case IAC: *pfrontp++ = c; break; } state = TS_DATA; break; case TS_SB: if (c == IAC) { state = TS_SE; } else { SB_ACCUM(c); } break; case TS_SE: if (c != SE) { if (c != IAC) { SB_ACCUM(IAC); } SB_ACCUM(c); state = TS_SB; } else { SB_TERM(); suboption(); /* handle sub-option */ state = TS_DATA; } break; case TS_WILL: if (hisopts[c] != OPT_YES) willoption(c); state = TS_DATA; continue; case TS_WONT: if (hisopts[c] != OPT_NO) wontoption(c); state = TS_DATA; continue; case TS_DO: if (myopts[c] != OPT_YES) dooption(c); state = TS_DATA; continue; case TS_DONT: if (myopts[c] != OPT_NO) { dontoption(c); } state = TS_DATA; continue; default: syslog(LOG_ERR, "panic state=%d\n", state); cleanup(); /* exit(1) */ } } } /* * getterminaltype * * Ask the other end to send along its terminal type. Output is the variable * terminaltype filled in. */ void getterminaltype() { 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(); } } } /* --------------------------------- */ /* log in BBS */ /* --------------------------------- */ #include <sys/types.h> #include <grp.h> #include <sys/resource.h> #include <utmp.h> #ifndef _PATH_UTMP #define _PATH_UTMP "/etc/utmp" #endif /* HP-UX 9.0 termios doesn't define these */ #ifndef FLUSHO #define FLUSHO 0 #define XTABS 0 #endif #ifndef OXTABS #define OXTABS XTABS #endif #ifndef LINUX #define _PATH_DEFPATH "/usr/ucb:/bin:/usr/bin:" #endif void slave_termios() { struct termios termios; /* * Finalize the terminal settings. Some systems default to 8 bits, others * to 7, so we should leave that alone. */ tcgetattr(0, &termios); termios.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON | IMAXBEL); termios.c_iflag &= ~IXANY; termios.c_lflag |= (ISIG | IEXTEN | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE); termios.c_lflag &= ~(ECHOPRT | TOSTOP | FLUSHO); termios.c_oflag |= (OPOST | ONLCR); termios.c_oflag &= ~OXTABS; termios.c_cc[VEOF] = 4; (void) tcsetattr(0, TCSANOW, &termios); } #ifdef HAVE_UTMP /* --------------------------------- */ /* log out process */ /* --------------------------------- */ /* 0 on failure, 1 on success */ void logout(line) register char *line; { int fd; if ((fd = open(_PATH_UTMP, O_RDWR, 0)) >= 0) { struct utmp ut; (void) lseek(fd, (long) (utmp_slot), L_SET); (void) read(fd, &ut, sizeof(struct utmp)); if (ut.ut_name[0] && !strncmp(ut.ut_line, line, sizeof(ut.ut_line))) { bzero(ut.ut_name, sizeof(ut.ut_name)); bzero(ut.ut_host, sizeof(ut.ut_host)); (void) time(&ut.ut_time); (void) lseek(fd, (long) -sizeof(struct utmp), L_INCR); (void) write(fd, &ut, sizeof(struct utmp)); } (void) close(fd); } } #endif /* ----------------------------------------------- */ /* 取得 remote user name 以判定身份 */ /* ----------------------------------------------- */ /* * rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC * 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote * host to look up the owner of a connection. The information should not be * used for authentication purposes. This routine intercepts alarm signals. * * Diagnostics are reported through syslog(3). * * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. */ #include <setjmp.h> #define STRN_CPY(d,s,l) { strncpy((d),(s),(l)); (d)[(l)-1] = 0; } #define STRING_LENGTH 60 #define RFC931_TIMEOUT 10 #define RFC931_PORT 113 /* Semi-well-known port */ #define ANY_PORT 0 /* Any old port will do */ /* ------------------------- */ /* timeout - handle timeouts */ /* ------------------------- */ static jmp_buf timebuf; static void timeout(sig) int sig; { longjmp(timebuf, sig); } void getremotename(from, rhost, rname) struct sockaddr_in *from; char *rhost; char *rname; { struct sockaddr_in our_sin; struct sockaddr_in rmt_sin; unsigned rmt_port, rmt_pt; unsigned our_port, our_pt; FILE *fp; char buffer[512], user[80], *cp; int s; struct hostent *hp; /* get remote host name */ hp = NULL; if (setjmp(timebuf) == 0) { signal(SIGALRM, timeout); alarm(3); hp = gethostbyaddr((char *) &from->sin_addr, sizeof(struct in_addr), from->sin_family); alarm(0); } strcpy(rhost, hp ? hp->h_name : (char *) inet_ntoa(from->sin_addr)); /* * Use one unbuffered stdio stream for writing to and for reading from the * RFC931 etc. server. This is done because of a bug in the SunOS 4.1.x * stdio library. The bug may live in other stdio implementations, too. * When we use a single, buffered, bidirectional stdio stream ("r+" or "w+" * mode) we read our own output. Such behaviour would make sense with * resources that support random-access operations, but not with sockets. */ s = sizeof our_sin; if (getsockname(0, (struct sockaddr*)&our_sin, &s) < 0) return; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("bbsd: socket in rfc931"); return; } if (!(fp = fdopen(s, "r+"))) { close(s); perror("bbsd:fdopen"); return; } /* * Set up a timer so we won't get stuck while waiting for the server. */ if (setjmp(timebuf) == 0) { signal(SIGALRM, timeout); alarm(RFC931_TIMEOUT); /* * Bind the local and remote ends of the query socket to the same IP * addresses as the connection under investigation. We go through all * this trouble because the local or remote system might have more than * one network address. The RFC931 etc. client sends only port numbers; * the server takes the IP addresses from the query socket. */ our_pt = ntohs(our_sin.sin_port); our_sin.sin_port = htons(ANY_PORT); rmt_sin = *from; rmt_pt = ntohs(rmt_sin.sin_port); rmt_sin.sin_port = htons(RFC931_PORT); setbuf(fp, (char *) 0); s = fileno(fp); if (bind(s, (struct sockaddr *) & our_sin, sizeof(our_sin)) >= 0 && connect(s, (struct sockaddr *) & rmt_sin, sizeof(rmt_sin)) >= 0) { /* * Send query to server. Neglect the risk that a 13-byte write would * have to be fragmented by the local system and cause trouble with * buggy System V stdio libraries. */ fprintf(fp, "%u,%u\r\n", rmt_pt, our_pt); fflush(fp); /* * Read response from server. Use fgets()/sscanf() so we can work * around System V stdio libraries that incorrectly assume EOF when a * read from a socket returns less than requested. */ if (fgets(buffer, sizeof(buffer), fp) && !ferror(fp) && !feof(fp) && sscanf(buffer, "%u , %u : USERID :%*[^:]:%79s", &rmt_port, &our_port, user) == 3 && rmt_pt == rmt_port && our_pt == our_port) { /* * Strip trailing carriage return. It is part of the protocol, not * part of the data. */ if (cp = strchr(user, '\r')) *cp = 0; strcpy(rname, user); } } alarm(0); } fclose(fp); } bad_host(char* name) { FILE* list; char buf[40]; if (list = fopen(BAD_HOST, "r")) { while (fgets(buf, 40, list)) { buf[strlen(buf) - 1] = '\0'; if (!strcmp(buf, name)) return 1; if (buf[strlen(buf) - 1] == '.' && !strncmp(buf, name, strlen(buf))) return 1; if (*buf == '.' && strlen(buf) < strlen(name) && !strcmp(buf, name + strlen(name) - strlen(buf))) return 1; } fclose(list); } return 0; } int get_tty() { int i, p, t, now, visit; int *busy; pid_t *pid; resolve_pptshm(); busy = &ppt_shm->busy; i = BUSY_PERIOD; while (*busy && i) { sleep(1); i--; } *busy = 1; now = time(0) - SYNC_PERIOD; if (ppt_shm->uptime <= now) { for (i = visit = 0, pid = ppt_shm->ppt; i < NUMTTYS; i++, pid++) { if (p = *pid) { errno = 0; if ((kill(p, 0) < 0) && (errno == ESRCH)) { *pid = 0; } else visit++; } } ppt_shm->visit = visit; (void) time(&ppt_shm->uptime); } else visit = ppt_shm->visit; if (visit < 250) { now &= NUMTTYS - 1; i = now; pid = &ppt_shm->ppt[i]; do { if (!*pid) { #ifdef BSD44 line[sizeof("/dev/ptyp") - 2] = "PqrsPQRS"[i & 7]; line[sizeof("/dev/ptyp") - 1] = "0123456789abcdefghijklmnopqrstuv"[(i >> 4)& 31]; #else line[sizeof("/dev/ptyp") - 2] = "klmnopqrstuvwxyzKLMNOPQRSTUVWXYZ"[(i >> 4) & 31]; line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i & 15]; #endif p = open(line, O_RDWR | O_NOCTTY); if (p > 0) { line[sizeof("/dev/") - 1] = 't'; t = open(line, O_RDWR | O_NOCTTY); if (t > 0) { *pid = getpid(); ppt_shm->visit = visit + 1; *busy = 0; slot = i; pty = p; return (t); } close(p); line[sizeof("/dev/") - 1] = 'p'; } } if (i == NUMTTYS - 1) { i = 0; pid = ppt_shm->ppt; } else { i++; pid++; } } while (i != now); } *busy = 0; sleep(5); #if 0 sprintf(maple, "客滿了,請稍後再來,目前線上人數 [%d] 人", visit); fatal(net, maple); #else fatal(net, "客滿了,請稍後再來。"); #endif } void start_client() { char rhost[40]; char rname[40] = "?"; int i, p, t; openlog("bbsd", LOG_PID | LOG_ODELAY, LOG_DAEMON); #ifdef __linux setpgrp(); #else setpgrp(0, 0); #endif net = 0; getremotename(&xsin, rhost, rname); /* RFC931 */ /* ban 掉 bad host / bad user */ if (bad_host(rhost) && !strcmp(rname, "?")) exit(1); /* ---------------------------- */ /* Get a pty, scan input lines. */ /* ---------------------------- */ t = get_tty(); #ifdef NO_SHM #ifdef STREAM_PTY if ((p = open("/dev/ptmx", O_RDWR)) < 0) fatal(net, "All network ports in use"); if (grantpt(p) < 0 || unlockpt(p) < 0) fatal(net, "Cannot initialize pty slave"); dup2(net, 0); if ((line = ptsname(p)) == 0 || (t = open(line, O_RDWR)) < 0) fatal(net, "Cannot find pty slave"); if (ioctl(t, I_PUSH, "ptem") < 0 || ioctl(t, I_PUSH, "ldterm") < 0 || ioctl(t, I_PUSH, "ttcompat") < 0 || ioctl(p, I_PUSH, "pckt") < 0) fatal(net, "Cannot push streams modules onto pty"); #else /* STREAM_PTY */ /* ----------------------------------------- */ /* hashing : increase the seaching pty speed */ /* ----------------------------------------- */ #ifdef HASHING { static char ptyname[] = "/dev/ptyXX"; line = ptyname; } i = getpid(); #ifdef BSD44 for (t = i + 256; i < t; i++) { line[sizeof("/dev/ptyp") - 2] = "PqrsPQRS"[i & 7]; line[sizeof("/dev/ptyp") - 1] = "0123456789abcdefghijklmnopqrstuv"[(i >> 4)& 31]; #else for (t = i + 512; i < t; i++) { line[sizeof("/dev/ptyp") - 2] = "klmnopqrstuvwxyzKLMNOPQRSTUVWXYZ"[(i >> 4) & 31]; line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i & 15]; #endif p = open(line, O_RDWR | O_NOCTTY); if (p > 0) goto gotpty; } if (p <= 0) { sleep(5); fatal(net, "客滿了,請稍後再來"); } #else for (t = 'P'; t <= 'T'; t++) { struct stat stb; static char ptyname[] = "/dev/ptyXX"; line = ptyname; line[strlen("/dev/pty")] = t; line[strlen("/dev/ptyp")] = '0'; if (stat(line, &stb) < 0) break; for (i = 0; i < 16; i++) { line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; close(open(line, O_RDWR | O_NOCTTY)); p = open(line, O_RDWR | O_NOCTTY); if (p > 0) break; } } if (p <= 0) fatal(net, "All network ports in use"); #endif /* HASHING */ /* NOTREACHED */ gotpty: /* dup2(net, 0); */ line[sizeof("/dev/") - 1] = 't'; /* close(open(line, O_RDWR)); */ t = open(line, O_RDWR | O_NOCTTY); if (t < 0) { fatalperror(net, line); } #ifdef ultrix { struct termios b; tcgetattr(t, &b); b.c_iflag |= ICRNL; b.c_oflag |= ONLCR; tcsetattr(t, TCSANOW, &b); } #endif /* ultrix */ #endif /* STREAM_PTY */ pty = p; #ifdef STREAM_PTY pts = t; #endif #endif NO_SHM /* get terminal type. */ getterminaltype(); i = fork(); if (i < 0) fatalperror(net, "fork"); else if (i) telnet(net, pty); /* Acquire a controlling terminal */ setsid(); #if defined(TIOCSCTTY) && !defined(BROKEN_TIOCSCTTY) ioctl(t, TIOCSCTTY, (caddr_t) 0); #else /* TIOCSCTTY */ i = t; if ((t = open(line, O_RDWR)) < 0) _exit(1); close(i); #endif /* TIOCSCTTY */ close(net); close(pty); fchmod(t, 0620); fchown(t, bbsuid, mygid); dup2(t, 0); dup2(t, 1); close(t); /* --------------- */ /* shortcut to BBS */ /* --------------- */ #if 0 for (i = getdtablesize(); i > 2; i--) close(i); #endif (void) setpriority(PRIO_PROCESS, 0, 0); { char *salt, *p; struct passwd* pwd; if (pwd = getpwnam(bbsuser)) salt = pwd->pw_passwd; else salt = "xx"; if (*pwd->pw_passwd) { p = getpass(PASSWD); if (strcmp(crypt(p, salt), pwd->pw_passwd)) { if (*bbsuser2) { bbsuser = bbsuser2; if (pwd = getpwnam(bbsuser)) { bbsuid = pwd->pw_uid; bbsgid = pwd->pw_gid; bbshome =pwd->pw_dir; bbsshell = pwd->pw_shell; sprintf(bbsprog, "%s/bin/bbs", bbshome); } else { sleep(5); exit(1); } } else { sleep(5); exit(1); } } } } slave_termios(); #ifdef HAVE_UTMP { register int fd; struct utmp utmp; off_t lseek(); utmp_slot = ttyslot() * sizeof(struct utmp); if (utmp_slot > 0 && (fd = open(_PATH_UTMP, O_WRONLY, 0)) >= 0) { memset((char *) &utmp, 0, sizeof(utmp)); (void) time(&utmp.ut_time); strncpy(utmp.ut_name, BBSUSER, sizeof(utmp.ut_name)); strncpy(utmp.ut_host, rhost, sizeof(utmp.ut_host)); strncpy(utmp.ut_line, line + sizeof("/dev/") - 1, sizeof(utmp.ut_line)); (void) lseek(fd, (long) (utmp_slot), L_SET); (void) write(fd, &utmp, sizeof(struct utmp)); (void) close(fd); } } #endif /* Give up root privileges: no way back from here. */ if (setgid(bbsgid)) exit(1); initgroups(bbsuser, bbsgid); if (setuid(bbsuid)) exit(1); /* * Now that we have given up root privilege do the stuff that must be done * as the real user: Kerberos or Secure RPC authentication, entering the * (possibly remote) home directory. */ /* Change to home directory */ if (chdir(bbshome)) exit(1); /* * Set up a new environment. With SYSV, some variables are always * preserved; some varables are never preserved, and some variables are * always clobbered. With BSD, nothing is always preserved, and some * variables are always clobbered. We add code to make sure that LD_* and * IFS are never preserved. */ environ[0] = 0; if (terminaltype) putenv(terminaltype); (void) setenv("HOME", bbshome, 1); (void) setenv("SHELL", bbsshell, 1); (void) setenv("REMOTEHOST", rhost, 1); (void) setenv("REMOTEUSERNAME", rname, 1); (void) setenv("USER", bbsuser, 1); (void) setenv("PATH", _PATH_DEFPATH, 0); { char RFC931[80]; sprintf(RFC931, "%s@%s", rname, rhost); (void) setenv("RFC931", RFC931, 1); } execl(bbsprog, "bbs", rhost, line, rname, NULL); syslog(LOG_ERR, "%s: %m", bbsprog); exit(1); } /* --------------------------------- */ /* stand-alone daemon */ /* --------------------------------- */ void reapchild() { int state, pid; /* signal(SIGCHLD, reapchild); */ while ((pid = waitpid(-1, &state, WNOHANG | WUNTRACED)) > 0); } /* ----------------------------------- */ /* check system / memory / CPU loading */ /* ----------------------------------- */ #ifdef HAVE_CHKLOAD int chkload(limit) int limit; { double cpu_load[3]; register int i; long avenrun[3]; #if defined(LINUX) FILE *fp; fp = fopen("/proc/loadavg", "r"); if (!fp) cpu_load[0] = cpu_load[1] = cpu_load[2] = 0; else { float av[3]; fscanf(fp, "%g %g %g", av, av + 1, av + 2); fclose(fp); cpu_load[0] = av[0]; cpu_load[1] = av[1]; cpu_load[2] = av[2]; } #elif defined(BSD44) getloadavg(cpu_load, 3); #else #include <nlist.h> #define VMUNIX "/vmunix" #define KMEM "/dev/kmem" static struct nlist nlst[] = { {"_avenrun"}, {0} }; static long offset = -1; int kmem; if ((kmem = open(KMEM, O_RDONLY)) == -1) return (1); if (offset < 0) { (void) nlist(VMUNIX, nlst); if (nlst[0].n_type == 0) return (1); offset = (long) nlst[0].n_value; } if (lseek(kmem, offset, L_SET) == -1) { close(kmem); return (1); } if (read(kmem, (char *) avenrun, sizeof(avenrun)) == -1) { close(kmem); return (1); } close(kmem); #define loaddouble(la) ((double)(la) / (1 << 8)) for (i = 0; i < 3; i++) cpu_load[i] = loaddouble(avenrun[i]); #endif i = cpu_load[0]; if (i < limit) i = 0; sprintf(maple, "系統負載 %.2f %.2f %.2f%s", cpu_load[0], cpu_load[1], cpu_load[2], (i ? ",請稍後再來\n" : "")); return i; } #endif void dokill() { kill(0, SIGKILL); } void start_daemon() { int n, fp; struct group *gr; char buf[80]; /* * More idiot speed-hacking --- the first time conversion makes the C * library open the files containing the locale definition and time zone. * If this hasn't happened in the parent process, it happens in the * children, once per connection --- and it does add up. */ time_t dummy = time(NULL); struct tm *dummy_time = localtime(&dummy); struct tm *other_dummy_time = gmtime(&dummy); strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", dummy_time); gr = (struct group *) getgrnam("tty"); mygid = gr ? gr->gr_gid : bbsgid; n = getdtablesize(); if (fork()) exit(0); sprintf(buf, "/var/run/bbsd.pid"); if ((fp = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0644)) >= 0) { sprintf(buf, "%5d\n", getpid()); write(fp, buf, 6); close(fp); } else { fprintf(stderr, "bbsd: cant log PID file [%s]\n", buf); exit(-1); } while (n) (void) close(--n); #if 0 chdir("/"); (void) open("/", O_RDONLY); #endif (void) open("/dev/null", O_RDONLY); (void) dup2(0, 1); n = open("/dev/tty", O_RDWR); if (n > 0) { ioctl(n, TIOCNOTTY, (char *) 0); (void) close(n); } openlog("bbsd", LOG_PID, LOG_AUTH); syslog(LOG_NOTICE, "start\n"); } int bind_port(port) int port; { int sock, on; struct linger onn; onn.l_onoff = 0; onn.l_linger = 0; sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) { syslog(LOG_NOTICE, "socket\n"); exit(1); } on = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) syslog(LOG_ERR, "(SO_REUSEADDR): %m"); if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) syslog(LOG_WARNING, "(SO_KEEPALIVE): %m"); on = 0; if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &onn, sizeof(onn)) < 0) syslog(LOG_ERR, "(SO_LINGER): %m"); #if 0 /* 0825 */ on = 4096; setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *) &on, sizeof(on)); setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *) &on, sizeof(on)); #endif xsin.sin_port = htons(port); if (bind(sock, (struct sockaddr *)&xsin, sizeof xsin) < 0) { syslog(LOG_INFO, "bbsd bind_port can't bind to %d",port); exit(1); } if (listen(sock, QLEN) < 0) { syslog(LOG_INFO, "bbsd bind_port can't listen to %d",port); exit(1); } FD_SET(sock, (fd_set *) & rfds); return sock; } void main(argc, argv) char *argv[]; { int msock, csock; /* socket for Master and Child */ int ofds, nfds; int th_low, th_high, overload; pid_t pid; time_t uptime; struct timeval tv; struct passwd* pwd; /* --------------------------------------------------- */ /* setup standalone */ /* --------------------------------------------------- */ start_daemon(); signal(SIGCHLD, reapchild); /* --------------------------------------------------- */ /* port binding */ /* --------------------------------------------------- */ memset(&xsin, 0, sizeof(xsin)); xsin.sin_family = AF_INET; rfds = 0; bbsuser = BBSUSER; bbsuser2 = BBSUSER2; if (argc > 1) { msock = -1; for (nfds = 1; nfds < argc; nfds++) { csock = atoi(argv[nfds]); if (csock > 0) msock = bind_port(csock); else break; } if (msock < 0) { syslog(LOG_INFO, "bbsd started with invalid arguments (no port)"); exit(1); } if (nfds < argc) bbsuser = argv[nfds++]; if (nfds < argc) bbsuser2 = argv[nfds++]; } else { static int ports[] = {23, 3001, 3002, 3003, 3004, 3005}; for (nfds = 0; nfds < sizeof(ports) / sizeof(int); nfds++) { msock = bind_port(ports[nfds]); } } if (!(pwd = getpwnam(bbsuser))) { fprintf(stderr, "No such user: %s\n", bbsuser); exit(1); } bbsuid = pwd->pw_uid; bbsgid = pwd->pw_gid; bbshome =pwd->pw_dir; bbsshell = pwd->pw_shell; sprintf(bbsprog, "%s/bin/bbs", bbshome); ofds = rfds; nfds = msock + 1; /* --------------------------------------------------- */ /* main loop */ /* --------------------------------------------------- */ th_low = TH_LOW; th_high = TH_HIGH; overload = uptime = 0; for (;;) { forever: #ifdef HAVE_CHKLOAD pid = time(0); if (pid > uptime) { overload = chkload(overload ? th_low : th_high); uptime = pid + overload + 60; /* 短時間內不再檢查 system load */ } #endif rfds = ofds; tv.tv_sec = 60 * 30; tv.tv_usec = 0; msock = select(nfds, (fd_set *) & rfds, NULL, NULL, &tv); if (msock < 0) { if (errno != EINTR) sleep(5); continue; } else if (msock == 0) /* No network traffic */ { continue; } #if 0 if (rfds & ofds == 0) continue; #endif msock = 0; csock = 1; for (;;) { if (csock & rfds) break; if (++msock >= nfds) goto forever; csock <<= 1; } slot = sizeof xsin; do { csock = accept(msock, (struct sockaddr *)&xsin, &slot); } while (csock < 0 && errno == EINTR); if (csock < 0) continue; #ifdef HAVE_CHKLOAD if (overload) { (void) write(csock, maple, strlen(maple)); close(csock); continue; } #endif pid = fork(); if (!pid) { FILE *fp; char buff[255]; /* 先顯示訊息,免得 user 失去耐心 */ if(fp=fopen("/home/bbs/BAN","r")) { while(fgets(buff,255,fp)) (void) write(csock,buff,sizeof(buff)-1); fclose(fp); sleep(1); close(csock); continue; } while (--nfds >= 0) close(nfds); dup2(csock, 0); close(msock); start_client(); } else { #if 0 if (pid < 0) { perror("fork"); } #endif close(csock); } } }