GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/spamd/sdl.c Lines: 0 147 0.0 %
Date: 2017-11-07 Branches: 0 117 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: sdl.c,v 1.22 2015/05/18 16:04:21 reyk Exp $ */
2
3
/*
4
 * Copyright (c) 2003-2007 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
/*
20
 * sdl.c - Implement spamd source lists
21
 *
22
 * This consists of everything we need to do to determine which lists
23
 * someone is on. Spamd gets the connecting address, and looks it up
24
 * against all lists to determine what deferral messages to feed back
25
 * to the connecting machine. - The redirection to spamd will happen
26
 * from pf in the kernel, first match will divert to us. Spamd (along with
27
 * setup) must keep track of *all* matches, so as to tell someone all the
28
 * lists that they are on.
29
 */
30
31
#include <sys/types.h>
32
#include <sys/socket.h>
33
#include <netinet/in.h>
34
#include <arpa/inet.h>
35
#include <errno.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include "sdl.h"
40
41
static void sdl_free(struct sdlist *);
42
static void sdl_clear(struct sdlist *);
43
44
extern int debug;
45
struct sdlist *blacklists = NULL;
46
int blc = 0, blu = 0;
47
48
int
49
sdl_add(char *sdname, char *sdstring, char **v4, u_int nv4, char **v6, u_int nv6)
50
{
51
	int i, idx = -1;
52
	char astring[40];
53
	char *addr = NULL;
54
	unsigned int maskbits;
55
56
	/*
57
	 * if a blacklist of same tag name is already there, replace it,
58
	 * otherwise append.
59
	 */
60
	for (i = 0; i < blu; i++) {
61
		if (strcmp(blacklists[i].tag, sdname) == 0) {
62
			idx = i;
63
			break;
64
		}
65
	}
66
	if (idx != -1) {
67
		if (debug > 0)
68
			printf("replacing list %s; %u new entries\n",
69
			    blacklists[idx].tag, nv4 + nv6);
70
		sdl_free(&blacklists[idx]);
71
	} else {
72
		if (debug > 0)
73
			printf("adding list %s; %u entries\n", sdname, nv4 + nv6);
74
		if (blu == blc) {
75
			struct sdlist *tmp;
76
77
			tmp = reallocarray(blacklists, blc + 128,
78
			    sizeof(struct sdlist));
79
			if (tmp == NULL)
80
				return (-1);
81
			blacklists = tmp;
82
			blc += 128;
83
			sdl_clear(&blacklists[blu]);
84
		}
85
		idx = blu;
86
	}
87
88
	if ((blacklists[idx].tag = strdup(sdname)) == NULL)
89
		goto misc_error;
90
	if ((blacklists[idx].string = strdup(sdstring)) == NULL)
91
		goto misc_error;
92
93
	/*
94
	 * Cycle through addrs by family, converting. We assume they are
95
	 * correctly formatted v4 and v6 addrs, if they don't all convert
96
	 * correctly, the add fails. Each address should be address/maskbits.
97
	 */
98
	if (nv4 != 0) {
99
		blacklists[idx].v4.naddrs = nv4;
100
		blacklists[idx].v4.addrs = reallocarray(NULL, nv4,
101
		    sizeof(struct sdentry_v4));
102
		if (blacklists[idx].v4.addrs == NULL)
103
			goto misc_error;
104
		for (i = 0; i < nv4; i++) {
105
			struct in_addr *m, *n;
106
			int j;
107
108
			n = &blacklists[idx].v4.addrs[i].sda;
109
			m = &blacklists[idx].v4.addrs[i].sdm;
110
111
			addr = v4[i];
112
			j = sscanf(addr, "%15[^/]/%u", astring, &maskbits);
113
			if (j != 2)
114
				goto parse_error;
115
			/*
116
			 * sanity check! we don't allow a 0 mask -
117
			 * don't blacklist the entire net.
118
			 */
119
			if (maskbits == 0 || maskbits > 32)
120
				goto parse_error;
121
			j = inet_pton(AF_INET, astring, n);
122
			if (j != 1)
123
				goto parse_error;
124
			if (debug > 0)
125
				printf("added %s/%u\n", astring, maskbits);
126
127
			/* set mask. */
128
			m->s_addr = 0xffffffffU << (32 - maskbits);
129
			m->s_addr = htonl(m->s_addr);
130
131
			/* mask off address bits that won't ever be used */
132
			n->s_addr = n->s_addr & m->s_addr;
133
		}
134
	}
135
	if (nv6 != 0) {
136
		blacklists[idx].v6.naddrs = nv6;
137
		blacklists[idx].v6.addrs = reallocarray(NULL, nv6,
138
		    sizeof(struct sdentry_v6));
139
		if (blacklists[idx].v6.addrs == NULL)
140
			goto misc_error;
141
142
		for (i = 0; i < nv6; i++) {
143
			int j, k;
144
			struct sdaddr_v6 *m, *n;
145
146
			n = &blacklists[idx].v6.addrs[i].sda;
147
			m = &blacklists[idx].v6.addrs[i].sdm;
148
149
			addr = v6[i];
150
			j = sscanf(addr, "%39[^/]/%u", astring, &maskbits);
151
			if (j != 2)
152
				goto parse_error;
153
			/*
154
			 * sanity check! we don't allow a 0 mask -
155
			 * don't blacklist the entire net.
156
			 */
157
			if (maskbits == 0 || maskbits > 128)
158
				goto parse_error;
159
			j = inet_pton(AF_INET6, astring, n);
160
			if (j != 1)
161
				goto parse_error;
162
			if (debug > 0)
163
				printf("added %s/%u\n", astring, maskbits);
164
165
			/* set mask, borrowed from pf */
166
			k = 0;
167
			for (j = 0; j < 4; j++)
168
				m->addr32[j] = 0;
169
			while (maskbits >= 32) {
170
				m->addr32[k++] = 0xffffffffU;
171
				maskbits -= 32;
172
			}
173
			for (j = 31; j > 31 - maskbits; --j)
174
				m->addr32[k] |= (1 << j);
175
			if (maskbits)
176
				m->addr32[k] = htonl(m->addr32[k]);
177
178
			/* mask off address bits that won't ever be used */
179
			for (j = 0; j < 4; j++)
180
				n->addr32[j] = n->addr32[j] & m->addr32[j];
181
		}
182
	}
183
	if (idx == blu) {
184
		blu++;
185
		sdl_clear(&blacklists[blu]);
186
	}
187
	return (0);
188
 parse_error:
189
	if (debug > 0)
190
		printf("sdl_add: parse error, \"%s\"\n", addr);
191
 misc_error:
192
	sdl_free(&blacklists[idx]);
193
	if (idx != blu) {
194
		memmove(&blacklists[idx], &blacklists[idx + 1],
195
		    (blu - idx) * sizeof(*blacklists));
196
		blu--;
197
	}
198
	return (-1);
199
}
200
201
void
202
sdl_del(char *sdname)
203
{
204
	int i, idx = -1;
205
206
	for (i = 0; i < blu; i++) {
207
		if (strcmp(blacklists[i].tag, sdname) == 0) {
208
			idx = i;
209
			break;
210
		}
211
	}
212
	if (idx != -1) {
213
		if (debug > 0)
214
			printf("clearing list %s\n", sdname);
215
		/* Must preserve tag. */
216
		free(blacklists[idx].string);
217
		free(blacklists[idx].v4.addrs);
218
		free(blacklists[idx].v6.addrs);
219
		blacklists[idx].string = NULL;
220
		blacklists[idx].v4.addrs = NULL;
221
		blacklists[idx].v6.addrs = NULL;
222
		blacklists[idx].v4.naddrs = 0;
223
		blacklists[idx].v6.naddrs = 0;
224
	}
225
}
226
227
/*
228
 * Return 1 if the addresses a (with mask m) matches address b
229
 * otherwise return 0. It is assumed that address a has been
230
 * pre-masked out, we only need to mask b.
231
 */
232
static int
233
match_addr_v4(struct in_addr *a, struct in_addr *m, struct in_addr *b)
234
{
235
	if (a->s_addr == (b->s_addr & m->s_addr))
236
		return (1);
237
	return (0);
238
}
239
240
/*
241
 * Return 1 if the addresses a (with mask m) matches address b
242
 * otherwise return 0. It is assumed that address a has been
243
 * pre-masked out, we only need to mask b.
244
 */
245
static int
246
match_addr_v6(struct sdaddr_v6 *a, struct sdaddr_v6 *m, struct sdaddr_v6 *b)
247
{
248
	if (((a->addr32[0]) == (b->addr32[0] & m->addr32[0])) &&
249
	    ((a->addr32[1]) == (b->addr32[1] & m->addr32[1])) &&
250
	    ((a->addr32[2]) == (b->addr32[2] & m->addr32[2])) &&
251
	    ((a->addr32[3]) == (b->addr32[3] & m->addr32[3])))
252
		return (1);
253
	return (0);
254
}
255
256
#define grow_sdlist(sd, c, l) do {					       \
257
	if (c == l) {							       \
258
		struct sdlist **tmp;					       \
259
									       \
260
		tmp = reallocarray(sd, l + 128, sizeof(struct sdlist *));      \
261
		if (tmp == NULL) {					       \
262
			/*						       \
263
			 * XXX out of memory - return what we have	       \
264
			 */						       \
265
			return (sdnew);					       \
266
		}							       \
267
		sd = tmp;						       \
268
		l += 128;						       \
269
	}								       \
270
} while (0)
271
272
static struct sdlist **
273
sdl_lookup_v4(struct sdlist *sdl, struct in_addr *src)
274
{
275
	struct sdentry_v4 *entry;
276
	int i, matches = 0;
277
	int sdnewlen = 0;
278
	struct sdlist **sdnew = NULL;
279
280
	while (sdl->tag != NULL) {
281
		for (i = 0; i < sdl->v4.naddrs; i++) {
282
			entry = &sdl->v4.addrs[i];
283
			if (match_addr_v4(&entry->sda, &entry->sdm, src)) {
284
				grow_sdlist(sdnew, matches, sdnewlen);
285
				sdnew[matches] = sdl;
286
				matches++;
287
				sdnew[matches] = NULL;
288
				break;
289
			}
290
		}
291
		sdl++;
292
	}
293
	return (sdnew);
294
}
295
296
static struct sdlist **
297
sdl_lookup_v6(struct sdlist *sdl, struct sdaddr_v6 *src)
298
{
299
	struct sdentry_v6 *entry;
300
	int i, matches = 0;
301
	int sdnewlen = 0;
302
	struct sdlist **sdnew = NULL;
303
304
	while (sdl->tag != NULL) {
305
		for (i = 0; i < sdl->v6.naddrs; i++) {
306
			entry = &sdl->v6.addrs[i];
307
			if (match_addr_v6(&entry->sda, &entry->sdm, src)) {
308
				grow_sdlist(sdnew, matches, sdnewlen);
309
				sdnew[matches] = sdl;
310
				matches++;
311
				sdnew[matches] = NULL;
312
				break;
313
			}
314
		}
315
		sdl++;
316
	}
317
	return (sdnew);
318
}
319
320
/*
321
 * Given an address and address family
322
 * return list of pointers to matching nodes. or NULL if none.
323
 */
324
struct sdlist **
325
sdl_lookup(struct sdlist *head, int af, void *src)
326
{
327
	if (head == NULL)
328
		return (NULL);
329
330
	switch (af) {
331
	case AF_INET:
332
		return (sdl_lookup_v4(head, src));
333
	case AF_INET6:
334
		return (sdl_lookup_v6(head, src));
335
	default:
336
		return (NULL);
337
	}
338
}
339
340
static void
341
sdl_free(struct sdlist *sdl)
342
{
343
	free(sdl->tag);
344
	free(sdl->string);
345
	free(sdl->v4.addrs);
346
	free(sdl->v6.addrs);
347
	sdl_clear(sdl);
348
}
349
350
static void
351
sdl_clear(struct sdlist *sdl)
352
{
353
	sdl->tag = NULL;
354
	sdl->string = NULL;
355
	sdl->v4.addrs = NULL;
356
	sdl->v4.naddrs = 0;
357
	sdl->v6.addrs = NULL;
358
	sdl->v6.naddrs = 0;
359
}