From cf48fad99532b7590b8bfea416532a7a84910bdf Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Fri, 30 Nov 2007 12:00:29 +0100 Subject: [PATCH] Begin work on the greylist module. Signed-off-by: Pierre Habouzit --- Makefile | 7 ++- greylist.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++ greylist.h | 37 +++++++++++ mk/tc.mk | 37 +++++++++++ 4 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 greylist.c create mode 100644 greylist.h create mode 100644 mk/tc.mk diff --git a/Makefile b/Makefile index 23ffcd1..3dcb94c 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ all: LDFLAGS += -Wl,--warn-common include mk/cflags.mk +include mk/tc.mk CFLAGS += --std=gnu99 -D_GNU_SOURCE prefix ?= /usr/local @@ -43,9 +44,11 @@ TESTS = tst-rbl GENERATED = tokens.h tokens.c -postlicyd_SOURCES = common.c threads.c str.c buffer.c rbl.c \ +postlicyd_SOURCES = common.c threads.c str.c buffer.c \ + greylist.c rbl.c \ $(GENERATED) postfix.c main-postlicyd.c -postlicyd_LIBADD = -lpthread +postlicyd_LIBADD = -lpthread $(TC_LIBS) +postlicyd_CFLAGS = $(TC_CFLAGS) pfix-srsd_SOURCES = common.c epoll.c buffer.c str.c main-srsd.c pfix-srsd_LIBADD = -lsrs2 diff --git a/greylist.c b/greylist.c new file mode 100644 index 0000000..9f4895a --- /dev/null +++ b/greylist.c @@ -0,0 +1,182 @@ +/******************************************************************************/ +/* pfixtools: a collection of postfix related tools */ +/* ~~~~~~~~~ */ +/* ________________________________________________________________________ */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in the */ +/* documentation and/or other materials provided with the distribution. */ +/* 3. The names of its contributors may not be used to endorse or promote */ +/* products derived from this software without specific prior written */ +/* permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND */ +/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE */ +/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ +/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS */ +/* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR */ +/* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF */ +/* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS */ +/* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN */ +/* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) */ +/* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF */ +/* THE POSSIBILITY OF SUCH DAMAGE. */ +/******************************************************************************/ + +/* + * Copyright © 2007 Pierre Habouzit + */ + +#include + +#include "greylist.h" +#include "str.h" + +static struct { + int do_awl; + int awl_count; + bool lookup_by_host; + + int delay; + int retry_window; + + TCBDB *awl_db, *obj_db; +} cfg; + +struct awl_entry { + int32_t count; + time_t last; +}; + +struct obj_entry { + time_t first; + time_t last; +}; + +const char *sender_normalize(const char *sender, char *buf, int len) +{ + const char *at = strchr(sender, '@'); + int rpos = 0, wpos = 0, userlen; + + if (!at) + return sender; + + /* strip extension used for VERP or alike */ + userlen = ((char *)memchr(sender, '+', at - sender) ?: at) - sender; + + while (rpos < userlen) { + int count = 0; + + while (isdigit(sender[rpos + count]) && rpos + count < userlen) + count++; + if (count && !isalnum(sender[rpos + count])) { + /* replace \<\d+\> with '#' */ + wpos += m_strputc(buf + wpos, len - wpos, '#'); + rpos += count; + count = 0; + } + while (isalnum(sender[rpos + count]) && rpos + count < userlen) + count++; + while (!isalnum(sender[rpos + count]) && rpos + count < userlen) + count++; + wpos += m_strncpy(buf + wpos, len - wpos, sender + rpos, count); + rpos += count; + } + + wpos += m_strputc(buf + wpos, len - wpos, '#'); + wpos += m_strcpy(buf + wpos, len - wpos, at + 1); + return buf; +} + +static const char * +c_net(const char *c_addr, const char *c_name, char *cnet, int cnetlen) +{ + char ip2[4], ip3[4]; + const char *dot, *p; + + if (cfg.lookup_by_host) + return c_addr; + + if (!(dot = strchr(c_addr, '.'))) + return c_addr; + if (!(dot = strchr(dot + 1, '.'))) + return c_addr; + + p = ++dot; + if (!(dot = strchr(dot, '.')) || dot - p > 3) + return c_addr; + m_strncpy(ip2, sizeof(ip2), p, dot - p); + + p = ++dot; + if (!(dot = strchr(dot, '.')) || dot - p > 3) + return c_addr; + m_strncpy(ip3, sizeof(ip3), p, dot - p); + + /* skip if contains the last two ip numbers in the hostname, + we assume it's a pool of dialup of a provider */ + if (strstr(c_name, ip2) && strstr(c_name, ip3)) + return c_addr; + + m_strncpy(cnet, cnetlen, c_addr, dot - c_addr); + return cnet; +} + +bool try_greylist(const char *sender, const char *c_addr, + const char *c_name, const char *rcpt) +{ + char sbuf[BUFSIZ], cnet[64], key[BUFSIZ]; + const void *res; + + time_t now = time(NULL); + struct obj_entry oent = { now, now }; + struct awl_entry aent = { 0, 0 }; + + int len, klen, c_addrlen = strlen(c_addr); + + + if (cfg.do_awl) { + res = tcbdbget3(cfg.awl_db, c_addr, c_addrlen, &len); + if (res && len == sizeof(aent)) { + memcpy(&aent, res, len); + } + if (aent.count > cfg.awl_count) { + if (now < aent.last + 3600) + goto incr_aent; + return true; + } + } + + klen = snprintf(key, sizeof(key), "%s/%s/%s", + c_net(c_addr, c_name, cnet, sizeof(cnet)), + sender_normalize(sender, sbuf, sizeof(sbuf)), rcpt); + klen = MIN(klen, ssizeof(key) - 1); + + res = tcbdbget3(cfg.obj_db, key, klen, &len); + if (res && len == sizeof(oent)) { + memcpy(&oent, res, len); + } + + if (oent.last - oent.first < cfg.delay + && now - oent.first > cfg.retry_window) + { + oent.first = now; + } + oent.last = now; + tcbdbput(cfg.obj_db, key, klen, &oent, sizeof(oent)); + if (oent.first + cfg.delay < now) { + if (cfg.do_awl) { + incr_aent: + aent.count++; + aent.last = now; + tcbdbput(cfg.awl_db, c_addr, c_addrlen, &aent, sizeof(aent)); + } + return true; + } + return false; +} diff --git a/greylist.h b/greylist.h new file mode 100644 index 0000000..7086fd6 --- /dev/null +++ b/greylist.h @@ -0,0 +1,37 @@ +/******************************************************************************/ +/* pfixtools: a collection of postfix related tools */ +/* ~~~~~~~~~ */ +/* ________________________________________________________________________ */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in the */ +/* documentation and/or other materials provided with the distribution. */ +/* 3. The names of its contributors may not be used to endorse or promote */ +/* products derived from this software without specific prior written */ +/* permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND */ +/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE */ +/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ +/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS */ +/* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR */ +/* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF */ +/* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS */ +/* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN */ +/* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) */ +/* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF */ +/* THE POSSIBILITY OF SUCH DAMAGE. */ +/******************************************************************************/ + +/* + * Copyright © 2007 Pierre Habouzit + */ + +bool try_greylist(const char *sender, const char *c_addr, + const char *c_name, const char *rcpt); diff --git a/mk/tc.mk b/mk/tc.mk new file mode 100644 index 0000000..e0275e8 --- /dev/null +++ b/mk/tc.mk @@ -0,0 +1,37 @@ +############################################################################## +# pfixtools: a collection of postfix related tools # +# ~~~~~~~~~ # +# ________________________________________________________________________ # +# # +# Redistribution and use in source and binary forms, with or without # +# modification, are permitted provided that the following conditions # +# are met: # +# # +# 1. Redistributions of source code must retain the above copyright # +# notice, this list of conditions and the following disclaimer. # +# 2. Redistributions in binary form must reproduce the above copyright # +# notice, this list of conditions and the following disclaimer in the # +# documentation and/or other materials provided with the distribution. # +# 3. The names of its contributors may not be used to endorse or promote # +# products derived from this software without specific prior written # +# permission. # +# # +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS # +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # +# THE POSSIBILITY OF SUCH DAMAGE. # +############################################################################## + +ifeq ('','$(shell pkg-config tokyocabinet || echo failed)') + TC_CFLAGS:=$(shell pkg-config --cflags tokyocabinet) + TC_LIBS:=$(shell pkg-config --libs tokyocabinet) +else + $(error tokyocabinet library not found) +endif -- 2.20.1