xref: /openbsd-src/libexec/spamd/sdl.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: sdl.c,v 1.18 2007/11/03 19:16:07 beck 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 macth will rdr 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 int match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
44     sa_family_t af);
45 
46 extern int debug;
47 struct sdlist *blacklists = NULL;
48 int blc = 0, blu = 0;
49 
50 int
51 sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc)
52 {
53 	int i, idx = -1;
54 	char astring[40];
55 	unsigned int maskbits;
56 	struct sdaddr *m, *n;
57 
58 	/*
59 	 * if a blacklist of same tag name is already there, replace it,
60 	 * otherwise append.
61 	 */
62 	for (i = 0; i < blu; i++) {
63 		if (strcmp(blacklists[i].tag, sdname) == 0) {
64 			idx = i;
65 			break;
66 		}
67 	}
68 	if (idx != -1) {
69 		if (debug > 0)
70 			printf("replacing list %s; %d new entries\n",
71 			    blacklists[idx].tag, addrc);
72 		sdl_free(&blacklists[idx]);
73 	} else {
74 		if (debug > 0)
75 			printf("adding list %s; %d entries\n", sdname, addrc);
76 		idx = blu;
77 	}
78 	if (idx == blu && blu == blc) {
79 		struct sdlist *tmp;
80 
81 		tmp = realloc(blacklists, (blc + 128) *
82 		    sizeof(struct sdlist));
83 		if (tmp == NULL)
84 			return (-1);
85 		blacklists = tmp;
86 		blc += 128;
87 		sdl_clear(&blacklists[idx]);
88 	}
89 
90 	if ((blacklists[idx].tag = strdup(sdname)) == NULL)
91 		goto misc_error;
92 	if ((blacklists[idx].string = strdup(sdstring)) == NULL)
93 		goto misc_error;
94 
95 	blacklists[idx].naddrs = addrc;
96 
97 	/*
98 	 * Cycle through addrs, converting. We assume they are correctly
99 	 * formatted v4 and v6 addrs, if they don't all convert correctly, the
100 	 * add fails. Each address should be address/maskbits
101 	 */
102 	blacklists[idx].addrs = calloc(addrc, sizeof(struct sdentry));
103 	if (blacklists[idx].addrs == NULL)
104 		goto misc_error;
105 
106 	for (i = 0; i < addrc; i++) {
107 		int j, k, af;
108 
109 		n = &blacklists[idx].addrs[i].sda;
110 		m = &blacklists[idx].addrs[i].sdm;
111 
112 		j = sscanf(addrs[i], "%39[^/]/%u", astring, &maskbits);
113 		if (j != 2)
114 			goto parse_error;
115 		if (maskbits > 128)
116 			goto parse_error;
117 		/*
118 		 * sanity check! we don't allow a 0 mask -
119 		 * don't blacklist the entire net.
120 		 */
121 		if (maskbits == 0)
122 			goto parse_error;
123 		if (strchr(astring, ':') != NULL)
124 			af = AF_INET6;
125 		else
126 			af = AF_INET;
127 		if (af == AF_INET && maskbits > 32)
128 			goto parse_error;
129 		j = inet_pton(af, astring, n);
130 		if (j != 1)
131 			goto parse_error;
132 		if (debug > 0)
133 			printf("added %s/%u\n", astring, maskbits);
134 
135 		/* set mask, borrowed from pf */
136 		k = 0;
137 		for (j = 0; j < 4; j++)
138 			m->addr32[j] = 0;
139 		while (maskbits >= 32) {
140 			m->addr32[k++] = 0xffffffff;
141 			maskbits -= 32;
142 		}
143 		for (j = 31; j > 31 - maskbits; --j)
144 			m->addr32[k] |= (1 << j);
145 		if (maskbits)
146 			m->addr32[k] = htonl(m->addr32[k]);
147 
148 		/* mask off address bits that won't ever be used */
149 		for (j = 0; j < 4; j++)
150 			n->addr32[j] = n->addr32[j] & m->addr32[j];
151 	}
152 	if (idx == blu) {
153 		blu++;
154 		blacklists[blu].tag = NULL;
155 	}
156 	return (0);
157  parse_error:
158 	if (debug > 0)
159 		printf("sdl_add: parse error, \"%s\"\n", addrs[i]);
160  misc_error:
161 	sdl_free(&blacklists[idx]);
162 	return (-1);
163 }
164 
165 void
166 sdl_del(char *sdname)
167 {
168 	int i, idx = -1;
169 
170 	for (i = 0; i < blu; i++) {
171 		if (strcmp(blacklists[i].tag, sdname) == 0) {
172 			idx = i;
173 			break;
174 		}
175 	}
176 	if (idx != -1) {
177 		if (debug > 0)
178 			printf("clearing list %s\n", sdname);
179 		free(blacklists[idx].string);
180 		free(blacklists[idx].addrs);
181 		blacklists[idx].string = NULL;
182 		blacklists[idx].addrs = NULL;
183 		blacklists[idx].naddrs = 0;
184 	}
185 }
186 
187 /*
188  * Return 1 if the addresses a (with mask m) matches address b
189  * otherwise return 0. It is assumed that address a has been
190  * pre-masked out, we only need to mask b.
191  */
192 int
193 match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
194     sa_family_t af)
195 {
196 	int	match = 0;
197 
198 	switch (af) {
199 	case AF_INET:
200 		if ((a->addr32[0]) ==
201 		    (b->addr32[0] & m->addr32[0]))
202 			match++;
203 		break;
204 	case AF_INET6:
205 		if (((a->addr32[0]) ==
206 		    (b->addr32[0] & m->addr32[0])) &&
207 		    ((a->addr32[1]) ==
208 		    (b->addr32[1] & m->addr32[1])) &&
209 		    ((a->addr32[2]) ==
210 		    (b->addr32[2] & m->addr32[2])) &&
211 		    ((a->addr32[3]) ==
212 		    (b->addr32[3] & m->addr32[3])))
213 			match++;
214 		break;
215 	}
216 	return (match);
217 }
218 
219 
220 /*
221  * Given an address and address family
222  * return list of pointers to matching nodes. or NULL if none.
223  */
224 struct sdlist **
225 sdl_lookup(struct sdlist *head, int af, void * src)
226 {
227 	int i, matches = 0;
228 	struct sdlist *sdl;
229 	struct sdentry *sda;
230 	struct sdaddr *source = (struct sdaddr *) src;
231 	int sdnewlen = 0;
232 	struct sdlist **sdnew = NULL;
233 
234 	if (head == NULL)
235 		return (NULL);
236 	else
237 		sdl = head;
238 	while (sdl->tag != NULL) {
239 		for (i = 0; i < sdl->naddrs; i++) {
240 			sda = sdl->addrs + i;
241 			if (match_addr(&sda->sda, &sda->sdm, source, af)) {
242 				if (matches == sdnewlen) {
243 					struct sdlist **tmp;
244 
245 					tmp = realloc(sdnew,
246 					    (sdnewlen + 128) *
247 					    sizeof(struct sdlist *));
248 					if (tmp == NULL)
249 						/*
250 						 * XXX out of memory -
251 						 * return what we have
252 						 */
253 						return (sdnew);
254 					sdnew = tmp;
255 					sdnewlen += 128;
256 				}
257 				sdnew[matches]= sdl;
258 				matches++;
259 				sdnew[matches]=NULL;
260 				break;
261 			}
262 		}
263 		sdl++;
264 	}
265 	return (sdnew);
266 }
267 
268 static void
269 sdl_free(struct sdlist *sdl)
270 {
271 	free(sdl->tag);
272 	free(sdl->string);
273 	free(sdl->addrs);
274 	sdl_clear(sdl);
275 }
276 
277 static void
278 sdl_clear(struct sdlist *sdl)
279 {
280 	sdl->tag = NULL;
281 	sdl->string = NULL;
282 	sdl->addrs = NULL;
283 	sdl->naddrs = 0;
284 }
285 
286