]>
Commit | Line | Data |
---|---|---|
edb26b39 P |
1 | # HG changeset patch |
2 | # Parent e319e618448fe7163635b08206f48d214e59d738 | |
3 | ||
4 | diff -r e319e618448f tools/sbounce/Makefile | |
5 | --- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
6 | +++ b/tools/sbounce/Makefile Sun Jul 14 18:14:29 2013 +0100 | |
7 | @@ -0,0 +1,54 @@ | |
8 | +# | |
9 | +# Makefile for sbounce | |
10 | +# | |
11 | + | |
12 | +CC=gcc | |
13 | +CFLAGS=-O3 -Wall -pedantic | |
14 | + | |
15 | +PROG=sbounce | |
16 | +PREFIX=/opt/ircd | |
17 | +DEFINES=-DSETTINGSFILE=\"${PREFIX}/lib/bounce.conf\"\ | |
18 | + -DLOGFILE=\"${PREFIX}/lib/${PROG}.log\"\ | |
19 | + -DPIDFILE=\"${PREFIX}/lib/${PROG}.pid\" | |
20 | + | |
21 | +all: ${PROG} logrotate.conf | |
22 | + | |
23 | +${PROG}: ${PROG}.o | |
24 | + gcc -O3 -lz -o ${PROG} ${PROG}.o | |
25 | + | |
26 | +sbounce.o: | |
27 | + gcc ${DEFINES} -c ${PROG}.c | |
28 | + | |
29 | +logrotate.conf: logrotate.conf.in | |
30 | + @sed -e 's,LOGFILE,${PREFIX}/lib/${PROG}.log,g'\ | |
31 | + -e 's,PIDFILE,${PREFIX}/lib/${PROG}.pid,g' <logrotate.conf.in >logrotate.conf | |
32 | + | |
33 | +install: all | |
34 | + @if [ ! -d ${PREFIX} -a ! -f ${PREFIX} ]; then \ | |
35 | + echo "Creating directory ${PREFIX}"; \ | |
36 | + mkdir ${PREFIX}; \ | |
37 | + chmod 555 ${PREFIX}; \ | |
38 | + fi | |
39 | + | |
40 | + @if [ ! -d ${PREFIX}/bin -a ! -f ${PREFIX}/bin ]; then \ | |
41 | + echo "Creating directory ${PREFIX}/bin"; \ | |
42 | + mkdir ${PREFIX}/bin; \ | |
43 | + chmod 555 ${PREFIX}/bin; \ | |
44 | + fi | |
45 | + | |
46 | + @if [ -f ${PREFIX}/bin/${PROG} ]; then \ | |
47 | + echo "Saving existing ${PREFIX}/bin/${PROG} as .old"; \ | |
48 | + mv ${PREFIX}/bin/${PROG} ${PREFIX}/bin/${PROG}.old; \ | |
49 | + fi | |
50 | + | |
51 | + cp ${PROG} ${PREFIX}/bin | |
52 | + chmod 555 ${PREFIX}/bin/${PROG} | |
53 | + | |
54 | + @if [ -d /etc/logrotate.d ]; then \ | |
55 | + echo "Installing /etc/logrotate.d/sbounce"; \ | |
56 | + cp logrotate.conf /etc/logrotate.d/sbounce; \ | |
57 | + chmod 444 /etc/logrotate.d/sbounce; \ | |
58 | + fi | |
59 | + | |
60 | +clean: | |
61 | + rm -f ${PROG}.o ${PROG} logrotate.conf | |
62 | diff -r e319e618448f tools/sbounce/logrotate.conf.in | |
63 | --- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
64 | +++ b/tools/sbounce/logrotate.conf.in Sun Jul 14 18:14:29 2013 +0100 | |
65 | @@ -0,0 +1,12 @@ | |
66 | +LOGFILE { | |
67 | + size 5M | |
68 | + rotate 7 | |
69 | + missingok | |
70 | + notifempty | |
71 | + postrotate | |
72 | + [ -f PIDFILE ] && kill -HUP `cat PIDFILE` | |
73 | + endscript | |
74 | + compress | |
75 | + delaycompress | |
76 | + notifempty | |
77 | +} | |
78 | diff -r e319e618448f tools/sbounce/sbounce.c | |
79 | --- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
80 | +++ b/tools/sbounce/sbounce.c Sun Jul 14 18:14:29 2013 +0100 | |
81 | @@ -0,0 +1,932 @@ | |
82 | +/* sbounce: splidge's zBounce alike. | |
83 | + * | |
84 | + * Version 1.2 | |
85 | + * | |
86 | + * Copyright (C) 2003 David Mansell. | |
87 | + * | |
88 | + * Redistribution and use in source and binary forms, with or without | |
89 | + * modification, are permitted provided that redistributions of source code | |
90 | + * retain the above copyright notice, this condition and the following | |
91 | + * disclaimer. | |
92 | + * | |
93 | + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | |
94 | + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
95 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
96 | + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
97 | + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
98 | + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
99 | + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
100 | + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
101 | + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
102 | + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
103 | + */ | |
104 | + | |
105 | +#include <zlib.h> | |
106 | +#include <assert.h> | |
107 | +#include <errno.h> | |
108 | +#include <unistd.h> | |
109 | +#include <stdio.h> | |
110 | +#include <stdlib.h> | |
111 | +#include <netdb.h> | |
112 | +#include <signal.h> | |
113 | +#include <stdarg.h> | |
114 | +#include <fcntl.h> | |
115 | +#include <time.h> | |
116 | +#include <string.h> | |
117 | +#include <sys/types.h> | |
118 | +#include <sys/socket.h> | |
119 | +#include <sys/poll.h> | |
120 | +#include <sys/ioctl.h> | |
121 | +#include <sys/types.h> | |
122 | +#include <sys/stat.h> | |
123 | +#include <sys/time.h> | |
124 | +#include <netinet/in.h> | |
125 | + | |
126 | +/* | |
127 | + * Various settings and constants that CAN be tweaked if you so desire.. | |
128 | + */ | |
129 | + | |
130 | +/* Buffer / network settings */ | |
131 | +#define BUFSIZE 65536 | |
132 | +#define MAXFDS 100 | |
133 | +#define LISTEN_RECVBUF 61440 | |
134 | +#define RECVBUF 61440 | |
135 | +#define SENDBUF 61440 | |
136 | + | |
137 | +/* File settings */ | |
138 | +#ifndef SETTINGSFILE | |
139 | +#define SETTINGSFILE "/opt/ircd/lib/bounce.conf" | |
140 | +#endif | |
141 | +#ifndef LOGFILE | |
142 | +#define LOGFILE "/opt/ircd/lib/sbounce.log" | |
143 | +#endif | |
144 | +#ifndef PIDFILE | |
145 | +#define PIDFILE "/opt/ircd/lib/sbounce.pid" | |
146 | +#endif | |
147 | + | |
148 | +#define STATSINTERVAL 300 /* How frequently to write compression stats to a file */ | |
149 | + | |
150 | +/* These two directly affect the lag induced by the bouncer.. */ | |
151 | +#define POLLTIMEOUT 100 /* The main poll timeout */ | |
152 | +#define FLUSHTIMEOUT 80000 /* How long (in uSec) between last output and forcing a flush */ | |
153 | + | |
154 | +#undef DONTDETACH /* Define this to make it not detach */ | |
155 | + | |
156 | +/* Magic constants and macros etc. - these should NOT be tweaked :) */ | |
157 | +#define SST_IDLE 0x00000 | |
158 | +#define SST_LISTEN_COMP 0x10001 | |
159 | +#define SST_LISTEN_DECOMP 0x10002 | |
160 | +#define SST_COMP 0x20001 | |
161 | +#define SST_DECOMP 0x20002 | |
162 | + | |
163 | +#define IsListenSocket(x) (ssa[(x)].type & 0x10000) | |
164 | +#define IsActiveSocket(x) (ssa[(x)].type & 0x20000) | |
165 | + | |
166 | +#define IsCompressSocket(x) (ssa[(x)].type & 0x1) | |
167 | +#define IsDecompressSocket(x) (ssa[(x)].type & 0x2) | |
168 | + | |
169 | +/* Function prototypes */ | |
170 | +void handlefd(int fd, short events); | |
171 | +void handlelistenfd(int fd, short events); | |
172 | + | |
173 | +int logfd; | |
174 | + | |
175 | +typedef struct ssock { | |
176 | + int type; | |
177 | + | |
178 | + /* poll() stuff - valid for all types */ | |
179 | + int pollfdpos; | |
180 | + short events; | |
181 | + | |
182 | + /* Address stuff - valid for listen types only */ | |
183 | + unsigned int listenaddr; | |
184 | + unsigned short listenport; | |
185 | + unsigned int remoteaddr; | |
186 | + unsigned short remoteport; | |
187 | + int marker; | |
188 | + | |
189 | + /* Things valid for connected sockets only */ | |
190 | + z_stream zs; | |
191 | + int companion; | |
192 | + unsigned char *inbuf; | |
193 | + unsigned char *outbuf; | |
194 | + unsigned char *overflow; | |
195 | + int overflowsize; | |
196 | + struct timeval lastoutput; | |
197 | + int dirty; | |
198 | + time_t lastdump; | |
199 | +} ssock; | |
200 | + | |
201 | +typedef struct validip { | |
202 | + unsigned int IPaddress; | |
203 | + struct validip *next; | |
204 | +} validip; | |
205 | + | |
206 | +ssock ssa[MAXFDS]; | |
207 | +struct pollfd pfd[MAXFDS]; | |
208 | +int cur_pfds; | |
209 | +validip *okhosts; | |
210 | +int needrehash; | |
211 | +char configfile[512]; | |
212 | + | |
213 | +void init() { | |
214 | + int i; | |
215 | + FILE *f; | |
216 | + | |
217 | + memset(ssa, 0, MAXFDS * sizeof(ssock)); | |
218 | + memset(pfd, 0, MAXFDS * sizeof(struct pollfd)); | |
219 | + | |
220 | + /* Non-zero init here */ | |
221 | + for(i=0;i<MAXFDS;i++) { | |
222 | + ssa[i].pollfdpos = -1; | |
223 | + ssa[i].companion = -1; | |
224 | + } | |
225 | + | |
226 | + cur_pfds=0; | |
227 | + okhosts=NULL; | |
228 | + needrehash=0; | |
229 | + | |
230 | +#ifndef DONTDETACH | |
231 | + if ((logfd=open(LOGFILE, O_CREAT | O_WRONLY | O_APPEND,00660)) < 0) { | |
232 | + printf("ERROR: can't open logfile.\n"); | |
233 | + exit(1); | |
234 | + } | |
235 | + | |
236 | + if (fork()) | |
237 | + exit(0); | |
238 | + | |
239 | + if (fork()) | |
240 | + exit(0); | |
241 | +#endif | |
242 | + | |
243 | + f = fopen(PIDFILE, "w"); | |
244 | + if (f == NULL) { | |
245 | + fprintf(stderr, "Couldn't create pid file \"%s\": %s", | |
246 | + PIDFILE, strerror(errno)); | |
247 | + } else { | |
248 | + fprintf(f, "%ld\n", (long) getpid()); | |
249 | + fclose(f); | |
250 | + } | |
251 | +} | |
252 | + | |
253 | +long usecdiff(struct timeval *tv1, struct timeval *tv2) { | |
254 | + long secdiff = tv2->tv_sec - tv1->tv_sec; | |
255 | + long usecdiff = tv2->tv_usec - tv1->tv_usec; | |
256 | + | |
257 | + return (secdiff * 1000000) + usecdiff; | |
258 | +} | |
259 | + | |
260 | +void logwrite(char *message, ...) { | |
261 | + char buf[512]; | |
262 | + char buf2[512]; | |
263 | + char buf3[512]; | |
264 | + struct tm *tmp; | |
265 | + time_t now; | |
266 | + int len; | |
267 | + | |
268 | + va_list va; | |
269 | + | |
270 | + va_start(va,message); | |
271 | + vsnprintf(buf,512,message,va); | |
272 | + va_end(va); | |
273 | + | |
274 | + now=time(NULL); | |
275 | + tmp=localtime(&now); | |
276 | + strftime(buf2, 512, "%Y-%m-%d %H:%M",tmp); | |
277 | +#ifdef DONTDETACH | |
278 | + printf("[%s] %s",buf2,buf); | |
279 | +#else | |
280 | + len=snprintf(buf3,512,"[%s] %s",buf2,buf); | |
281 | + | |
282 | + write(logfd, buf3, len); | |
283 | +#endif | |
284 | +} | |
285 | + | |
286 | +char *IPtostr(unsigned int ip) { | |
287 | + static char buf1[15]; | |
288 | + static char buf2[15]; | |
289 | + char *buf; | |
290 | + static int count=0; | |
291 | + | |
292 | + if ((count++)%2) | |
293 | + buf=buf1; | |
294 | + else | |
295 | + buf=buf2; | |
296 | + | |
297 | + sprintf(buf,"%d.%d.%d.%d",ip&255,(ip>>8)&255,(ip>>16)&255,(ip>>24)&255); | |
298 | + | |
299 | + return buf; | |
300 | +} | |
301 | + | |
302 | +/* setpoll(): Set the specified fd to be checked for the specified events. | |
303 | + * If events==0, remove the fd from the array. | |
304 | + */ | |
305 | + | |
306 | +void setpoll(int fd, short events) { | |
307 | + if (events) { | |
308 | + if (ssa[fd].pollfdpos > -1) { | |
309 | + /* Already in the array.. */ | |
310 | + assert(pfd[ssa[fd].pollfdpos].fd == fd); | |
311 | + pfd[ssa[fd].pollfdpos].events=events; | |
312 | + ssa[fd].events=events; | |
313 | + return; | |
314 | + } else { | |
315 | + /* Not in the array, add to the end */ | |
316 | + ssa[fd].pollfdpos=cur_pfds; | |
317 | + pfd[cur_pfds].fd=fd; | |
318 | + pfd[cur_pfds].events=events; | |
319 | + pfd[cur_pfds].revents=0; | |
320 | + ssa[fd].events=events; | |
321 | + cur_pfds++; | |
322 | + | |
323 | + return; | |
324 | + } | |
325 | + } else { | |
326 | + if (ssa[fd].pollfdpos==-1) { | |
327 | + /* This FD wasn't in the array */ | |
328 | + return; | |
329 | + } else { | |
330 | + cur_pfds--; | |
331 | + if (ssa[fd].pollfdpos!=cur_pfds) { | |
332 | + /* We need to swap the entry from the end in here */ | |
333 | + pfd[ssa[fd].pollfdpos].fd = pfd[cur_pfds].fd; | |
334 | + pfd[ssa[fd].pollfdpos].events = pfd[cur_pfds].events; | |
335 | + pfd[ssa[fd].pollfdpos].revents = pfd[cur_pfds].revents; | |
336 | + | |
337 | + ssa[pfd[cur_pfds].fd].pollfdpos = ssa[fd].pollfdpos; | |
338 | + } | |
339 | + | |
340 | + ssa[fd].pollfdpos=-1; | |
341 | + ssa[fd].events=0; | |
342 | + return; | |
343 | + } | |
344 | + } | |
345 | +} | |
346 | + | |
347 | +/* | |
348 | + * dopoll(): Calls poll(), and then calls handlefd() for each fd that had | |
349 | + * events. | |
350 | + */ | |
351 | + | |
352 | +int dopoll(int timeout) { | |
353 | + int i,ret; | |
354 | + | |
355 | + if ((ret=poll(pfd, cur_pfds, timeout))) { | |
356 | + if (ret<0 && errno!=EINTR) { | |
357 | + logwrite("poll() error: %d\n",errno); | |
358 | + return 0; | |
359 | + } | |
360 | + for(i=0;i<cur_pfds;i++) { | |
361 | + if (pfd[i].revents) { | |
362 | + handlefd(pfd[i].fd, pfd[i].revents); | |
363 | + } | |
364 | + } | |
365 | + return 1; | |
366 | + } else { | |
367 | + return 0; | |
368 | + } | |
369 | +} | |
370 | + | |
371 | +/* | |
372 | + * initconnection(): Set up to compressor/decompressor for the specified socket. | |
373 | + * Allocate buffers and set socket options. | |
374 | + */ | |
375 | + | |
376 | +void initconnection(int fd) { | |
377 | + unsigned int rbuf=RECVBUF, sbuf=SENDBUF, opt=1; | |
378 | + | |
379 | + ssa[fd].inbuf =(unsigned char *)malloc(BUFSIZE); | |
380 | + ssa[fd].outbuf=(unsigned char *)malloc(BUFSIZE); | |
381 | + memset(&(ssa[fd].zs), 0, sizeof(z_stream)); | |
382 | + | |
383 | + if (IsCompressSocket(fd)) { | |
384 | + if (deflateInit(&(ssa[fd].zs), 6) != Z_OK) { | |
385 | + logwrite("Error initialising deflate!\n"); | |
386 | + } | |
387 | + } else { | |
388 | + if (inflateInit(&(ssa[fd].zs)) != Z_OK) { | |
389 | + logwrite("Error initialising inflate!\n"); | |
390 | + } | |
391 | + } | |
392 | + | |
393 | + ssa[fd].zs.next_in=ssa[fd].inbuf; | |
394 | + ssa[fd].zs.avail_in=0; | |
395 | + ssa[fd].zs.next_out=ssa[fd].outbuf; | |
396 | + ssa[fd].zs.avail_out=0; | |
397 | + | |
398 | + ssa[fd].dirty=0; | |
399 | + ssa[fd].lastdump=time(NULL); | |
400 | + | |
401 | + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*) &rbuf, sizeof(rbuf)) || | |
402 | + setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*) &sbuf, sizeof(sbuf))) { | |
403 | + logwrite("Error setting socket buffers on fd %d\n",fd); | |
404 | + } | |
405 | + | |
406 | + if (ioctl(fd, FIONBIO, &opt) == -1) { | |
407 | + logwrite("Error setting socket non-blocking on fd %d\n",fd); | |
408 | + } | |
409 | + | |
410 | + ssa[fd].overflowsize=0; | |
411 | +} | |
412 | + | |
413 | +/* | |
414 | + * killconnection(): Kills off the compressor/decompressor and frees all buffers | |
415 | + */ | |
416 | + | |
417 | +void killconnection(int fd) { | |
418 | + close(fd); | |
419 | + setpoll(fd,0); | |
420 | + free(ssa[fd].inbuf); | |
421 | + free(ssa[fd].outbuf); | |
422 | + if (IsCompressSocket(fd)) { | |
423 | + deflateEnd(&(ssa[fd].zs)); | |
424 | + } else { | |
425 | + inflateEnd(&(ssa[fd].zs)); | |
426 | + } | |
427 | + ssa[fd].type=SST_IDLE; | |
428 | +} | |
429 | + | |
430 | +/* | |
431 | + * flushdata(): Flushes all data from the specified fd. | |
432 | + * Returns 0 if all data was written. | |
433 | + * Returns 1 if there is some data left. | |
434 | + * Returns -1 if there was a socket error. | |
435 | + */ | |
436 | + | |
437 | +int flushdata(int fd) { | |
438 | + int genbytes,bytesout; | |
439 | + int companionfd=ssa[fd].companion; | |
440 | + | |
441 | + if ((genbytes=ssa[fd].overflowsize)==0) | |
442 | + return 0; /* nothing there */ | |
443 | + | |
444 | + bytesout=write(companionfd, ssa[fd].overflow, genbytes); | |
445 | + | |
446 | + if (bytesout!=genbytes) { | |
447 | + /* It didn't all get written */ | |
448 | + if (bytesout==-1) { | |
449 | + if (errno==EAGAIN) { | |
450 | + return 1; | |
451 | + } else { | |
452 | + logwrite("Error writing to fd %d: %d\n",companionfd,errno); | |
453 | + return -1; | |
454 | + } | |
455 | + } | |
456 | + | |
457 | + /* Short write - I'm ASSUMING this is cos the socket buffer filled up */ | |
458 | + ssa[fd].overflowsize-=bytesout; | |
459 | + ssa[fd].overflow+=bytesout; | |
460 | + return 1; | |
461 | + } | |
462 | + | |
463 | + /* Clear the overflow and return */ | |
464 | + ssa[fd].overflowsize=0; | |
465 | + | |
466 | + return 0; | |
467 | +} | |
468 | + | |
469 | +/* | |
470 | + * handledata(): Handles data outstanding on the specified fd. | |
471 | + * Returns 0 if everything went OK | |
472 | + * Returns 1 if there was an error | |
473 | + * | |
474 | + * This function will manipulate the poll() array as necessary if sockets become blocked. | |
475 | + */ | |
476 | + | |
477 | +int handledata(int fd, int forceflush) { | |
478 | + int ret; | |
479 | + int companionfd = ssa[fd].companion; | |
480 | + struct timezone tz; | |
481 | + | |
482 | + /* Check we don't have outstanding data to write.. */ | |
483 | + if ((ret=flushdata(fd))) { | |
484 | + if (ret==1) { | |
485 | + return 0; | |
486 | + } else { | |
487 | + return 1; | |
488 | + } | |
489 | + } | |
490 | + | |
491 | + if (ssa[fd].dirty == 0) { | |
492 | + gettimeofday(&(ssa[fd].lastoutput), &tz); | |
493 | + } | |
494 | + | |
495 | + if (ssa[fd].zs.avail_in || forceflush) { | |
496 | + for(;;) { | |
497 | + ssa[fd].zs.next_out=ssa[fd].outbuf; | |
498 | + ssa[fd].zs.avail_out=BUFSIZE; | |
499 | + | |
500 | + if (IsCompressSocket(fd)) { | |
501 | + ret=deflate(&(ssa[fd].zs), forceflush ? Z_SYNC_FLUSH : 0); | |
502 | + } else { | |
503 | + ret=inflate(&(ssa[fd].zs), Z_SYNC_FLUSH); | |
504 | + } | |
505 | + | |
506 | + if (ret != Z_OK && ret != Z_BUF_ERROR) { | |
507 | + logwrite("Compression error %d on fd %d.\n",ret,fd); | |
508 | + return 1; | |
509 | + } | |
510 | + | |
511 | + /* Mark all generated data as overflow */ | |
512 | + ssa[fd].overflowsize = (BUFSIZE - ssa[fd].zs.avail_out); | |
513 | + ssa[fd].overflow = ssa[fd].outbuf; | |
514 | + | |
515 | + /* If it actually produced anything, make a note.. */ | |
516 | + if (ssa[fd].overflowsize) { | |
517 | + gettimeofday(&(ssa[fd].lastoutput), &tz); | |
518 | + | |
519 | + /* And flush */ | |
520 | + if ((ret=flushdata(fd))) { | |
521 | + if (ret==1) { | |
522 | + /* It's full - swap the poll() stuff around */ | |
523 | + setpoll(fd, ssa[fd].events & ~POLLIN); | |
524 | + setpoll(companionfd, ssa[companionfd].events | POLLOUT); | |
525 | + ssa[fd].dirty=0; | |
526 | + return 0; | |
527 | + } else { | |
528 | + /* It broke, return error */ | |
529 | + return 1; | |
530 | + } | |
531 | + } | |
532 | + } | |
533 | + | |
534 | + if (ssa[fd].zs.avail_in == 0) | |
535 | + break; | |
536 | + } | |
537 | + } | |
538 | + | |
539 | + /* OK, we dealt with everything */ | |
540 | + if (!(ssa[fd].events & POLLIN)) { | |
541 | + /* We need to swap poll() things back */ | |
542 | + setpoll(fd, ssa[fd].events | POLLIN); | |
543 | + setpoll(companionfd, ssa[companionfd].events & ~POLLOUT); | |
544 | + ssa[fd].dirty=1; | |
545 | + } | |
546 | + | |
547 | + return 0; | |
548 | +} | |
549 | + | |
550 | +/* | |
551 | + * handlefd(): Deals with events occuring on the specified fd. | |
552 | + */ | |
553 | + | |
554 | +void handlefd(int fd, short events) { | |
555 | + /* Palm off listen sockets elsewhere */ | |
556 | + int bytes; | |
557 | + int companionfd; | |
558 | + | |
559 | + companionfd=ssa[fd].companion; | |
560 | + | |
561 | + if (IsListenSocket(fd)) { | |
562 | + handlelistenfd(fd, events); | |
563 | + return; | |
564 | + } | |
565 | + | |
566 | + if (events & POLLIN) { | |
567 | + /* Input data to be handled by the [de]compressor - read all data, compress | |
568 | + and send to companion socket. If the companion blocks, we make the companion | |
569 | + poll for POLLOUT and stop this socket polling for POLLIN. */ | |
570 | + | |
571 | + /* It's an INVARIANT that if we were polling for POLLIN there is nothing in the input OR output buffers | |
572 | + * from this socket. */ | |
573 | + | |
574 | + if (ssa[fd].overflowsize || ssa[fd].zs.avail_in || !(ssa[fd].events & POLLIN)) { | |
575 | + logwrite("Unexpected input data on fd %d. - overflowsize=%d avail_in=%d ev=%d pfdev=%d rev=%d\n",fd,ssa[fd].overflowsize, | |
576 | + ssa[fd].zs.avail_in, pfd[ssa[fd].pollfdpos].events, ssa[fd].events, events); | |
577 | + } else { | |
578 | + if ((bytes=read(fd, ssa[fd].inbuf, BUFSIZE))<=0) { | |
579 | + if (bytes==0) { | |
580 | + /* EOF */ | |
581 | + logwrite("Connection closed (EOF) - closing fds %d and %d.\n",fd,companionfd); | |
582 | + killconnection(companionfd); | |
583 | + killconnection(fd); | |
584 | + return; | |
585 | + } else if (errno==EAGAIN) { | |
586 | + /* Just EAGAIN.. return and come back later */ | |
587 | + return; | |
588 | + } else { | |
589 | + logwrite("Connection close (Read error - %s) - closing fds %d and %d.\n",strerror(errno),fd,companionfd); | |
590 | + killconnection(companionfd); | |
591 | + killconnection(fd); | |
592 | + return; | |
593 | + } | |
594 | + } | |
595 | + | |
596 | + ssa[fd].zs.next_in=ssa[fd].inbuf; | |
597 | + ssa[fd].zs.avail_in=bytes; | |
598 | + | |
599 | + if (handledata(fd, 0)) { | |
600 | + /* Error return - close connection */ | |
601 | + logwrite("Connection closed (ERROR) - closing fds %d and %d.\n",fd,companionfd); | |
602 | + killconnection(companionfd); | |
603 | + killconnection(fd); | |
604 | + return; | |
605 | + } | |
606 | + ssa[fd].dirty=1; | |
607 | + if (time(NULL) - ssa[fd].lastdump > STATSINTERVAL) { | |
608 | + ssa[fd].lastdump=time(NULL); | |
609 | + logwrite("fd %d Stats: %d bytes in, %d bytes out.\n",fd,ssa[fd].zs.total_in,ssa[fd].zs.total_out); | |
610 | + } | |
611 | + } | |
612 | + } | |
613 | + | |
614 | + if (events & POLLOUT) { | |
615 | + /* We can write - need to grab output data from companion socket and write it out. | |
616 | + If we manage to empty the buffer, we should set the companion socket to wait | |
617 | + for POLLIN again -- handledata() does ALL this for us */ | |
618 | + | |
619 | + if (handledata(companionfd, 0)) { | |
620 | + /* Error return - close connection */ | |
621 | + logwrite("Connection closed (ERROR) - closing fds %d and %d.\n",fd,companionfd); | |
622 | + killconnection(companionfd); | |
623 | + killconnection(fd); | |
624 | + return; | |
625 | + } | |
626 | + } | |
627 | + | |
628 | + if (events & (POLLERR|POLLHUP|POLLNVAL)) { | |
629 | + /* Something has broken - close this socket and companion and clean everything up */ | |
630 | + logwrite("Connection error - closing fds %d and %d.\n",fd,ssa[fd].companion); | |
631 | + killconnection(ssa[fd].companion); | |
632 | + killconnection(fd); | |
633 | + } | |
634 | +} | |
635 | + | |
636 | +/* | |
637 | + * handlelistenfd(): Deals with activity on a listening FD (i.e. incoming connection) | |
638 | + */ | |
639 | + | |
640 | +void handlelistenfd(int fd, short events) { | |
641 | + int newfd, companionfd; | |
642 | + struct sockaddr_in sin; | |
643 | + unsigned int len=sizeof(struct sockaddr_in); | |
644 | + validip *vip; | |
645 | + int res; | |
646 | + | |
647 | + if (events & POLLIN) { | |
648 | + /* We have a connection - need to accept(), initialise the [de]compressor and set up | |
649 | + * a companion socket, initiating a non-blocking connect() to the remote address. */ | |
650 | + | |
651 | + newfd=accept(fd, (struct sockaddr *) &sin, &len); | |
652 | + | |
653 | + if (newfd >= MAXFDS) { | |
654 | + logwrite("FD %d out of range - closing connection. Recompile with larger MAXFDS\n",newfd); | |
655 | + close(newfd); | |
656 | + return; | |
657 | + } | |
658 | + | |
659 | + /* Check that this host is authorised to connect */ | |
660 | + for (vip=okhosts;vip;vip=vip->next) { | |
661 | + if (vip->IPaddress == sin.sin_addr.s_addr) | |
662 | + break; | |
663 | + } | |
664 | + | |
665 | + if (!vip) { | |
666 | + logwrite("Rejecting unauthorised connection from %d.%d.%d.%d\n",sin.sin_addr.s_addr & 255, | |
667 | + (sin.sin_addr.s_addr >> 8) & 255, (sin.sin_addr.s_addr >> 16) & 255, | |
668 | + (sin.sin_addr.s_addr >> 24) & 255); | |
669 | + close(newfd); | |
670 | + return; | |
671 | + } | |
672 | + | |
673 | + /* Set the type */ | |
674 | + ssa[newfd].type = ssa[fd].type+0x10000; | |
675 | + initconnection(newfd); | |
676 | + | |
677 | + /* By default we listen on the NEW socket we open only.. */ | |
678 | + setpoll(newfd,0); | |
679 | + | |
680 | + /* Now set up the companion socket */ | |
681 | + if ((companionfd=socket(AF_INET,SOCK_STREAM,0))==-1) { | |
682 | + logwrite("Error creating companion socket!\n"); | |
683 | + killconnection(newfd); | |
684 | + return; | |
685 | + } | |
686 | + | |
687 | + if (companionfd >= MAXFDS) { | |
688 | + logwrite("FD %d out of range - closing connection. Recompile with larger MAXFDS.\n",companionfd); | |
689 | + close(companionfd); | |
690 | + killconnection(newfd); | |
691 | + return; | |
692 | + } | |
693 | + | |
694 | + logwrite("Accepted connection from %d.%d.%d.%d. fd=%d, companion=%d\n",sin.sin_addr.s_addr & 255, | |
695 | + (sin.sin_addr.s_addr >> 8) & 255, (sin.sin_addr.s_addr >> 16) & 255, | |
696 | + (sin.sin_addr.s_addr >> 24) & 255, newfd, companionfd); | |
697 | + | |
698 | + if (ssa[newfd].type == SST_COMP) | |
699 | + ssa[companionfd].type = SST_DECOMP; | |
700 | + else | |
701 | + ssa[companionfd].type = SST_COMP; | |
702 | + | |
703 | + initconnection(companionfd); | |
704 | + | |
705 | + sin.sin_addr.s_addr=ssa[fd].remoteaddr; | |
706 | + sin.sin_port = htons(ssa[fd].remoteport); | |
707 | + if ((res=connect(companionfd, (struct sockaddr *) &sin, sizeof(struct sockaddr)))) { | |
708 | + if (errno != EINPROGRESS) { | |
709 | + logwrite("Error connecting companion socket: %d\n",errno); | |
710 | + killconnection(newfd); | |
711 | + killconnection(companionfd); | |
712 | + return; | |
713 | + } | |
714 | + } | |
715 | + | |
716 | + setpoll(companionfd, POLLIN|POLLOUT); | |
717 | + | |
718 | + /* Set the companion fields up */ | |
719 | + ssa[companionfd].companion = newfd; | |
720 | + ssa[newfd].companion = companionfd; | |
721 | + } | |
722 | + | |
723 | + if (events & (POLLERR|POLLHUP|POLLNVAL)) { | |
724 | + /* Something has broken - this shouldn't happen (famous last words) | |
725 | + * but clean up the listen socket */ | |
726 | + ssa[fd].type = SST_IDLE; | |
727 | + close(fd); | |
728 | + setpoll(fd, 0); | |
729 | + } | |
730 | +} | |
731 | + | |
732 | +/* | |
733 | + * openlistenfd(): Creates a listening socket | |
734 | + */ | |
735 | + | |
736 | +int openlistenfd(int type, unsigned int listenip, unsigned short listenport, | |
737 | + unsigned int remoteip, unsigned short remoteport) { | |
738 | + struct sockaddr_in sin; | |
739 | + int fd; | |
740 | + unsigned int opt=1; | |
741 | + | |
742 | + if ((fd=socket(AF_INET,SOCK_STREAM,0))==-1) { | |
743 | + logwrite("openlistenfd(): Error creating socket\n"); | |
744 | + return 1; | |
745 | + } | |
746 | + | |
747 | + logwrite("Creating %s listener on %s:%d forwarding to %s:%d\n", | |
748 | + type==SST_LISTEN_COMP ? "compressing" : "decompressing", IPtostr(listenip),listenport,IPtostr(remoteip),remoteport); | |
749 | + | |
750 | + if (fd>=MAXFDS) { | |
751 | + logwrite("openlistenfd(): fd out of range - recompile with larger MAXFDS\n"); | |
752 | + close(fd); | |
753 | + return 1; | |
754 | + } | |
755 | + | |
756 | + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof(opt))!=0) { | |
757 | + logwrite("openlistenfd(): Error setting SO_REUSEADDR\n"); | |
758 | + close(fd); | |
759 | + return 1; | |
760 | + } | |
761 | + | |
762 | + /* Initialiase the addresses */ | |
763 | + memset(&sin,0,sizeof(sin)); | |
764 | + sin.sin_family=AF_INET; | |
765 | + sin.sin_port=htons(listenport); | |
766 | + sin.sin_addr.s_addr=listenip; | |
767 | + | |
768 | + if (bind(fd, (struct sockaddr *) &sin, sizeof(sin))) { | |
769 | + logwrite("openlistenfd(): Unable to bind socket.\n"); | |
770 | + close(fd); | |
771 | + return 1; | |
772 | + } | |
773 | + | |
774 | + opt = LISTEN_RECVBUF; | |
775 | + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*) &opt, sizeof(opt))) { | |
776 | + logwrite("openlistenfd(): Error setting socket buffer size.\n"); | |
777 | + } | |
778 | + | |
779 | + if (listen(fd,5)) { | |
780 | + logwrite("openlistenfd(): Unable to listen.\n"); | |
781 | + close(fd); | |
782 | + return 1; | |
783 | + } | |
784 | + | |
785 | + /* Now do the specific setup - only the following fields are relevant for listeners */ | |
786 | + ssa[fd].type = type; | |
787 | + ssa[fd].listenaddr = listenip; | |
788 | + ssa[fd].listenport = listenport; | |
789 | + ssa[fd].remoteaddr = remoteip; | |
790 | + ssa[fd].remoteport = remoteport; | |
791 | + ssa[fd].marker = 1; | |
792 | + | |
793 | + setpoll(fd, POLLIN); | |
794 | + | |
795 | + return 0; | |
796 | +} | |
797 | + | |
798 | +/* | |
799 | + * parseconfig(): Reads the config file, setting up and destroying listeners as necessary | |
800 | + */ | |
801 | + | |
802 | +int parseconfig(const char *filename) { | |
803 | + validip *vip, *nvip; | |
804 | + FILE *fp; | |
805 | + char buf[512]; | |
806 | + int i; | |
807 | + unsigned int ip1,ip2; | |
808 | + unsigned short port1, port2; | |
809 | + unsigned int type; | |
810 | + struct hostent *hep; | |
811 | + char *cp,*cp2; | |
812 | + int found; | |
813 | + | |
814 | + /* Check that we can open the file before we blow away the old state.. */ | |
815 | + if (!(fp=fopen(filename,"r"))) { | |
816 | + logwrite("parseconfig(): Can't open config file!\n"); | |
817 | + return 1; | |
818 | + } | |
819 | + | |
820 | + /* Clear out the list of trusted IPs */ | |
821 | + for (vip=okhosts;vip;vip=nvip) { | |
822 | + nvip=vip->next; | |
823 | + free(vip); | |
824 | + } | |
825 | + okhosts=NULL; | |
826 | + | |
827 | + /* Clear all markers */ | |
828 | + for (i=0;i<MAXFDS;i++) { | |
829 | + ssa[i].marker=0; | |
830 | + } | |
831 | + | |
832 | + while(!feof(fp)) { | |
833 | + fgets(buf, 512, fp); | |
834 | + | |
835 | + if (feof(fp)) | |
836 | + break; | |
837 | + | |
838 | + /* Check for valid config types.. */ | |
839 | + if (*buf=='A') { | |
840 | + if (buf[1]!=':') { | |
841 | + logwrite("parseconfig(): malformed config line %s\n",buf); | |
842 | + continue; | |
843 | + } | |
844 | + | |
845 | + for (cp=buf+2;*cp;cp++) { | |
846 | + if (*cp=='\n') { | |
847 | + *cp='\0'; | |
848 | + break; | |
849 | + } | |
850 | + } | |
851 | + | |
852 | + hep=gethostbyname(buf+2); | |
853 | + if (hep && hep->h_addr) { | |
854 | + vip=malloc(sizeof(struct validip)); | |
855 | + vip->IPaddress=*(unsigned int *)hep->h_addr; | |
856 | + vip->next=okhosts; | |
857 | + okhosts=vip; | |
858 | + } else { | |
859 | + logwrite("parseconfig(): unable to parse: %s\n",buf+2); | |
860 | + } | |
861 | + } | |
862 | + | |
863 | + if (*buf=='p' || *buf=='P') { | |
864 | + type=(*buf=='p'?SST_LISTEN_DECOMP:SST_LISTEN_COMP); | |
865 | + | |
866 | + if (buf[1]!=':') { | |
867 | + logwrite("parseconfig(): malformed config line %s\n",buf); | |
868 | + continue; | |
869 | + } | |
870 | + | |
871 | + /* P:212.115.48.227:4480:212.115.48.164:4410 */ | |
872 | + cp2=buf+2; | |
873 | + for (cp=buf+2;*cp && *cp!=':';cp++); | |
874 | + if (!*cp) { | |
875 | + logwrite("parseconfig(): malformed config line %s\n",buf); | |
876 | + continue; | |
877 | + } | |
878 | + | |
879 | + *cp++='\0'; | |
880 | + hep=gethostbyname(cp2); | |
881 | + if (!hep || !(ip1=*(unsigned int *)hep->h_addr)) { | |
882 | + logwrite("parseconfig(): Invalid host %s\n",cp2); | |
883 | + continue; | |
884 | + } | |
885 | + | |
886 | + if (!(port1=strtol(cp, &cp, 10))) { | |
887 | + logwrite("parseconfig(): Invalid config line\n"); | |
888 | + continue; | |
889 | + } | |
890 | + | |
891 | + if (*cp++!=':') { | |
892 | + logwrite("parseconfig(): Malformed config line\n"); | |
893 | + continue; | |
894 | + } | |
895 | + | |
896 | + cp2=cp; | |
897 | + for (;*cp && *cp!=':';cp++); | |
898 | + if (!cp) { | |
899 | + logwrite("parseconfig(): malformed config line.\n"); | |
900 | + continue; | |
901 | + } | |
902 | + | |
903 | + *cp++='\0'; | |
904 | + hep=gethostbyname(cp2); | |
905 | + if (!hep || !(ip2=*((unsigned int *)hep->h_addr))) { | |
906 | + logwrite("parseconfig(): Invalid host %s\n",cp2); | |
907 | + continue; | |
908 | + } | |
909 | + | |
910 | + if (!(port2=strtol(cp, &cp, 10))) { | |
911 | + logwrite("parseconfig(): Invalid config line\n"); | |
912 | + continue; | |
913 | + } | |
914 | + | |
915 | + /* Check for matching listeners.. */ | |
916 | + found=0; | |
917 | + for (i=0;i<MAXFDS;i++) { | |
918 | + if (IsListenSocket(i) && ssa[i].listenaddr==ip1 && ssa[i].listenport==port1 && | |
919 | + ssa[i].remoteaddr==ip2 && ssa[i].remoteport==port2) { | |
920 | + /* Found one, just set the type (allows p: <-> P: rehashes) */ | |
921 | + ssa[i].type=type; | |
922 | + ssa[i].marker=1; | |
923 | + found=1; | |
924 | + break; | |
925 | + } | |
926 | + } | |
927 | + if (!found) | |
928 | + openlistenfd(type, ip1, port1, ip2, port2); | |
929 | + } | |
930 | + } | |
931 | + | |
932 | + fclose(fp); | |
933 | + | |
934 | + /* Kill off dead listeners */ | |
935 | + for (i=0;i<MAXFDS;i++) { | |
936 | + if (IsListenSocket(i) && ssa[i].marker==0) { | |
937 | + logwrite("Closing extinct listen socket %d\n",i); | |
938 | + setpoll(i,0); | |
939 | + ssa[i].type=SST_IDLE; | |
940 | + close(i); | |
941 | + } | |
942 | + } | |
943 | + | |
944 | + return 0; | |
945 | +} | |
946 | + | |
947 | +void handlehup(int x) { | |
948 | + needrehash=1; | |
949 | +} | |
950 | + | |
951 | +void dorehash() { | |
952 | + close(logfd); | |
953 | + if ((logfd=open(LOGFILE, O_CREAT | O_WRONLY | O_APPEND,00660)) < 0) { | |
954 | + printf("ERROR: can't reopen logfile.\n"); | |
955 | + exit(1); | |
956 | + } | |
957 | + logwrite("Received SIGHUP - reloading config file.\n"); | |
958 | + parseconfig(configfile); | |
959 | + needrehash=0; | |
960 | +} | |
961 | + | |
962 | +int main(int argc, char **argv) { | |
963 | + struct sigaction sa; | |
964 | + int i; | |
965 | + struct timezone tz; | |
966 | + struct timeval tv; | |
967 | + | |
968 | + sa.sa_handler=handlehup; | |
969 | + sigemptyset(&(sa.sa_mask)); | |
970 | + sa.sa_flags=0; | |
971 | + | |
972 | + strncpy(configfile, (argc > 1 ? argv[1] : SETTINGSFILE), 511); | |
973 | + | |
974 | + init(); | |
975 | + sigaction(SIGHUP, &sa, NULL); | |
976 | + sa.sa_handler=SIG_IGN; | |
977 | + sigaction(SIGPIPE, &sa, NULL); | |
978 | + | |
979 | + if (parseconfig(configfile)) { | |
980 | + return 1; | |
981 | + } | |
982 | + | |
983 | + for(;;) { | |
984 | + if (!dopoll(POLLTIMEOUT)) { | |
985 | + for (i=0;i<MAXFDS;i++) { | |
986 | + if (ssa[i].dirty==1 && IsActiveSocket(i)) { | |
987 | + /* Flush everything */ | |
988 | + handledata(i,1); | |
989 | + ssa[i].dirty=0; | |
990 | + } | |
991 | + } | |
992 | + while (!dopoll(1000)) { | |
993 | + if (needrehash) | |
994 | + dorehash(); | |
995 | + } | |
996 | + } else { | |
997 | + /* something happened - do a more detailed dirty check */ | |
998 | + gettimeofday(&tv, &tz); | |
999 | + for (i=0;i<MAXFDS;i++) { | |
1000 | + if (ssa[i].dirty==1 && IsActiveSocket(i)) { | |
1001 | + if (usecdiff(&(ssa[i].lastoutput), &tv) > FLUSHTIMEOUT) { | |
1002 | + handledata(i,1); | |
1003 | + ssa[i].dirty=0; | |
1004 | + } | |
1005 | + } | |
1006 | + } | |
1007 | + } | |
1008 | + if (needrehash) | |
1009 | + dorehash(); | |
1010 | + } | |
1011 | + | |
1012 | + return 0; | |
1013 | +} |