# HG changeset patch # Parent e319e618448fe7163635b08206f48d214e59d738 diff -r e319e618448f tools/sbounce/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/sbounce/Makefile Sun Jul 14 18:14:29 2013 +0100 @@ -0,0 +1,54 @@ +# +# Makefile for sbounce +# + +CC=gcc +CFLAGS=-O3 -Wall -pedantic + +PROG=sbounce +PREFIX=/opt/ircd +DEFINES=-DSETTINGSFILE=\"${PREFIX}/lib/bounce.conf\"\ + -DLOGFILE=\"${PREFIX}/lib/${PROG}.log\"\ + -DPIDFILE=\"${PREFIX}/lib/${PROG}.pid\" + +all: ${PROG} logrotate.conf + +${PROG}: ${PROG}.o + gcc -O3 -lz -o ${PROG} ${PROG}.o + +sbounce.o: + gcc ${DEFINES} -c ${PROG}.c + +logrotate.conf: logrotate.conf.in + @sed -e 's,LOGFILE,${PREFIX}/lib/${PROG}.log,g'\ + -e 's,PIDFILE,${PREFIX}/lib/${PROG}.pid,g' logrotate.conf + +install: all + @if [ ! -d ${PREFIX} -a ! -f ${PREFIX} ]; then \ + echo "Creating directory ${PREFIX}"; \ + mkdir ${PREFIX}; \ + chmod 555 ${PREFIX}; \ + fi + + @if [ ! -d ${PREFIX}/bin -a ! -f ${PREFIX}/bin ]; then \ + echo "Creating directory ${PREFIX}/bin"; \ + mkdir ${PREFIX}/bin; \ + chmod 555 ${PREFIX}/bin; \ + fi + + @if [ -f ${PREFIX}/bin/${PROG} ]; then \ + echo "Saving existing ${PREFIX}/bin/${PROG} as .old"; \ + mv ${PREFIX}/bin/${PROG} ${PREFIX}/bin/${PROG}.old; \ + fi + + cp ${PROG} ${PREFIX}/bin + chmod 555 ${PREFIX}/bin/${PROG} + + @if [ -d /etc/logrotate.d ]; then \ + echo "Installing /etc/logrotate.d/sbounce"; \ + cp logrotate.conf /etc/logrotate.d/sbounce; \ + chmod 444 /etc/logrotate.d/sbounce; \ + fi + +clean: + rm -f ${PROG}.o ${PROG} logrotate.conf diff -r e319e618448f tools/sbounce/logrotate.conf.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/sbounce/logrotate.conf.in Sun Jul 14 18:14:29 2013 +0100 @@ -0,0 +1,12 @@ +LOGFILE { + size 5M + rotate 7 + missingok + notifempty + postrotate + [ -f PIDFILE ] && kill -HUP `cat PIDFILE` + endscript + compress + delaycompress + notifempty +} diff -r e319e618448f tools/sbounce/sbounce.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/sbounce/sbounce.c Sun Jul 14 18:14:29 2013 +0100 @@ -0,0 +1,932 @@ +/* sbounce: splidge's zBounce alike. + * + * Version 1.2 + * + * Copyright (C) 2003 David Mansell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that redistributions of source code + * retain the above copyright notice, this condition and the following + * disclaimer. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Various settings and constants that CAN be tweaked if you so desire.. + */ + +/* Buffer / network settings */ +#define BUFSIZE 65536 +#define MAXFDS 100 +#define LISTEN_RECVBUF 61440 +#define RECVBUF 61440 +#define SENDBUF 61440 + +/* File settings */ +#ifndef SETTINGSFILE +#define SETTINGSFILE "/opt/ircd/lib/bounce.conf" +#endif +#ifndef LOGFILE +#define LOGFILE "/opt/ircd/lib/sbounce.log" +#endif +#ifndef PIDFILE +#define PIDFILE "/opt/ircd/lib/sbounce.pid" +#endif + +#define STATSINTERVAL 300 /* How frequently to write compression stats to a file */ + +/* These two directly affect the lag induced by the bouncer.. */ +#define POLLTIMEOUT 100 /* The main poll timeout */ +#define FLUSHTIMEOUT 80000 /* How long (in uSec) between last output and forcing a flush */ + +#undef DONTDETACH /* Define this to make it not detach */ + +/* Magic constants and macros etc. - these should NOT be tweaked :) */ +#define SST_IDLE 0x00000 +#define SST_LISTEN_COMP 0x10001 +#define SST_LISTEN_DECOMP 0x10002 +#define SST_COMP 0x20001 +#define SST_DECOMP 0x20002 + +#define IsListenSocket(x) (ssa[(x)].type & 0x10000) +#define IsActiveSocket(x) (ssa[(x)].type & 0x20000) + +#define IsCompressSocket(x) (ssa[(x)].type & 0x1) +#define IsDecompressSocket(x) (ssa[(x)].type & 0x2) + +/* Function prototypes */ +void handlefd(int fd, short events); +void handlelistenfd(int fd, short events); + +int logfd; + +typedef struct ssock { + int type; + + /* poll() stuff - valid for all types */ + int pollfdpos; + short events; + + /* Address stuff - valid for listen types only */ + unsigned int listenaddr; + unsigned short listenport; + unsigned int remoteaddr; + unsigned short remoteport; + int marker; + + /* Things valid for connected sockets only */ + z_stream zs; + int companion; + unsigned char *inbuf; + unsigned char *outbuf; + unsigned char *overflow; + int overflowsize; + struct timeval lastoutput; + int dirty; + time_t lastdump; +} ssock; + +typedef struct validip { + unsigned int IPaddress; + struct validip *next; +} validip; + +ssock ssa[MAXFDS]; +struct pollfd pfd[MAXFDS]; +int cur_pfds; +validip *okhosts; +int needrehash; +char configfile[512]; + +void init() { + int i; + FILE *f; + + memset(ssa, 0, MAXFDS * sizeof(ssock)); + memset(pfd, 0, MAXFDS * sizeof(struct pollfd)); + + /* Non-zero init here */ + for(i=0;itv_sec - tv1->tv_sec; + long usecdiff = tv2->tv_usec - tv1->tv_usec; + + return (secdiff * 1000000) + usecdiff; +} + +void logwrite(char *message, ...) { + char buf[512]; + char buf2[512]; + char buf3[512]; + struct tm *tmp; + time_t now; + int len; + + va_list va; + + va_start(va,message); + vsnprintf(buf,512,message,va); + va_end(va); + + now=time(NULL); + tmp=localtime(&now); + strftime(buf2, 512, "%Y-%m-%d %H:%M",tmp); +#ifdef DONTDETACH + printf("[%s] %s",buf2,buf); +#else + len=snprintf(buf3,512,"[%s] %s",buf2,buf); + + write(logfd, buf3, len); +#endif +} + +char *IPtostr(unsigned int ip) { + static char buf1[15]; + static char buf2[15]; + char *buf; + static int count=0; + + if ((count++)%2) + buf=buf1; + else + buf=buf2; + + sprintf(buf,"%d.%d.%d.%d",ip&255,(ip>>8)&255,(ip>>16)&255,(ip>>24)&255); + + return buf; +} + +/* setpoll(): Set the specified fd to be checked for the specified events. + * If events==0, remove the fd from the array. + */ + +void setpoll(int fd, short events) { + if (events) { + if (ssa[fd].pollfdpos > -1) { + /* Already in the array.. */ + assert(pfd[ssa[fd].pollfdpos].fd == fd); + pfd[ssa[fd].pollfdpos].events=events; + ssa[fd].events=events; + return; + } else { + /* Not in the array, add to the end */ + ssa[fd].pollfdpos=cur_pfds; + pfd[cur_pfds].fd=fd; + pfd[cur_pfds].events=events; + pfd[cur_pfds].revents=0; + ssa[fd].events=events; + cur_pfds++; + + return; + } + } else { + if (ssa[fd].pollfdpos==-1) { + /* This FD wasn't in the array */ + return; + } else { + cur_pfds--; + if (ssa[fd].pollfdpos!=cur_pfds) { + /* We need to swap the entry from the end in here */ + pfd[ssa[fd].pollfdpos].fd = pfd[cur_pfds].fd; + pfd[ssa[fd].pollfdpos].events = pfd[cur_pfds].events; + pfd[ssa[fd].pollfdpos].revents = pfd[cur_pfds].revents; + + ssa[pfd[cur_pfds].fd].pollfdpos = ssa[fd].pollfdpos; + } + + ssa[fd].pollfdpos=-1; + ssa[fd].events=0; + return; + } + } +} + +/* + * dopoll(): Calls poll(), and then calls handlefd() for each fd that had + * events. + */ + +int dopoll(int timeout) { + int i,ret; + + if ((ret=poll(pfd, cur_pfds, timeout))) { + if (ret<0 && errno!=EINTR) { + logwrite("poll() error: %d\n",errno); + return 0; + } + for(i=0;i STATSINTERVAL) { + ssa[fd].lastdump=time(NULL); + logwrite("fd %d Stats: %d bytes in, %d bytes out.\n",fd,ssa[fd].zs.total_in,ssa[fd].zs.total_out); + } + } + } + + if (events & POLLOUT) { + /* We can write - need to grab output data from companion socket and write it out. + If we manage to empty the buffer, we should set the companion socket to wait + for POLLIN again -- handledata() does ALL this for us */ + + if (handledata(companionfd, 0)) { + /* Error return - close connection */ + logwrite("Connection closed (ERROR) - closing fds %d and %d.\n",fd,companionfd); + killconnection(companionfd); + killconnection(fd); + return; + } + } + + if (events & (POLLERR|POLLHUP|POLLNVAL)) { + /* Something has broken - close this socket and companion and clean everything up */ + logwrite("Connection error - closing fds %d and %d.\n",fd,ssa[fd].companion); + killconnection(ssa[fd].companion); + killconnection(fd); + } +} + +/* + * handlelistenfd(): Deals with activity on a listening FD (i.e. incoming connection) + */ + +void handlelistenfd(int fd, short events) { + int newfd, companionfd; + struct sockaddr_in sin; + unsigned int len=sizeof(struct sockaddr_in); + validip *vip; + int res; + + if (events & POLLIN) { + /* We have a connection - need to accept(), initialise the [de]compressor and set up + * a companion socket, initiating a non-blocking connect() to the remote address. */ + + newfd=accept(fd, (struct sockaddr *) &sin, &len); + + if (newfd >= MAXFDS) { + logwrite("FD %d out of range - closing connection. Recompile with larger MAXFDS\n",newfd); + close(newfd); + return; + } + + /* Check that this host is authorised to connect */ + for (vip=okhosts;vip;vip=vip->next) { + if (vip->IPaddress == sin.sin_addr.s_addr) + break; + } + + if (!vip) { + logwrite("Rejecting unauthorised connection from %d.%d.%d.%d\n",sin.sin_addr.s_addr & 255, + (sin.sin_addr.s_addr >> 8) & 255, (sin.sin_addr.s_addr >> 16) & 255, + (sin.sin_addr.s_addr >> 24) & 255); + close(newfd); + return; + } + + /* Set the type */ + ssa[newfd].type = ssa[fd].type+0x10000; + initconnection(newfd); + + /* By default we listen on the NEW socket we open only.. */ + setpoll(newfd,0); + + /* Now set up the companion socket */ + if ((companionfd=socket(AF_INET,SOCK_STREAM,0))==-1) { + logwrite("Error creating companion socket!\n"); + killconnection(newfd); + return; + } + + if (companionfd >= MAXFDS) { + logwrite("FD %d out of range - closing connection. Recompile with larger MAXFDS.\n",companionfd); + close(companionfd); + killconnection(newfd); + return; + } + + logwrite("Accepted connection from %d.%d.%d.%d. fd=%d, companion=%d\n",sin.sin_addr.s_addr & 255, + (sin.sin_addr.s_addr >> 8) & 255, (sin.sin_addr.s_addr >> 16) & 255, + (sin.sin_addr.s_addr >> 24) & 255, newfd, companionfd); + + if (ssa[newfd].type == SST_COMP) + ssa[companionfd].type = SST_DECOMP; + else + ssa[companionfd].type = SST_COMP; + + initconnection(companionfd); + + sin.sin_addr.s_addr=ssa[fd].remoteaddr; + sin.sin_port = htons(ssa[fd].remoteport); + if ((res=connect(companionfd, (struct sockaddr *) &sin, sizeof(struct sockaddr)))) { + if (errno != EINPROGRESS) { + logwrite("Error connecting companion socket: %d\n",errno); + killconnection(newfd); + killconnection(companionfd); + return; + } + } + + setpoll(companionfd, POLLIN|POLLOUT); + + /* Set the companion fields up */ + ssa[companionfd].companion = newfd; + ssa[newfd].companion = companionfd; + } + + if (events & (POLLERR|POLLHUP|POLLNVAL)) { + /* Something has broken - this shouldn't happen (famous last words) + * but clean up the listen socket */ + ssa[fd].type = SST_IDLE; + close(fd); + setpoll(fd, 0); + } +} + +/* + * openlistenfd(): Creates a listening socket + */ + +int openlistenfd(int type, unsigned int listenip, unsigned short listenport, + unsigned int remoteip, unsigned short remoteport) { + struct sockaddr_in sin; + int fd; + unsigned int opt=1; + + if ((fd=socket(AF_INET,SOCK_STREAM,0))==-1) { + logwrite("openlistenfd(): Error creating socket\n"); + return 1; + } + + logwrite("Creating %s listener on %s:%d forwarding to %s:%d\n", + type==SST_LISTEN_COMP ? "compressing" : "decompressing", IPtostr(listenip),listenport,IPtostr(remoteip),remoteport); + + if (fd>=MAXFDS) { + logwrite("openlistenfd(): fd out of range - recompile with larger MAXFDS\n"); + close(fd); + return 1; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof(opt))!=0) { + logwrite("openlistenfd(): Error setting SO_REUSEADDR\n"); + close(fd); + return 1; + } + + /* Initialiase the addresses */ + memset(&sin,0,sizeof(sin)); + sin.sin_family=AF_INET; + sin.sin_port=htons(listenport); + sin.sin_addr.s_addr=listenip; + + if (bind(fd, (struct sockaddr *) &sin, sizeof(sin))) { + logwrite("openlistenfd(): Unable to bind socket.\n"); + close(fd); + return 1; + } + + opt = LISTEN_RECVBUF; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*) &opt, sizeof(opt))) { + logwrite("openlistenfd(): Error setting socket buffer size.\n"); + } + + if (listen(fd,5)) { + logwrite("openlistenfd(): Unable to listen.\n"); + close(fd); + return 1; + } + + /* Now do the specific setup - only the following fields are relevant for listeners */ + ssa[fd].type = type; + ssa[fd].listenaddr = listenip; + ssa[fd].listenport = listenport; + ssa[fd].remoteaddr = remoteip; + ssa[fd].remoteport = remoteport; + ssa[fd].marker = 1; + + setpoll(fd, POLLIN); + + return 0; +} + +/* + * parseconfig(): Reads the config file, setting up and destroying listeners as necessary + */ + +int parseconfig(const char *filename) { + validip *vip, *nvip; + FILE *fp; + char buf[512]; + int i; + unsigned int ip1,ip2; + unsigned short port1, port2; + unsigned int type; + struct hostent *hep; + char *cp,*cp2; + int found; + + /* Check that we can open the file before we blow away the old state.. */ + if (!(fp=fopen(filename,"r"))) { + logwrite("parseconfig(): Can't open config file!\n"); + return 1; + } + + /* Clear out the list of trusted IPs */ + for (vip=okhosts;vip;vip=nvip) { + nvip=vip->next; + free(vip); + } + okhosts=NULL; + + /* Clear all markers */ + for (i=0;ih_addr) { + vip=malloc(sizeof(struct validip)); + vip->IPaddress=*(unsigned int *)hep->h_addr; + vip->next=okhosts; + okhosts=vip; + } else { + logwrite("parseconfig(): unable to parse: %s\n",buf+2); + } + } + + if (*buf=='p' || *buf=='P') { + type=(*buf=='p'?SST_LISTEN_DECOMP:SST_LISTEN_COMP); + + if (buf[1]!=':') { + logwrite("parseconfig(): malformed config line %s\n",buf); + continue; + } + + /* P:212.115.48.227:4480:212.115.48.164:4410 */ + cp2=buf+2; + for (cp=buf+2;*cp && *cp!=':';cp++); + if (!*cp) { + logwrite("parseconfig(): malformed config line %s\n",buf); + continue; + } + + *cp++='\0'; + hep=gethostbyname(cp2); + if (!hep || !(ip1=*(unsigned int *)hep->h_addr)) { + logwrite("parseconfig(): Invalid host %s\n",cp2); + continue; + } + + if (!(port1=strtol(cp, &cp, 10))) { + logwrite("parseconfig(): Invalid config line\n"); + continue; + } + + if (*cp++!=':') { + logwrite("parseconfig(): Malformed config line\n"); + continue; + } + + cp2=cp; + for (;*cp && *cp!=':';cp++); + if (!cp) { + logwrite("parseconfig(): malformed config line.\n"); + continue; + } + + *cp++='\0'; + hep=gethostbyname(cp2); + if (!hep || !(ip2=*((unsigned int *)hep->h_addr))) { + logwrite("parseconfig(): Invalid host %s\n",cp2); + continue; + } + + if (!(port2=strtol(cp, &cp, 10))) { + logwrite("parseconfig(): Invalid config line\n"); + continue; + } + + /* Check for matching listeners.. */ + found=0; + for (i=0;i P: rehashes) */ + ssa[i].type=type; + ssa[i].marker=1; + found=1; + break; + } + } + if (!found) + openlistenfd(type, ip1, port1, ip2, port2); + } + } + + fclose(fp); + + /* Kill off dead listeners */ + for (i=0;i 1 ? argv[1] : SETTINGSFILE), 511); + + init(); + sigaction(SIGHUP, &sa, NULL); + sa.sa_handler=SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); + + if (parseconfig(configfile)) { + return 1; + } + + for(;;) { + if (!dopoll(POLLTIMEOUT)) { + for (i=0;i FLUSHTIMEOUT) { + handledata(i,1); + ssa[i].dirty=0; + } + } + } + } + if (needrehash) + dorehash(); + } + + return 0; +}