📄 sockd_negotiate.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_negotiate.c,v 1.65 1999/12/22 09:29:26 karls Exp $";__BEGIN_DECLSstatic intsend_negotiate __P((const struct sockd_mother_t *mother, const struct sockd_negotiate_t *neg));/* * Sends "neg" to "s". Also ack's that we have freed a slot to "s". * Returns: * On success: 0 * On failure: -1 * If some other problem prevented success: > 0 */static intrecv_negotiate __P((const struct sockd_mother_t *mother));/* * Tries to receive a client from mother "s". * Returns: * On success: 0 * If a error happened to connection with "s": -1 * If some other problem prevented success: > 0 */static voiddelete_negotiate __P((const struct sockd_mother_t *mother, struct sockd_negotiate_t *neg));/* * Frees any state occupied by "neg", including closing any * descriptors and sending a ack that we have deleted a "negotiate" * object to "mother". */static intneg_fillset __P((fd_set *set));/* * Sets all descriptors in our list in the set "set". * Returns the highest descriptor in our list, or -1 if we don't * have any descriptors open currently. */static voidneg_clearset __P((struct sockd_negotiate_t *neg, fd_set *set));/* * Clears all filedescriptors in "neg" from "set". */static struct sockd_negotiate_t *neg_getset __P((fd_set *set));/* * Goes through our list until it finds a negotiate object where atleast * one of the descriptors is set. * Returns: * On success: pointer to the found object. * On failure: NULL. */static intallocated __P((void));/* * Returns the number of allocated (active) objects. */static intcompleted __P((void));/* * Returns the number of objects completed and ready to be sent currently. */static intallocated __P((void));/* * Returns the number of objects currently allocated for use. */static voidproctitleupdate __P((void));/* * Updates the title of this process. */static struct timeval *neg_gettimeout __P((struct timeval *timeout));/* * Fills in "timeout" with time til the first clients connection * expires. * Returns: * If there is a timeout: pointer to filled in "timeout". * If there is no timeout: NULL. */static struct sockd_negotiate_t *neg_gettimedout __P((void));/* * Scans all clients for one that has timed out according to config * settings. * Returns: * If timed out client found: pointer to it. * Else: NULL. */static voidsiginfo __P((int sig));/* * Print information about our current connections. */__END_DECLSstatic struct sockd_negotiate_t negv[SOCKD_NEGOTIATEMAX];static int negc = ELEMENTS(negv);voidrun_negotiate(mother) struct sockd_mother_t *mother;{ const char *function = "run_negotiate()"; struct sigaction sigact; sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = siginfo;#if HAVE_SIGNAL_SIGINFO if (sigaction(SIGINFO, &sigact, NULL) != 0) serr(EXIT_FAILURE, "%s: sigaction(SIGINFO)", function);#endif /* HAVE_SIGNAL_SIGINFO */ /* same handler, for systems without SIGINFO. */ if (sigaction(SIGUSR1, &sigact, NULL) != 0) serr(EXIT_FAILURE, "%s: sigaction(SIGINFO)", function); proctitleupdate(); /* CONSTCOND */ while (1) { fd_set rset, wsetmem, *wset = NULL; int fdbits, p; struct sockd_negotiate_t *neg; struct timeval timeout; fdbits = neg_fillset(&rset); FD_SET(mother->s, &rset); fdbits = MAX(fdbits, mother->s); /* if we have a completed request check whether we can send to mother. */ if (completed() > 0) { FD_ZERO(&wsetmem); FD_SET(mother->s, &wsetmem); wset = &wsetmem; } ++fdbits; switch (selectn(fdbits, &rset, wset, NULL, neg_gettimeout(&timeout))) { case -1: SERR(-1); /* NOTREACHED */ case 0: { const char *reason = "negotiation timed out"; if ((neg = neg_gettimedout()) == NULL) continue; /* should only be possible if sighup received. */ iolog(&neg->rule, &neg->state, OPERATION_ABORT, &neg->src, &neg->dst, reason, strlen(reason)); delete_negotiate(mother, neg); continue; } } if (FD_ISSET(mother->s, &rset)) { if (recv_negotiate(mother) == -1) sockdexit(-EXIT_FAILURE); FD_CLR(mother->s, &rset); } while ((neg = neg_getset(&rset)) != NULL) { neg_clearset(neg, &rset); if ((p = recv_request(neg->s, &neg->req, &neg->negstate)) <= 0) { const char *reason = NULL; /* init or gcc complains. */ switch (p) { case 0: reason = "eof from client"; break; case -1: switch (errno) { case 0: reason = *neg->negstate.emsg == NUL ? "socks protocol error" : neg->negstate.emsg; break; case EINTR: case EAGAIN:#if EAGAIN != EWOULDBLOCK case EWOULDBLOCK:#endif continue; /* ok, retry. */ default: reason = strerror(errno); } } iolog(&neg->rule, &neg->state, OPERATION_ABORT, &neg->src, &neg->dst, reason, strlen(reason)); delete_negotiate(mother, neg); } else if (wset != NULL && FD_ISSET(mother->s, wset)) { /* read a complete request, send to mother. */ switch (send_negotiate(mother, neg)) { case -1: sockdexit(-EXIT_FAILURE); /* NOTREACHED */ case 0: delete_negotiate(mother, neg); /* sent to mother ok. */ break; } } } }}static intsend_negotiate(mother, neg) const struct sockd_mother_t *mother; const struct sockd_negotiate_t *neg;{ const char *function = "send_negotiate()"; struct iovec iovec[1]; struct sockd_request_t req; int fdsendt, w; struct msghdr msg; CMSG_AALLOC(sizeof(int));#if HAVE_SENDMSG_DEADLOCK if (socks_lock(mother->lock, F_WRLCK, 0) != 0) return 1;#endif /* HAVE_SENDMSG_DEADLOCK */ /* copy needed fields from negotiate */ /* LINTED pointer casts may be troublesome */ sockshost2sockaddr(&neg->src, (struct sockaddr *)&req.from); req.req = neg->req; req.rule = neg->rule; req.state = neg->state; req.state.command = req.req.command; req.state.version = req.req.version; /* LINTED pointer casts may be troublesome */ sockshost2sockaddr(&neg->dst, (struct sockaddr *)&req.to); iovec[0].iov_base = &req; iovec[0].iov_len = sizeof(req); fdsendt = 0; CMSG_ADDOBJECT(neg->s, sizeof(neg->s) * fdsendt++); msg.msg_iov = iovec; msg.msg_iovlen = ELEMENTS(iovec); msg.msg_name = NULL; msg.msg_namelen = 0; CMSG_SETHDR_SEND(sizeof(int) * fdsendt); slog(LOG_DEBUG, "sending request to mother"); if ((w = sendmsg(mother->s, &msg, 0)) != sizeof(req)) switch (errno) { case EAGAIN: case ENOBUFS: w = 1; /* temporal error. */ break; default: swarn("%s: sendmsg(): %d of %d", function, w, sizeof(req)); }#if HAVE_SENDMSG_DEADLOCK socks_unlock(mother->lock);#endif /* HAVE_SENDMSG_DEADLOCK */ return w == sizeof(req) ? 0 : w;}static intrecv_negotiate(mother) const struct sockd_mother_t *mother;{ const char *function = "recv_negotiate()"; struct sockd_negotiate_t *neg; struct iovec iovec[1]; struct sockaddr addr; socklen_t len; unsigned char command; int permit, i, r, fdexpect, fdreceived; struct msghdr msg; CMSG_AALLOC(sizeof(int)); iovec[0].iov_base = &command; iovec[0].iov_len = sizeof(command); msg.msg_iov = iovec; msg.msg_iovlen = ELEMENTS(iovec); msg.msg_name = NULL; msg.msg_namelen = 0; CMSG_SETHDR_RECV(sizeof(cmsgmem)); if ((r = recvmsgn(mother->s, &msg, 0, sizeof(command))) != sizeof(command)) { switch (r) { case -1: swarn("%s: recvmsg() from mother", function); break; case 0: slog(LOG_DEBUG, "%s: recvmsg(): mother closed connection", function); break; default: swarnx("%s: recvmsg(): unexpected %d/%d bytes from mother", function, r, sizeof(command)); } return -1; } fdexpect = 1; /* constant */ SASSERTX(command == SOCKD_NEWREQUEST); /* find a free slot. */ for (i = 0, neg = NULL; i < negc; ++i) if (!negv[i].allocated) { neg = &negv[i]; break; } if (neg == NULL) SERRX(allocated());#if !HAVE_DEFECT_RECVMSG SASSERT(CMSG_GETLEN(msg) == sizeof(int) * fdexpect);#endif fdreceived = 0; CMSG_GETOBJECT(neg->s, sizeof(neg->s) * fdreceived++); /* get local and remote address. */ len = sizeof(addr); if (getpeername(neg->s, &addr, &len) != 0) { swarn("%s: getpeername()", function); return 1; } sockaddr2sockshost(&addr, &neg->src); len = sizeof(addr); if (getsockname(neg->s, &addr, &len) != 0) { swarn("%s: getsockname()", function); return 1; } sockaddr2sockshost(&addr, &neg->dst); neg->state.command = SOCKS_ACCEPT; neg->state.protocol = SOCKS_TCP; neg->state.version = SOCKS_V5; /* anything valid. */ neg->state.auth.method = AUTHMETHOD_NONE; /* pointer fixup */ neg->req.auth = &neg->state.auth; permit = rulespermit(neg->s, &neg->rule, &neg->state, &neg->src, &neg->dst); iolog(&neg->rule, &neg->state, OPERATION_ACCEPT, &neg->src, &neg->dst, NULL, 0); neg->allocated = 1; if (!permit) { delete_negotiate(mother, neg); return 0; } time(&neg->state.time.negotiate_start); proctitleupdate(); return 0;}static voiddelete_negotiate(mother, neg) const struct sockd_mother_t *mother; struct sockd_negotiate_t *neg;{ const char *function = "delete_negotiate()"; static const struct sockd_negotiate_t neginit; const char command = SOCKD_FREESLOT; SASSERTX(neg->allocated); close(neg->s); *neg = neginit; /* ack we have freed a slot. */ if (writen(mother->ack, &command, sizeof(command)) != sizeof(command)) swarn("%s: writen()", function); proctitleupdate();}static intneg_fillset(set) fd_set *set;{ int i, max; FD_ZERO(set); for (i = 0, max = -1; i < negc; ++i) if (negv[i].allocated) { negv[i].ignore = 0; FD_SET(negv[i].s, set); max = MAX(max, negv[i].s); } return max;}static voidneg_clearset(neg, set) struct sockd_negotiate_t *neg; fd_set *set;{ FD_CLR(neg->s, set); neg->ignore = 1;}static struct sockd_negotiate_t *neg_getset(set) fd_set *set;{ int i; for (i = 0; i < negc; ++i) if (negv[i].allocated) { if (negv[i].ignore) continue; if (negv[i].negstate.complete) return &negv[i]; if (FD_ISSET(negv[i].s, set)) return &negv[i]; } return NULL;}static intallocated(void){ int i, alloc; for (i = 0, alloc = 0; i < negc; ++i) if (negv[i].allocated) ++alloc; return alloc;}static intcompleted(void){ int i, completec; for (i = 0, completec = 0; i < negc; ++i) if (negv[i].allocated && negv[i].negstate.complete) ++completec; return completec;}static voidproctitleupdate(void){ setproctitle("negotiator: %d/%d", allocated(), SOCKD_NEGOTIATEMAX);}static struct timeval *neg_gettimeout(timeout) struct timeval *timeout;{ time_t timenow; int i; if (config.timeout.negotiate == 0 || (allocated() == completed())) return NULL; timeout->tv_sec = config.timeout.negotiate; timeout->tv_usec = 0; time(&timenow); for (i = 0; i < negc; ++i) if (!negv[i].allocated) continue; else timeout->tv_sec = MAX(0, MIN(timeout->tv_sec, difftime(config.timeout.negotiate, (time_t)difftime(timenow, negv[i].state.time.negotiate_start)))); return timeout;}static struct sockd_negotiate_t *neg_gettimedout(void){ int i; time_t timenow; if (config.timeout.negotiate == 0) return NULL; time(&timenow); for (i = 0; i < negc; ++i) { if (!negv[i].allocated) continue; if (negv[i].ignore) continue; else if (difftime(timenow, negv[i].state.time.negotiate_start) >= config.timeout.negotiate) return &negv[i]; } return NULL;}/* ARGSUSED */static voidsiginfo(sig) int sig;{ int i; time_t timenow; time(&timenow); for (i = 0; i < negc; ++i) if (!negv[i].allocated) continue; else { char srcstring[MAXSOCKSHOSTSTRING]; slog(LOG_INFO, "%s: negotiating for %.0fs", sockshost2string(&negv[i].src, srcstring, sizeof(srcstring)), difftime(timenow, negv[i].state.time.negotiate_start)); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -