Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
299 lines (258 sloc) 8.99 KB
/* ---------------------------------------------------------------------------
* couriergrey - Greylisting filter for Courier
* Copyright (C) 2007-2012 Matthias Wimmer <m@tthias.eu>
*
* 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 Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
* ---------------------------------------------------------------------------
* vi: sw=4:tabstop=8
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "couriergrey.h"
#include <cstring>
#include <cerrno>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <cstdio>
#include <poll.h>
#include <glibmm.h>
#include <list>
#include <syslog.h>
#include <netinet/in.h>
#include <popt.h>
#define SOCKET_BACKLOG_SIZE 10
int main(int argc, char const** argv) {
int do_version = 0;
int dump_whitelist = 0;
int dump_database = 0;
int expire_database = 0;
int ret = 0;
char const* socket_location = LOCALSTATEDIR "/lib/courier/allfilters/couriergrey";
char const* whitelist_location = CONFIG_DIR "/whitelist_ip";
struct poptOption options[] = {
{ "version", 'v', POPT_ARG_NONE, &do_version, 0, N_("print server version"), NULL},
{ "socket", 's', POPT_ARG_STRING, &socket_location, 0, N_("location of the filter domain socket"), "path"},
{ "whitelist", 'w', POPT_ARG_STRING, &whitelist_location, 0, N_("location of the whitelist file"), "path"},
{ "expire", 'e', POPT_ARG_INT, &expire_database, 0, N_("expire old database entries"), "days"},
{ "dumpwhitelist", 0, POPT_ARG_NONE, &dump_whitelist, 0, N_("dump the content of the parsed whitelist"), NULL},
{ "dumpdatabase", 0, POPT_ARG_NONE, &dump_database, 0, N_("dump the content of the greylisting database"), NULL},
POPT_AUTOHELP
POPT_TABLEEND
};
// Init multithreading
Glib::thread_init();
// Open Logging
::openlog(PACKAGE, LOG_PID, LOG_MAIL);
// parse command line options
poptContext pCtx = poptGetContext(NULL, argc, argv, options, 0);
while ((ret = poptGetNextOpt(pCtx)) >= 0) {
switch (ret) {
// access argument by poptGetOptArg(pCtx)
}
}
// error parsing command line?
if (ret < -1) {
std::cout << poptBadOption(pCtx, POPT_BADOPTION_NOALIAS) << ": " << poptStrerror(ret) << std::endl;
::closelog();
return 1;
}
// anything left?
if (poptPeekArg(pCtx) != NULL) {
// XXX i20n
std::cout << N_("Invalid argument: ") << poptGetArg(pCtx) << std::endl;
::closelog();
return 1;
}
// print version information?
if (do_version) {
// XXX i20n
std::cout << PACKAGE << N_(" version ") << VERSION << std::endl << std::endl;
std::cout << N_("Used filter socket is: ") << socket_location << std::endl;
std::cout << N_("Used whitelist is: ") << whitelist_location << std::endl;
std::cout << N_("Database is: ") << LOCALSTATEDIR "/cache/" PACKAGE "/deliveryattempts.gdbm" << std::endl;
::closelog();
return 0;
}
// read whitelist
couriergrey::whitelist used_whitelist(whitelist_location);
// dump whitelist if requested
if (dump_whitelist) {
used_whitelist.dump();
::closelog();
return 0;
}
// expire database if requested
if (expire_database > 0) {
try {
couriergrey::timestore db;
std::cout << N_("Expiring database entries older than ") << expire_database << N_(" days.") << std::endl;
db.expire(expire_database);
return 0;
} catch (Glib::ustring msg) {
std::cerr << msg << std::endl;
return 1;
}
}
// dump database if requested
if (dump_database) {
try {
couriergrey::timestore db;
std::cout << N_("Content of the greylist database:") << std::endl;
std::list<std::string> keys = db.get_keys();
for (std::list<std::string>::const_iterator p = keys.begin(); p != keys.end(); ++p) {
std::cout << *p << std::endl;
std::pair<std::time_t, std::time_t> times = db.fetch(*p);
struct std::tm first_time_tm;
gmtime_r(&times.first, &first_time_tm);
struct std::tm last_time_tm;
gmtime_r(&times.second, &last_time_tm);
char first_time[128];
char last_time[128];
std::size_t first_time_size = strftime(first_time, sizeof(first_time), "%Y-%m-%dT%H:%M:%SZ", &first_time_tm);
std::size_t last_time_size = strftime(last_time, sizeof(last_time), "%Y-%m-%dT%H:%M:%SZ", &last_time_tm);
if (times.second - times.first >= 120) {
std::cout << " A";
}
std::cout << "\t";
if (first_time_size > 0) {
std::cout << first_time << " ";
}
if (last_time_size > 0) {
std::cout << last_time;
}
std::cout << std::endl;
}
return 0;
} catch (Glib::ustring msg) {
std::cerr << msg << std::endl;
return 1;
}
}
// open the domain socket
int domain_socket = -1;
{
struct sockaddr_un addr;
// calculate the temporary location where we create the socket
std::string temp_location = socket_location;
std::string::size_type last_slash = temp_location.rfind("/");
if (last_slash == std::string::npos) {
temp_location.insert(0, ".");
} else {
temp_location.insert(last_slash+1, ".");
}
// check length of the socket location
if (temp_location.length() >= sizeof(addr.sun_path)) {
std::cerr << N_("Socket name to long: ") << temp_location << std::endl;
::closelog();
return 1;
}
// unlink previously existing socket at the temp_location
ret = ::unlink(temp_location.c_str());
if (ret && errno != ENOENT) {
std::cerr << N_("Problem creating domain socket at location ") << temp_location << ": " << std::strerror(errno) << std::endl;
::closelog();
return 1;
}
// create the domain socket
domain_socket = ::socket(PF_UNIX, SOCK_STREAM, 0);
if (domain_socket == -1) {
std::cerr << N_("Problem creating a unix domain socket: ") << std::strerror(errno) << std::endl;
::closelog();
return 1;
}
// if we opened the socket on fd#3 we have not been called as courierfilter
if (domain_socket == 3) {
::close(domain_socket);
std::cerr << N_("This file is not intended to be called directly.") << std::endl;
::closelog();
return 1;
}
// bind to the location
std::memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
std::strncpy(addr.sun_path, temp_location.c_str(), sizeof(addr.sun_path)-1);
ret = ::bind(domain_socket, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
if (ret) {
std::cerr << N_("Could not bind to socket ") << temp_location << ": " << std::strerror(errno) << std::endl;
::closelog();
return 1;
}
// start listening on the socket
ret = ::listen(domain_socket, SOCKET_BACKLOG_SIZE);
if (ret) {
std::cerr << N_("Could not listen on socket ") << temp_location << ": " << std::strerror(errno) << std::endl;
::closelog();
return 1;
}
// move socket to final location
ret = std::rename(temp_location.c_str(), socket_location);
if (ret) {
std::cerr << N_("Cannot move socket to its operating location ") << socket_location << ": " << std::strerror(errno) << std::endl;
::closelog();
return 1;
}
}
// close fd #3 to signal that we are ready
::close(3);
// log that we are up
::syslog(LOG_INFO, "%s started and ready", PACKAGE);
// start waiting for something to happen
for (;;) {
struct pollfd fds[2];
for (int c=0; c<2; c++) {
std::memset(&fds[c], 0, sizeof(struct pollfd));
}
fds[0].fd = 0;
fds[1].fd = domain_socket;
fds[1].events = POLLIN;
ret = ::poll(fds, 2, -1);
if (ret < 0) {
std::cerr << N_("Error waiting for I/O events: ") << std::strerror(errno) << std::endl;
break;
} else if (ret > 0) {
if (fds[0].revents & POLLHUP) {
// stdin closed, we have to shutdown
break;
}
if (fds[1].revents & POLLIN) {
// new connection, accept it
int accepted_connection = ::accept(domain_socket, NULL, 0);
couriergrey::message_processor* processor = new couriergrey::message_processor(accepted_connection, used_whitelist);
try {
Glib::Thread::create(sigc::mem_fun(*processor, &couriergrey::message_processor::do_process), false);
} catch (Glib::ThreadError const& te) {
::syslog(LOG_INFO, "ThreadError caught in main thread: %s", te.what().c_str());
}
}
} else {
std::clog << "XXX Returned without event ..." << std::endl;
}
}
// cleanup
::close(domain_socket);
::unlink(socket_location);
// log that we are done
::syslog(LOG_INFO, "%s shut down", PACKAGE);
// we're done
::closelog();
return 0;
}
You can’t perform that action at this time.