1 /* $OpenBSD: sdl.c,v 1.16 2007/03/26 16:36:18 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
sdl_add(char * sdname,char * sdstring,char ** addrs,int addrc)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 = malloc(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
166 /*
167 * Return 1 if the addresses a (with mask m) matches address b
168 * otherwise return 0. It is assumed that address a has been
169 * pre-masked out, we only need to mask b.
170 */
171 int
match_addr(struct sdaddr * a,struct sdaddr * m,struct sdaddr * b,sa_family_t af)172 match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
173 sa_family_t af)
174 {
175 int match = 0;
176
177 switch (af) {
178 case AF_INET:
179 if ((a->addr32[0]) ==
180 (b->addr32[0] & m->addr32[0]))
181 match++;
182 break;
183 case AF_INET6:
184 if (((a->addr32[0]) ==
185 (b->addr32[0] & m->addr32[0])) &&
186 ((a->addr32[1]) ==
187 (b->addr32[1] & m->addr32[1])) &&
188 ((a->addr32[2]) ==
189 (b->addr32[2] & m->addr32[2])) &&
190 ((a->addr32[3]) ==
191 (b->addr32[3] & m->addr32[3])))
192 match++;
193 break;
194 }
195 return (match);
196 }
197
198
199 /*
200 * Given an address and address family
201 * return list of pointers to matching nodes. or NULL if none.
202 */
203 struct sdlist **
sdl_lookup(struct sdlist * head,int af,void * src)204 sdl_lookup(struct sdlist *head, int af, void * src)
205 {
206 int i, matches = 0;
207 struct sdlist *sdl;
208 struct sdentry *sda;
209 struct sdaddr *source = (struct sdaddr *) src;
210 int sdnewlen = 0;
211 struct sdlist **sdnew = NULL;
212
213 if (head == NULL)
214 return (NULL);
215 else
216 sdl = head;
217 while (sdl->tag != NULL) {
218 for (i = 0; i < sdl->naddrs; i++) {
219 sda = sdl->addrs + i;
220 if (match_addr(&sda->sda, &sda->sdm, source, af)) {
221 if (matches == sdnewlen) {
222 struct sdlist **tmp;
223
224 tmp = realloc(sdnew,
225 (sdnewlen + 128) *
226 sizeof(struct sdlist *));
227 if (tmp == NULL)
228 /*
229 * XXX out of memory -
230 * return what we have
231 */
232 return (sdnew);
233 sdnew = tmp;
234 sdnewlen += 128;
235 }
236 sdnew[matches]= sdl;
237 matches++;
238 sdnew[matches]=NULL;
239 break;
240 }
241 }
242 sdl++;
243 }
244 return (sdnew);
245 }
246
247 static void
sdl_free(struct sdlist * sdl)248 sdl_free(struct sdlist *sdl)
249 {
250 free(sdl->tag);
251 free(sdl->string);
252 free(sdl->addrs);
253 sdl_clear(sdl);
254 }
255
256 static void
sdl_clear(struct sdlist * sdl)257 sdl_clear(struct sdlist *sdl)
258 {
259 sdl->tag = NULL;
260 sdl->string = NULL;
261 sdl->addrs = NULL;
262 sdl->naddrs = 0;
263 }
264
265