GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/spamdb/spamdb.c Lines: 0 183 0.0 %
Date: 2017-11-07 Branches: 0 96 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: spamdb.c,v 1.31 2016/11/29 17:21:52 mestre Exp $	*/
2
3
/*
4
 * Copyright (c) 2004 Bob Beck.  All rights reserved.
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/socket.h>
21
#include <netinet/in.h>
22
#include <arpa/inet.h>
23
#include <db.h>
24
#include <err.h>
25
#include <fcntl.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <time.h>
30
#include <netdb.h>
31
#include <ctype.h>
32
#include <errno.h>
33
#include <unistd.h>
34
35
#include "grey.h"
36
37
/* things we may add/delete from the db */
38
#define WHITE 0
39
#define TRAPHIT 1
40
#define SPAMTRAP 2
41
42
int	dblist(DB *);
43
int	dbupdate(DB *, char *, int, int);
44
45
int
46
dbupdate(DB *db, char *ip, int add, int type)
47
{
48
	DBT		dbk, dbd;
49
	struct gdata	gd;
50
	time_t		now;
51
	int		r;
52
	struct addrinfo hints, *res;
53
54
	now = time(NULL);
55
	memset(&hints, 0, sizeof(hints));
56
	hints.ai_family = PF_UNSPEC;
57
	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
58
	hints.ai_flags = AI_NUMERICHOST;
59
	if (add && (type == TRAPHIT || type == WHITE)) {
60
		if (getaddrinfo(ip, NULL, &hints, &res) != 0) {
61
			warnx("invalid ip address %s", ip);
62
			goto bad;
63
		}
64
		freeaddrinfo(res);
65
	}
66
	memset(&dbk, 0, sizeof(dbk));
67
	dbk.size = strlen(ip);
68
	dbk.data = ip;
69
	memset(&dbd, 0, sizeof(dbd));
70
	if (!add) {
71
		/* remove entry */
72
		r = db->get(db, &dbk, &dbd, 0);
73
		if (r == -1) {
74
			warn("db->get failed");
75
			goto bad;
76
		}
77
		if (r) {
78
			warnx("no entry for %s", ip);
79
			goto bad;
80
		} else if (db->del(db, &dbk, 0)) {
81
			warn("db->del failed");
82
			goto bad;
83
		}
84
	} else {
85
		/* add or update entry */
86
		r = db->get(db, &dbk, &dbd, 0);
87
		if (r == -1) {
88
			warn("db->get failed");
89
			goto bad;
90
		}
91
		if (r) {
92
			int i;
93
94
			/* new entry */
95
			memset(&gd, 0, sizeof(gd));
96
			gd.first = now;
97
			gd.bcount = 1;
98
			switch (type) {
99
			case WHITE:
100
				gd.pass = now;
101
				gd.expire = now + WHITEEXP;
102
				break;
103
			case TRAPHIT:
104
				gd.expire = now + TRAPEXP;
105
				gd.pcount = -1;
106
				break;
107
			case SPAMTRAP:
108
				gd.expire = 0;
109
				gd.pcount = -2;
110
				/* ensure address is of the form user@host */
111
				if (strchr(ip, '@') == NULL)
112
					errx(-1, "not an email address: %s", ip);
113
				/* ensure address is lower case*/
114
				for (i = 0; ip[i] != '\0'; i++)
115
					if (isupper((unsigned char)ip[i]))
116
						ip[i] = tolower((unsigned char)ip[i]);
117
				break;
118
			default:
119
				errx(-1, "unknown type %d", type);
120
			}
121
			memset(&dbk, 0, sizeof(dbk));
122
			dbk.size = strlen(ip);
123
			dbk.data = ip;
124
			memset(&dbd, 0, sizeof(dbd));
125
			dbd.size = sizeof(gd);
126
			dbd.data = &gd;
127
			r = db->put(db, &dbk, &dbd, 0);
128
			if (r) {
129
				warn("db->put failed");
130
				goto bad;
131
			}
132
		} else {
133
			if (gdcopyin(&dbd, &gd) == -1) {
134
				/* whatever this is, it doesn't belong */
135
				db->del(db, &dbk, 0);
136
				goto bad;
137
			}
138
			gd.pcount++;
139
			switch (type) {
140
			case WHITE:
141
				gd.pass = now;
142
				gd.expire = now + WHITEEXP;
143
				break;
144
			case TRAPHIT:
145
				gd.expire = now + TRAPEXP;
146
				gd.pcount = -1;
147
				break;
148
			case SPAMTRAP:
149
				gd.expire = 0; /* XXX */
150
				gd.pcount = -2;
151
				break;
152
			default:
153
				errx(-1, "unknown type %d", type);
154
			}
155
156
			memset(&dbk, 0, sizeof(dbk));
157
			dbk.size = strlen(ip);
158
			dbk.data = ip;
159
			memset(&dbd, 0, sizeof(dbd));
160
			dbd.size = sizeof(gd);
161
			dbd.data = &gd;
162
			r = db->put(db, &dbk, &dbd, 0);
163
			if (r) {
164
				warn("db->put failed");
165
				goto bad;
166
			}
167
		}
168
	}
169
	return (0);
170
 bad:
171
	return (1);
172
}
173
174
int
175
dblist(DB *db)
176
{
177
	DBT		dbk, dbd;
178
	struct gdata	gd;
179
	int		r;
180
181
	/* walk db, list in text format */
182
	memset(&dbk, 0, sizeof(dbk));
183
	memset(&dbd, 0, sizeof(dbd));
184
	for (r = db->seq(db, &dbk, &dbd, R_FIRST); !r;
185
	    r = db->seq(db, &dbk, &dbd, R_NEXT)) {
186
		char *a, *cp;
187
188
		if ((dbk.size < 1) || gdcopyin(&dbd, &gd) == -1) {
189
			db->close(db);
190
			errx(1, "bogus size db entry - bad db file?");
191
		}
192
		a = malloc(dbk.size + 1);
193
		if (a == NULL)
194
			err(1, "malloc");
195
		memcpy(a, dbk.data, dbk.size);
196
		a[dbk.size]='\0';
197
		cp = strchr(a, '\n');
198
		if (cp == NULL) {
199
			/* this is a non-greylist entry */
200
			switch (gd.pcount) {
201
			case -1: /* spamtrap hit, with expiry time */
202
				printf("TRAPPED|%s|%lld\n", a,
203
				    (long long)gd.expire);
204
				break;
205
			case -2: /* spamtrap address */
206
				printf("SPAMTRAP|%s\n", a);
207
				break;
208
			default: /* whitelist */
209
				printf("WHITE|%s|||%lld|%lld|%lld|%d|%d\n", a,
210
				    (long long)gd.first, (long long)gd.pass,
211
				    (long long)gd.expire, gd.bcount,
212
				    gd.pcount);
213
				break;
214
			}
215
		} else {
216
			char *helo, *from, *to;
217
218
			/* greylist entry */
219
			*cp = '\0';
220
			helo = cp + 1;
221
			from = strchr(helo, '\n');
222
			if (from == NULL) {
223
				warnx("No from part in grey key %s", a);
224
				free(a);
225
				goto bad;
226
			}
227
			*from = '\0';
228
			from++;
229
			to = strchr(from, '\n');
230
			if (to == NULL) {
231
				/* probably old format - print it the
232
				 * with an empty HELO field instead
233
				 * of erroring out.
234
				 */
235
				printf("GREY|%s|%s|%s|%s|%lld|%lld|%lld|%d|%d\n",
236
				    a, "", helo, from, (long long)gd.first,
237
				    (long long)gd.pass, (long long)gd.expire,
238
				    gd.bcount, gd.pcount);
239
240
			} else {
241
				*to = '\0';
242
				to++;
243
				printf("GREY|%s|%s|%s|%s|%lld|%lld|%lld|%d|%d\n",
244
				    a, helo, from, to, (long long)gd.first,
245
				    (long long)gd.pass, (long long)gd.expire,
246
				    gd.bcount, gd.pcount);
247
			}
248
		}
249
		free(a);
250
	}
251
	db->close(db);
252
	db = NULL;
253
	return (0);
254
 bad:
255
	db->close(db);
256
	db = NULL;
257
	errx(1, "incorrect db format entry");
258
	/* NOTREACHED */
259
	return (1);
260
}
261
262
extern char *__progname;
263
264
static int
265
usage(void)
266
{
267
	fprintf(stderr, "usage: %s [[-Tt] -a keys] [[-Tt] -d keys]\n", __progname);
268
	exit(1);
269
	/* NOTREACHED */
270
}
271
272
int
273
main(int argc, char **argv)
274
{
275
	int i, ch, action = 0, type = WHITE, r = 0, c = 0;
276
	HASHINFO	hashinfo;
277
	DB		*db;
278
279
	while ((ch = getopt(argc, argv, "adtT")) != -1) {
280
		switch (ch) {
281
		case 'a':
282
			action = 1;
283
			break;
284
		case 'd':
285
			action = 2;
286
			break;
287
		case 't':
288
			type = TRAPHIT;
289
			break;
290
		case 'T':
291
			type = SPAMTRAP;
292
			break;
293
		default:
294
			usage();
295
			break;
296
		}
297
	}
298
	argc -= optind;
299
	argv += optind;
300
	if (action == 0 && type != WHITE)
301
		usage();
302
303
	memset(&hashinfo, 0, sizeof(hashinfo));
304
	db = dbopen(PATH_SPAMD_DB, O_EXLOCK | (action ? O_RDWR : O_RDONLY),
305
	    0600, DB_HASH, &hashinfo);
306
	if (db == NULL) {
307
		err(1, "cannot open %s for %s", PATH_SPAMD_DB,
308
		    action ? "writing" : "reading");
309
	}
310
311
	if (action == 0) {
312
		if (pledge("stdio rpath flock cpath wpath", NULL) == -1)
313
			err(1, "pledge");
314
	} else {
315
		if (pledge("stdio rpath wpath flock cpath", NULL) == -1)
316
			err(1, "pledge");
317
	}
318
319
	switch (action) {
320
	case 0:
321
		return dblist(db);
322
	case 1:
323
		for (i=0; i<argc; i++)
324
			if (argv[i][0] != '\0') {
325
				c++;
326
				r += dbupdate(db, argv[i], 1, type);
327
			}
328
		if (c == 0)
329
			errx(2, "no addresses specified");
330
		break;
331
	case 2:
332
		for (i=0; i<argc; i++)
333
			if (argv[i][0] != '\0') {
334
				c++;
335
				r += dbupdate(db, argv[i], 0, type);
336
			}
337
		if (c == 0)
338
			errx(2, "no addresses specified");
339
		break;
340
	default:
341
		errx(-1, "bad action");
342
	}
343
	db->close(db);
344
	return (r);
345
}