From 9c45d9ad13fdf439d44d7443ae75da15ea0223ed Mon Sep 17 00:00:00 2001 From: Sam Varshavchik Date: Mon, 19 Aug 2013 16:39:41 -0400 Subject: Initial checkin Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy. --- tcpd/tcpd.c | 2151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2151 insertions(+) create mode 100644 tcpd/tcpd.c (limited to 'tcpd/tcpd.c') 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 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_FCNTL_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#if HAVE_UNISTD_H +#include +#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 + + +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; irrname, 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; iancount+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= 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 -- cgit v1.2.3