~ubuntu-branches/ubuntu/utopic/couriergrey/utopic

1 by Marco Balmer
Import upstream version 0.3.1
1
/* ---------------------------------------------------------------------------
2
 *  couriergrey - Greylisting filter for Courier
1.1.1 by Marco Balmer
Import upstream version 0.3.2
3
 *  Copyright (C) 2007-2012  Matthias Wimmer <m@tthias.eu>
1 by Marco Balmer
Import upstream version 0.3.1
4
 *
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
1.1.1 by Marco Balmer
Import upstream version 0.3.2
15
 *  You should have received a copy of the GNU Library General Public
16
 *  License along with this program; if not, write to the Free Software
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18
 *  USA.
1 by Marco Balmer
Import upstream version 0.3.1
19
 * ---------------------------------------------------------------------------
1.1.1 by Marco Balmer
Import upstream version 0.3.2
20
 * vi: sw=4:tabstop=8
1 by Marco Balmer
Import upstream version 0.3.1
21
 */
22
23
#ifdef HAVE_CONFIG_H
24
#   include <config.h>
25
#endif
26
27
#include "couriergrey.h"
28
#include <cstring>
29
#include <cerrno>
30
#include <iostream>
31
#include <unistd.h>
32
#include <sys/types.h>
33
#include <sys/socket.h>
34
#include <arpa/inet.h>
35
#include <sys/stat.h>
36
#include <sys/un.h>
37
#include <cstdio>
38
#include <poll.h>
39
#include <glibmm.h>
40
#include <list>
41
#include <syslog.h>
42
#include <netinet/in.h>
43
#include <popt.h>
44
45
#define SOCKET_BACKLOG_SIZE 10
46
47
int main(int argc, char const** argv) {
48
    int do_version = 0;
49
    int dump_whitelist = 0;
50
    int dump_database = 0;
51
    int expire_database = 0;
52
    int ret = 0;
53
    char const* socket_location = LOCALSTATEDIR "/lib/courier/allfilters/couriergrey";
54
    char const* whitelist_location = CONFIG_DIR "/whitelist_ip";
55
56
    struct poptOption options[] = {
57
	{ "version", 'v', POPT_ARG_NONE, &do_version, 0, N_("print server version"), NULL},
58
	{ "socket", 's', POPT_ARG_STRING, &socket_location, 0, N_("location of the filter domain socket"), "path"},
59
	{ "whitelist", 'w', POPT_ARG_STRING, &whitelist_location, 0, N_("location of the whitelist file"), "path"},
60
	{ "expire", 'e', POPT_ARG_INT, &expire_database, 0, N_("expire old database entries"), "days"},
61
	{ "dumpwhitelist", 0, POPT_ARG_NONE, &dump_whitelist, 0, N_("dump the content of the parsed whitelist"), NULL},
62
	{ "dumpdatabase", 0, POPT_ARG_NONE, &dump_database, 0, N_("dump the content of the greylisting database"), NULL},
63
	POPT_AUTOHELP
64
	POPT_TABLEEND
65
    };
66
67
    // Init multithreading
68
    Glib::thread_init();
69
70
    // Open Logging
71
    ::openlog(PACKAGE, LOG_PID, LOG_MAIL);
72
73
    // parse command line options
74
    poptContext pCtx = poptGetContext(NULL, argc, argv, options, 0);
75
    while ((ret = poptGetNextOpt(pCtx)) >= 0) {
76
	switch (ret) {
77
	    // access argument by poptGetOptArg(pCtx)
78
	}
79
    }
80
81
    // error parsing command line?
82
    if (ret < -1) {
83
	std::cout << poptBadOption(pCtx, POPT_BADOPTION_NOALIAS) << ": " << poptStrerror(ret) << std::endl;
84
	::closelog();
85
	return 1;
86
    }
87
88
    // anything left?
89
    if (poptPeekArg(pCtx) != NULL) {
90
	// XXX i20n
91
	std::cout << N_("Invalid argument: ") << poptGetArg(pCtx) << std::endl;
92
	::closelog();
93
	return 1;
94
    }
95
96
    // print version information?
97
    if (do_version) {
98
	// XXX i20n
99
	std::cout << PACKAGE << N_(" version ") << VERSION << std::endl << std::endl;
100
	std::cout << N_("Used filter socket is: ") << socket_location << std::endl;
101
	std::cout << N_("Used whitelist is: ") << whitelist_location << std::endl;
102
	std::cout << N_("Database is: ") << LOCALSTATEDIR "/cache/" PACKAGE "/deliveryattempts.gdbm" << std::endl;
103
	::closelog();
104
	return 0;
105
    }
106
107
    // read whitelist
108
    couriergrey::whitelist used_whitelist(whitelist_location);
109
110
    // dump whitelist if requested
111
    if (dump_whitelist) {
112
	used_whitelist.dump();
113
	::closelog();
114
	return 0;
115
    }
116
117
    // expire database if requested
118
    if (expire_database > 0) {
119
	try {
120
	    couriergrey::timestore db;
121
122
	    std::cout << N_("Expiring database entries older than ") << expire_database << N_(" days.") << std::endl;
123
124
	    db.expire(expire_database);
125
126
	    return 0;
127
	} catch (Glib::ustring msg) {
128
	    std::cerr << msg << std::endl;
129
	    return 1;
130
	}
131
    }
132
133
    // dump database if requested
134
    if (dump_database) {
135
	try {
136
	    couriergrey::timestore db;
137
138
	    std::cout << N_("Content of the greylist database:") << std::endl;
139
140
	    std::list<std::string> keys = db.get_keys();
141
	    for (std::list<std::string>::const_iterator p = keys.begin(); p != keys.end(); ++p) {
142
		std::cout << *p << std::endl;
143
		std::pair<std::time_t, std::time_t> times = db.fetch(*p);
144
		struct std::tm first_time_tm;
145
		gmtime_r(&times.first, &first_time_tm);
146
		struct std::tm last_time_tm;
147
		gmtime_r(&times.second, &last_time_tm);
148
		char first_time[128];
149
		char last_time[128];
150
		std::size_t first_time_size = strftime(first_time, sizeof(first_time), "%Y-%m-%dT%H:%M:%SZ", &first_time_tm);
151
		std::size_t last_time_size = strftime(last_time, sizeof(last_time), "%Y-%m-%dT%H:%M:%SZ", &last_time_tm);
1.1.1 by Marco Balmer
Import upstream version 0.3.2
152
		if (times.second - times.first >= 120) {
153
		    std::cout << " A";
154
		}
1 by Marco Balmer
Import upstream version 0.3.1
155
		std::cout << "\t";
156
		if (first_time_size > 0) {
157
		    std::cout << first_time << " ";
158
		}
159
		if (last_time_size > 0) {
160
		    std::cout << last_time;
161
		}
162
		std::cout << std::endl;
163
	    }
164
	    return 0;
165
	} catch (Glib::ustring msg) {
166
	    std::cerr << msg << std::endl;
167
	    return 1;
168
	}
169
    }
170
171
    // open the domain socket
172
    int domain_socket = -1;
173
    {
174
	struct sockaddr_un addr;
175
176
	// calculate the temporary location where we create the socket
177
	std::string temp_location = socket_location;
178
	std::string::size_type last_slash = temp_location.rfind("/");
179
	if (last_slash == std::string::npos) {
180
	    temp_location.insert(0, ".");
181
	} else {
182
	    temp_location.insert(last_slash+1, ".");
183
	}
184
185
	// check length of the socket location
186
	if (temp_location.length() >= sizeof(addr.sun_path)) {
187
	    std::cerr << N_("Socket name to long: ") << temp_location << std::endl;
188
	    ::closelog();
189
	    return 1;
190
	}
191
192
	// unlink previously existing socket at the temp_location
193
	ret = ::unlink(temp_location.c_str());
194
	if (ret && errno != ENOENT) {
195
	    std::cerr << N_("Problem creating domain socket at location ") << temp_location << ": " << std::strerror(errno) << std::endl;
196
	    ::closelog();
197
	    return 1;
198
	}
199
200
	// create the domain socket
201
	domain_socket = ::socket(PF_UNIX, SOCK_STREAM, 0);
202
	if (domain_socket == -1) {
203
	    std::cerr << N_("Problem creating a unix domain socket: ") << std::strerror(errno) << std::endl;
204
	    ::closelog();
205
	    return 1;
206
	}
207
208
	// if we opened the socket on fd#3 we have not been called as courierfilter
209
	if (domain_socket == 3) {
210
	    ::close(domain_socket);
211
	    std::cerr << N_("This file is not intended to be called directly.") << std::endl;
212
	    ::closelog();
213
	    return 1;
214
	}
215
216
	// bind to the location
217
	std::memset(&addr, 0, sizeof(addr));
218
	addr.sun_family = AF_UNIX;
219
	std::strncpy(addr.sun_path, temp_location.c_str(), sizeof(addr.sun_path)-1);
220
	ret = ::bind(domain_socket, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
221
	if (ret) {
222
	    std::cerr << N_("Could not bind to socket ") << temp_location << ": " << std::strerror(errno) << std::endl;
223
	    ::closelog();
224
	    return 1;
225
	}
226
227
	// start listening on the socket
228
	ret = ::listen(domain_socket, SOCKET_BACKLOG_SIZE);
229
	if (ret) {
230
	    std::cerr << N_("Could not listen on socket ") << temp_location << ": " << std::strerror(errno) << std::endl;
231
	    ::closelog();
232
	    return 1;
233
	}
234
235
	// move socket to final location
236
	ret = std::rename(temp_location.c_str(), socket_location);
237
	if (ret) {
238
	    std::cerr << N_("Cannot move socket to its operating location ") << socket_location << ": " << std::strerror(errno) << std::endl;
239
	    ::closelog();
240
	    return 1;
241
	}
242
    }
243
244
    // close fd #3 to signal that we are ready
245
    ::close(3);
246
247
    // log that we are up
248
    ::syslog(LOG_INFO, "%s started and ready", PACKAGE);
249
250
    // start waiting for something to happen
251
    for (;;) {
252
	struct pollfd fds[2];
253
254
	for (int c=0; c<2; c++) {
255
	    std::memset(&fds[c], 0, sizeof(struct pollfd));
256
	}
257
	fds[0].fd = 0;
258
	fds[1].fd = domain_socket;
259
	fds[1].events = POLLIN;
260
261
	ret = ::poll(fds, 2, -1);
262
	if (ret < 0) {
263
	    std::cerr << N_("Error waiting for I/O events: ") << std::strerror(errno) << std::endl;
264
	    break;
265
	} else if (ret > 0) {
266
	    if (fds[0].revents & POLLHUP) {
267
		// stdin closed, we have to shutdown
268
		break;
269
	    }
270
271
	    if (fds[1].revents & POLLIN) {
272
		// new connection, accept it
273
		int accepted_connection = ::accept(domain_socket, NULL, 0);
274
275
		couriergrey::message_processor* processor = new couriergrey::message_processor(accepted_connection, used_whitelist);
1.1.1 by Marco Balmer
Import upstream version 0.3.2
276
		try {
277
		    Glib::Thread::create(sigc::mem_fun(*processor, &couriergrey::message_processor::do_process), false);
278
		} catch (Glib::ThreadError const& te) {
279
		    ::syslog(LOG_INFO, "ThreadError caught in main thread: %s", te.what().c_str());
280
		}
1 by Marco Balmer
Import upstream version 0.3.1
281
	    }
282
	} else {
283
	    std::clog << "XXX Returned without event ..." << std::endl;
284
	}
285
286
    }
287
288
    // cleanup
289
    ::close(domain_socket);
290
    ::unlink(socket_location);
291
292
    // log that we are done
293
    ::syslog(LOG_INFO, "%s shut down", PACKAGE);
294
295
    // we're done
296
    ::closelog();
297
    return 0;
298
}