xref: /openbsd-src/libexec/spamd/sdl.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: sdl.c,v 1.7 2003/07/06 21:57:27 deraadt Exp $ */
2 /*
3  * Copyright (c) 2003 Bob Beck.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 /*
27  * sdl.c - Implement spamd source lists
28  *
29  * This consists of everything we need to do to determine which lists
30  * someone is on. Spamd gets the connecting address, and looks it up
31  * against all lists to determine what deferral messages to feed back
32  * to the connecting machine. - The redirection to spamd will happen
33  * from pf in the kernel, first macth will rdr to us. Spamd (along with
34  * setup) must keep track of *all* matches, so as to tell someone all the
35  * lists that they are on.
36  */
37 
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include "sdl.h"
47 
48 static void sdl_free(struct sdlist *);
49 static void sdl_clear(struct sdlist *);
50 int match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
51     sa_family_t af);
52 
53 extern int debug;
54 struct sdlist *blacklists = NULL;
55 int blc = 0, blu = 0;
56 
57 int
58 sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc)
59 {
60 	int i, index = -1;
61 	char astring[40];
62 	unsigned int maskbits;
63 	struct sdaddr *m, *n;
64 
65 	/*
66 	 * if a blacklist of same tag name is already there, replace it,
67 	 * otherwise append.
68 	 */
69 	for (i = 0; i < blu; i++) {
70 		if (strcmp(blacklists[i].tag, sdname) == 0) {
71 				index = i;
72 				break;
73 		}
74 	}
75 	if (index != -1) {
76 		if (debug > 0)
77 			printf("replacing list %s\n", blacklists[index].tag);
78 		sdl_free(&blacklists[index]);
79 	} else {
80 		if (debug > 0)
81 			printf("adding list %s\n", sdname);
82 		index = blu;
83 	}
84 	if (index == blu && blu == blc) {
85 		struct sdlist *tmp;
86 		tmp = realloc (blacklists, (blc + 128) *
87 		    sizeof(struct sdlist));
88 		if (tmp == NULL)
89 			return (-1);
90 		blacklists = tmp;
91 		blc += 128;
92 		sdl_clear(&blacklists[index]);
93 	}
94 
95 	if ((blacklists[index].tag = strdup(sdname)) == NULL)
96 		goto misc_error;
97 	if ((blacklists[index].string = strdup(sdstring)) == NULL)
98 		goto misc_error;
99 
100 	blacklists[index].naddrs = addrc;
101 
102 	/*
103 	 * Cycle through addrs, converting. We assume they are correctly
104 	 * formatted v4 and v6 addrs, if they don't all convert correcly, the
105 	 * add fails. Each address should be address/maskbits
106 	 */
107 	blacklists[index].addrs = malloc(addrc * sizeof(struct sdentry));
108 	if (blacklists[index].addrs == NULL)
109 		goto misc_error;
110 
111 	for(i = 0; i < addrc; i++) {
112 		int j, k, af;
113 
114 		n = &blacklists[index].addrs[i].sda;
115 		m = &blacklists[index].addrs[i].sdm;
116 
117 		j = sscanf(addrs[i], "%39[^/]/%u", astring, &maskbits);
118 		if (j != 2)
119 			goto parse_error;
120 		if (maskbits > 128)
121 			goto parse_error;
122 		/*
123 		 * sanity check! we don't allow a 0 mask -
124 		 * don't blacklist the entire net.
125 		 */
126 		if (maskbits == 0)
127 			goto parse_error;
128 		if (strchr(astring, ':') != NULL)
129 			af = AF_INET6;
130 		else
131 			af = AF_INET;
132 		if (af == AF_INET && maskbits > 32)
133 			goto parse_error;
134 		j = inet_pton(af, astring, n);
135 		if (j != 1)
136 			goto parse_error;
137 		if (debug > 0)
138 			printf("added %s/%u\n", astring, maskbits);
139 
140 		/* set mask, borrowed from pf */
141 		k = 0;
142 		for (j = 0; j < 4; j++)
143 			m->addr32[j] = 0;
144 		while (maskbits >= 32) {
145 			m->addr32[k++] = 0xffffffff;
146 			maskbits -= 32;
147 		}
148 		for (j = 31; j > 31 - maskbits; --j)
149 			m->addr32[k] |= (1 << j);
150 		if (maskbits)
151 			m->addr32[k] = htonl(m->addr32[k]);
152 
153 		/* mask off address bits that won't ever be used */
154 		for (j = 0; j < 4; j++)
155 			n->addr32[j] = n->addr32[j] & m->addr32[j];
156 	}
157 	if (index == blu) {
158 		blu++;
159 		blacklists[blu].tag = NULL;
160 	}
161 	return (0);
162  parse_error:
163 	if (debug > 0)
164 		printf("sdl_add: parse error, \"%s\"\n", addrs[i]);
165  misc_error:
166 	sdl_free(&blacklists[index]);
167 	return (-1);
168 }
169 
170 
171 /*
172  * Return 1 if the addresses a (with mask m) matches address b
173  * otherwise return 0. It is assumed that address a has been
174  * pre-masked out, we only need to mask b.
175  */
176 int
177 match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
178     sa_family_t af)
179 {
180 	int	match = 0;
181 
182 	switch (af) {
183 	case AF_INET:
184 		if ((a->addr32[0]) ==
185 		    (b->addr32[0] & m->addr32[0]))
186 			match++;
187 		break;
188 	case AF_INET6:
189 		if (((a->addr32[0]) ==
190 		     (b->addr32[0] & m->addr32[0])) &&
191 		    ((a->addr32[1]) ==
192 		     (b->addr32[1] & m->addr32[1])) &&
193 		    ((a->addr32[2]) ==
194 		     (b->addr32[2] & m->addr32[2])) &&
195 		    ((a->addr32[3]) ==
196 		     (b->addr32[3] & m->addr32[3])))
197 			match++;
198 		break;
199 	}
200 	return (match);
201 }
202 
203 
204 /*
205  * Given an address and address family
206  * return list of pointers to matching nodes. or NULL if none.
207  */
208 struct sdlist **
209 sdl_lookup(struct sdlist *head, int af, void * src)
210 {
211 	int i, matches = 0;
212 	struct sdlist *sdl;
213 	struct sdentry *sda;
214 	struct sdaddr *source = (struct sdaddr *) src;
215 	static int sdnewlen = 0;
216 	static struct sdlist **sdnew = NULL;
217 
218 	if (head == NULL)
219 		return (NULL);
220 	else
221 		sdl = head;
222 	while (sdl->tag != NULL) {
223 		for (i = 0; i < sdl->naddrs; i++) {
224 			sda = sdl->addrs + i;
225 			if (match_addr(&sda->sda, &sda->sdm, source, af)) {
226 				if (matches == sdnewlen) {
227 					struct sdlist **tmp;
228 
229 					tmp = realloc(sdnew,
230 					    (sdnewlen + 128) *
231 					     sizeof(struct sdlist *));
232 					if (tmp == NULL)
233 						/*
234 						 * XXX out of memory -
235 						 * return what we have
236 						 */
237 						return (sdnew);
238 					sdnew = tmp;
239 					sdnewlen += 128;
240 				}
241 				sdnew[matches]= sdl;
242 				matches++;
243 				sdnew[matches]=NULL;
244 				break;
245 			}
246 		}
247 		sdl++;
248 	}
249 	return (sdnew);
250 }
251 
252 static void
253 sdl_free(struct sdlist *sdl)
254 {
255 	if (sdl->tag != NULL)
256 		free(sdl->tag);
257 
258 	if (sdl->string != NULL)
259 		free(sdl->string);
260 
261 	if (sdl->addrs != NULL)
262 		free(sdl->addrs);
263 
264 	sdl_clear(sdl);
265 }
266 
267 static void
268 sdl_clear(struct sdlist *sdl)
269 {
270 	sdl->tag = NULL;
271 	sdl->string = NULL;
272 	sdl->addrs = NULL;
273 	sdl->naddrs = 0;
274 }
275 
276