📄 sockd.c
字号:
/* * Copyright (c) 1997, 1998, 1999 * Inferno Nettverk A/S, Norway. 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. The above copyright notice, this list of conditions and the following * disclaimer must appear in all copies of the software, derivative works * or modified versions, and any portions thereof, aswell as in all * supporting documentation. * 2. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by * Inferno Nettverk A/S, Norway. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * Inferno Nettverk A/S requests users of this software to return to * * Software Distribution Coordinator or sdc@inet.no * Inferno Nettverk A/S * Oslo Research Park * Gaustadal閑n 21 * N-0349 Oslo * Norway * * any improvements or extensions that they make and grant Inferno Nettverk A/S * the rights to redistribute these changes. * */#include "common.h"static const char rcsid[] ="$Id: sockd.c,v 1.248 1999/12/20 13:07:42 karls Exp $"; /* * signal handlers */__BEGIN_DECLSstatic voidchecksettings __P((void));static voidsiginfo __P((int sig));static voidsigchld __P((int sig));static voidsigalrm __P((int sig));static voidsighup __P((int sig));static voidsigserverbroadcast __P((int sig));/* * Broadcasts "sig" to all other servers. * */static voidserverinit __P((int argc, char *argv[], char *envp[]));/* * Initialises options/config. "argc" and "argv" should be * the arguments passed to main(). * Exits on failure. */static voidusage __P((int code));/* * print usage. */static voidshowversion __P((void));/* * show versioninfo and exits. */static voidshowlicense __P((void));/* * shows license and exits. */#if DIAGNOSTIC && HAVE_MALLOC_OPTIONS extern char *malloc_options;#endif /* DIAGNOSTIC && HAVE_MALLOC_OPTIONS */#if HAVE_PROGNAMEextern char *__progname;#elsechar *__progname = "sockd"; /* default. */#endif /* HAVE_PROGNAME */extern char *optarg;__END_DECLSint#if HAVE_SETPROCTITLEmain(argc, argv)#elsemain(argc, argv, envp)#endif /* HAVE_SETPROCTITLE */ int argc; char *argv[];#if !HAVE_SETPROCTITLE char *envp[];#endif /* HAVE_SETPROCTITLE */{ struct sigaction sigact; int p, maxfd, dforchild; FILE *fp;#if HAVE_SETPROCTITLE char *envp[] = { NULL }; /* dummy. */#endif /* HAVE_SETPROCTITLE */ const int exitsignalv[] = { SIGINT, SIGQUIT, SIGBUS, SIGSEGV, SIGTERM }; const size_t exitsignalc = ELEMENTS(exitsignalv); const int ignoresignalv[] = { SIGPIPE }; const size_t ignoresignalc = ELEMENTS(ignoresignalv);#if DIAGNOSTIC && HAVE_MALLOC_OPTIONS malloc_options = "AJ";#endif /* DIAGNOSTIC && HAVE_MALLOC_OPTIONS */ serverinit(argc, argv, envp); showconfig(&config); socks_seteuid(NULL, config.uid.unprivileged); /* for chroot and needing every descriptor we can get. */ dforchild = config.log.type & LOGTYPE_SYSLOG ? -1 : 0; /* syslog takes one */ for (p = 0, maxfd = getdtablesize(); p < maxfd; ++p) { int i; /* don't close config/log files. */ if (socks_logmatch((size_t)p, &config.log)) continue; ++dforchild; /* descriptor will be usable by child. */ /* sockets we listen on. */ for (i = 0; i < config.internalc; ++i) { if (p == config.internalv[i].s) break;#if NEED_ACCEPTLOCK if (config.option.serverc > 1) if (p == config.internalv[i].lock) break;#endif } if (i < config.internalc) continue; close(p); } initlog(); /* for syslog. */ /* * Check system limits against what we need. * Enough descriptors for each childprocess? +2 for mother connections. */ /* CONSTCOND */ maxfd = MAX(SOCKD_NEGOTIATEMAX, MAX(SOCKD_REQUESTMAX, SOCKD_IOMAX * FDPASS_MAX)) + 2; if (dforchild < maxfd) { struct rlimit rlimit; rlimit.rlim_cur = maxfd; rlimit.rlim_max = maxfd; if (setrlimit(RLIMIT_OFILE, &rlimit) != 0) { if (errno != EPERM) serr(EXIT_FAILURE, "setrlimit(RLIMIT_OFILE, %d)", rlimit.rlim_max); else if (getdtablesize() < SOCKD_NEGOTIATEMAX + 2) serr(EXIT_FAILURE, "%d descriptors configured for negotiation, %d available", SOCKD_NEGOTIATEMAX + 2, getdtablesize()); else if (getdtablesize() < SOCKD_REQUESTMAX + 2) serr(EXIT_FAILURE, "%d descriptors configured for requestcompletion, %d available", SOCKD_REQUESTMAX + 2, getdtablesize()); else if (getdtablesize() < SOCKD_IOMAX * FDPASS_MAX + 2) serr(EXIT_FAILURE, "%d descriptors configured for i/o, %d available", SOCKD_IOMAX * FDPASS_MAX + 2, getdtablesize()); else SERRX(getdtablesize()); } } /* set up signalhandlers. */ sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART | SA_NOCLDSTOP; sigact.sa_handler = siginfo;#if HAVE_SIGNAL_SIGINFO if (sigaction(SIGINFO, &sigact, NULL) != 0) { swarn("sigaction(SIGINFO)"); return EXIT_FAILURE; }#endif /* HAVE_SIGNAL_SIGINFO */ /* same handler, for systems without SIGINFO. */ if (sigaction(SIGUSR1, &sigact, NULL) != 0) { swarn("sigaction(SIGUSR1)"); return EXIT_FAILURE; } sigact.sa_handler = sighup; if (sigaction(SIGHUP, &sigact, NULL) != 0) { swarn("sigaction(SIGHUP)"); return EXIT_FAILURE; } sigact.sa_handler = sigchld; if (sigaction(SIGCHLD, &sigact, NULL) != 0) { swarn("sigaction(SIGCHLD)"); return EXIT_FAILURE; } sigact.sa_handler = sockdexit; for (p = 0; (size_t)p < exitsignalc; ++p) if (sigaction(exitsignalv[p], &sigact, NULL) != 0) swarn("sigaction(%d)", exitsignalv[p]); sigact.sa_handler = SIG_IGN; for (p = 0; (size_t)p < ignoresignalc; ++p) if (sigaction(ignoresignalv[p], &sigact, NULL) != 0) swarn("sigaction(%d)", ignoresignalv[p]); sigact.sa_flags = 0; /* want to be interrupted. */ sigact.sa_handler = sigalrm; if (sigaction(SIGALRM, &sigact, NULL) != 0) { swarn("sigaction(SIGALRM)"); return EXIT_FAILURE; } socks_seteuid(NULL, config.uid.privileged); if ((fp = fopen(SOCKD_PIDFILE, "w")) == NULL) swarn("open(%s)", SOCKD_PIDFILE); socks_seteuid(NULL, config.uid.unprivileged); if (fp != NULL) { if (fprintf(fp, "%lu\n", (unsigned long)config.state.pid) == EOF) swarn("fprintf(%s)", SOCKD_PIDFILE); fclose(fp); } time(&config.stat.boot); /* fork of requested number of servers. Start at one 'cause we are "it". */ for (p = 1; p < config.option.serverc; ++p) { pid_t pid; if ((pid = fork()) == -1) swarn("fork()"); else if (pid == 0) { config.state.pid = getpid(); break; } else config.state.motherpidv[p] = pid; } if (childcheck(CHILD_NEGOTIATE) <= 0 || childcheck(CHILD_REQUEST) <= 0 || childcheck(CHILD_IO) <= 0) serr(EXIT_FAILURE, "childcheck() failed"); slog(LOG_INFO, "%s/server v%s running", PACKAGE, VERSION); /* * main loop; accept new connections and handle our children. */ /* CONSTCOND */ while (1) { int client; struct sockd_child_t *child; fd_set rset; int rbits; rbits = fillset(&rset); ++rbits; switch ((p = select(rbits, &rset, NULL, NULL, NULL))) { case 0: SERR(p); /* NOTREACHED */ case -1: if (errno == EINTR) continue; SERR(p); /* NOTREACHED */ } /* * handle our children. */ /* first, get ack of free slots. */ while ((child = getset(SOCKD_FREESLOT, &rset)) != NULL) { char command; int childisbad = 0; if ((p = readn(child->ack, &command, sizeof(command))) != sizeof(command)) { swarn("readn(child->ack) from %schild %lu failed", childtype2string(child->type), (unsigned long)child->pid); childisbad = 1; } else { SASSERTX(command == SOCKD_FREESLOT); ++child->freec; } clearset(SOCKD_FREESLOT, child, &rset); if (childisbad) removechild(child->pid); } /* next, get new requests. */ while ((child = getset(SOCKD_NEWREQUEST, &rset)) != NULL) { int childisbad = 0;#if DIAGNOSTIC int freed = freedescriptors(config.option.debug ? "start" : NULL);#endif switch (child->type) { /* * in the order a packet travels between children; * negotiate -> request -> io. */ case CHILD_NEGOTIATE: { int flags; struct sockd_request_t req; struct sockd_child_t *reqchild; if ((reqchild = nextchild(CHILD_REQUEST)) == NULL) break; /* no child to accept a new request. */ SASSERTX(reqchild->freec > 0); /* receive request from negotiator child... */ if ((p = recv_req(child->s, &req)) != 0) { childisbad = 1; break; } ++config.stat.negotiate.received; /* set descriptor to blocking for request... */ if ((flags = fcntl(req.s, F_GETFL, 0)) == -1 || fcntl(req.s, F_SETFL, flags & ~O_NONBLOCK) == -1) swarn("%s: fcntl()"); /* and send it to a request child. */ if ((p = send_req(reqchild->s, &req)) == 0) { --reqchild->freec; ++config.stat.request.sendt; } else { clearset(SOCKD_NEWREQUEST, child, &rset); childisbad = 1; child = reqchild; } close(req.s); break; } case CHILD_REQUEST: { struct sockd_io_t io; struct sockd_child_t *iochild; if ((iochild = nextchild(CHILD_IO)) == NULL) break; /* no child to accept new io. */ SASSERTX(iochild->freec > 0); /* get io from request child ... */ if ((p = recv_io(child->s, &io)) != 0) { childisbad = 1; break; } ++config.stat.request.received; /* and send it to a io child. */ if ((p = send_io(iochild->s, &io)) == 0) { --iochild->freec; ++config.stat.io.sendt; } else { clearset(SOCKD_NEWREQUEST, child, &rset); childisbad = 1; child = iochild; } close_iodescriptors(&io); break; } case CHILD_IO: /* * the only thing a iochild should return is a ack each time * it finishes with a io, that is handled in loop above. */ break; }#if DIAGNOSTIC SASSERTX(freed == freedescriptors(config.option.debug ? "end" : NULL));#endif clearset(SOCKD_NEWREQUEST, child, &rset); if (childisbad) /* error/eof from child. */ switch (errno) { case EMFILE: case ENFILE: break; /* child is ok, we are not. */ default: removechild(child->pid); } } /* handled our children. Is there a new connection pending? */ for (p = 0; p < config.internalc; ++p) { char accepted[MAXSOCKADDRSTRING]; if (FD_ISSET(config.internalv[p].s, &rset)) { const struct listenaddress_t *l = &config.internalv[p]; struct sockd_child_t *negchild; struct sockaddr from; socklen_t len; if ((negchild = nextchild(CHILD_NEGOTIATE)) == NULL) break; /* no free negotiator children, don't accept(). */#if NEED_ACCEPTLOCK if (config.option.serverc > 1) if (socks_lock(l->lock, F_WRLCK, 0) != 0) continue;#endif#if HAVE_SENDMSG_DEADLOCK if (socks_lock(negchild->lock, F_WRLCK, 0) != 0) {#if NEED_ACCEPTLOCK if (config.option.serverc > 1) socks_unlock(l->lock);#endif /* NEED_ACCEPTLOCK */ continue; }#endif /* HAVE_SENDMSG_DEADLOCK */ len = sizeof(from); if ((client = acceptn(l->s, &from, &len)) == -1) switch (errno) {#ifdef EPROTO case EPROTO: /* overloaded SVR4 error */#endif case EWOULDBLOCK: /* BSD */ case ECONNABORTED: /* POSIX */ /* rest appears to be linux stuff according to apache src. */#ifdef ECONNRESET case ECONNRESET:#endif#ifdef ETIMEDOUT case ETIMEDOUT:#endif#ifdef EHOSTUNREACH case EHOSTUNREACH:#endif#ifdef ENETUNREACH case ENETUNREACH:#endif#if NEED_ACCEPTLOCK if (config.option.serverc > 1) socks_unlock(l->lock);#endif /* NEED_ACCEPTLOCK */#if HAVE_SENDMSG_DEADLOCK socks_unlock(negchild->lock);#endif /* HAVE_SENDMSG_DEADLOCK */ continue; /* connection aborted/failed. */ case ENFILE: continue; /* * this should never happen since childcheck(), if
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -