xref: /openbsd-src/libexec/spamd/sdl.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /*	$OpenBSD: sdl.c,v 1.10 2003/09/26 16:07:29 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 
87 		tmp = realloc(blacklists, (blc + 128) *
88 		    sizeof(struct sdlist));
89 		if (tmp == NULL)
90 			return (-1);
91 		blacklists = tmp;
92 		blc += 128;
93 		sdl_clear(&blacklists[index]);
94 	}
95 
96 	if ((blacklists[index].tag = strdup(sdname)) == NULL)
97 		goto misc_error;
98 	if ((blacklists[index].string = strdup(sdstring)) == NULL)
99 		goto misc_error;
100 
101 	blacklists[index].naddrs = addrc;
102 
103 	/*
104 	 * Cycle through addrs, converting. We assume they are correctly
105 	 * formatted v4 and v6 addrs, if they don't all convert correctly, the
106 	 * add fails. Each address should be address/maskbits
107 	 */
108 	blacklists[index].addrs = malloc(addrc * sizeof(struct sdentry));
109 	if (blacklists[index].addrs == NULL)
110 		goto misc_error;
111 
112 	for(i = 0; i < addrc; i++) {
113 		int j, k, af;
114 
115 		n = &blacklists[index].addrs[i].sda;
116 		m = &blacklists[index].addrs[i].sdm;
117 
118 		j = sscanf(addrs[i], "%39[^/]/%u", astring, &maskbits);
119 		if (j != 2)
120 			goto parse_error;
121 		if (maskbits > 128)
122 			goto parse_error;
123 		/*
124 		 * sanity check! we don't allow a 0 mask -
125 		 * don't blacklist the entire net.
126 		 */
127 		if (maskbits == 0)
128 			goto parse_error;
129 		if (strchr(astring, ':') != NULL)
130 			af = AF_INET6;
131 		else
132 			af = AF_INET;
133 		if (af == AF_INET && maskbits > 32)
134 			goto parse_error;
135 		j = inet_pton(af, astring, n);
136 		if (j != 1)
137 			goto parse_error;
138 		if (debug > 0)
139 			printf("added %s/%u\n", astring, maskbits);
140 
141 		/* set mask, borrowed from pf */
142 		k = 0;
143 		for (j = 0; j < 4; j++)
144 			m->addr32[j] = 0;
145 		while (maskbits >= 32) {
146 			m->addr32[k++] = 0xffffffff;
147 			maskbits -= 32;
148 		}
149 		for (j = 31; j > 31 - maskbits; --j)
150 			m->addr32[k] |= (1 << j);
151 		if (maskbits)
152 			m->addr32[k] = htonl(m->addr32[k]);
153 
154 		/* mask off address bits that won't ever be used */
155 		for (j = 0; j < 4; j++)
156 			n->addr32[j] = n->addr32[j] & m->addr32[j];
157 	}
158 	if (index == blu) {
159 		blu++;
160 		blacklists[blu].tag = NULL;
161 	}
162 	return (0);
163  parse_error:
164 	if (debug > 0)
165 		printf("sdl_add: parse error, \"%s\"\n", addrs[i]);
166  misc_error:
167 	sdl_free(&blacklists[index]);
168 	return (-1);
169 }
170 
171 
172 /*
173  * Return 1 if the addresses a (with mask m) matches address b
174  * otherwise return 0. It is assumed that address a has been
175  * pre-masked out, we only need to mask b.
176  */
177 int
178 match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
179     sa_family_t af)
180 {
181 	int	match = 0;
182 
183 	switch (af) {
184 	case AF_INET:
185 		if ((a->addr32[0]) ==
186 		    (b->addr32[0] & m->addr32[0]))
187 			match++;
188 		break;
189 	case AF_INET6:
190 		if (((a->addr32[0]) ==
191 		     (b->addr32[0] & m->addr32[0])) &&
192 		    ((a->addr32[1]) ==
193 		     (b->addr32[1] & m->addr32[1])) &&
194 		    ((a->addr32[2]) ==
195 		     (b->addr32[2] & m->addr32[2])) &&
196 		    ((a->addr32[3]) ==
197 		     (b->addr32[3] & m->addr32[3])))
198 			match++;
199 		break;
200 	}
201 	return (match);
202 }
203 
204 
205 /*
206  * Given an address and address family
207  * return list of pointers to matching nodes. or NULL if none.
208  */
209 struct sdlist **
210 sdl_lookup(struct sdlist *head, int af, void * src)
211 {
212 	int i, matches = 0;
213 	struct sdlist *sdl;
214 	struct sdentry *sda;
215 	struct sdaddr *source = (struct sdaddr *) src;
216 	static int sdnewlen = 0;
217 	static struct sdlist **sdnew = NULL;
218 
219 	if (head == NULL)
220 		return (NULL);
221 	else
222 		sdl = head;
223 	while (sdl->tag != NULL) {
224 		for (i = 0; i < sdl->naddrs; i++) {
225 			sda = sdl->addrs + i;
226 			if (match_addr(&sda->sda, &sda->sdm, source, af)) {
227 				if (matches == sdnewlen) {
228 					struct sdlist **tmp;
229 
230 					tmp = realloc(sdnew,
231 					    (sdnewlen + 128) *
232 					     sizeof(struct sdlist *));
233 					if (tmp == NULL)
234 						/*
235 						 * XXX out of memory -
236 						 * return what we have
237 						 */
238 						return (sdnew);
239 					sdnew = tmp;
240 					sdnewlen += 128;
241 				}
242 				sdnew[matches]= sdl;
243 				matches++;
244 				sdnew[matches]=NULL;
245 				break;
246 			}
247 		}
248 		sdl++;
249 	}
250 	return (sdnew);
251 }
252 
253 static void
254 sdl_free(struct sdlist *sdl)
255 {
256 	free(sdl->tag);
257 	free(sdl->string);
258 	free(sdl->addrs);
259 	sdl_clear(sdl);
260 }
261 
262 static void
263 sdl_clear(struct sdlist *sdl)
264 {
265 	sdl->tag = NULL;
266 	sdl->string = NULL;
267 	sdl->addrs = NULL;
268 	sdl->naddrs = 0;
269 }
270 
271