📄 sockd_child.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_child.c,v 1.118 2000/01/05 10:38:58 michaels Exp $";#define MOTHER 0 /* descriptor mother reads/writes on. */#define CHILD 1 /* descriptor child reads/writes on. */__BEGIN_DECLSstatic intsetchildtype __P((int type, struct sockd_child_t ***childv, int **childc, void (**function)(struct sockd_mother_t *mother)));/* * Sets "childv", "childc" and "function" to the correct value depending * on "type". */static intfindchild __P((pid_t pid, int childc, const struct sockd_child_t *childv));/* * Finds the child with pid "pid" in the array "childv". Searching * Elements in "childv" is given by "childc". * Returns: * On success: the index of the child in "childv". * On failure: -1. */__END_DECLSstatic struct sockd_child_t *iochildv; /* all our iochildren */static int iochildc;static struct sockd_child_t *negchildv; /* all our negotiatorchildren */static int negchildc;static struct sockd_child_t *reqchildv; /* all our requestchildren */static int reqchildc;struct sockd_child_t *addchild(type) int type;{ const char *function = "addchild()"; /* * It is better to reserve some descriptors for temporary use * than to get errors when passing them and thus lose clients. */ const int reserved = FDPASS_MAX /* max descriptors we pass. */ + 1 /* need a descriptor for accept(). */ + 2; /* for each new child. */ struct sockd_mother_t mother; struct sockd_child_t **childv; int *childc; void (*childfunction)(struct sockd_mother_t *mother); pid_t pid; int optval, flags; int pipev[] = { -1, -1 }; int ackpipev[] = { -1, -1 }; /* * XXX This is a expensive test which shouldn't be hard to optimize * away. It only happens when we are running low on slots though, * so assume it's "good enough" until I get the time to fix it. */ if (freedescriptors(NULL) < reserved) { errno = EMFILE; swarn(function); return NULL; } /* create datapipe. */ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipev) != 0) { swarn("%s: socketpair(AF_LOCAL, SOCK_STREAM)", function); return NULL; } /* and ackpipe. */ if (pipe(ackpipev) != 0) { swarn("%s: pipe()", function); closev(pipev, ELEMENTS(pipev)); return NULL; } /* * Try to set socketbuffer and watermarks to a optimal size. */ switch (type = setchildtype(type, &childv, &childc, &childfunction)) { case CHILD_NEGOTIATE: /* * A negotiator child receives only descriptors, so mothers * send buffer can be small, and so can the child's receive buffer. * The child sends a sockd_request_t struct back to mother, so * mothers recv buffer has to be considerably bigger, as does * childs send buffer. */ /* negotiator shouldn't block on sending to mother. */ if ((flags = fcntl(pipev[CHILD], F_GETFL, 0)) == -1 || fcntl(pipev[CHILD], F_SETFL, flags | O_NONBLOCK) == -1) swarn("%s: fcntl()", function);#if HAVE_SENDMSG_DEADLOCK if ((mother.lock = socks_mklock(SOCKS_LOCKFILE)) == -1) { swarn("%s: socks_mklock()", function); closev(pipev, ELEMENTS(pipev)); closev(ackpipev, ELEMENTS(ackpipev)); return NULL; }#endif /* HAVE_SENDMSG_DEADLOCK */ optval = sizeof(struct sockd_request_t) * (SOCKD_NEGOTIATEMAX + 1); if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) != 0 || setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt(SO_RCVBUF/SO_SNDBUF)", function);#if HAVE_SO_SNDLOWAT optval = sizeof(struct sockd_request_t) * LOWATSKEW; if (setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDLOWAT, &optval, sizeof(optval)) != 0 || setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVLOWAT, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt(SO_SNDLOWAT/SO_RCVLOWAT)", function);#endif break; case CHILD_REQUEST: /* * A request child receives a sockd_request_t structure, * it sends back a sockd_io_t structure. */#if HAVE_SENDMSG_DEADLOCK mother.lock = -1; /* doesn't need lock. */#endif /* HAVE_SENDMSG_DEADLOCK */ optval = sizeof(struct sockd_request_t) * (SOCKD_REQUESTMAX + 1); if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) != 0 || setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt()", function); optval = sizeof(struct sockd_io_t) * (SOCKD_REQUESTMAX + 1); if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) != 0 || setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt()", function);#if HAVE_SO_SNDLOWAT optval = sizeof(struct sockd_request_t) * LOWATSKEW; if (setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVLOWAT, &optval, sizeof(optval)) != 0 || setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDLOWAT, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt(SO_RCVLOWAT)", function); optval = sizeof(struct sockd_io_t) * LOWATSKEW; if (setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDLOWAT, &optval, sizeof(optval)) != 0 || setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVLOWAT, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt(SO_RCVLOWAT/SO_SNDLOWAT)", function);#endif break; case CHILD_IO: /* * A io child receives a sockd_io_t structure, * it sends back only a ack. */#if HAVE_SENDMSG_DEADLOCK mother.lock = -1; /* doesn't need lock. */#endif /* HAVE_SENDMSG_DEADLOCK */ optval = sizeof(struct sockd_io_t) * (SOCKD_IOMAX + 1); if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) != 0 || setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt(SO_SNDBUF/SO_RCVBUF)", function); optval = sizeof(int) * (SOCKD_IOMAX + 1); if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) != 0 || setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt(SO_RCVBUF/SO_SNDBUF)", function);#if HAVE_SO_SNDLOWAT optval = sizeof(struct sockd_io_t) * LOWATSKEW; if (setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVLOWAT, &optval, sizeof(optval)) != 0 || setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDLOWAT, &optval, sizeof(optval)) != 0) swarn("%s: setsockopt(SO_RCVLOWAT)", function);#endif break; default: SERRX(type); } switch ((pid = fork())) { case -1: swarn("%s: fork()", function); closev(pipev, ELEMENTS(pipev)); closev(ackpipev, ELEMENTS(ackpipev));#if HAVE_SENDMSG_DEADLOCK if (mother.lock != -1) close(mother.lock);#endif /* HAVE_SENDMSG_DEADLOCK */ return NULL; case 0: { size_t i, maxfd; struct sigaction sigact; config.state.type = type; config.state.pid = getpid(); initlog(); slog(LOG_DEBUG, "created new %schild", childtype2string(type));#if 0 slog(LOG_DEBUG, "sleeping..."); sleep(20);#endif mother.s = pipev[CHILD]; mother.ack = ackpipev[CHILD]; /* * It would be nice to be able to lose all privileges here * but unfortunately we can't, yet. * * negotiation children: * could need privileges to check password. * * request children: * could need privileges to bind port. * * io children: * doesn't really need any, but a sighup() performs misc. * seteuid() tests that would fail if we lose privileges. */ switch (type) { case CHILD_NEGOTIATE:#if HAVE_LIBWRAP#if SOCKD_NEGOTIATEMAX > 1 resident = 1;#endif /* SOCKD_NEGOTIATEMAX > 1 */#endif /* HAVE_LIBWRAP */ break; case CHILD_REQUEST:#if HAVE_LIBWRAP#if SOCKD_REQUESTMAX > 1 resident = 1;#endif /* SOCKD_REQUESTMAX > 1 */#endif /* HAVE_LIBWRAP */ break; case CHILD_IO:#if HAVE_LIBWRAP#if SOCKD_IOMAX > 1 resident = 1;#endif /* SOCKD_IOMAX > 1 */#endif /* HAVE_LIBWRAP */ break; default: SERRX(type); } sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; /* signals mother has set up but which we ignore at this point. */ sigact.sa_handler = SIG_IGN;#if HAVE_SIGNAL_SIGINFO if (sigaction(SIGINFO, &sigact, NULL) != 0) swarn("%s: sigaction(SIGINFO)", function);#endif /* HAVE_SIGNAL_SIGINFO */ if (sigaction(SIGUSR1, &sigact, NULL) != 0) swarn("%s: sigaction(USR1)", function); /* delete everything we got from parent. */ for (i = 0, maxfd = getdtablesize(); i < maxfd; ++i) { /* exceptions */ if (i == (size_t)mother.s#if HAVE_SENDMSG_DEADLOCK || i == (size_t)mother.lock#endif /* HAVE_SENDMSG_DEADLOCK */ || i == (size_t)mother.ack) continue; if (socks_logmatch(i, &config.log)) continue; close((int)i); } initlog(); /* for syslog. */ childfunction(&mother); /* NOTREACHED */ } default: { struct sockd_child_t *newchildv; if ((newchildv = (struct sockd_child_t *)realloc(*childv, sizeof(**childv) * (*childc + 1))) == NULL) { slog(LOG_WARNING, "%s: %s", function, NOMEM); closev(pipev, ELEMENTS(pipev)); closev(ackpipev, ELEMENTS(ackpipev)); return NULL; } *childv = newchildv; (*childv)[*childc].type = type; (*childv)[*childc].pid = pid; (*childv)[*childc].s = pipev[MOTHER];#if HAVE_SENDMSG_DEADLOCK (*childv)[*childc].lock = mother.lock;#endif /* HAVE_SENDMSG_DEADLOCK */ (*childv)[*childc].ack = ackpipev[MOTHER]; close(pipev[CHILD]); close(ackpipev[CHILD]); switch ((*childv)[*childc].type) { case CHILD_NEGOTIATE: (*childv)[*childc].freec = SOCKD_NEGOTIATEMAX; break; case CHILD_REQUEST: (*childv)[*childc].freec = SOCKD_REQUESTMAX; break; case CHILD_IO: (*childv)[*childc].freec = SOCKD_IOMAX; break; default: SERRX((*childv)[*childc].type); } return &(*childv)[(*childc)++]; } }}intchildcheck(type) int type;{ int child, proxyc; int min, max; struct sockd_child_t **childv; int *childc; switch (type) { case -CHILD_NEGOTIATE: case CHILD_NEGOTIATE: childc = &negchildc; childv = &negchildv; min = SOCKD_FREESLOTS; max = SOCKD_NEGOTIATEMAX; break; case -CHILD_REQUEST: case CHILD_REQUEST: childc = &reqchildc; childv = &reqchildv; min = SOCKD_FREESLOTS; max = SOCKD_REQUESTMAX; break; case -CHILD_IO: case CHILD_IO: childc = &iochildc; childv = &iochildv; /* attempt to keep in a state where we can accept all requests. */ min = MAX(SOCKD_FREESLOTS, childcheck(-CHILD_REQUEST)); max = SOCKD_IOMAX; break; default: SERRX(type); } /* * get a estimate over how many (new) clients our children are able to * accept in total. */ for (child = 0, proxyc = 0; child < *childc; ++child) { SASSERTX((*childv)[child].freec <= max); proxyc += type < 0 ? max : (*childv)[child].freec; } if (type >= 0) if (proxyc < min && config.state.addchild) if (addchild(type) != NULL) return childcheck(type); else config.state.addchild = 0; /* don't retry until a child dies. */ return proxyc;}intfillset(set) fd_set *set;{ int negc, reqc, ioc; int i, dbits; /* * There is no point in setting data descriptor of child N unless * child N+1 is able to accept the data from child N. So find * out if we have slots of the various types available . */ ioc = childcheck(CHILD_IO); reqc = childcheck(CHILD_REQUEST); negc = childcheck(CHILD_NEGOTIATE); FD_ZERO(set); dbits = -1; /* new clients we accept. */ if (negc > 0) for (i = 0; i < config.internalc; ++i) { SASSERTX(config.internalv[i].s >= 0); FD_SET(config.internalv[i].s, set); dbits = MAX(dbits, config.internalv[i].s); } /* negotiator children. */ for (i = 0; i < negchildc; ++i) { if (reqc > 0) { SASSERTX(negchildv[i].s >= 0); FD_SET(negchildv[i].s, set); dbits = MAX(dbits, negchildv[i].s); } /* we can always accept a ack ofcourse. */ SASSERTX(negchildv[i].ack >= 0); FD_SET(negchildv[i].ack, set); dbits = MAX(dbits, negchildv[i].ack); } /* request children. */ for (i = 0; i < reqchildc; ++i) { if (ioc > 0) { SASSERTX(reqchildv[i].s >= 0); FD_SET(reqchildv[i].s, set); dbits = MAX(dbits, reqchildv[i].s); } /* we can always accept a ack ofcourse. */ SASSERTX(reqchildv[i].ack >= 0); FD_SET(reqchildv[i].ack, set); dbits = MAX(dbits, reqchildv[i].ack);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -