--- /dev/null
+*.sw[op]
+*.bb*
+*.da
+*.dat
+*.gcov
+gmon.out
+config.log
+config.cache
+config.status
+Makefile
+bopm
+bopchecker
+setup.h
+.deps
+stamp-h
+stamp-h.in
--- /dev/null
+bin_PROGRAMS = bopm bopchecker
+
+AM_CPPFLAGS = -DBOPM_ETCDIR="\"$(sysconfdir)\"" -DBOPM_LOGDIR="\"$(localstatedir)\""
+
+bopm_SOURCES = compat.c compat.h config.c config.h dnsbl.c dnsbl.h extern.h \
+ irc.c irc.h log.c log.h main.c match.c match.h misc.c misc.h opercmd.c \
+ opercmd.h options.h scan.c scan.h setup.h stats.c stats.h
+bopchecker_SOURCES = bopchecker.c bopchecker.h compat.c compat.h config.c \
+ config.h match.c match.h misc.c misc.h scan.c scan.h setup.h
+
+bopm_LDADD = @LNSL@ @LSOCKET@
+bopchecker_LDADD = @LNSL@ @LSOCKET@
--- /dev/null
+# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+CC = @CC@
+LNSL = @LNSL@
+LSOCKET = @LSOCKET@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+bin_PROGRAMS = bopm bopchecker
+
+AM_CPPFLAGS = -DBOPM_ETCDIR="\"$(sysconfdir)\"" -DBOPM_LOGDIR="\"$(localstatedir)\""
+
+bopm_SOURCES = compat.c compat.h config.c config.h dnsbl.c dnsbl.h extern.h \
+ irc.c irc.h log.c log.h main.c match.c match.h misc.c misc.h opercmd.c \
+ opercmd.h options.h scan.c scan.h setup.h stats.c stats.h
+
+bopchecker_SOURCES = bopchecker.c bopchecker.h compat.c compat.h config.c \
+ config.h match.c match.h misc.c misc.h scan.c scan.h setup.h
+
+
+bopm_LDADD = @LNSL@ @LSOCKET@
+bopchecker_LDADD = @LNSL@ @LSOCKET@
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = setup.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(bin_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I.
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+bopm_OBJECTS = compat.o config.o dnsbl.o irc.o log.o main.o match.o \
+misc.o opercmd.o scan.o stats.o
+bopm_DEPENDENCIES =
+bopm_LDFLAGS =
+bopchecker_OBJECTS = bopchecker.o compat.o config.o match.o misc.o \
+scan.o
+bopchecker_DEPENDENCIES =
+bopchecker_LDFLAGS =
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DIST_COMMON = ./stamp-h.in Makefile.am Makefile.in setup.h.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+DEP_FILES = .deps/bopchecker.P .deps/compat.P .deps/config.P \
+.deps/dnsbl.P .deps/irc.P .deps/log.P .deps/main.P .deps/match.P \
+.deps/misc.P .deps/opercmd.P .deps/scan.P .deps/stats.P
+SOURCES = $(bopm_SOURCES) $(bopchecker_SOURCES)
+OBJECTS = $(bopm_OBJECTS) $(bopchecker_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .s
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES)
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+setup.h: stamp-h
+ @if test ! -f $@; then \
+ rm -f stamp-h; \
+ $(MAKE) stamp-h; \
+ else :; fi
+stamp-h: $(srcdir)/setup.h.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES= CONFIG_HEADERS=src/setup.h \
+ $(SHELL) ./config.status
+ @echo timestamp > stamp-h 2> /dev/null
+$(srcdir)/setup.h.in: $(srcdir)/stamp-h.in
+ @if test ! -f $@; then \
+ rm -f $(srcdir)/stamp-h.in; \
+ $(MAKE) $(srcdir)/stamp-h.in; \
+ else :; fi
+$(srcdir)/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOHEADER)
+ @echo timestamp > $(srcdir)/stamp-h.in 2> /dev/null
+
+mostlyclean-hdr:
+
+clean-hdr:
+
+distclean-hdr:
+ -rm -f setup.h
+
+maintainer-clean-hdr:
+
+mostlyclean-binPROGRAMS:
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+distclean-binPROGRAMS:
+
+maintainer-clean-binPROGRAMS:
+
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ list='$(bin_PROGRAMS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ done
+
+.s.o:
+ $(COMPILE) -c $<
+
+.S.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+bopm: $(bopm_OBJECTS) $(bopm_DEPENDENCIES)
+ @rm -f bopm
+ $(LINK) $(bopm_LDFLAGS) $(bopm_OBJECTS) $(bopm_LDADD) $(LIBS)
+
+bopchecker: $(bopchecker_OBJECTS) $(bopchecker_DEPENDENCIES)
+ @rm -f bopchecker
+ $(LINK) $(bopchecker_LDFLAGS) $(bopchecker_OBJECTS) $(bopchecker_LDADD) $(LIBS)
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) setup.h.in $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)setup.h.in$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags setup.h.in $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = src
+
+distdir: $(DISTFILES)
+ here=`cd $(top_builddir) && pwd`; \
+ top_distdir=`cd $(top_distdir) && pwd`; \
+ distdir=`cd $(distdir) && pwd`; \
+ cd $(top_srcdir) \
+ && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu src/Makefile
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+
+DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
+
+-include $(DEP_FILES)
+
+mostlyclean-depend:
+
+clean-depend:
+
+distclean-depend:
+ -rm -rf .deps
+
+maintainer-clean-depend:
+
+%.o: %.c
+ @echo '$(COMPILE) -c $<'; \
+ $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+ @-cp .deps/$(*F).pp .deps/$(*F).P; \
+ tr ' ' '\012' < .deps/$(*F).pp \
+ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+ >> .deps/$(*F).P; \
+ rm .deps/$(*F).pp
+
+%.lo: %.c
+ @echo '$(LTCOMPILE) -c $<'; \
+ $(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+ @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
+ < .deps/$(*F).pp > .deps/$(*F).P; \
+ tr ' ' '\012' < .deps/$(*F).pp \
+ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+ >> .deps/$(*F).P; \
+ rm -f .deps/$(*F).pp
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+all-recursive-am: setup.h
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+install-exec-am: install-binPROGRAMS
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-binPROGRAMS
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS) setup.h
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-hdr mostlyclean-binPROGRAMS \
+ mostlyclean-compile mostlyclean-tags mostlyclean-depend \
+ mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-hdr clean-binPROGRAMS clean-compile clean-tags \
+ clean-depend clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-hdr distclean-binPROGRAMS distclean-compile \
+ distclean-tags distclean-depend distclean-generic \
+ clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-hdr maintainer-clean-binPROGRAMS \
+ maintainer-clean-compile maintainer-clean-tags \
+ maintainer-clean-depend maintainer-clean-generic \
+ distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \
+mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
+maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
+mostlyclean-compile distclean-compile clean-compile \
+maintainer-clean-compile tags mostlyclean-tags distclean-tags \
+clean-tags maintainer-clean-tags distdir mostlyclean-depend \
+distclean-depend clean-depend maintainer-clean-depend info-am info \
+dvi-am dvi check check-am installcheck-am installcheck all-recursive-am \
+install-exec-am install-exec install-data-am install-data install-am \
+install uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
int OPT_DEBUG = 1;
char *CONFNAME = DEFAULTNAME;
+char *CONFDIR = BOPM_ETCDIR;
char *CONFFILE;
int RC = 0;
signal(SIGPIPE, SIG_IGN);
- len = strlen(CONFNAME) + strlen(CONFEXT) + 2;
+ len = strlen(CONFDIR) + strlen(CONFNAME) + strlen(CONFEXT) + 3;
CONFFILE = malloc(len * sizeof(*CONFFILE));
- snprintf(CONFFILE, len, "%s.%s", CONFNAME, CONFEXT);
+ snprintf(CONFFILE, len, "%s/%s.%s", CONFDIR, CONFNAME, CONFEXT);
/*
* The only things we need in a conf file are SCANIP and
--- /dev/null
+/*
+Copyright (C) 2002 Erik Fears
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+
+ Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "setup.h"
+
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <time.h>
+#include <errno.h>
+
+#include "irc.h"
+#include "opercmd.h"
+#include "scan.h"
+#include "dnsbl.h"
+#include "config.h"
+#include "extern.h"
+#include "log.h"
+
+extern unsigned int STAT_DNSBL_MATCHES;
+
+/*
+ * Check an ip address for presence in a DNS (black|block)list. All we need
+ * to do is reverse the octets, append the BL zone and then do a
+ * gethostbyname. If that gives us an answer, then it is on the BL.
+ */
+int dnsbl_check(const char *addr, const char *irc_nick,
+ const char *irc_user, char *irc_addr)
+{
+ size_t buflen;
+ struct in_addr in;
+ struct hostent *he;
+ char *buf;
+ unsigned char a, b, c, d;
+
+ if (!inet_aton(addr, &in)) {
+ log("DNSBL -> Invalid address '%s', ignoring.", addr);
+ return(0);
+ }
+
+ d = (unsigned char) (in.s_addr >> 24) & 0xFF;
+ c = (unsigned char) (in.s_addr >> 16) & 0xFF;
+ b = (unsigned char) (in.s_addr >> 8) & 0xFF;
+ a = (unsigned char) in.s_addr & 0xFF;
+
+ /* Enough for a reversed IP and the zone. */
+ buflen = 18 + strlen(CONF_DNSBL_ZONE);
+ buf = malloc(buflen * sizeof(*buf));
+
+#ifdef WORDS_BIGENDIAN
+ snprintf(buf, buflen, "%d.%d.%d.%d.%s.", a, b, c, d,
+ CONF_DNSBL_ZONE);
+#else
+ snprintf(buf, buflen, "%d.%d.%d.%d.%s.", d, c, b, a,
+ CONF_DNSBL_ZONE);
+#endif
+
+ if (OPT_DEBUG)
+ log("DNSBL -> Checking %s", buf);
+
+ if (!(he = gethostbyname(buf))) {
+ switch(h_errno) {
+ /*
+ * Some of these errors indicate serious problems
+ * that will require admin intervention.
+ */
+ case NO_RECOVERY:
+ irc_send("PRIVMSG %s :Whilst checking '%s' "
+ "against my dnsbl zone '%s', I got a "
+ "non-recoverable name server error. Might "
+ "want to take a look, check if your local "
+ "resolver is working!?", CONF_CHANNELS, addr,
+ CONF_DNSBL_ZONE);
+ break;
+ case TRY_AGAIN:
+ irc_send("PRIVMSG %s :Whilst checking '%s' "
+ "against my dnsbl zone '%s', I got a "
+ "temporary nameserver error. Might want to "
+ "take a look, check if your local resolver is "
+ "working!?", CONF_CHANNELS, addr,
+ CONF_DNSBL_ZONE);
+ break;
+
+ default:
+ /*
+ * Some other error but we don't care, we can just
+ * treat this IP as OK.
+ */
+ break;
+ }
+
+ free(buf);
+ return(0);
+ }
+ free(buf);
+
+ /*
+ * We got an answer, so we need to kline this IP now.
+ */
+ irc_kline(irc_addr, (char *)addr);
+ log("DNSBL -> %s appears in BL zone %s", addr, CONF_DNSBL_ZONE);
+ irc_send("PRIVMSG %s :DNSBL: %s!%s@%s appears in BL zone %s",
+ CONF_CHANNELS, irc_nick, irc_user, irc_addr, CONF_DNSBL_ZONE);
+
+ STAT_DNSBL_MATCHES++;
+ return(1);
+}
+
+/*
+ * Send an email to report this open proxy.
+ */
+void dnsbl_report(struct scan_struct *ss)
+{
+ char buf[4096], cmdbuf[512];
+ FILE *fp;
+
+ if (!ss || !ss->addr)
+ return;
+
+ snprintf(cmdbuf, sizeof(cmdbuf), "%s -t", CONF_SENDMAIL);
+ snprintf(buf, sizeof(buf),
+ "From: %s <%s>\n"
+ "To: %s\n"
+ "Subject: BOPM Report\n\n"
+ "%s: %s\n\n"
+ "%s\n", CONF_NICK, CONF_DNSBL_FROM, CONF_DNSBL_TO,
+ ss->protocol->type, ss->addr, ss->conn_notice);
+
+ if ((fp = popen(cmdbuf, "w")) == NULL) {
+ log("DNSBL -> Failed to create pipe to '%s' for email "
+ "report!", cmdbuf);
+ irc_send("PRIVMSG %s :I was trying to create a pipe to "
+ "'%s' to send a DNSBL report, and it failed! I'll "
+ "give up for now.", CONF_CHANNELS, cmdbuf);
+ return;
+ }
+
+ fputs(buf, fp);
+ pclose(fp);
+ log("DNSBL -> Sent report to %s", CONF_DNSBL_TO);
+}
--- /dev/null
+#ifndef DNSBL_H
+#define DNSBL_H
+
+extern int dnsbl_check(const char *addr, const char *irc_nick,
+ const char *irc_user, char *irc_addr);
+extern void dnsbl_report(struct scan_struct *ss);
+
+#endif
--- /dev/null
+#ifndef EXTERN_H
+#define EXTERN_H
+
+extern int h_errno;
+
+extern char *CONF_SERVER;
+extern char *CONF_PASSWORD;
+extern char *CONF_USER;
+extern char *CONF_NICK;
+extern char *CONF_REALNAME;
+extern char *CONF_OPER;
+extern char *CONF_OPER_MODES;
+extern char *CONF_REASON;
+extern char *CONF_SCANIP;
+extern char *CONF_BINDIRC;
+extern char *CONF_BINDSCAN;
+extern char *CONF_CHANNELS;
+extern char *CONF_KEYS;
+extern char *CONF_NICKSERV_IDENT;
+extern char *CONF_CHANSERV_INVITE;
+extern char *CONF_KLINE_COMMAND;
+extern char *CONF_DNSBL_ZONE;
+extern char *CONF_DNSBL_FROM;
+extern char *CONF_DNSBL_TO;
+extern char *CONF_SENDMAIL;
+extern char *CONF_HELP_EMAIL;
+extern char *CONF_AWAY;
+extern char *CONF_TARGET_STRING;
+extern string_list *CONF_EXCLUDE;
+
+extern unsigned int CONF_PORT;
+extern unsigned int CONF_SCANPORT;
+extern unsigned int CONF_FDLIMIT;
+extern unsigned int CONF_TIMEOUT;
+
+extern unsigned int OPT_DEBUG;
+
+extern time_t STAT_START_TIME;
+extern unsigned int STAT_NUM_CONNECTS;
+
+#endif
--- /dev/null
+/*
+Copyright (C) 2002 Erik Fears
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+
+ Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include "irc.h"
+#include "log.h"
+#include "config.h"
+#include "opercmd.h"
+#include "scan.h"
+#include "dnsbl.h"
+#include "stats.h"
+#include "extern.h"
+#include "options.h"
+#include "match.h"
+#include "compat.h"
+
+static void irc_init(void);
+static void irc_connect(void);
+static void irc_reconnect(void);
+static void irc_read(void);
+static void irc_parse(void);
+static void do_perform(void);
+static void do_connect(char *addr, char *irc_nick, char *irc_user,
+ char *irc_addr, char *conn_notice);
+static void do_hybrid_connect(int tokens, char **token);
+static void do_trircd_connect(int tokens, char **token);
+static void do_ultimateircd_connect(int tokens, char **token);
+static void do_xnet_connect(int tokens, char **token);
+static char *get_chan_key(const char *channel);
+static char *check_channel(const char *channel);
+
+extern char *CONFFILE;
+extern time_t LAST_REAP_TIME;
+
+/*
+ * Certain variables we don't want to allocate memory for over and over
+ * again so global scope is given.
+ */
+
+char IRC_RAW[MSGLENMAX]; /* Buffer to read data into */
+char IRC_SENDBUFF[MSGLENMAX]; /* Send buffer */
+int IRC_RAW_LEN = 0; /* Position of IRC_RAW */
+
+int IRC_FD = -1; /* File descriptor for IRC client */
+struct sockaddr_in IRC_SVR; /* Sock Address Struct for IRC server */
+struct sockaddr_in IRC_LOCAL; /* Sock Address Struct for Bind */
+struct hostent *IRC_HOST; /* Hostent struct for IRC server */
+fd_set IRC_READ_FDSET; /* fd_set for IRC (read) data for select()*/
+
+struct timeval IRC_TIMEOUT; /* timeval struct for select() timeout */
+time_t IRC_NICKSERV_LAST = 0; /* Last notice from nickserv */
+time_t IRC_LAST = 0; /* Last full line of data from irc server*/
+
+/* Give one cycle to the IRC client, which
+ * will allow it to poll for data and handle
+ * that data if need be.
+ */
+
+void irc_cycle(void)
+{
+ if (IRC_FD <= 0) {
+ /* No socket open. */
+ config_load(CONFFILE); /* Reload config. */
+ irc_init(); /* Resolve remote host. */
+ irc_connect(); /* Connect to remote host. */
+ }
+
+ IRC_TIMEOUT.tv_sec = 0;
+ /*
+ * Block .05 seconds to avoid excessive CPU use on select(). */
+ IRC_TIMEOUT.tv_usec = 50000;
+
+ FD_ZERO(&IRC_READ_FDSET);
+
+ FD_SET(IRC_FD, &IRC_READ_FDSET);
+
+ switch (select((IRC_FD + 1), &IRC_READ_FDSET, 0, 0, &IRC_TIMEOUT)) {
+ case -1:
+ return;
+ break;
+ case 0:
+ break;
+ default:
+ /* Check if IRC data is available. */
+ if (FD_ISSET(IRC_FD, &IRC_READ_FDSET))
+ irc_read();
+ break;
+ }
+}
+
+
+/* Allocate socket file descriptor for connection,
+ * and resolve remote host.
+ *
+ */
+
+static void irc_init(void)
+{
+ if (IRC_FD)
+ close(IRC_FD);
+
+ memset(&IRC_SVR, 0, sizeof(IRC_SVR));
+ memset(&IRC_LOCAL, 0, sizeof(IRC_LOCAL));
+
+ /* Resolve IRC host. */
+ if (!(IRC_HOST = gethostbyname(CONF_SERVER))) {
+ switch(h_errno) {
+ case HOST_NOT_FOUND:
+ log("IRC -> gethostbyname(): The specified host "
+ "(%s) is unknown", CONF_SERVER);
+ break;
+ case NO_ADDRESS:
+ log("IRC -> gethostbyname(): The specified name "
+ "(%s) exists, but does not have an IP",
+ CONF_SERVER);
+ break;
+ case NO_RECOVERY:
+ log("IRC -> gethostbyname(): An unrecoverable "
+ "error occured resolving (%s)", CONF_SERVER);
+ break;
+ case TRY_AGAIN:
+ log("IRC -> gethostbyname(): Error occured with "
+ "authoritive name server (%s)", CONF_SERVER);
+ break;
+ default:
+ log("IRC -> gethostbyname(): Unknown error "
+ "resolving (%s)", CONF_SERVER);
+ break;
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ IRC_SVR.sin_family = AF_INET;
+ IRC_SVR.sin_port = htons(CONF_PORT);
+ IRC_SVR.sin_addr = *((struct in_addr *) IRC_HOST->h_addr);
+
+ if (IRC_SVR.sin_addr.s_addr == INADDR_NONE) {
+ log("IRC -> Unknown error resolving remote host (%s)",
+ CONF_SERVER);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Request file desc for IRC client socket. */
+ IRC_FD = socket(PF_INET, SOCK_STREAM, 0);
+
+ if (IRC_FD == -1) {
+ switch(errno) {
+ case EINVAL:
+ case EPROTONOSUPPORT:
+ log("IRC -> socket(): SOCK_STREAM is not "
+ "supported on this domain");
+ break;
+ case ENFILE:
+ log("IRC -> socket(): Not enough free file "
+ "descriptors to allocate IRC socket");
+ break;
+ case EMFILE:
+ log("IRC -> socket(): Process table overflow when "
+ "requesting file descriptor");
+ break;
+ case EACCES:
+ log("IRC -> socket(): Permission denied to create "
+ "socket of type SOCK_STREAM");
+ break;
+ case ENOMEM:
+ log("IRC -> socket(): Insufficient memory to "
+ "allocate socket");
+ break;
+ default:
+ log("IRC -> socket(): Unknown error allocating "
+ "socket");
+ break;
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ if (CONF_BINDIRC) {
+ if (!inet_aton(CONF_BINDIRC, &(IRC_LOCAL.sin_addr))) {
+ log("IRC -> bind(): %s is an invalid address",
+ CONF_BINDIRC);
+ exit(EXIT_FAILURE);
+ }
+
+ IRC_LOCAL.sin_family = AF_INET;
+ IRC_LOCAL.sin_port = 0;
+
+ if (bind(IRC_FD, (struct sockaddr *)&IRC_LOCAL,
+ sizeof(struct sockaddr_in)) == -1) {
+ switch(errno) {
+ case EACCES:
+ log("IRC -> bind(): No access to bind to %s",
+ CONF_BINDIRC);
+ break;
+ default:
+ log("IRC -> bind(): Error binding to %s (%d)",
+ CONF_BINDIRC, errno);
+ break;
+ }
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+
+/*
+ * Send data to remote IRC host.
+ */
+
+
+void irc_send(char *data, ...)
+{
+ va_list arglist;
+ char data2[MSGLENMAX];
+ char tosend[MSGLENMAX];
+
+ va_start(arglist, data);
+ vsnprintf(data2, MSGLENMAX - 1, data, arglist);
+ va_end(arglist);
+
+ if (OPT_DEBUG >= 2)
+ log("IRC SEND -> %s", data2);
+
+ snprintf(tosend, MSGLENMAX - 1, "%s\n", data2);
+
+ if (send(IRC_FD, tosend, strlen(tosend), 0) == -1) {
+ /* Return of -1 indicates error sending data; we reconnect. */
+ irc_reconnect();
+ }
+}
+
+/*
+ * K:line given ip for given reason.
+ */
+
+void irc_kline(char *addr, char *ip)
+{
+ irc_send(CONF_KLINE_COMMAND, addr, ip);
+}
+
+/*
+ * Create socket and connect to IRC server specificied in config file
+ * (CONF_SERVER) with port CONF_PORT.
+ */
+
+static void irc_connect(void)
+{
+ /* Connect to IRC server as client. */
+ if (connect(IRC_FD, (struct sockaddr *) &IRC_SVR,
+ sizeof(IRC_SVR)) == -1) {
+ switch(errno) {
+ case EISCONN:
+ /* Already connected */
+ return;
+ case ECONNREFUSED:
+ log("IRC -> connect(): Connection refused by (%s)",
+ CONF_SERVER);
+ break;
+ case ETIMEDOUT:
+ log("IRC -> connect(): Timed out connecting to (%s)",
+ CONF_SERVER);
+ break;
+ case ENETUNREACH:
+ log("IRC -> connect(): Network unreachable");
+ break;
+ case EALREADY:
+ /* Previous attempt not complete */
+ return;
+ default:
+ log("IRC -> connect(): Unknown error connecting to (%s)",
+ CONF_SERVER);
+
+ if (OPT_DEBUG >= 1)
+ log(strerror(errno));
+ }
+ exit(EXIT_FAILURE);
+ }
+
+#ifdef WITH_UNREAL
+ irc_send("PROTOCTL HCN");
+#endif /* WITH_UNREAL */
+
+ irc_send("NICK %s",CONF_NICK);
+ if (CONF_PASSWORD)
+ irc_send("PASS %s",CONF_PASSWORD);
+ irc_send("USER %s %s %s :%s", CONF_USER, CONF_USER, CONF_USER,
+ CONF_REALNAME);
+}
+
+
+static void irc_reconnect(void)
+{
+ if(IRC_FD > 0)
+ close(IRC_FD);
+
+ /* Set IRC_FD 0 for reconnection on next irc_cycle(). */
+ IRC_FD = 0;
+
+ log("IRC -> Connection to (%s) lost, rehashing and reconnecting.",
+ CONF_SERVER);
+}
+
+/*
+ * Read one character at a time until an endline is hit, at which time control
+ * is passed to irc_parse() to parse that line.
+ */
+
+static void irc_read(void)
+{
+ int len;
+ char c;
+
+ while ((len = read(IRC_FD, &c, 1))) {
+ if (len <= 0) {
+ irc_reconnect();
+ return;
+ }
+
+ if (c == '\r')
+ continue;
+
+ if (c == '\n') {
+ /* Null string. */
+ IRC_RAW[IRC_RAW_LEN] = 0;
+ /* Parse line. */
+ irc_parse();
+ /* Reset counter. */
+ IRC_RAW_LEN = 0;
+ break;
+ }
+
+ if (c != '\r' && c != '\n' && c != 0)
+ IRC_RAW[IRC_RAW_LEN++] = c;
+ }
+
+ if (len <= 0) {
+ irc_reconnect();
+ return;
+ }
+}
+
+/*
+ * A full line has been read by irc_read(); this function begins parsing it.
+ */
+
+static void irc_parse(void)
+{
+ char nick[NICKMAX];
+ char *token[32];
+ time_t present;
+ size_t prefixlen;
+ unsigned int tokens;
+ char *irc_channel, *key, *user, *target, *msg;
+
+ tokens = 0;
+
+ /* Update timeout tracking. */
+ time(&IRC_LAST);
+
+ if(OPT_DEBUG >= 2)
+ log("IRC READ -> %s", IRC_RAW);
+
+ /*
+ * Tokenize the first 32 words in the incoming data, we really don't
+ * need to worry about anything else and we don't need the original
+ * string for anything.
+ */
+
+ token[tokens] = strtok(IRC_RAW, " ");
+
+ while (++tokens < 32 && (token[tokens] = strtok(NULL, " ")))
+ ;
+
+ /* Anything with less than 1 token is useless to us. */
+
+ if (tokens <= 1)
+ return;
+
+ if (!strcasecmp(token[0], "PING")) {
+ irc_send("PONG %s", token[1]);
+ return;
+ }
+
+ /* 001 is sent on initial connect to the IRC host. */
+
+ if (!strcasecmp(token[1], "001")) {
+ do_perform();
+ if (CONF_AWAY)
+ irc_send("AWAY :%s (/msg %s INFO)", CONF_AWAY, CONF_NICK);
+ irc_send("OPER %s", CONF_OPER);
+ irc_send("MODE %s %s", CONF_NICK, CONF_OPER_MODES);
+ return;
+ }
+
+ /* 471, 473, 474, 475 are 'Cannot Join' messages. */
+
+ if (!strcasecmp(token[1], "471") ||
+ !strcasecmp(token[1], "473") ||
+ !strcasecmp(token[1], "474") ||
+ !strcasecmp(token[1], "475")) {
+ if(CONF_CHANSERV_INVITE) {
+ /* 4th token is channel we can't join. */
+ irc_send(CONF_CHANSERV_INVITE, token[3]);
+ }
+ return;
+ }
+
+ /*
+ * Handle invites, complicated code ahead is due to a decision not to
+ * use strtok() or strstr() in the following block
+ */
+
+ if (!strcasecmp(token[1], "INVITE")) {
+ /* token 4 is the channel, + 1 to shift past ':'. */
+ irc_channel = check_channel(token[3] + 1);
+
+ if (irc_channel) {
+ key = get_chan_key(irc_channel);
+
+ if (key)
+ irc_send("JOIN %s %s", irc_channel, key);
+ else
+ irc_send("JOIN %s", irc_channel);
+
+ return;
+ }
+ }
+
+ /* Handle nickserv identification. */
+
+ if (!strcasecmp(token[1], "NOTICE") && strchr(token[0], '@')) {
+ if (CONF_NICKSERV_IDENT &&
+ !strcasecmp(strtok(token[0] + 1, "!") , "NICKSERV")) {
+ time(&present);
+ /*
+ * If last used notice was greater than/equal to
+ * 10 sec ago
+ */
+ if ((present - IRC_NICKSERV_LAST) >= 10) {
+ /* Identify to nickserv. */
+ irc_send(CONF_NICKSERV_IDENT);
+ /* Record last ident. */
+ time(&IRC_NICKSERV_LAST);
+ }
+ return;
+ }
+ }
+
+ /* Handle rejoining when kicked. */
+
+ /* :grifferz!goats@pc-62-30-219-54-pb.blueyonder.co.uk KICK #wg penguinBopm :test */
+ if (!strcasecmp(token[1], "KICK") &&
+ !strcasecmp(token[3], CONF_NICK)) {
+ /*
+ * Someone kicked us from channel token[2] so let's
+ * rejoin.
+ */
+ log("IRC -> Kicked from %s by %s! (%s)", token[2],
+ token[0], token[4]);
+ key = get_chan_key(token[2]);
+
+ if (key)
+ irc_send("JOIN %s %s", token[2], key);
+ else
+ irc_send("JOIN %s", token[2]);
+ return;
+ }
+
+ /* Any messages from users that we need to respond to. */
+ if (!strcasecmp(token[1], "PRIVMSG") && token[0][0] == ':') {
+ /* work out who it was from */
+ strncpy(nick, token[0] + 1, NICKMAX);
+ user = index(nick, '!');
+
+ if(user) {
+ /*
+ * Nick is currently the first 32 chars of a userhost,
+ * so null terminate at !
+ */
+ *user = '\0';
+
+ msg = token[3];
+
+ if (msg && msg[0] == ':')
+ msg++;
+
+ prefixlen = strlen(msg);
+
+ if (token[2][0] == '#' || token[2][0] == '&')
+ target = CONF_CHANNELS;
+ else
+ target = nick;
+
+ /* CTCP VERSION */
+ if (strncasecmp(msg, "\001VERSION\001", 9) == 0) {
+ irc_send("NOTICE %s :\001VERSION Blitzed "
+ "Open Proxy Monitor %s\001", nick,
+ VERSION);
+ return;
+ }
+
+ if (strncasecmp(msg, "INFO", 4) == 0) {
+ irc_send("NOTICE %s :This bot is designed "
+ "to scan incoming connections for the "
+ "presence of open SOCKS, HTTP and "
+ "other similar servers.", nick);
+ irc_send("NOTICE %s :These misconfigured "
+ "servers allow anyone to abuse them "
+ "to 'bounce' through, and are "
+ "frequently used to harass. As a "
+ "result, use of such proxies is not "
+ "permitted on this IRC network.", nick);
+ irc_send("NOTICE %s :If you found this bot "
+ "because of NukeNabber or other "
+ "firewall software on your computer, "
+ "please be aware that this is \ 2not\ 2 "
+ "a nuke or any other form of abusive "
+ "activity.", nick);
+ irc_send("NOTICE %s :You can get more "
+ "information about this bot and what "
+ "it does by contacting %s.", nick,
+ CONF_HELP_EMAIL);
+ return;
+ }
+
+ if (strncasecmp(msg, CONF_NICK,
+ prefixlen > 3 ? prefixlen : 3) &&
+ strcasecmp(msg, "!all")) {
+ /*
+ * Not in the form we accept, ignore this
+ * message
+ */
+ return;
+ }
+
+ if (!token[4]) {
+ irc_send("PRIVMSG %s :Some form of "
+ "command would be nice.", target);
+ return;
+ }
+
+ if (strncasecmp(token[4], "STAT", 4) == 0) {
+ do_stats(target);
+ return;
+ }
+
+ /* Otherwise it might be an oper command. */
+ do_oper_cmd(nick, token[4], token[5], target);
+ }
+ }
+
+ if (!strcasecmp(token[1], "302")) {
+ check_userhost(token[3]);
+ return;
+ }
+
+ /* Search for +c notices. */
+ /* Some ircd (just hybrid/xnet/df?) don't send a server name. */
+ if (tokens >= 11 && strcasecmp(token[0], "NOTICE") == 0 &&
+ strcasecmp(token[6], "connecting:") == 0)
+ do_xnet_connect(tokens, token);
+
+ if (token[0][0] == ':') {
+ /* Toss any notices NOT from a server. */
+
+ if (strchr(token[0], '@'))
+ return;
+
+ if (tokens >= 11 && strcmp(token[7], "connecting:") == 0)
+ do_hybrid_connect(tokens, token);
+ else if (tokens >= 9 && strcmp(token[4], "connecting:") == 0)
+ do_trircd_connect(tokens, token);
+ else if (tokens >= 17 && strcmp(token[8], "Client") == 0 &&
+ strcmp(token[9], "connecting") == 0)
+ do_ultimateircd_connect(tokens, token);
+ }
+}
+
+/*
+ * Perform on connect functions.
+ */
+
+static void do_perform(void)
+{
+ log("IRC -> Connected to %s:%d", CONF_SERVER, CONF_PORT);
+
+ if (CONF_NICKSERV_IDENT) {
+ /* Identify to nickserv. */
+ irc_send(CONF_NICKSERV_IDENT);
+ }
+
+ /* Join all listed channels. */
+ if (CONF_KEYS)
+ irc_send("JOIN %s %s", CONF_CHANNELS, CONF_KEYS);
+ else
+ irc_send("JOIN %s", CONF_CHANNELS);
+}
+
+/*
+ * Functions we need to perform ~1 seconds.
+ */
+
+void irc_timer(void)
+{
+ time_t present;
+
+ time(&present);
+
+ /* No data in NODATA_TIMEOUT minutes (set in options.h). */
+ if ((present - IRC_LAST) >= NODATA_TIMEOUT) {
+ irc_reconnect();
+ /* Make sure we dont do this again for another 5 minutes */
+ time(&IRC_LAST);
+ }
+
+ /* Get rid of old command structures. */
+ if ((present - LAST_REAP_TIME) >= 120) {
+ reap_commands(present);
+ time(&LAST_REAP_TIME);
+ }
+}
+
+static void do_connect(char *addr, char *irc_nick, char *irc_user,
+ char *irc_addr, char *conn_notice)
+{
+ string_list *list;
+
+ /*
+ * Check that neither the user's IP nor host matches anything in our
+ * exclude list.
+ */
+ for (list = (string_list *) CONF_EXCLUDE; list; list = list->next) {
+ if (match(list->text, addr) || match(list->text, irc_addr)) {
+ if (OPT_DEBUG) {
+ log("SCAN -> excluded user %s!%s@%s",
+ irc_nick, irc_user, irc_addr);
+ }
+ return;
+ }
+ }
+
+ if (CONF_DNSBL_ZONE &&
+ dnsbl_check(addr, irc_nick, irc_user, irc_addr))
+ return;
+
+ scan_connect(addr, irc_addr, irc_nick, irc_user, 0, conn_notice);
+}
+
+/*
+ * :porkscratchings.pa.us.blitzed.org NOTICE grifferz :*** Notice -- Client connecting: griff (goats@pc-62-30-219-54-pb.blueyonder.co.uk) [62.30.219.54] {1}
+ */
+static void do_hybrid_connect(int tokens, char **token)
+{
+ char conn_notice[MSGLENMAX];
+ char *addr; /* IP of remote host in connection notices */
+ char *irc_addr; /* IRC host address of the remote host */
+ char *irc_user;
+ char *irc_nick;
+
+ /* Paranoia. */
+ if (tokens < 11)
+ return;
+
+ STAT_NUM_CONNECTS++;
+
+ /*
+ * Take a copy of the original connect notice now in case we need it
+ * for evidence later.
+ */
+ snprintf(conn_notice, sizeof(conn_notice),
+ "%s %s %s %s %s %s %s %s %s %s %s", token[0], token[1],
+ token[2], token[3], token[4], token[5], token[6], token[7],
+ token[8], token[9], token[10]);
+
+ /* Make sure it is null terminated. */
+ conn_notice[MSGLENMAX - 1] = '\0';
+
+ /*
+ * Token 11 is the IP of the remote host enclosed in [ ]. We need
+ * to remove it from [ ] and pass it to the scanner.
+ */
+
+ /* Shift over 1 byte to pass over [. */
+ addr = token[10] + 1;
+ /* Replace ] with a \0. */
+ addr = strtok(addr, "]");
+
+ /* Token 9 is the nickname of the connecting client. */
+ irc_nick = token[8];
+
+ /*
+ * Token 10 is (user@host), we want to parse the user/host out for
+ * future reference in case we need to kline the host.
+ */
+
+ /* Shift one byte over to discard '('. */
+ irc_user = token[9] + 1;
+ /* Username is everything before the '@'. */
+ if(!(irc_user = strtok(irc_user, "@")))
+ return;
+ /* irc_addr is everything between '@' and closing ')'. */
+ if(!(irc_addr = strtok(NULL , ")")))
+ return;
+
+ do_connect(addr, irc_nick, irc_user, irc_addr, conn_notice);
+}
+
+/*
+ * :test.teklan.com.tr NOTICE &CONNECTS :Client connecting: griff (andy@pc-62-30-219-54-pb.blueyonder.co.uk) [62.30.219.54] {1}
+ */
+static void do_trircd_connect(int tokens, char **token)
+{
+ char conn_notice[MSGLENMAX];
+ char *addr; /* IP of remote host in connection notices */
+ char *irc_addr; /* IRC host address of the remote host */
+ char *irc_user;
+ char *irc_nick;
+
+ /* Paranoia. */
+ if (tokens < 9)
+ return;
+
+ STAT_NUM_CONNECTS++;
+
+ /*
+ * Take a copy of the original connect notice now in case we need it
+ * for evidence later.
+ */
+ snprintf(conn_notice, sizeof(conn_notice),
+ "%s %s %s %s %s %s %s %s %s", token[0], token[1], token[2],
+ token[3], token[4], token[5], token[6], token[7], token[8]);
+
+ /* Make sure it is null terminated. */
+ conn_notice[MSGLENMAX - 1] = '\0';
+
+ /*
+ * Token 8 is the IP of the remote host enclosed in [ ]. We need
+ * to remove it from [ ] and pass it to the scanner.
+ */
+
+ /* Shift over 1 byte to pass over [. */
+ addr = token[7] + 1;
+ /* Replace ] with a \0. */
+ addr = strtok(addr, "]");
+
+ /* Token 6 is the nickname of the connecting client */
+ irc_nick = token[5];
+
+ /*
+ * Token 7 is (user@host), we want to parse the user/host out for
+ * future reference in case we need to kline the host.
+ */
+
+ /* Shift one byte over to discard '('. */
+ irc_user = token[6] + 1;
+ /* username is everything before the '@' */
+ if(!(irc_user = strtok(irc_user, "@")))
+ return;
+ /* irc_addr is everything between '@' and closing ')' */
+ if(!(irc_addr = strtok(NULL , ")")))
+ return;
+
+ do_connect(addr, irc_nick, irc_user, irc_addr, conn_notice);
+}
+
+/*
+ * :CurCuNa.NeT NOTICE AndyBopm :*** Connect/Exit -- from CurCuNa.NeT: Client connecting on port 6667: Misafir (jirc@213.14.40.51) [213.14.40.51] {1} [JPilot jIRC applet User]
+ */
+static void do_ultimateircd_connect(int tokens, char **token)
+{
+ char conn_notice[MSGLENMAX];
+ char *addr; /* IP of remote host in connection notices */
+ char *irc_addr; /* IRC host address of the remote host */
+ char *irc_user;
+ char *irc_nick;
+
+ /* Paranoia. */
+ if (tokens < 17)
+ return;
+
+ STAT_NUM_CONNECTS++;
+
+ /*
+ * Take a copy of the original connect notice now in case we need it
+ * for evidence later.
+ */
+ snprintf(conn_notice, sizeof(conn_notice),
+ "%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", token[0],
+ token[1], token[2], token[3], token[4], token[5], token[6],
+ token[7], token[8], token[9], token[10], token[11], token[12],
+ token[13], token[14], token[15], token[16]);
+
+ /* Make sure it is null terminated. */
+ conn_notice[MSGLENMAX - 1] = '\0';
+
+ /*
+ * Token 16 is the IP of the remote host enclosed in [ ]. We need
+ * to remove it from [ ] and pass it to the scanner.
+ */
+
+ /* Shift over 1 byte to pass over [. */
+ addr = token[15] + 1;
+ /* Replace ] with a \0. */
+ addr = strtok(addr, "]");
+
+ /* Token 14 is the nickname of the connecting client. */
+ irc_nick = token[13];
+
+ /*
+ * Token 15 is (user@host), we want to parse the user/host out for
+ * future reference in case we need to kline the host.
+ */
+
+ /* Shift one byte over to discard '('. */
+ irc_user = token[14] + 1;
+ /* Username is everything before the '@'. */
+ if(!(irc_user = strtok(irc_user, "@")))
+ return;
+ /* irc_addr is everything between '@' and closing ')' */
+ if(!(irc_addr = strtok(NULL , ")")))
+ return;
+
+ do_connect(addr, irc_nick, irc_user, irc_addr, conn_notice);
+}
+
+
+
+/*
+ * NOTICE BopmMirage :*** Notice -- Client connecting: Iain (iain@modem-449.gacked.dialup.pol.co.uk) [62.25.241.193] {1} (6667)
+ */
+static void do_xnet_connect(int tokens, char **token)
+{
+ char conn_notice[MSGLENMAX];
+ char *addr; /* IP of remote host in connection notices */
+ char *irc_addr; /* IRC host address of the remote host */
+ char *irc_user;
+ char *irc_nick;
+
+ /* Paranoia. */
+ if (tokens < 11)
+ return;
+
+ STAT_NUM_CONNECTS++;
+
+ /*
+ * Take a copy of the original connect notice now in case we need
+ * it for evidence later.
+ */
+ snprintf(conn_notice, sizeof(conn_notice),
+ "%s %s %s %s %s %s %s %s %s %s %s", token[0], token[1],
+ token[2], token[3], token[4], token[5], token[6], token[7],
+ token[8], token[9], token[10]);
+
+ /* Make sure it is null terminated. */
+ conn_notice[MSGLENMAX - 1] = '\0';
+
+ /*
+ * Token 10 is the IP of the remote host enclosed in [ ]. We need to
+ * remove it from [ ] and pass it to the scanner.
+ */
+
+ /* Shift over 1 byte to pass over [. */
+ addr = token[9] + 1;
+ /* Replace ] with a \0. */
+ addr = strtok(addr, "]");
+
+ /* Token 8 is the nickname of the connecting client. */
+ irc_nick = token[7];
+
+ /*
+ * Token 9 is (user@host), we want to parse the user/host out for
+ * future reference in case we need to kline the host.
+ */
+
+ /* Shift one byte over to discard '('. */
+ irc_user = token[8] + 1;
+ /* Username is everything before the '@'. */
+ if(!(irc_user = strtok(irc_user, "@")))
+ return;
+ /* irc_addr is everything between '@' and closing ')'. */
+ if(!(irc_addr = strtok(NULL , ")")))
+ return;
+
+ do_connect(addr, irc_nick, irc_user, irc_addr, conn_notice);
+}
+
+/*
+ * Return a pointer into CONF_KEYS for the given channel, or NULL if there
+ * is no key.
+ */
+static char *get_chan_key(const char *channel)
+{
+ int ci, ki;
+ char *kp;
+ size_t i, h, len;
+
+ ci = 0;
+
+ if (!CONF_KEYS || !channel)
+ return(NULL);
+
+ len = strlen(CONF_CHANNELS);
+
+ for (i = 0; i < len; i++) {
+ if (CONF_CHANNELS[i] != '#')
+ continue;
+
+ for (h = (i + 1); h < len; h++) {
+ if (CONF_CHANNELS[h] == ',' ||
+ CONF_CHANNELS[h] == ' ' || h == (len - 1)) {
+ if (CONF_CHANNELS[h] == ',')
+ ci++;
+
+ if (h == (len - 1))
+ h++;
+
+ if (strlen(channel) != (h - i))
+ break;
+
+ if (!strncasecmp(&(CONF_CHANNELS[i]),
+ channel, strlen(channel))) {
+ for (kp = CONF_KEYS, ki = 0;
+ (ki < ci) && (kp = strchr(kp, ','));
+ ki++) ; /* empty loop */
+
+ if (kp && *kp == ',')
+ kp++;
+
+ if (kp && *kp)
+ return(kp);
+ else
+ return(NULL);
+ }
+ }
+ }
+ }
+ return(NULL);
+}
+
+/*
+ * Check if channel is one of our configured report channels, and return a
+ * pointer to it if so, or NULL if not.
+ */
+static char *check_channel(const char *channel)
+{
+ size_t i, len, h;
+
+ len = strlen(CONF_CHANNELS);
+
+ for (i = 0; i < len; i++) {
+ if (CONF_CHANNELS[i] != '#')
+ continue;
+
+ for (h = (i + 1); h < len; h++) {
+ if (CONF_CHANNELS[h] == ',' ||
+ CONF_CHANNELS[h] == ' ' || h == (len - 1)) {
+ if(h == (len - 1))
+ h++;
+
+ if (strlen(channel) != (h - i))
+ break;
+
+ if (!strncasecmp(&(CONF_CHANNELS[i]),
+ channel, strlen(channel))) {
+ return(&(CONF_CHANNELS[i]));
+ }
+ }
+ }
+ }
+ return(NULL);
+}
--- /dev/null
+#ifndef IRC_H
+#define IRC_H
+
+#define NICKMAX 32
+#define MSGLENMAX 513
+
+extern void irc_send(char *data, ...);
+extern void irc_kline(char *addr, char *ip);
+extern void irc_cycle(void);
+extern void irc_timer(void);
+
+#endif
--- /dev/null
+/*
+Copyright (C) 2002 Erik Fears
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+
+ Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "setup.h"
+
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#endif
+
+#include <time.h>
+
+#include "config.h"
+#include "extern.h"
+#include "log.h"
+
+
+FILE *logfile;
+
+void log_open(char *filename)
+{
+ logfile = fopen(filename, "a");
+
+ if (!logfile) {
+ perror("Cannot open log file. Aborting.");
+ exit(EXIT_FAILURE);
+ }
+}
+
+void log_close(void)
+{
+ fclose(logfile);
+}
+
+void log(char *data, ...)
+{
+ char data2[513];
+ char buf_present[25];
+ va_list arglist;
+ time_t present;
+ struct tm *tm_present;
+
+ if (!OPT_DEBUG && !logfile)
+ return;
+
+ time(&present);
+ tm_present = gmtime(&present);
+ strftime(buf_present, sizeof(buf_present), "%b %d %H:%M:%S %Y",
+ tm_present);
+
+ va_start(arglist, data);
+ vsnprintf(data2, 512, data, arglist);
+ va_end(arglist);
+
+ if (OPT_DEBUG) {
+ fprintf(stderr, "[%s] %s\n", buf_present, data2);
+ } else {
+ fprintf(logfile, "[%s] %s\n", buf_present, data2);
+ fflush(logfile);
+ }
+}
+
--- /dev/null
+#ifndef LOG_H
+#define LOG_H
+
+extern void log_open(char *filename);
+extern void log_close(void);
+extern void log(char *data, ...);
+
+#endif
+
--- /dev/null
+/*
+Copyright (C) 2002 Erik Fears
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+
+ Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <signal.h>
+#include <unistd.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#include "config.h"
+#include "extern.h"
+#include "irc.h"
+#include "log.h"
+#include "opercmd.h"
+#include "scan.h"
+#include "stats.h"
+#include "options.h"
+
+static RETSIGTYPE do_signal(int signum);
+
+int ALARMED = 0;
+
+unsigned int OPT_DEBUG = 0;
+char *CONFNAME = DEFAULTNAME;
+
+char *CONFDIR = BOPM_ETCDIR;
+char *LOGDIR = BOPM_LOGDIR;
+char *CONFFILE, *LOGFILE, *PIDFILE;
+
+struct sigaction ALARMACTION;
+struct sigaction INTACTION;
+
+int main(int argc, char **argv)
+{
+ char spid[16];
+ int pid, c, lenc, lenl, lenp;
+ FILE *pidout;
+
+ do_stats_init();
+ do_scan_init();
+
+ while (1) {
+ c = getopt(argc, argv, "dc:v:");
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'c':
+ CONFNAME = strdup(optarg);
+ break;
+ case 'd':
+ OPT_DEBUG++;
+ break;
+ case 'v':
+ CONFDIR = strdup(optarg);
+ break;
+ case '?':
+ default:
+ /* Unknown arg, guess we'll just do nothing for now. */
+ break;
+ }
+ }
+
+ lenc = strlen(CONFDIR) + strlen(CONFNAME) + strlen(CONFEXT) + 3;
+ lenl = strlen(LOGDIR) + strlen(CONFNAME) + strlen(LOGEXT) + 3;
+ lenp = strlen(LOGDIR) + strlen(CONFNAME) + strlen(PIDEXT) + 3;
+
+ CONFFILE = (char *) malloc(lenc * sizeof(*CONFFILE));
+ LOGFILE = (char *) malloc(lenl * sizeof(*LOGFILE));
+ PIDFILE = (char *) malloc(lenp * sizeof(*PIDFILE));
+
+ snprintf(CONFFILE, lenc, "%s/%s.%s", CONFDIR, CONFNAME, CONFEXT);
+ snprintf(LOGFILE, lenl, "%s/%s.%s", LOGDIR, CONFNAME, LOGEXT);
+ snprintf(PIDFILE, lenp, "%s/%s.%s", LOGDIR, CONFNAME, PIDEXT);
+
+ /* Fork off. */
+
+ if (!OPT_DEBUG) {
+ if ((pid = fork()) < 0) {
+ perror("fork()");
+ exit(EXIT_FAILURE);
+ } else if (pid != 0) {
+ pidout = fopen(PIDFILE, "w");
+ snprintf(spid, 16, "%d", pid);
+
+ if (pidout) {
+ fwrite(spid, sizeof(char), strlen(spid),
+ pidout);
+ }
+
+ fclose(pidout);
+ _exit(EXIT_SUCCESS);
+ }
+
+ /* Get us in our own process group. */
+ if (setpgid(0, 0) < 0) {
+ perror("setpgid()");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reset file mode. */
+ /* shasta: o+w is BAD, mmkay? */
+ umask(002);
+
+ /* Close file descriptors. */
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ log_open(LOGFILE);
+ } else {
+ log("MAIN -> Debug level %d", OPT_DEBUG);
+ }
+
+ log("MAIN -> BOPM %s started.", VERSION);
+ log("MAIN -> Reading configuration file...");
+
+ config_load(CONFFILE);
+
+ /* Setup alarm & int handlers. */
+
+ ALARMACTION.sa_handler = &(do_signal);
+ ALARMACTION.sa_flags = SA_RESTART;
+ INTACTION.sa_handler = &(do_signal);
+
+ sigaction(SIGALRM, &ALARMACTION, 0);
+ sigaction(SIGINT, &INTACTION, 0);
+
+ /* Ignore SIGPIPE. */
+ signal(SIGPIPE, SIG_IGN);
+
+ alarm(1);
+
+ while (1) {
+ irc_cycle();
+ scan_cycle();
+
+ if (ALARMED) {
+ irc_timer();
+ scan_timer();
+ ALARMED = 0;
+ }
+ }
+
+ if (!OPT_DEBUG)
+ log_close();
+ return(0);
+}
+
+static void do_signal(int signum)
+{
+ switch (signum) {
+ case SIGALRM:
+ ALARMED = 1;
+ alarm(1);
+ break;
+ case SIGINT:
+ log("MAIN -> Caught SIGINT, bye!");
+ exit(0);
+ break;
+ }
+}
--- /dev/null
+/*
+ Copyright (C) 1990-2002 by Jarkko Oikarinen, Thomas Helvey,
+ Douglas A Lewis and the hybrid ircd development team. Minor
+ changes were made by and are copyrighted by Robert Levin of
+ the dancer-ircd development team.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 1
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include <assert.h>
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+# include <stdarg.h>
+# include <string.h>
+#endif
+
+#include <time.h>
+#include <unistd.h>
+
+#include "match.h"
+
+#define ToLower(c) (ToLowerTab[(unsigned char)(c)])
+
+const unsigned char ToLowerTab[] = {
+ 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
+ 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f,
+ ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
+ '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ ':', ';', '<', '=', '>', '?',
+ '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '\\', ']', '^',
+ '_',
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
+ 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
+ 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+ 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+ 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+ 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+ 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+#define MATCH_MAX_CALLS 512 /* ACK! This dies when it's less that this and
+ we have long lines to parse */
+
+/*
+ * Compare if a given string (name) matches the given mask (which can
+ * contain wild cards: '*' - match any number of chars, '?' - match any
+ * single character.
+ *
+ * return 1, if match
+ * 0, if no match
+ */
+
+/*
+ * match()
+ * Iterative matching function, rather than recursive.
+ * Written by Douglas A Lewis (dalewis@acsu.buffalo.edu)
+ *
+ * behavior change - (Thomas Helvey <tomh@inxpress.net>)
+ *
+ * removed escape handling, none of the masks used with this function
+ * should contain an escape '\\' unless you are searching for one, it is no
+ * longer possible to escape * and ?.
+ *
+ * Moved calls rollup to function body, since match isn't recursive there
+ * isn't any reason to have it exposed to the file, this change also has
+ * the added benefit of making match reentrant. :)
+ *
+ * Added asserts, mask and name cannot be null.
+ *
+ * Changed ma and na to unsigned to get rid of casting.
+ *
+ * NOTICE: match is now a boolean operation, not a lexical comparison; if a
+ * line matches a mask, true (1) is returned, otherwise false (0) is
+ * returned.
+ */
+
+int match(const char *mask, const char *name)
+{
+ const unsigned char *m = (const unsigned char *) mask;
+ const unsigned char *n = (const unsigned char *) name;
+ const unsigned char *ma = (const unsigned char *) mask;
+ const unsigned char *na = (const unsigned char *) name;
+ int wild = 0;
+ int calls = 0;
+
+ assert(0 != mask);
+ assert(0 != name);
+
+ if (!mask || !name)
+ return 0;
+
+ while (calls++ < MATCH_MAX_CALLS) {
+ if (*m == '*') {
+ /* XXX - shouldn't need to spin here, the mask
+ * should have been collapsed before match is
+ * called
+ */
+ while (*m == '*')
+ m++;
+
+ wild = 1;
+ ma = m;
+ na = n;
+ }
+
+ if (!*m) {
+ if (!*n)
+ return 1;
+
+ for (m--; (m > (const unsigned char *) mask) && (*m == '?'); m--)
+ ; /* empty loop */
+
+ if ((*m == '*') && (m > (const unsigned char *) mask))
+ return 1;
+
+ if (!wild)
+ return 0;
+
+ m = ma;
+ n = ++na;
+ } else if (!*n) {
+ /* XXX - shouldn't need to spin here, the mask
+ * should have been collapsed before match is
+ * called
+ */
+ while (*m == '*')
+ m++;
+
+ return (*m == 0);
+ }
+
+ if (ToLower(*m) != ToLower(*n) && *m != '?') {
+ if (!wild)
+ return 0;
+
+ m = ma;
+ n = ++na;
+ } else {
+ if (*m)
+ m++;
+
+ if (*n)
+ n++;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * collapse a pattern string into minimal components.
+ * This particular version is "in place", so that it changes the pattern
+ * which is to be reduced to a "minimal" size.
+ *
+ * behavior modification (Thomas Helvey <tomh@inxpress.net>)
+ * Removed mask escapes, we don't escape wildcards or call match on a mask.
+ * This change is somewhat subtle, the old version converted \\*** to \\**,
+ * the new version blindly converts it to \\*.
+ *
+ * Removed code that did a lot of work but achieved nothing, testing showed
+ * that the code in test for '?' produced exactly the same results as code
+ * that ignored '?'. The only thing you can do with a mask is to remove
+ * adjacent '*' characters, attempting anything else breaks the re.
+ *
+ * convert adjacent *'s to a single *
+ */
+char *collapse(char *pattern)
+{
+ char *s = pattern;
+ char *s1;
+ char *t;
+
+ /* XXX - null pointers OK? */
+ if (s) {
+ for (; *s; s++) {
+ if ('*' == *s) {
+ t = s1 = s + 1;
+ while ('*' == *t)
+ ++t;
+
+ if (s1 != t) {
+ while ((*s1++ = *t++))
+ ; /* empty loop */
+ }
+ }
+ }
+ }
+ return pattern;
+}
+
--- /dev/null
+#ifndef MATCH_H
+#define MATCH_H
+
+int match(const char *mask, const char *name);
+char *collapse(char *pattern);
+
+#endif
--- /dev/null
+/*
+Copyright (C) 2002 Erik Fears
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+
+ Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <time.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+#include "config.h"
+#include "extern.h"
+
+/*
+ * Split a time_t into an English-language explanation of how
+ * much time it represents, e.g. "2 hours 45 minutes 8 seconds"
+ */
+char *dissect_time(time_t time)
+{
+ static char buf[64];
+ unsigned int years, weeks, days, hours, minutes, seconds;
+
+ years = weeks = days = hours = minutes = seconds = 0;
+
+ while (time >= 60 * 60 * 24 * 365) {
+ time -= 60 * 60 * 24 * 365;
+ years++;
+ }
+
+ while (time >= 60 * 60 * 24 * 7) {
+ time -= 60 * 60 * 24 * 7;
+ weeks++;
+ }
+
+ while (time >= 60 * 60 * 24) {
+ time -= 60 * 60 * 24;
+ days++;
+ }
+
+ while (time >= 60 * 60) {
+ time -= 60 * 60;
+ hours++;
+ }
+
+ while (time >= 60) {
+ time -= 60;
+ minutes++;
+ }
+
+ seconds = time;
+
+ if (years) {
+ snprintf(buf, sizeof(buf),
+ "%d year%s, %d week%s, %d day%s, %02d:%02d:%02d",
+ years, years == 1 ? "" : "s", weeks,
+ weeks == 1 ? "" : "s", days, days == 1 ? "" : "s",
+ hours, minutes, seconds);
+ } else if (weeks) {
+ snprintf(buf, sizeof(buf),
+ "%d week%s, %d day%s, %02d:%02d:%02d", weeks,
+ weeks == 1 ? "" : "s", days, days == 1 ? "" : "s",
+ hours, minutes, seconds);
+ } else if (days) {
+ snprintf(buf, sizeof(buf), "%d day%s, %02d:%02d:%02d",
+ days, days == 1 ? "" : "s", hours, minutes, seconds);
+ } else if (hours) {
+ if (minutes || seconds) {
+ snprintf(buf, sizeof(buf),
+ "%d hour%s, %d minute%s, %d second%s", hours,
+ hours == 1 ? "" : "s", minutes,
+ minutes == 1 ? "" : "s", seconds,
+ seconds == 1 ? "" : "s");
+ } else {
+ snprintf(buf, sizeof(buf), "%d hour%s", hours,
+ hours == 1 ? "" : "s");
+ }
+ } else if (minutes) {
+ snprintf(buf, sizeof(buf), "%d minute%s, %d second%s",
+ minutes, minutes == 1 ? "" : "s", seconds,
+ seconds == 1 ? "" : "s");
+ } else {
+ snprintf(buf, sizeof(buf), "%d second%s", seconds,
+ seconds == 1 ? "" : "s");
+ }
+
+ return(buf);
+}
+
+/*
+ * Strip leading/tailing characters from null terminated str and return a
+ * pointer to the new string.
+ */
+
+char *clean(char *str)
+{
+ size_t i;
+ /* Position of last non space. */
+ int lastnon;
+ /* Position of first non space. */
+ int firstnon;
+
+ lastnon = 0;
+ firstnon = 0;
+
+ /* Dont need to deal with 1 character */
+ if (strlen(str) <= 1)
+ return str;
+
+ for (i = 0; i < strlen(str); i++) {
+ if (firstnon == 0 && str[i] != ' ')
+ firstnon = i;
+ if (str[i] != ' ')
+ lastnon = i;
+ }
+
+ /* Null terminate before the tailing spaces. */
+ str[lastnon + 1] = 0;
+
+ /* Return pointer to point after leading spaces. */
+ return(str + (firstnon - 1));
+}
--- /dev/null
+#ifndef MISC_H
+#define MISC_H
+
+extern char *dissect_time(time_t time);
+extern char *clean(char *str);
+
+#endif
--- /dev/null
+/*
+Copyright (C) 2002 Erik Fears
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+
+ Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "setup.h"
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <time.h>
+
+#include "irc.h"
+#include "log.h"
+#include "misc.h"
+#include "opercmd.h"
+#include "scan.h"
+#include "config.h"
+#include "extern.h"
+
+static void checkoper(const char *nick, const char *param,
+ const char *target, unsigned int cmd_type);
+static void delete_command(unsigned int index);
+
+struct command cmd_stack[MAXCMD];
+time_t LAST_REAP_TIME;
+
+void do_oper_cmd(const char *nick, const char *cmd, const char *param,
+ const char *target)
+{
+ if (strncasecmp(cmd, "CHECK", 5) == 0) {
+ if (!param) {
+ irc_send("PRIVMSG %s :*ring* Hello, cluephone for "
+ "%s, you need to specify a host or IP address "
+ "if you want me to do any checking, yo.",
+ target, nick);
+ return;
+ }
+ checkoper(nick, param, target, CMD_CHECK);
+ } else {
+ irc_send("PRIVMSG %s :Sorry, I don't know how to %s, %s.",
+ target, cmd, nick);
+ }
+ return;
+}
+
+static void checkoper(const char *nick, const char *param,
+ const char *target, unsigned int cmd_type)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAXCMD; i++) {
+ if (cmd_stack[i].type == CMD_NONE) {
+ cmd_stack[i].type = cmd_type;
+ cmd_stack[i].param = strdup(param);
+ cmd_stack[i].target = strdup(target);
+ strncpy(cmd_stack[i].nick, nick, NICKMAX);
+ break;
+ }
+ }
+
+ if (i == MAXCMD) {
+ irc_send("PRIVMSG %s :Too many queued commands, try later.",
+ target);
+ } else {
+ irc_send("USERHOST %s", nick);
+ }
+}
+
+/*
+ * Check a userhost to see if it is from an oper, we're looking for the
+ * asterisk (*) between the nick and the ident as below:
+ * :grifferz*=+goats@pc-62-30-219-54-pb.blueyonder.co.uk
+ */
+void check_userhost(const char *userhost)
+{
+ int c, oper;
+ char *tmp;
+
+ oper = 0;
+
+ tmp = strchr(userhost, '=');
+
+ if (!tmp) {
+ /* Looks like they quit, oh well, just ignore it. */
+ return;
+ }
+
+ if (*(tmp - 1) == '*') {
+ oper = 1;
+ tmp--;
+ }
+
+ /* Null terminate userhost so we have a nickname there now. */
+ *tmp = '\0';
+
+ /* Go through the command list looking for commands by this person */
+ for (c = 0; c < MAXCMD; c++) {
+ if (!strcasecmp(userhost + 1, cmd_stack[c].nick)) {
+ if (oper) {
+ /* Do the command. */
+ if (cmd_stack[c].type == CMD_CHECK) {
+ do_manual_check(&cmd_stack[c]);
+ }
+ } else {
+ irc_send("PRIVMSG %s :You are not an IRC "
+ "Operator. Go away.",
+ cmd_stack[c].target);
+ }
+
+ delete_command(c);
+ }
+ }
+}
+
+/*
+ * Set a command to CMD_NONE and free all resources it used, so that it
+ * can be reused later.
+ */
+static void delete_command(unsigned int index)
+{
+ if (cmd_stack[index].type == CMD_NONE)
+ return;
+
+ cmd_stack[index].type = CMD_NONE;
+ cmd_stack[index].added = 0;
+ free(cmd_stack[index].param);
+ free(cmd_stack[index].target);
+}
+
+/*
+ * Delete any commands which are more than 2 minutes old, they are almost certainly
+ * no longer relevant.
+ */
+void reap_commands(time_t present)
+{
+ int c;
+
+ for (c = 0; c < MAXCMD; c++) {
+ if (cmd_stack[c].type != CMD_NONE &&
+ (present - cmd_stack[c].added >= 120)) {
+ irc_send("PRIVMSG %s :Reaping dead command from "
+ "%s of type %u with param '%s', added %s ago.",
+ cmd_stack[c].nick, cmd_stack[c].type,
+ cmd_stack[c].param,
+ dissect_time(present - cmd_stack[c].added));
+ delete_command(c);
+ }
+ }
+}
--- /dev/null
+#ifndef OPERCMD_H
+#define OPERCMD_H
+
+struct command {
+ /* Types defined below. */
+ /* THIS MUST BE CMD_NONE FOR AN EMPTY COMMAND! */
+ unsigned int type;
+
+ /* Command parameter.
+ * <erik> but i cant think of any commands bopm will ever have that is
+ * multiple parameters
+ */
+ char *param;
+
+ /* Who ordered it. */
+ char nick[NICKMAX];
+
+ /* Where the reply is to be sent. */
+ char *target;
+
+ /*
+ * When it was added, because we might need to remove it if it does
+ * not get executed.
+ */
+ time_t added;
+};
+
+/* Allow 5 outstanding commands. */
+#define MAXCMD 5
+
+/* One for each oper command we support. */
+#define CMD_NONE 0
+#define CMD_CHECK 1
+
+extern void do_oper_cmd(const char *nick, const char *cmd,
+ const char *param, const char *target);
+extern void check_userhost(const char *userhost);
+extern void reap_commands(time_t present);
+
+#endif
--- /dev/null
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+/* The default name for conf, log, pid files */
+#define DEFAULTNAME "bopm"
+
+/* file extensions */
+/* config */
+#define CONFEXT "conf"
+
+/* log file */
+#define LOGEXT "log"
+
+/* PID file */
+#define PIDEXT "pid"
+
+/* Defines time in which bot will timeout * if no data is received
+ * (default 15 min) */
+#define NODATA_TIMEOUT 900
+
+#define MAX_POLL 1024
+
+/* Client buffer size for scan
+ * Increase as needed, but should never need to be be over 512 bytes */
+
+#define SCANBUFFER 128
+
+/* Max data to read from any port before closing the connection
+ * (to avoid flood attempts)
+ */
+#define MAXREAD 4096
+
+#endif /* OPTIONS_H */
--- /dev/null
+/*
+Copyright (C) 2002 Erik Fears
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+
+ Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "setup.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
+#endif
+
+#include "config.h"
+#include "irc.h"
+#include "log.h"
+#include "opercmd.h"
+#include "scan.h"
+#include "stats.h"
+#include "dnsbl.h"
+#include "extern.h"
+#include "options.h"
+
+static void scan_memfail(void);
+static void scan_establish(scan_struct *conn);
+static void scan_check(void);
+static void scan_negfail(scan_struct *conn);
+static void scan_readready(scan_struct *conn);
+static void scan_read(scan_struct *conn);
+static void scan_openproxy(scan_struct *conn);
+static void scan_writeready(scan_struct *conn);
+static void scan_add(scan_struct *newconn);
+static void scan_del(scan_struct *delconn);
+static int scan_w_squid(struct scan_struct *conn);
+static int scan_w_socks4(struct scan_struct *conn);
+static int scan_w_socks5(struct scan_struct *conn);
+static int scan_w_cisco(struct scan_struct *conn);
+static int scan_w_wingate(struct scan_struct *conn);
+
+/* Linked list head for connections. */
+struct scan_struct *CONNECTIONS = 0;
+
+char SENDBUFF[513];
+
+/* Keep track of numbers of open FD's, for use with FDLIMIT. */
+unsigned int FD_USE = 0;
+
+
+/*
+ * Protocol Name, Port, Write Handler, Read Handler
+ *
+ * Always scan Cisco before Wingate, because cisco routers only allow 4
+ * connects at once.
+ */
+
+protocol_hash SCAN_PROTOCOLS[] = {
+
+ {"HTTP" , 8080, &(scan_w_squid), 0 ,0 },
+/* {"HTTP" , 8001, &(scan_w_squid), 0 ,0 }, */
+/* {"HTTP" , 8000, &(scan_w_squid), 0 ,0 }, */
+ {"HTTP" , 3128, &(scan_w_squid), 0 ,0 },
+ {"HTTP" , 80, &(scan_w_squid), 0 ,0 },
+ {"Socks4" , 1080, &(scan_w_socks4), 0 ,0 },
+ {"Socks5" , 1080, &(scan_w_socks5), 0 ,0 },
+ {"Cisco" , 23, &(scan_w_cisco), 0 ,0 },
+ {"Wingate" , 23, &(scan_w_wingate), 0 ,0 },
+};
+
+size_t SCAN_NUMPROTOCOLS;
+
+void do_scan_init(void)
+{
+ SCAN_NUMPROTOCOLS = sizeof(SCAN_PROTOCOLS) / sizeof(protocol_hash);
+}
+
+
+static void scan_memfail(void)
+{
+ log("SCAN -> Error allocating memory.");
+ exit(EXIT_FAILURE);
+}
+
+
+
+/*
+ * IRC client has receieved a +c notice from the remote server. scan_connect
+ * is called with the connecting IP, where we will begin to establish the
+ * proxy testing.
+ */
+
+void scan_connect(char *addr, char *irc_addr, char *irc_nick,
+ char *irc_user, int verbose, char *conn_notice)
+{
+ size_t i;
+ scan_struct *newconn;
+
+ if (OPT_DEBUG) {
+ log("SCAN -> checking user %s!%s@%s", irc_nick, irc_user,
+ irc_addr);
+ }
+
+ /*
+ * Loop through the protocols creating a seperate connection struct
+ * for each port/protocol.
+ */
+
+ for (i = 0; i < SCAN_NUMPROTOCOLS; i++) {
+ newconn = malloc(sizeof(*newconn));
+
+ if (!newconn)
+ scan_memfail();
+
+ newconn->addr = strdup(addr);
+ newconn->irc_addr = strdup(irc_addr);
+ newconn->irc_nick = strdup(irc_nick);
+ newconn->irc_user = strdup(irc_user);
+ /* This is allocated later on in scan_establish to save on
+ * memory.
+ */
+ newconn->data = 0;
+ newconn->verbose = verbose;
+ newconn->bytes_read = 0;
+ newconn->fd = 0;
+
+ if (conn_notice)
+ newconn->conn_notice = strdup(conn_notice);
+ else
+ newconn->conn_notice = 0;
+
+ /*
+ * Give struct a link to information about the protocol it
+ * will be handling.
+ */
+ newconn->protocol = &(SCAN_PROTOCOLS[i]);
+
+ memset(&(newconn->sockaddr), 0, sizeof(struct sockaddr_in));
+
+ /* Fill in sockaddr with information about remote host. */
+ newconn->sockaddr.sin_family = AF_INET;
+ newconn->sockaddr.sin_port =
+ htons(newconn->protocol->port);
+ newconn->sockaddr.sin_addr.s_addr = inet_addr(addr);
+ /* Queue connection. */
+ newconn->state = STATE_UNESTABLISHED;
+
+ /* Add struct to list of connections. */
+ scan_add(newconn);
+
+ /* If we have available FD's, overide queue. */
+ if (FD_USE < CONF_FDLIMIT)
+ scan_establish(newconn);
+ else if (OPT_DEBUG >= 3) {
+ log("SCAN -> File Descriptor limit (%d) reached, "
+ "queuing scan for %s", CONF_FDLIMIT,
+ newconn->addr);
+ }
+ }
+}
+
+/*
+ * Get FD for new socket, bind to interface and connect() (non blocking) then
+ * set conn to ESTABLISHED for write check.
+ */
+static void scan_establish(scan_struct *conn)
+{
+ /* For local bind() */
+ struct sockaddr_in SCAN_LOCAL;
+
+ memset(&SCAN_LOCAL, 0, sizeof(struct sockaddr_in));
+
+ /* Setup SCAN_LOCAL for local bind() */
+ if (CONF_BINDSCAN) {
+ if (!inet_aton(CONF_BINDSCAN, &(SCAN_LOCAL.sin_addr))) {
+ log("SCAN -> bind(): %s is an invalid address",
+ CONF_BINDSCAN);
+ exit(EXIT_FAILURE);
+ }
+
+ SCAN_LOCAL.sin_family = AF_INET;
+ SCAN_LOCAL.sin_port = 0;
+ }
+
+ /* Request file descriptor for socket. */
+ conn->fd = socket(PF_INET, SOCK_STREAM, 0);
+
+ /* Increase global FD Use counter. */
+ FD_USE++;
+
+ /* If error, mark connection for close. */
+ if (conn->fd == -1) {
+ log("SCAN -> Error allocating file descriptor.");
+ conn->state = STATE_CLOSED;
+ return;
+ }
+
+ /* Bind to specific interface designated in conf file. */
+ if (CONF_BINDSCAN) {
+ if (bind(conn->fd, (struct sockaddr *)&SCAN_LOCAL,
+ sizeof(struct sockaddr_in)) == -1) {
+ switch (errno) {
+ case EACCES:
+ log("SCAN -> bind(): No access to bind to %s",
+ CONF_BINDSCAN);
+ break;
+ default:
+ log("SCAN -> bind(): Error binding to %s",
+ CONF_BINDSCAN);
+ break;
+
+ }
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Log create time of connection for timeouts. */
+ time(&(conn->create_time));
+ /* Flag conn established (for write). */
+ conn->state = STATE_ESTABLISHED;
+ /* Set socket non blocking. */
+ fcntl(conn->fd, F_SETFL, O_NONBLOCK);
+ /* Connect! */
+ connect(conn->fd, (struct sockaddr *) &(conn->sockaddr),
+ sizeof(conn->sockaddr));
+
+ /* Allocate memory for the scan buffer. */
+ conn->data = malloc((SCANBUFFER + 1) * sizeof(char));
+ conn->datasize = 0;
+
+}
+
+
+/*
+ * Pass one cycle to the proxy scanner so it can do neccessary functions like
+ * testing for sockets to be written to and read from.
+ */
+
+void scan_cycle(void)
+{
+ scan_check();
+}
+
+
+/*
+ * Test for sockets to be written/read to.
+ */
+
+static void scan_check(void)
+{
+#ifdef HAVE_SYS_POLL_H
+ /* MAX_POLL is defined in options.h */
+ static struct pollfd ufds[MAX_POLL];
+ unsigned long size, i;
+#else /* select() */
+ fd_set w_fdset;
+ fd_set r_fdset;
+ struct timeval scan_timeout;
+ int highfd;
+#endif /* HAVE_SYS_POLL_H */
+
+ struct scan_struct *ss;
+
+ if (!CONNECTIONS)
+ return;
+
+#ifdef HAVE_SYS_POLL_H
+ size = 0;
+
+ /* Get size of list we're interested in. */
+ for (ss = CONNECTIONS; ss; ss = ss->next) {
+ if (ss->state != STATE_CLOSED &&
+ ss->state != STATE_UNESTABLISHED)
+ size++;
+ }
+
+ i = 0;
+
+ /* Setup each element now. */
+ for (ss = CONNECTIONS; ss; ss = ss->next) {
+ if (ss->state == STATE_CLOSED ||
+ ss->state == STATE_UNESTABLISHED)
+ continue;
+
+ ufds[i].events = 0;
+ ufds[i].revents = 0;
+ ufds[i].fd = ss->fd;
+
+ /* Check for HUNG UP. */
+ ufds[i].events |= POLLHUP;
+ /* Check for INVALID FD */
+ ufds[i].events |= POLLNVAL;
+
+ switch (ss->state) {
+ case STATE_ESTABLISHED:
+ /* Check for NO BLOCK ON WRITE. */
+ ufds[i].events |= POLLOUT;
+ break;
+ case STATE_SENT:
+ /* Check for data to be read. */
+ ufds[i].events |= POLLIN;
+ break;
+ }
+
+ if (++i >= MAX_POLL)
+ break;
+ }
+
+#else /* select() */
+ FD_ZERO(&w_fdset);
+ FD_ZERO(&r_fdset);
+ highfd = 0;
+
+ /* Add connections to appropriate sets. */
+
+ for (ss = CONNECTIONS; ss; ss = ss->next) {
+ if (ss->state == STATE_ESTABLISHED) {
+ if(ss->fd > highfd)
+ highfd = ss->fd;
+
+ FD_SET(ss->fd, &w_fdset);
+ continue;
+ }
+
+ if (ss->state == STATE_SENT) {
+ if (ss->fd > highfd)
+ highfd = ss->fd;
+
+ FD_SET(ss->fd, &r_fdset);
+ }
+ }
+
+ /* No timeout. */
+ scan_timeout.tv_sec = 0;
+ scan_timeout.tv_usec= 0;
+
+#endif /* HAVE_SYS_POLL_H */
+
+
+#ifdef HAVE_SYS_POLL_H
+ switch (poll(ufds, size, 0)) {
+#else /* select() */
+ switch (select((highfd + 1), &r_fdset, &w_fdset, 0, &scan_timeout)) {
+#endif /* HAVE_SYS_POLL_H */
+ case -1:
+ /* error in select/poll */
+ return;
+ case 0:
+ break;
+ default:
+ /* Pass pointer to connection to handler. */
+
+#ifdef HAVE_SYS_POLL_H
+ for (ss = CONNECTIONS; ss; ss = ss->next) {
+ for (i = 0; i < size; i++) {
+ if (ufds[i].fd == ss->fd) {
+ if (ufds[i].revents & POLLIN)
+ scan_readready(ss);
+
+ if (ufds[i].revents & POLLOUT)
+ scan_writeready(ss);
+
+ if (ufds[i].revents & POLLHUP)
+ scan_negfail(ss);
+
+ break;
+ }
+ }
+ }
+#else
+
+ for (ss = CONNECTIONS; ss; ss = ss->next) {
+ if ((ss->state == STATE_ESTABLISHED) &&
+ FD_ISSET(ss->fd, &w_fdset))
+ scan_writeready(ss);
+
+ if ((ss->state == STATE_SENT) &&
+ FD_ISSET(ss->fd, &r_fdset))
+ scan_readready(ss);
+ }
+#endif /* HAVE_SYS_POLL_H */
+ }
+}
+
+
+/*
+ * Negotiation failed - Read returned false, we discard the connection as a
+ * closed proxy to save CPU.
+ */
+static void scan_negfail(scan_struct *conn)
+{
+ if (conn->verbose) {
+ irc_send("PRIVMSG %s :%s (%d): Connection to %s closed, "
+ "negotiation failed (%d bytes read)", CONF_CHANNELS,
+ conn->protocol->type, conn->protocol->port,
+ conn->irc_addr, conn->bytes_read);
+ }
+ conn->state = STATE_CLOSED;
+}
+
+/*
+ * Poll or select returned back that this connection is ready for read.
+ */
+static void scan_readready(scan_struct *conn)
+{
+ char c;
+
+ while(1) {
+ switch (read(conn->fd, &c, 1)) {
+ case 0:
+ case -1:
+ return;
+
+ default:
+ conn->bytes_read++;
+ if (c == 0 || c == '\r')
+ continue;
+
+ if(c == '\n') {
+ conn->data[conn->datasize] = 0;
+ conn->datasize = 0;
+ scan_read(conn);
+ continue;
+ }
+
+ /* Avoid freezing from reading endless data. */
+ if (conn->bytes_read >= MAXREAD) {
+ conn->state = STATE_CLOSED;
+ return;
+ }
+
+ if (conn->datasize < SCANBUFFER) {
+ /* -1 to pad for null term. */
+ conn->data[(++conn->datasize) - 1] = c;
+ }
+ }
+ }
+}
+
+/*
+ * Read one line in from remote, check line against target line.
+ */
+static void scan_read(scan_struct *conn)
+{
+ if (OPT_DEBUG >= 3)
+ log("SCAN -> Checking data from %s [%s:%d] against "
+ "TARGET_STRING: %s", conn->addr, conn->protocol->type,
+ conn->protocol->port, conn->data);
+ if (strstr(conn->data, CONF_TARGET_STRING))
+ scan_openproxy(conn);
+}
+
+/*
+ * Test proved positive for open proxy.
+ */
+static void scan_openproxy(scan_struct *conn)
+{
+ scan_struct *ss;
+
+ irc_kline(conn->irc_addr, conn->addr);
+
+ if (CONF_DNSBL_FROM && CONF_DNSBL_TO && CONF_SENDMAIL &&
+ !conn->verbose)
+ dnsbl_report(conn);
+
+ log("OPEN PROXY -> %s: %s!%s@%s (%d)", conn->protocol->type,
+ conn->irc_nick, conn->irc_user, conn->irc_addr,
+ conn->protocol->port);
+
+ irc_send("PRIVMSG %s :%s (%d): OPEN PROXY -> %s!%s@%s",
+ CONF_CHANNELS, conn->protocol->type, conn->protocol->port,
+ conn->irc_nick, conn->irc_user, conn->irc_addr);
+
+ /* Increase number OPEN (insecure) of this type. */
+ conn->protocol->stat_numopen++;
+
+ conn->state = STATE_CLOSED;
+
+ /*
+ * Flag connections with the same addr CLOSED aswell, but only if this
+ * is not a verbose check. When it is verbose/manual, we care about
+ * all types of proxy. When it is automatic (i.e. when a user
+ * connects) we want them killed quickly sow e can move on.
+ */
+ if (!conn->verbose) {
+ for (ss = CONNECTIONS;ss;ss = ss->next) {
+ if (conn->sockaddr.sin_addr.s_addr == ss->sockaddr.sin_addr.s_addr)
+ ss->state = STATE_CLOSED;
+ }
+ }
+}
+
+/*
+ * Poll or select returned back that this connect is ready for write.
+ */
+static void scan_writeready(scan_struct *conn)
+{
+ /* If write returns true, flag STATE_SENT. */
+ if ((*conn->protocol->w_handler)(conn))
+ conn->state = STATE_SENT;
+
+ /* Increase number attempted negotiated of this type. */
+ conn->protocol->stat_num++;
+}
+
+/*
+ * Link struct to connection list.
+ */
+static void scan_add(scan_struct *newconn)
+{
+ scan_struct *ss;
+
+ /* Only item in list. */
+
+ if (!CONNECTIONS) {
+ newconn->next = 0;
+ CONNECTIONS = newconn;
+ } else {
+ /* Link to end of list. */
+ for(ss = CONNECTIONS; ss; ss = ss->next) {
+ if (!ss->next) {
+ newconn->next = 0;
+ ss->next = newconn;
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Unlink struct from connection list and free its memory.
+ */
+static void scan_del(scan_struct *delconn)
+{
+ scan_struct *ss;
+ scan_struct *lastss;
+
+ if (delconn->fd > 0)
+ close(delconn->fd);
+
+ /* 1 file descriptor freed up for use. */
+ if(delconn->fd)
+ FD_USE--;
+
+ lastss = 0;
+
+ for(ss = CONNECTIONS; ss; ss = ss->next) {
+ if (ss == delconn) {
+ /* Link around deleted node */
+ if (lastss == 0)
+ CONNECTIONS = ss->next;
+ else
+ lastss->next = ss->next;
+
+ free(ss->addr);
+ free(ss->irc_addr);
+ free(ss->irc_nick);
+ free(ss->irc_user);
+
+ if (ss->conn_notice)
+ free(ss->conn_notice);
+
+ /* If it's established, free the scan buffer. */
+ if (delconn->data)
+ free(delconn->data);
+
+ free(ss);
+
+ break;
+ }
+
+ lastss = ss;
+ }
+
+}
+
+/*
+ * Alarm signaled, loop through connections and remove any we don't need
+ * anymore.
+ */
+void scan_timer()
+{
+ scan_struct *ss;
+ scan_struct *nextss;
+
+ time_t present;
+ time(&present);
+
+ /*
+ * Check for timed out connections and also check if queued
+ * (UNESTABLISHED) connections can be established now.
+ */
+
+ for (ss = CONNECTIONS; ss;) {
+ if (ss->state == STATE_UNESTABLISHED) {
+ if (FD_USE < CONF_FDLIMIT) {
+ scan_establish(ss);
+ if (OPT_DEBUG >= 3) {
+ log("SCAN -> File descriptor free, "
+ "continuing queued scan on %s",
+ ss->addr);
+ }
+ } else {
+ ss = ss->next;
+
+ /*
+ * Continue to avoid timeout checks on an
+ * unestablished connection.
+ */
+ continue;
+ }
+ }
+
+ if (((present - ss->create_time) >= CONF_TIMEOUT) ||
+ (ss->state == STATE_CLOSED)) {
+ /* State closed or timed out, remove */
+ if (ss->verbose && (ss->state != STATE_CLOSED)) {
+ if (ss->bytes_read) {
+ irc_send("PRIVMSG %s :%s (%d): "
+ "Negotiation to %s timed out "
+ "(%d bytes read).",
+ CONF_CHANNELS,
+ ss->protocol->type,
+ ss->protocol->port,
+ ss->irc_addr, ss->bytes_read);
+ } else {
+ irc_send("PRIVMSG %s :%s (%d): "
+ "Negotiation to %s timed out "
+ "(No response).",
+ CONF_CHANNELS,
+ ss->protocol->type,
+ ss->protocol->port,
+ ss->irc_addr);
+ }
+ }
+
+ nextss = ss->next;
+ scan_del(ss);
+ ss = nextss;
+ continue;
+ }
+ ss = ss->next;
+ }
+}
+
+
+
+/*
+ * Function for handling open HTTP data.
+ *
+ * Return 1 on success.
+ */
+static int scan_w_squid(struct scan_struct *conn)
+{
+ snprintf(SENDBUFF, 128, "CONNECT %s:%d HTTP/1.0\r\n\r\n",
+ CONF_SCANIP, CONF_SCANPORT);
+ send(conn->fd, SENDBUFF, strlen(SENDBUFF), 0);
+
+ return(1);
+}
+
+
+/*
+ * CONNECT request byte order for socks4
+ *
+ * +----+----+----+----+----+----+----+----+----+----+....+----+
+ * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
+ * +----+----+----+----+----+----+----+----+----+----+....+----+
+ * # of bytes: 1 1 2 4 variable 1
+ *
+ * VN = Version, CD = Command Code (1 is connect request)
+ */
+static int scan_w_socks4(struct scan_struct *conn)
+{
+ struct in_addr addr;
+ unsigned long laddr;
+ int len;
+
+ if (inet_aton(CONF_SCANIP, &addr) == 0) {
+ log("SCAN -> scan_w_socks4 : %s is not a valid IP",
+ CONF_SCANIP);
+ }
+
+ laddr = htonl(addr.s_addr);
+
+ len = snprintf(SENDBUFF, 512, "%c%c%c%c%c%c%c%c%c", 4, 1,
+ (((unsigned short) CONF_SCANPORT) >> 8) & 0xFF,
+ (((unsigned short) CONF_SCANPORT) & 0xff),
+ (char) (laddr >> 24) & 0xFF, (char) (laddr >> 16) & 0xFF,
+ (char) (laddr >> 8) & 0xFF, (char) laddr & 0xFF, 0);
+
+ send(conn->fd, SENDBUFF, len, 0);
+ return(1);
+}
+
+/*
+ * Send version authentication selection message to socks5
+ *
+ * +----+----------+----------+
+ * |VER | NMETHODS | METHODS |
+ * +----+----------+----------+
+ * | 1 | 1 | 1 to 255 |
+ * +----+----------+----------+
+ *
+ * VER always contains 5, for socks version 5
+ * Method 0 is 'No authentication required'
+ */
+static int scan_w_socks5(struct scan_struct *conn)
+{
+ int len;
+
+ /* Version 5, 1 number of methods, 0 method (no auth). */
+ len = snprintf(SENDBUFF, 512, "%c%c%c", 5, 1, 0);
+ send(conn->fd, SENDBUFF, len, 0);
+
+ return(1);
+}
+
+/*
+ * Cisco scanning
+ *
+ * Some cisco routers have 'cisco' set as password which allow open telnet
+ * relay. Attempt to connect using cisco as a password, then give command for
+ * telnet to the scanip/scanport
+ */
+static int scan_w_cisco(struct scan_struct *conn)
+{
+ int len;
+
+ len = snprintf(SENDBUFF, 512, "cisco\r\n");
+ send(conn->fd, SENDBUFF, len, 0);
+
+ len = snprintf(SENDBUFF, 512, "telnet %s %d\r\n", CONF_SCANIP,
+ CONF_SCANPORT);
+ send(conn->fd, SENDBUFF, len, 0);
+
+ return(1);
+}
+
+
+/*
+ * Open wingates require no authentication, they will send a prompt when
+ * connect. No need to send any data.
+ */
+static int scan_w_wingate(struct scan_struct *conn)
+{
+ int len;
+
+ len = snprintf(SENDBUFF, 512, "%s:%d\r\n", CONF_SCANIP,
+ CONF_SCANPORT);
+ send(conn->fd, SENDBUFF, len, 0);
+
+ return(1);
+}
+
+
+/*
+ * Manually check a host for proxies.
+ */
+void do_manual_check(struct command *c)
+{
+ struct hostent *he;
+ char *ip;
+
+ if (!(he = gethostbyname(c->param))) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ irc_send("PRIVMSG %s :Host '%s' is unknown, dummy.",
+ c->target, c->param);
+ return;
+ case NO_ADDRESS:
+ irc_send("PRIVMSG %s :The specified name '%s' "
+ "exists, but has no address.", c->target,
+ c->param);
+ return;
+ case NO_RECOVERY:
+ irc_send("PRIVMSG %s :An unrecoverable error "
+ "occured whilst resolving '%s'.", c->target,
+ c->param);
+ return;
+ case TRY_AGAIN:
+ irc_send("PRIVMSG %s :A temporary nameserver "
+ "error occurred.", c->target);
+ return;
+ default:
+ irc_send("PRIVMSG %s :Unknown error resolving "
+ "'%s' (sorry!)", c->target, c->param);
+ return;
+ }
+ }
+
+ ip = inet_ntoa(*((struct in_addr *) he->h_addr));
+
+ irc_send("PRIVMSG %s :Checking %s [%s] for open proxies at "
+ "request of %s...", CONF_CHANNELS, c->param, ip, c->nick);
+
+ if(CONF_DNSBL_ZONE)
+ dnsbl_check(ip, "*", "*", c->param);
+
+ /* Scan using verbose. */
+ scan_connect(ip, c->param, "*", "*", 1, 0);
+}
+
--- /dev/null
+#ifndef SCAN_H
+#define SCAN_H
+
+typedef struct protocol_hash protocol_hash;
+typedef struct scan_struct scan_struct;
+typedef int (*scan_function) (scan_struct *);
+
+#define STATE_UNESTABLISHED 1
+#define STATE_ESTABLISHED 2
+#define STATE_SENT 3
+#define STATE_CLOSED 4
+
+struct protocol_hash
+{
+ char *type; /* Plaintext name of protocol to scan */
+ int port; /* Port to scan protocol on */
+ scan_function w_handler; /* Function to handle specific protocol */
+ unsigned int stat_num;
+ unsigned int stat_numopen;
+};
+
+struct scan_struct
+{
+ scan_struct *next;
+ char *addr; /* Address of remote host (IP) */
+ char *irc_addr; /* Hostname of user on IRC (for kline) */
+ char *irc_nick; /* Nickname of user on IRC (for logging) */
+ char *irc_user; /* Username of user on IRC (for logging) */
+ char *conn_notice; /* original server notice for this connect, used
+ * for evidence */
+
+ char *data; /* Buffered data */
+ int datasize; /* Length of buffered data */
+
+ int fd; /* File descriptor of socket */
+ struct sockaddr_in sockaddr; /* holds information about remote host for socket() */
+ time_t create_time; /* Creation time, for timeout */
+ int state; /* Status of scan */
+ unsigned int bytes_read; /* Number of bytes received */
+ protocol_hash *protocol; /* Pointer to protocol type */
+ int verbose; /* report progress to channel verbosely? */
+};
+
+extern void do_scan_init(void);
+extern void scan_connect(char *addr, char *irc_addr, char *irc_nick,
+ char *irc_user, int verbose, char *conn_notice);
+extern void scan_cycle(void);
+extern void scan_timer(void);
+extern void do_manual_check(struct command *c);
+
+#endif
--- /dev/null
+/* src/setup.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the `inet_aton' function. */
+#undef HAVE_INET_ATON
+
+/* Define if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define if you have the <sys/poll.h> header file. */
+#undef HAVE_SYS_POLL_H
+
+/* Define if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your <sys/time.h> declares `struct tm'. */
+#undef TM_IN_SYS_TIME
+
+/* Version number of package */
+#undef VERSION
+
+/* Define if you wish to force use of select() even when poll() may be present
+ */
+#undef WITH_SELECT
+
+/* Define if you are using Unreal ircd (any version) */
+#undef WITH_UNREAL
+
+/* Define if your processor stores words with the most significant byte first
+ (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#undef size_t
--- /dev/null
+/*
+Copyright (C) 2002 Erik Fears
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+
+ Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "setup.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stdio.h>
+#include <time.h>
+
+#include "irc.h"
+#include "misc.h"
+#include "stats.h"
+#include "config.h"
+#include "extern.h"
+#include "opercmd.h"
+#include "scan.h"
+#include "stats.h"
+
+time_t STAT_START_TIME;
+unsigned int STAT_NUM_CONNECTS;
+unsigned int STAT_DNSBL_MATCHES;
+
+extern protocol_hash SCAN_PROTOCOLS[];
+extern size_t SCAN_NUMPROTOCOLS;
+
+extern unsigned int FD_USE;
+
+void do_stats_init(void)
+{
+ STAT_START_TIME = time(NULL);
+ STAT_NUM_CONNECTS = 0;
+}
+
+void do_stats(const char *target)
+{
+ time_t now, uptime;
+ size_t i;
+
+ now = time(NULL);
+ uptime = now - STAT_START_TIME;
+
+ irc_send("PRIVMSG %s :Uptime: %s", target, dissect_time(uptime));
+ irc_send("PRIVMSG %s :Using %d/%d file descriptors", target, FD_USE, CONF_FDLIMIT);
+
+ if (CONF_DNSBL_ZONE) {
+ irc_send("PRIVMSG %s :DNSBL: %u successful lookup%s from "
+ "zone %s", target, STAT_DNSBL_MATCHES,
+ STAT_DNSBL_MATCHES == 1 ? "" : "s", CONF_DNSBL_ZONE);
+ }
+
+ for(i = 0; i < SCAN_NUMPROTOCOLS; i++) {
+ irc_send("PRIVMSG %s :Found %u %s (%d), %u open.", target,
+ SCAN_PROTOCOLS[i].stat_num, SCAN_PROTOCOLS[i].type,
+ SCAN_PROTOCOLS[i].port,SCAN_PROTOCOLS[i].stat_numopen);
+ }
+
+ irc_send("PRIVMSG %s :Number of connects: %u (%.2f/minute)",
+ target, STAT_NUM_CONNECTS, STAT_NUM_CONNECTS ?
+ (float)STAT_NUM_CONNECTS / ((float)uptime / 60.0) : 0.0);
+
+ return;
+}
--- /dev/null
+#ifndef STATS_H
+#define STATS_H
+
+extern void do_stats_init(void);
+extern void do_stats(const char *target);
+
+#endif