summaryrefslogtreecommitdiffstats
path: root/tcpd/tcpd.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /tcpd/tcpd.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'tcpd/tcpd.c')
-rw-r--r--tcpd/tcpd.c2151
1 files changed, 2151 insertions, 0 deletions
diff --git a/tcpd/tcpd.c b/tcpd/tcpd.c
new file mode 100644
index 0000000..d3959b7
--- /dev/null
+++ b/tcpd/tcpd.c
@@ -0,0 +1,2151 @@
+/*
+** Copyright 1998 - 2013 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+** OK - the poop is that if we include socks.h after stdio.h, SOCKSfwrite
+** does not get prototyped.
+** If we include socks.h before stdio.h, gcc will complain about getc being
+** redefined. The easiest solution is to simply undef getc, because we
+** don't use it here.
+*/
+
+#include "soxwrap/soxwrap.h"
+#include <stdio.h>
+
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "waitlib/waitlib.h"
+#include "rfc1035/rfc1035.h"
+#include "liblock/config.h"
+#include "liblock/liblock.h"
+#include "tcpremoteinfo.h"
+#include "numlib/numlib.h"
+#include "argparse.h"
+
+#include <netdb.h>
+
+
+static const char *accessarg=0;
+static const char *accesslocal=0;
+static const char *denymsgarg=0;
+static const char *listenarg=0;
+static const char *ipaddrarg=0;
+static const char *userarg=0;
+static const char *grouparg=0;
+static const char *maxprocsarg=0;
+static const char *warnarg=0;
+static const char *maxperiparg=0;
+static const char *maxpercarg=0;
+static const char *droparg=0;
+static const char *nodnslookup=0;
+static const char *noidentlookup=0;
+static const char *stderrarg=0;
+static const char *stderrloggerarg=0;
+static const char *pidarg=0;
+static const char *proxyarg=0;
+static const char *restartarg=0;
+static const char *stoparg=0;
+static const char *stderrloggername=0;
+
+static char *lockfilename;
+
+static void setup_block(const char *);
+static void setup_allow(const char *);
+
+static struct args arginfo[]={
+ {"access", &accessarg},
+ {"accesslocal", &accesslocal},
+ {"denymsg", &denymsgarg},
+ {"drop", &droparg},
+ {"address", &ipaddrarg},
+ {"block", 0, setup_block},
+ {"allow", 0, setup_allow},
+ {"group", &grouparg},
+ {"listen", &listenarg},
+ {"maxperc", &maxpercarg},
+ {"maxperip", &maxperiparg},
+ {"maxprocs", &maxprocsarg},
+ {"warn", &warnarg},
+ {"nodnslookup", &nodnslookup},
+ {"noidentlookup", &noidentlookup},
+ {"pid", &pidarg},
+ {"restart", &restartarg},
+ {"stderr", &stderrarg},
+ {"stderrlogger", &stderrloggerarg},
+ {"stderrloggername", &stderrloggername},
+ {"stop", &stoparg},
+ {"user", &userarg},
+ {"proxy", &proxyarg},
+ {0}
+ } ;
+
+/* Ports we're listening on: */
+
+static struct portinfo {
+ struct portinfo *next;
+ const char *ipaddr; /* Specific IP addr, or 0 */
+ const char *servname; /* Service name/port */
+
+ int fd1, fd2; /* BSD may need both IPv4 and IPv6 sockets */
+} *fdlist=0;
+static int maxfd;
+
+static int nprocs, maxperc, maxperip, nwarn;
+static pid_t *pids;
+static time_t last_alert=0, last_warn=0;
+static RFC1035_ADDR *addrs;
+
+static int sighup_received=0;
+
+struct blocklist_s {
+ struct blocklist_s *next;
+ char *zone; /* zone to lookup */
+ char *display_zone; /* zone for var_ZONE */
+ char *var;
+ struct in_addr ia; /* 0, anything */
+ char *msg; /* NULL, query for TXT record */
+ int allow; /* This is an -allow entry */
+ } *blocklist=0;
+
+extern int openaccess(const char *);
+extern void closeaccess();
+extern char *chkaccess(const char *);
+
+/*
+** Process -block and -allow parameters
+*/
+static void setup_block_allow(const char *blockinfo, int isallow)
+{
+struct blocklist_s *newbl=(struct blocklist_s *)malloc(sizeof(*blocklist));
+char *p;
+struct blocklist_s **blptr;
+const char *ip;
+
+ for (blptr= &blocklist; *blptr; blptr=&(*blptr)->next)
+ ;
+
+ if (!newbl || (newbl->zone=malloc(strlen(blockinfo)+1)) == 0)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ *blptr=newbl;
+ newbl->next=0;
+ newbl->allow=isallow;
+
+ strcpy(newbl->zone, blockinfo);
+
+ newbl->var=0;
+ newbl->msg=0;
+ newbl->display_zone=0;
+ newbl->ia.s_addr=INADDR_ANY;
+
+ /* Look for var or IP address */
+
+ for (p=newbl->zone; *p; ++p)
+ {
+ if (*p == '=')
+ {
+ *p++=0;
+ newbl->display_zone=p;
+ continue;
+ }
+
+ if (*p == '/')
+ break;
+
+ if (*p == ',')
+ {
+ *p++=0;
+ newbl->var=p;
+ break;
+ }
+ }
+
+ if (newbl->display_zone == 0)
+ newbl->display_zone = newbl->zone;
+
+ ip=0;
+
+ for (; *p; p++)
+ {
+ if (*p == ',')
+ break;
+
+ if (*p == '/')
+ {
+ *p++=0;
+ ip=p;
+ break;
+ }
+ }
+
+ for (; *p; p++)
+ {
+ if (*p == ',')
+ {
+ *p++=0;
+ newbl->msg=p;
+ break;
+ }
+ }
+
+ if (ip)
+ {
+ rfc1035_aton_ipv4(ip, &newbl->ia);
+ }
+}
+
+static void setup_block(const char *blockinfo)
+{
+ setup_block_allow(blockinfo, 0);
+}
+
+static void setup_allow(const char *blockinfo)
+{
+ setup_block_allow(blockinfo, 1);
+}
+
+static int isid(const char *p)
+{
+ while (*p)
+ {
+ if (*p < '0' || *p > '9') return (0);
+ ++p;
+ }
+ return (1);
+}
+
+static RETSIGTYPE sigexit(int n)
+{
+ kill( -getpid(), SIGTERM);
+ _exit(0);
+
+#if RETSIGTYPE != void
+ return (0)
+#endif
+}
+
+static RETSIGTYPE sighup(int n)
+{
+ sighup_received=1;
+
+ signal(SIGHUP, sighup);
+
+#if RETSIGTYPE != void
+ return (0)
+#endif
+}
+
+/*
+** Initialize a single listening socket
+*/
+
+static struct portinfo *createport(const char *a, const char *s)
+{
+ struct portinfo *p=(struct portinfo *)malloc(sizeof(struct portinfo));
+
+ if (!p)
+ {
+ perror("malloc");
+ return (NULL);
+ }
+
+ p->next=fdlist;
+ fdlist=p;
+ p->ipaddr=a;
+ p->servname=s;
+ p->fd1=p->fd2= -1;
+ return (p);
+}
+
+static int parseaddr(const char *p)
+{
+ char *buf=strdup(p);
+ char *q, *a, *s;
+
+ if (!buf)
+ {
+ perror("malloc");
+ return (-1);
+ }
+
+ for (q=buf; (q=strtok(q, ",")) != NULL; q=0)
+ {
+ if ((s=strrchr(q, '.')) != 0)
+ {
+ *s++=0;
+ a=q;
+ }
+ else
+ {
+ a=0;
+ s=q;
+ }
+
+ if (createport(a, s) == NULL)
+ return (-1);
+ }
+
+ if (ipaddrarg)
+ {
+ struct portinfo *p;
+
+ for (p=fdlist; p; p=p->next)
+ {
+ if (p->ipaddr && strcmp(p->ipaddr, "0"))
+ continue;
+ p->ipaddr=ipaddrarg;
+ }
+ }
+
+ return (0);
+}
+
+/*
+** Create one socket, bound to a specific host/port
+*/
+
+static int mksocket(const char *ipaddrarg, /* Host/IP address */
+ const char *servname, /* Service/port */
+ int flags)
+
+#define MKS_USEAFINET4 1
+#define MKS_ERROK 2
+
+{
+ struct servent *servptr;
+ int port;
+ int fd;
+
+ RFC1035_ADDR addr;
+ RFC1035_NETADDR netaddr;
+ const struct sockaddr *sinaddr;
+ int sinaddrlen;
+
+#if RFC1035_IPV6
+ struct sockaddr_in6 sin6;
+#endif
+
+ struct sockaddr_in sin4;
+
+ int af;
+
+ servptr=getservbyname(servname, "tcp");
+ if (servptr)
+ port=servptr->s_port;
+ else
+ {
+ port=atoi(servname);
+ if (port <= 0 || port > 65535)
+ {
+ fprintf(stderr, "Invalid port: %s\n", servname);
+ return (-1);
+ }
+ port=htons(port);
+ }
+
+ /* Create an IPv6 or an IPv4 socket */
+
+#if RFC1035_IPV6
+ if (flags & MKS_USEAFINET4)
+ {
+ fd=socket(PF_INET, SOCK_STREAM, 0);
+ af=AF_INET;
+ }
+ else
+#endif
+ fd=rfc1035_mksocket(SOCK_STREAM, 0, &af);
+
+ if (fd < 0)
+ {
+ perror("socket");
+ return (-1);
+ }
+
+ /* Figure out what to bind based on what socket we created */
+
+ if (ipaddrarg && strcmp(ipaddrarg, "0"))
+ {
+ if (rfc1035_aton(ipaddrarg, &addr) < 0)
+ {
+ fprintf(stderr,"Invalid IP address: %s\n", ipaddrarg);
+ close(fd);
+ return (-1);
+ }
+
+ if (rfc1035_mkaddress(af, &netaddr, &addr, port, &sinaddr,
+ &sinaddrlen))
+ {
+ fprintf(stderr,"Unable to bind IP address: %s\n",
+ ipaddrarg);
+ close(fd);
+ return (-1);
+ }
+ }
+ else /* Bind default address */
+ {
+#if RFC1035_IPV6
+ if (af == AF_INET6)
+ {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family=AF_INET6;
+ sin6.sin6_addr=in6addr_any;
+ sin6.sin6_port=port;
+ sinaddr=(const struct sockaddr *)&sin6;
+ sinaddrlen=sizeof(sin6);
+ }
+ else
+#endif
+ if (af == AF_INET)
+ {
+ sin4.sin_family=AF_INET;
+ sin4.sin_addr.s_addr=INADDR_ANY;
+ sin4.sin_port=port;
+ sinaddr=(const struct sockaddr *)&sin4;
+ sinaddrlen=sizeof(sin4);
+ }
+ else
+ {
+ errno=EAFNOSUPPORT;
+ perror("socket");
+ close(fd);
+ return (-1);
+ }
+ }
+
+ {
+ int dummy=1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&dummy, sizeof(dummy)) < 0)
+ {
+ perror("setsockopt");
+ }
+ }
+
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC))
+ {
+ perror("fcntl");
+ close(fd);
+ return (-1);
+ }
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK))
+ {
+ perror("fcntl");
+ close(fd);
+ return (-1);
+ }
+
+ if (sox_bind(fd, (struct sockaddr *)sinaddr, sinaddrlen) < 0)
+ {
+ if (flags & MKS_ERROK)
+ {
+ close(fd);
+ return (-2);
+ }
+
+ perror("bind");
+ close(fd);
+ return (-1);
+ }
+
+ if (sox_listen(fd,
+#ifdef SOMAXCONN
+ SOMAXCONN
+#else
+ 5
+#endif
+ ))
+ {
+ if (flags && MKS_ERROK)
+ {
+ close(fd);
+ return (-2);
+ }
+ perror("listen");
+ close(fd);
+ return (-1);
+ }
+ return (fd);
+}
+
+static int mksockets()
+{
+ struct portinfo *p;
+
+ maxfd= -1;
+
+ for (p=fdlist; p; p=p->next)
+ {
+ int fd;
+ struct in_addr addr;
+ int fd_flag=0;
+
+ if (p->ipaddr && strcmp(p->ipaddr, "0"))
+ {
+ /* FreeBSD needs AF_INET binds for IPv4 addys */
+
+ if (rfc1035_aton_ipv4(p->ipaddr, &addr) == 0)
+ {
+ fd_flag=MKS_USEAFINET4;
+ }
+ }
+
+ fd=mksocket(p->ipaddr, p->servname, fd_flag);
+
+ if (fd < 0)
+ break;
+
+ p->fd1=fd;
+
+ if (fd > maxfd)
+ maxfd=fd;
+
+ /* BSD requires both an IPv6 and an IPv4 socket */
+
+#if RFC1035_IPV6
+ if (p->ipaddr == 0 || strcmp(p->ipaddr, "0") == 0)
+ {
+ fd=mksocket(p->ipaddr, p->servname,
+ (MKS_USEAFINET4|MKS_ERROK));
+
+ if (fd == -2)
+ continue; /* Ok if bind failed */
+ if (fd < 0)
+ break;
+
+ if (fd > maxfd)
+ maxfd=fd;
+ p->fd2=fd;
+ }
+#endif
+
+ }
+
+ if (p) /* Clean up after ourselves, after an error */
+ {
+ for (p=fdlist; p; p=p->next)
+ {
+ if (p->fd1 >= 0)
+ close(p->fd1);
+ if (p->fd2 >= 0)
+ close(p->fd2);
+ }
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int init(int argc, char **argv)
+{
+int argn;
+
+struct group *gr;
+int i;
+gid_t gid=0;
+const char *servname;
+int forced=0;
+int lockfd=-1;
+
+ argn=argparse(argc, argv, arginfo);
+
+ if ((stoparg || restartarg) && pidarg == 0)
+ {
+ fprintf(stderr, "%s: -pid argument is required.\n", argv[0]);
+ return (-1);
+ }
+
+ if (pidarg)
+ {
+ lockfilename=malloc(strlen(pidarg)+sizeof(".lock"));
+ if (!lockfilename)
+ {
+ perror("malloc");
+ return (-1);
+ }
+ strcat(strcpy(lockfilename, pidarg), ".lock");
+ }
+
+ if (stoparg)
+ {
+ ll_daemon_stop(lockfilename, pidarg);
+ exit(0);
+ }
+
+ if (restartarg)
+ {
+ ll_daemon_restart(lockfilename, pidarg);
+ exit(0);
+ }
+
+ if (argc - argn < 2)
+ {
+ fprintf(stderr, "Usage: %s [options] port prog arg1 arg2...\n",
+ argv[0]);
+ return (-1);
+ }
+
+ if (pidarg) { /* -start implied for backwards compatibility */
+ lockfd=ll_daemon_start(lockfilename);
+ if (lockfd < 0)
+ {
+ perror("ll_daemon_start");
+ return (-1);
+ }
+ }
+
+ servname=argv[argn++];
+
+ if (parseaddr(servname))
+ {
+ close(lockfd);
+ return (-1);
+ }
+
+ if (mksockets())
+ {
+ close(lockfd);
+ return (-1);
+ }
+
+ signal(SIGINT, sigexit);
+ signal(SIGHUP, sighup);
+ signal(SIGTERM, sigexit);
+
+#if 0
+ {
+ int fd2;
+ int dummy;
+
+ perror("bind");
+ if (!forcebindarg || errno != EADDRINUSE)
+ {
+ sox_close(fd);
+ return (-1);
+ }
+
+ /* Poke around */
+
+ if ((fd2=rfc1035_mksocket(SOCK_STREAM, 0, &dummy)) < 0)
+ /* Better get same socket as fd */
+ {
+ perror("socket");
+ sox_close(fd);
+ return (-1);
+ }
+
+ if (sox_connect(fd2, (struct sockaddr *)sinaddr,
+ sinaddrlen) == 0)
+ {
+ sox_close(fd2);
+ sox_close(fd);
+ return (-1);
+ }
+ sox_close(fd2);
+ savepid();
+ sleep(60);
+ forced=1;
+ }
+#endif
+
+ if (pidarg)
+ ll_daemon_started(pidarg, lockfd);
+
+ if (grouparg)
+ {
+ if (isid(grouparg))
+ gid=atoi(grouparg);
+ else if ((gr=getgrnam(grouparg)) == 0)
+ {
+ fprintf(stderr, "Group not found: %s\n", grouparg);
+ close(lockfd);
+ return (-1);
+ }
+ else gid=gr->gr_gid;
+
+ libmail_changegroup(gid);
+ }
+
+ if (userarg)
+ {
+ uid_t uid;
+
+ if (isid(userarg))
+ {
+ uid=atoi(userarg);
+ libmail_changeuidgid(uid, getgid());
+ }
+ else
+ {
+ gid_t g=getgid(), *gp=0;
+
+ if (grouparg) gp= &g;
+ libmail_changeusername(userarg, gp);
+ }
+ }
+
+ if (pidarg && ll_daemon_resetio())
+ {
+ perror("ll_daemon_resetio");
+ close(lockfd);
+ return (-1);
+ }
+
+ if (stderrloggerarg)
+ {
+ pid_t p;
+ int waitstat;
+ int pipefd[2];
+ const char *progname=argv[argn];
+
+ if (pipe(pipefd) < 0)
+ {
+ perror("pipe");
+ return (-1);
+ }
+
+ signal(SIGCHLD, SIG_DFL);
+ while ((p=fork()) == -1)
+ {
+ sleep(5);
+ }
+
+ if (p == 0)
+ {
+ signal(SIGHUP, SIG_IGN);
+ sox_close(0);
+ sox_dup(pipefd[0]);
+ sox_close(pipefd[0]);
+ sox_close(pipefd[1]);
+ sox_close(1);
+ open("/dev/null", O_WRONLY);
+ sox_close(2);
+ sox_dup(1);
+ closeaccess();
+ while ((p=fork()) == -1)
+ {
+ sleep(5);
+ }
+ if (p == 0)
+ {
+ const char *p=strrchr(progname, '/');
+
+ if (p) ++p;
+ else p=progname;
+
+ if (stderrloggername && *stderrloggername)
+ p=stderrloggername;
+
+ execl(stderrloggerarg, stderrloggerarg,
+ p, (char *)0);
+ perror(stderrloggerarg);
+ _exit(5);
+ }
+ _exit(0);
+ }
+ sox_close(2);
+ sox_dup(pipefd[1]);
+ sox_close(pipefd[0]);
+ sox_close(pipefd[1]);
+ while (wait(&waitstat) != p)
+ ;
+ }
+ else if (stderrarg)
+ {
+ int fd=open(stderrarg, O_WRONLY|O_APPEND|O_CREAT, 0660);
+
+ if (!fd)
+ {
+ perror(stderrarg);
+ return (-1);
+ }
+ sox_close(2);
+ sox_dup(fd);
+ sox_close(fd);
+ }
+
+ nprocs=40;
+ if (maxprocsarg)
+ {
+ nprocs=atoi(maxprocsarg);
+ if (nprocs <= 0)
+ {
+ fprintf(stderr, "Invalid -maxprocsarg option.\n");
+ return (-1);
+ }
+ }
+
+ nwarn= nprocs - (nprocs / 10 + 1);
+
+ if (warnarg)
+ {
+ int c=atoi(warnarg);
+
+ if (c >= 0 && c <= nprocs)
+ nwarn=c;
+ }
+
+ if ((pids=malloc(sizeof(*pids)*nprocs)) == 0)
+ {
+ perror("malloc");
+ return (-1);
+ }
+ if ((addrs=malloc(sizeof(*addrs)*nprocs)) == 0)
+ {
+ free(pids);
+ perror("malloc");
+ return (-1);
+ }
+
+
+ for (i=0; i<nprocs; i++)
+ pids[i]= -1;
+
+ maxperc=nprocs;
+ maxperip=4;
+
+ if (maxpercarg)
+ {
+ maxperc=atoi(maxpercarg);
+ if (maxperc <= 0)
+ {
+ fprintf(stderr, "Invalid -maxperc option.\n");
+ free(pids);
+ free(addrs);
+ return (-1);
+ }
+ }
+ if (maxperiparg)
+ {
+ maxperip=atoi(maxperiparg);
+ if (maxperip <= 0)
+ {
+ fprintf(stderr, "Invalid -maxperip option.\n");
+ free(pids);
+ free(addrs);
+ return (-1);
+ }
+ }
+ if (forced)
+ {
+ fprintf(stderr, "couriertcpd: ready.\n");
+ fflush(stderr);
+ }
+ return (argn);
+}
+
+static void run(int, const RFC1035_ADDR *, int, const char *, char **);
+
+static void doreap(pid_t p, int wait_stat)
+{
+int n;
+
+ for (n=0; n<nprocs; n++)
+ if (p == pids[n])
+ {
+ pids[n]= -1;
+ break;
+ }
+}
+
+static RETSIGTYPE childsig(int signum)
+{
+ signum=signum;
+ wait_reap(doreap, childsig);
+#if RETSIGTYPE != void
+ return (0);
+#endif
+}
+
+static int doallowaccess(char *, int);
+
+#if RFC1035_IPV6
+
+static int allowaccess(const RFC1035_ADDR *sin, int port)
+{
+char buf[RFC1035_MAXNAMESIZE+1+6];
+char *q;
+int i;
+
+ if (IN6_IS_ADDR_V4MAPPED(sin))
+ {
+ const char *p=inet_ntop(AF_INET6, sin, buf, sizeof(buf)-6);
+
+ if (p && (q=strrchr(buf, ':')) != 0)
+ return (doallowaccess(q+1, port));
+ return (1);
+ }
+ q=buf;
+ for (i=0; i<sizeof(*sin); i += 2)
+ {
+#define B(i) ((unsigned long)((unsigned char *)sin)[i])
+ unsigned long n=(B(i) << 8) | B(i+1);
+#undef B
+ sprintf(q, ":%04lx", n);
+ q += 5;
+ }
+ *q=0;
+ return (doallowaccess(buf, port));
+}
+
+#else
+static int allowaccess(const RFC1035_ADDR *sin, int port)
+{
+char buf[RFC1035_NTOABUFSIZE+6];
+
+ rfc1035_ntoa(sin, buf);
+ return (doallowaccess(buf, port));
+}
+#endif
+
+static int doallowaccess(char *buf, int port)
+{
+char *accessptr;
+char *p, *q, *r;
+int quote=0;
+int l;
+
+ if (accessarg == 0) return (1);
+
+ if (port) snprintf(buf+strlen(buf), 7, ".%d", ntohs(port));
+ while ((accessptr= *buf ? chkaccess(buf):0) == 0)
+ {
+ if ((accessptr=strrchr(buf, '.')) == 0
+#if RFC1035_IPV6
+ && (accessptr=strrchr(buf, ':')) == 0
+#endif
+ )
+ {
+ if (port)
+ {
+ snprintf(buf, 8, "*.%d", ntohs(port));
+ if ((accessptr=chkaccess(buf)) != 0)
+ break;
+ }
+ if ((accessptr=chkaccess("*")) != 0)
+ break;
+ return (1);
+ }
+ *accessptr=0;
+ }
+
+ if (strncmp(accessptr, "deny", 4) == 0)
+ {
+ free(accessptr);
+ return (0);
+ }
+
+ p=accessptr;
+ if (strncmp(accessptr, "allow", 5) == 0)
+ {
+ p += 5;
+ if (*p == ',') ++p;
+ }
+
+ while ( p && *p )
+ {
+ q=p;
+ r=q;
+ while (*p)
+ {
+ if (*p == ',' && !quote)
+ {
+ *p++=0;
+ break;
+ }
+ if (!quote && (*p == '"' || *p == '\''))
+ {
+ quote=*p;
+ p++;
+ continue;
+ }
+ if (quote && *p == quote)
+ {
+ quote=0;
+ p++;
+ continue;
+ }
+ *r++=*p++;
+ }
+ *r=0;
+ if (strchr(q, '=') == 0)
+ {
+ char *r=malloc(strlen(q)+2);
+
+ if (!r)
+ {
+ perror("malloc");
+ return (0);
+ }
+ q=strcat(strcpy(r, q), "=");
+ }
+
+ while (*q && isspace((int)(unsigned char)*q)) ++q;
+ while ((l=strlen(q)) > 0
+ && isspace((int)(unsigned char)q[l-1]))
+ q[--l]=0;
+ putenv(q);
+ }
+ return (1);
+}
+
+/* Wait until we have at least one available slot left */
+
+static int getfreeslot(int *pidptr)
+{
+ int n;
+
+ for (;;)
+ {
+ wait_block();
+
+ for (n=0; n<nprocs; n++)
+ {
+ if (pids[*pidptr] == (pid_t)-1) break;
+ if (++*pidptr >= nprocs) *pidptr=0;
+ }
+ if (pids[*pidptr] != (pid_t)-1)
+ {
+ wait_forchild(doreap, childsig);
+ continue;
+ }
+ break;
+ }
+ wait_clear(childsig);
+ return (*pidptr);
+}
+
+static void accepted(int, int, RFC1035_NETADDR *, int, const char *, char **);
+
+static int doit(int argn, int argc, char **argv)
+{
+ char **ptrs;
+ int pidptr;
+ struct portinfo *pi;
+ fd_set fdr, fdrcopy;
+ int dummy;
+
+ ptrs=(char **)malloc((argc-argn+1) * sizeof(char *));
+ if (!ptrs)
+ {
+ perror("malloc");
+ return (-1);
+ }
+ for (dummy=0; dummy<argc-argn; dummy++)
+ {
+ ptrs[dummy]=argv[argn+dummy];
+ }
+ ptrs[dummy]=0;
+
+ if (listenarg)
+ {
+ dummy=atoi(listenarg);
+ if (dummy <= 0)
+ {
+ fprintf(stderr, "Invalid -listen option.\n");
+ exit(1);
+ }
+ }
+
+ FD_ZERO(&fdrcopy);
+ for (pi=fdlist; pi; pi=pi->next)
+ {
+ if (pi->fd1 >= 0)
+ FD_SET(pi->fd1, &fdrcopy);
+
+ if (pi->fd2 >= 0)
+ FD_SET(pi->fd2, &fdrcopy);
+ }
+
+ pidptr=0;
+
+ signal(SIGCHLD, childsig);
+
+#if HAVE_SETPGRP
+#if SETPGRP_VOID
+ setpgrp();
+#else
+ setpgrp(0, 0);
+#endif
+#else
+#if HAVE_SETPGID
+ setpgid(0, 0);
+#endif
+#endif
+#ifdef TIOCNOTTY
+
+ {
+ int fd=open("/dev/tty", O_RDWR);
+
+ if (fd >= 0)
+ {
+ ioctl(fd, TIOCNOTTY, 0);
+ close(fd);
+ }
+ }
+#endif
+
+ signal(SIGPIPE, SIG_IGN);
+ for (;;)
+ {
+ int n;
+ int sockfd;
+ RFC1035_NETADDR sin;
+ socklen_t sinl;
+
+ fdr=fdrcopy;
+
+ if (select(maxfd+1, &fdr, NULL, NULL, NULL) <= 0)
+ {
+ if (errno != EINTR)
+ perror("accept");
+ continue;
+ }
+
+ for (pi=fdlist; pi; pi=pi->next)
+ {
+ if (pi->fd1 >= 0 && FD_ISSET(pi->fd1, &fdr) &&
+ ((n=getfreeslot(&pidptr)),
+ (sinl = sizeof(sin)),
+ (sockfd=sox_accept(pi->fd1,
+ (struct sockaddr *)&sin,
+ &sinl))) >= 0)
+ {
+ accepted(n, sockfd, &sin, sinl,
+ argv[argn], ptrs);
+ }
+
+ if (pi->fd2 >= 0 && FD_ISSET(pi->fd2, &fdr) &&
+ ((n=getfreeslot(&pidptr)),
+ (sinl = sizeof(sin)),
+ (sockfd=sox_accept(pi->fd2,
+ (struct sockaddr *)&sin,
+ &sinl))) >= 0)
+ {
+ accepted(n, sockfd, &sin, sinl,
+ argv[argn], ptrs);
+ }
+ }
+ }
+}
+
+static void denied(int sockfd)
+{
+ if (denymsgarg) {
+ if (write(sockfd, denymsgarg, strlen(denymsgarg)) < 0 ||
+ write(sockfd, "\n", 1) < 0)
+ {
+ sox_close(sockfd);
+ _exit(1);
+ }
+ }
+ sox_close(sockfd);
+ _exit(0);
+}
+
+static void accepted(int n, int sockfd, RFC1035_NETADDR *sin, int sinl,
+ const char *prog,
+ char **args)
+{
+ RFC1035_ADDR addr;
+ int addrport;
+#ifdef SO_LINGER
+ int dummy;
+ struct linger l;
+#endif
+ pid_t p;
+ int cnt;
+
+ if (rfc1035_sockaddrip(sin, sinl, &addr)
+ || rfc1035_sockaddrport(sin, sinl, &addrport))
+ {
+ sox_close(sockfd);
+ return;
+ }
+
+ /* Turn off the CLOEXEC and NONBLOCK bits */
+
+ if (fcntl(sockfd, F_SETFD, 0))
+ {
+ perror("fcntl");
+ sox_close(sockfd);
+ return;
+ }
+
+ if (fcntl(sockfd, F_SETFL, 0))
+ {
+ perror("fcntl");
+ sox_close(sockfd);
+ return;
+ }
+
+ if (sighup_received)
+ {
+ sighup_received=0;
+ if (accessarg)
+ {
+ closeaccess();
+ if (openaccess(accessarg))
+ perror(accessarg);
+ }
+ }
+
+#ifdef SO_KEEPALIVE
+ dummy=1;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
+ (const char *)&dummy, sizeof(dummy)) < 0)
+ {
+ perror("setsockopt");
+ }
+#endif
+
+#ifdef SO_LINGER
+ l.l_onoff=0;
+ l.l_linger=0;
+
+ if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER,
+ (const char *)&l, sizeof(l)) < 0)
+ {
+ perror("setsockopt");
+ }
+#endif
+ wait_block();
+ if ((p=fork()) == -1)
+ {
+ perror("fork");
+ sox_close(sockfd);
+ return;
+ }
+
+ if (p == 0)
+ {
+ wait_restore(childsig);
+
+ if (accesslocal) /* Lookup local interface address too? */
+ {
+ RFC1035_NETADDR lsin;
+ RFC1035_ADDR laddr;
+ int lport;
+ socklen_t i=sizeof(lsin);
+
+ if (sox_getsockname(sockfd, (struct sockaddr *)&lsin, &i) == 0 &&
+ rfc1035_sockaddrip(&lsin, i, &laddr) == 0 &&
+ rfc1035_sockaddrport(&lsin, i, &lport) == 0 &&
+ allowaccess(&laddr,lport) == 0)
+ {
+ sox_close(sockfd);
+ _exit(0);
+ }
+ }
+
+ if (allowaccess(&addr,0) == 0)
+ {
+ denied(sockfd);
+ }
+
+ run(sockfd, &addr, addrport, prog, args);
+ }
+ pids[n]=p;
+
+ memcpy(addrs+n, &addr, sizeof(addr));
+ sox_close(sockfd);
+ wait_clear(childsig);
+
+ for (cnt=n=0; n<nprocs; n++)
+ if (pids[n] != (pid_t)-1)
+ ++cnt;
+
+ if (cnt == nprocs)
+ {
+ time_t t;
+
+ time(&t);
+ if (last_alert == 0 || last_alert > t || last_alert < t - 60)
+ {
+ last_alert=t;
+ fprintf(stderr,
+ "ALERT: %d maximum active connections.\n",
+ nprocs);
+ }
+ }
+ else if (cnt >= nwarn)
+ {
+ time_t t;
+
+ time(&t);
+ if (last_warn == 0 || last_warn > t || last_warn < t - 60)
+ {
+ last_warn=t;
+ fprintf(stderr, "WARN: %d active connections.\n",
+ cnt);
+ }
+ }
+}
+
+static void mysetenv(const char *name, const char *val)
+{
+char *p=malloc(strlen(name)+strlen(val)+2);
+
+ if (!p)
+ {
+ perror("malloc");
+ _exit(1);
+ }
+ putenv(strcat(strcat(strcpy(p, name), "="), val));
+}
+
+/*
+** Convert IP address to host name. Make sure the IP address resolves
+** backwards and forwards.
+*/
+
+static void ip2host(const RFC1035_ADDR *addr, const char *env)
+{
+const char *remotehost="softdnserr";
+char buf[RFC1035_MAXNAMESIZE+1];
+
+#if TCPDUSERFC1035
+struct rfc1035_res res;
+#endif
+
+ if (nodnslookup) return;
+
+ rfc1035_ntoa(addr, buf);
+
+#if TCPDUSERFC1035
+
+ rfc1035_init_resolv(&res);
+
+ if (rfc1035_ptr(&res, addr, buf) != 0)
+ {
+ if (errno == ENOENT)
+ remotehost=0;
+ }
+ else
+ {
+ RFC1035_ADDR *ias;
+ unsigned nias, n;
+
+ if (rfc1035_a(&res, buf, &ias, &nias) != 0)
+ {
+ if (errno == ENOENT)
+ remotehost=0;
+ }
+ else
+ {
+ remotehost=0;
+ for (n=0; n<nias; n++)
+ {
+ char a[RFC1035_MAXNAMESIZE];
+ char b[RFC1035_MAXNAMESIZE];
+
+ rfc1035_ntoa(&ias[n], a);
+ rfc1035_ntoa(addr, b);
+
+ if (strcmp(a, b) == 0)
+ {
+ remotehost=buf;
+ }
+ }
+ }
+ }
+ rfc1035_destroy_resolv(&res);
+
+#else
+
+ {
+ struct hostent *he;
+ unsigned n;
+ struct in_addr in;
+
+#if RFC1035_IPV6
+
+ if (IN6_IS_ADDR_V4MAPPED(addr))
+ memcpy(&in, (char *)addr + 12, 4);
+ else return;
+#else
+ in= *addr;
+#endif
+
+ he=gethostbyaddr( (char *)&in, sizeof(in), AF_INET);
+ if (!he)
+ {
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ remotehost=0;
+ break;
+ }
+ }
+ else
+ {
+ strcpy(buf, he->h_name);
+ he=gethostbyname(buf);
+ if (!he)
+ {
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ remotehost=0;
+ break;
+ }
+ }
+ else for (n=0, remotehost=0; he->h_addr_list[n]; n++)
+ {
+ struct in_addr hin;
+
+ if (he->h_addrtype != AF_INET ||
+ he->h_length < sizeof(hin))
+ break;
+ memcpy((char *)&hin, he->h_addr_list[n],
+ sizeof(hin));
+ if (hin.s_addr == in.s_addr)
+ {
+ remotehost=buf;
+ break;
+ }
+ }
+
+ }
+ }
+#endif
+ if (remotehost)
+ mysetenv(env, remotehost);
+}
+
+static void mkmymsg(const char *varname, const char *msg)
+{
+const char *p=getenv("TCPREMOTEIP");
+char *q=malloc(strlen(msg)+1+strlen(p));
+char *r;
+
+ if (!q)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ for (r=q; *msg; msg++)
+ {
+ if (*msg == '@')
+ {
+ strcpy(r, p);
+ while (*r) r++;
+ ++msg;
+ break;
+ }
+ *r++=*msg;
+ }
+ while (*msg)
+ *r++=*msg++;
+ *r=0;
+ mysetenv(varname, q);
+ free(q);
+}
+
+static void set_allow_variable(const char *varname, const char *msg)
+{
+ static int found = 0;
+ char buf[32];
+ mysetenv(varname, ""); /* Whitelist */
+
+ sprintf(buf, "ALLOW_%d", found++);
+ mysetenv(buf, varname);
+
+ (void)msg; /* not used, could tweak behavior of -allow */
+}
+
+static void set_txt_response(const char *varname,
+ const char *txt)
+{
+ char *p=malloc(strlen(varname)+20);
+
+ strcat(strcpy(p, varname), "_TXT");
+ mysetenv(p, txt);
+ free(p);
+
+}
+
+static void set_zone(const char *varname,
+ const char *zone)
+{
+ char *p=malloc(strlen(varname)+20);
+
+ strcat(strcpy(p, varname), "_ZONE");
+ mysetenv(p, zone);
+ free(p);
+
+}
+
+
+static void set_a_response(const char *varname,
+ const struct in_addr *in)
+{
+ char buf[RFC1035_NTOABUFSIZE+6];
+ char *p;
+
+ rfc1035_ntoa_ipv4(in, buf);
+
+ p=malloc(strlen(varname)+20);
+
+ strcat(strcpy(p, varname), "_IP");
+ mysetenv(p, buf);
+ free(p);
+}
+
+static int is_a_rr(struct rfc1035_reply *replyp,
+ const struct rfc1035_rr *rrptr,
+ const char *wanted_hostname)
+{
+ char buf[RFC1035_MAXNAMESIZE+1];
+
+ /*
+ ** Go through the DNS response, and check every A record
+ ** in there.
+ */
+
+ rfc1035_replyhostname(replyp, rrptr->rrname, buf);
+ if (rfc1035_hostnamecmp(buf, wanted_hostname)) return 0;
+
+ if (rrptr->rrtype != RFC1035_TYPE_A)
+ return 0;
+
+ return 1;
+}
+
+/*
+** Process TXT records in DNSBL lookup response.
+*/
+
+static int search_txt_records(struct rfc1035_res *res,
+ int allow,
+ const char *varname,
+ struct rfc1035_reply *replyp,
+ const char *wanted_hostname)
+{
+ char buf[RFC1035_MAXNAMESIZE+1];
+ int j;
+
+ if ((j=rfc1035_replysearch_all(res,
+ replyp, wanted_hostname,
+ RFC1035_TYPE_TXT,
+ RFC1035_CLASS_IN, 0)) >= 0)
+ {
+ rfc1035_rr_gettxt(replyp->allrrs[j], 0, buf);
+
+ if (buf[0]) /* is this necessary? */
+ {
+ if (!allow)
+ mysetenv(varname, buf);
+ set_txt_response(varname, buf);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** check_blocklist is called once for each blocklist query to process.
+*/
+
+static void docheckblocklist(struct blocklist_s *p, const char *nameptr)
+{
+ const char *q;
+ const char *varname=p->var;
+ char hostname[RFC1035_MAXNAMESIZE+1];
+ int wanttxt;
+ struct rfc1035_reply *replyp;
+ struct rfc1035_res res;
+ unsigned int i;
+ int found;
+
+ hostname[0]=0;
+ strncat(hostname, nameptr, RFC1035_MAXNAMESIZE);
+
+ if (!varname) varname="BLOCK";
+
+ if ((q=getenv(varname)) != 0) return;
+ /* Env var already set */
+
+ rfc1035_init_resolv(&res);
+
+ /*
+ ** The third parameter has opposite meanings. For -block, the last
+ ** component specifies a custom message that overrides any TXT record
+ ** in the DNS access list. For -allow, it simply asks for TXT records
+ ** to be fetched, for use by external software.
+ */
+
+ if (p->allow)
+ wanttxt = p->msg != 0;
+ else
+ wanttxt = (p->msg == 0 || *p->msg == 0);
+
+ (void)rfc1035_resolve_cname(&res,
+ hostname,
+ wanttxt ? RFC1035_TYPE_ANY:RFC1035_TYPE_A,
+ RFC1035_CLASS_IN, &replyp, 0);
+
+ if (!replyp)
+ {
+ rfc1035_destroy_resolv(&res);
+ return;
+ }
+
+ found=0;
+
+ for (i=0; i<replyp->ancount+replyp->nscount+replyp->arcount; i++)
+ {
+ if (!is_a_rr(replyp, replyp->allrrs[i], hostname))
+ continue;
+
+ if (p->ia.s_addr != INADDR_ANY &&
+ p->ia.s_addr != replyp->allrrs[i]->rr.inaddr.s_addr)
+ continue;
+
+ set_zone(varname, p->display_zone);
+ set_a_response(varname, &replyp->allrrs[i]->rr.inaddr);
+
+ /*
+ ** The -block option was kind enough to supply the
+ ** error message.
+ */
+
+ if (!p->allow && p->msg && *p->msg)
+ {
+ mkmymsg(varname, p->msg);
+ continue;
+ }
+
+ /*
+ ** search_txt_records takes care of setting varname for
+ ** -blocks, and we must set it for -allows.
+ */
+
+ if (p->allow)
+ set_allow_variable(varname, p->msg);
+
+ if (!search_txt_records(&res, p->allow, varname, replyp,
+ hostname) && !p->allow)
+ {
+ /*
+ ** Even though we did not find a TXT record, we're here
+ ** because of an A record, so for -blocks, we must
+ ** set varname to something.
+ */
+ mysetenv(varname, "Access denied.");
+ }
+
+ found=1;
+ break;
+ }
+
+ /*
+ ** Last chance: if all we got is a TXT record, and we were not looking
+ ** for a specific IP address, then take what we've got.
+ */
+
+ if (p->ia.s_addr == INADDR_ANY && !found)
+ {
+ if (search_txt_records(&res, p->allow, varname, replyp,
+ hostname))
+ {
+ /*
+ ** search_txt_record takes care of setting varname
+ ** for -blocks, and we must do it for -allows
+ */
+ if (p->allow)
+ mysetenv(varname, ""); /* Whitelist */
+ set_zone(varname, p->display_zone);
+ }
+ }
+
+ rfc1035_replyfree(replyp);
+ rfc1035_destroy_resolv(&res);
+}
+
+static void check_blocklist_ipv4(struct blocklist_s *p,
+ const struct in_addr *ia)
+{
+unsigned a,b,c,d;
+char hostname[RFC1035_MAXNAMESIZE+1];
+const unsigned char *q=(const unsigned char *)ia;
+
+ /* Calculate DNS query hostname */
+
+ a=q[0];
+ b=q[1];
+ c=q[2];
+ d=q[3];
+
+ /* Silently ignore exceedingly long zones */
+ if (snprintf(hostname, sizeof hostname,
+ "%u.%u.%u.%u.%s", d, c, b, a, p->zone) <= RFC1035_MAXNAMESIZE)
+ docheckblocklist(p, hostname);
+}
+
+#if RFC1035_IPV6
+
+static void check_blocklist(struct blocklist_s *p, const RFC1035_ADDR *ia)
+{
+ char hostname[RFC1035_MAXNAMESIZE+1];
+
+ /*
+ ** 16 byte IPv6 address. 32 nybbles. Each nybble followed by a dot:
+ ** 64 characters.
+ */
+
+ char decimal_address[65];
+ char bytebuf[5];
+ int i;
+
+ if (IN6_IS_ADDR_V4MAPPED(ia))
+ {
+ struct in_addr ia4;
+
+ memcpy(&ia4, (const char *)ia + 12, 4);
+ check_blocklist_ipv4(p, &ia4);
+ }
+
+ decimal_address[0]=0;
+
+ for (i=0; i<16; ++i)
+ {
+ unsigned char byte=((struct in6_addr *)ia)->s6_addr[15-i];
+
+ sprintf(bytebuf, "%x.%x.",(byte & 0x0F), ((byte >> 4) & 0x0F));
+ strcat(decimal_address, bytebuf);
+ }
+
+ /* Silently ignore exceedingly long zones */
+ if (snprintf(hostname, sizeof hostname,
+ "%s%s", decimal_address, p->zone) <= RFC1035_MAXNAMESIZE)
+ docheckblocklist(p, hostname);
+}
+
+#else
+static void check_blocklist(struct blocklist_s *p, const RFC1035_ADDR *ia)
+{
+ check_blocklist_ipv4(p, ia);
+}
+#endif
+
+static void check_drop(int sockfd)
+{
+ const char *p, *q;
+ char *r;
+
+ p=droparg;
+
+ if (p && !*p)
+ p="BLOCK";
+
+ for (; p && *p; q=p)
+ {
+ if (*p == ',')
+ {
+ q= ++p;
+ continue;
+ }
+
+ for (q=p; *q; ++q)
+ if (*q == ',')
+ break;
+
+ r=malloc(q-p+1);
+
+ if (!r)
+ {
+ perror("malloc");
+ _exit(1);
+ }
+
+ memcpy(r, p, q-p);
+ r[q-p]=0;
+
+ p=getenv(r);
+ free(r);
+
+ if (p && *p)
+ {
+ fprintf(stderr,
+ "WARN: dropped blocked connection from %s\n",
+ getenv("TCPREMOTEIP"));
+ denied(sockfd);
+ }
+ }
+}
+
+static void proxy();
+
+static void run(int fd, const RFC1035_ADDR *addr, int addrport,
+ const char *prog, char **argv)
+{
+RFC1035_NETADDR lsin;
+RFC1035_ADDR laddr;
+int lport;
+
+socklen_t i;
+int ipcnt, ccnt;
+char buf[RFC1035_MAXNAMESIZE+128];
+struct blocklist_s *bl;
+const char *remoteinfo;
+const char *p;
+
+ i=sizeof(lsin);
+ if (sox_getsockname(fd, (struct sockaddr *)&lsin, &i) ||
+ rfc1035_sockaddrip(&lsin, i, &laddr) ||
+ rfc1035_sockaddrport(&lsin, i, &lport))
+ {
+ fprintf(stderr, "getsockname failed.\n");
+ exit(1);
+ }
+
+ if (!noidentlookup && (remoteinfo=tcpremoteinfo(
+ &laddr, lport,
+ addr, addrport, 0)) != 0)
+ {
+ char *q=malloc(sizeof("TCPREMOTEINFO=")+strlen(remoteinfo));
+
+ if (!q)
+ {
+ perror("malloc");
+ _exit(1);
+ }
+
+ strcat(strcpy(q, "TCPREMOTEINFO="), remoteinfo);
+ putenv(q);
+ }
+
+/* check if it's an exception to the global ip limit */
+ if( (p=getenv("MAXCPERIP")) != NULL )
+ {
+ int j = atoi(p);
+
+ if( j > 0 )
+ maxperip = j;
+ }
+
+ for (i=0, ipcnt=ccnt=0; i<nprocs; i++)
+ {
+ RFC1035_ADDR *psin;
+ int j;
+
+ if (pids[i] == (pid_t)-1) continue;
+
+ psin=addrs+i;
+
+ for (j=0; j<sizeof(*addr); j++)
+ if ( ((char *)addr)[j] != ((char *)psin)[j])
+ break;
+
+ if (j >= sizeof(*addr) &&
+ ++ipcnt >= maxperip)
+ {
+ rfc1035_ntoa(addr, buf);
+ fprintf(stderr,"ALERT: Maximum connection limit reached for %s\n",buf);
+ _exit(0); /* Too many from same IP address */
+ }
+
+ if ( j >= sizeof(*addr)-1 &&
+ ++ccnt >= maxperc)
+ _exit(0); /* Too many from same netblock */
+
+ }
+
+ rfc1035_ntoa(addr, buf);
+ mysetenv("TCPREMOTEIP", buf);
+ sprintf(buf, "%d", ntohs(addrport));
+ mysetenv("TCPREMOTEPORT", buf);
+ ip2host(addr, "TCPREMOTEHOST");
+
+ rfc1035_ntoa(&laddr, buf);
+ mysetenv("TCPLOCALIP", buf);
+ sprintf(buf, "%d", ntohs(lport));
+ mysetenv("TCPLOCALPORT", buf);
+ ip2host(&laddr, "TCPLOCALHOST");
+
+ for (bl=blocklist; bl; bl=bl->next)
+ check_blocklist(bl, addr);
+
+ check_drop(fd);
+ sox_close(0);
+ sox_close(1);
+ sox_dup(fd);
+ sox_dup(fd);
+ sox_close(fd);
+ if (stderrarg && strcmp(stderrarg, "socket") == 0)
+ {
+ sox_close(2);
+ sox_dup(1);
+ }
+ proxy();
+ signal(SIGPIPE, SIG_DFL);
+
+ execv(prog, argv);
+ perror(prog);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+int argn=init(argc, argv);
+int rc;
+
+ if (argn < 0)
+ {
+ exit(1);
+ }
+ if (accessarg && openaccess(accessarg))
+ perror(accessarg);
+ if (accesslocal && !accessarg)
+ fprintf(stderr,"-accesslocal requires -access\n");
+ rc=doit(argn, argc, argv);
+ kill( -getpid(), SIGTERM);
+ exit(rc);
+ return (0);
+}
+
+#if 1
+
+static void proxy()
+{
+}
+
+#else
+
+/*
+** SOCKSv5 does not support wildcards binds, for now, so there's no
+** reason to manually proxy anything, yet.
+*/
+
+
+/***************************************************************************
+
+Manual proxy, to support SOCKS encryption. Because encrypted connection to
+the SOCKS server is supported transparently in libsocks5, we can't just
+run the app, because we'll lose libsocks5's intercept of read/write, et al
+
+***************************************************************************/
+
+struct proxybuf {
+ char buffer[BUFSIZ];
+ char *p;
+ int buffered;
+ int rfd, wfd;
+ } ;
+
+static void proxy_init(struct proxybuf *b, int r, int w)
+{
+ b->buffered=0;
+ b->rfd=r;
+ b->wfd=w;
+}
+
+static int proxy_setfd(struct proxybuf *b, fd_set *r, fd_set *w, int *max)
+{
+ /* If we have something buffered, write it out */
+
+ if (b->buffered)
+ {
+ FD_SET(b->wfd, w);
+ if (b->wfd > *max) *max=b->wfd;
+ return (1);
+ }
+
+ if (b->rfd < 0)
+ {
+ if (b->wfd >= 0)
+ {
+ sox_close(b->wfd);
+ b->wfd= -1;
+ }
+ return (0); /* Nothing else to do */
+ }
+
+ if (b->rfd > *max) *max=b->rfd;
+ FD_SET(b->rfd, r);
+ return (1);
+}
+
+static void proxy_dofd(struct proxybuf *b, fd_set *r, fd_set *w)
+{
+ if (b->buffered)
+ {
+ if (FD_ISSET(b->wfd, w))
+ {
+ int n=sox_write(b->wfd, b->p, b->buffered);
+
+ if (n <= 0)
+ {
+ sox_close(b->rfd);
+ sox_close(b->wfd);
+ b->rfd=b->wfd= -1;
+ b->buffered=0;
+ return;
+ }
+ b->p += n;
+ b->buffered -= n;
+ }
+ return;
+ }
+
+ if (b->rfd >= 0 && FD_ISSET(b->rfd, r))
+ {
+ int n=sox_read(b->rfd, b->buffer, sizeof(b->buffer));
+
+ if (n <= 0)
+ {
+ sox_close(b->rfd);
+ sox_close(b->wfd);
+ b->rfd=b->wfd= -1;
+ b->buffered=0;
+ return;
+ }
+ b->p = b->buffer;
+ b->buffered=n;
+ }
+}
+
+static void proxy_do(struct proxybuf *);
+
+static void proxy()
+{
+int pipefd0[2], pipefd1[2], pipefd2[2];
+pid_t p, p2;
+int waitstat;
+struct proxybuf proxy_[3];
+
+ if (!proxyarg) return;
+
+ if (pipe(pipefd0) || pipe(pipefd1) || pipe(pipefd2))
+ {
+ perror("pipe");
+ exit(1);
+ }
+
+ p=fork();
+ if (p == -1)
+ {
+ perror("fork");
+ exit(1);
+ }
+
+ /*
+ ** The parent goes on its merry way, but first makes sure that the
+ ** child process is OK.
+ */
+
+ if (p)
+ {
+ while ((p2=wait(&waitstat)) != p)
+ {
+ if (p2 == -1 && errno != EINTR)
+ {
+ perror("wait");
+ exit(1);
+ }
+ }
+ if (waitstat)
+ exit(0);
+ sox_close(0);
+ sox_close(1);
+ sox_close(2);
+ errno=EINVAL;
+ if (sox_dup(pipefd0[0]) != 0 ||
+ sox_dup(pipefd1[1]) != 1 ||
+ sox_dup(pipefd2[1]) != 2)
+ {
+ perror("dup(app)");
+ exit(1);
+ }
+ sox_close(pipefd0[0]);
+ sox_close(pipefd0[1]);
+ sox_close(pipefd1[0]);
+ sox_close(pipefd1[1]);
+ sox_close(pipefd2[0]);
+ sox_close(pipefd2[1]);
+ return;
+ }
+
+ p=fork();
+ if (p == -1) exit(1);
+ if (p) exit(0);
+
+ sox_close(pipefd0[0]);
+ sox_close(pipefd1[1]);
+ sox_close(pipefd2[1]);
+
+ proxy_init(&proxy_[0], 0, pipefd0[1]);
+ proxy_init(&proxy_[1], pipefd1[0], 1);
+ proxy_init(&proxy_[2], pipefd2[0], 2);
+ proxy_do(proxy_);
+ exit(0);
+}
+
+static void proxy_do(struct proxybuf *p)
+{
+fd_set r, w;
+
+ for (;;)
+ {
+ int m=0;
+ int rc0, rc1, rc2;
+
+ FD_ZERO(&r);
+ FD_ZERO(&w);
+
+ rc0=proxy_setfd(p, &r, &w, &m);
+ rc1=proxy_setfd(p+1, &r, &w, &m);
+ rc2=proxy_setfd(p+2, &r, &w, &m);
+
+ if (rc0 == 0 && rc1 == 0 && rc2 == 0)
+ break;
+
+ if (rc0 == 0 || rc1 == 0 || rc2 == 0)
+ alarm(10);
+
+ if (select(m+1, &r, &w, 0, 0) < 0)
+ {
+ perror("select");
+ break;
+ }
+
+ proxy_dofd(p, &r, &w);
+ proxy_dofd(p+1, &r, &w);
+ proxy_dofd(p+2, &r, &w);
+ }
+}
+#endif