/src/unbound/services/rpz.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * services/rpz.c - rpz service |
3 | | * |
4 | | * Copyright (c) 2019, NLnet Labs. All rights reserved. |
5 | | * |
6 | | * This software is open source. |
7 | | * |
8 | | * Redistribution and use in source and binary forms, with or without |
9 | | * modification, are permitted provided that the following conditions |
10 | | * are met: |
11 | | * |
12 | | * Redistributions of source code must retain the above copyright notice, |
13 | | * this list of conditions and the following disclaimer. |
14 | | * |
15 | | * Redistributions in binary form must reproduce the above copyright notice, |
16 | | * this list of conditions and the following disclaimer in the documentation |
17 | | * and/or other materials provided with the distribution. |
18 | | * |
19 | | * Neither the name of the NLNET LABS nor the names of its contributors may |
20 | | * be used to endorse or promote products derived from this software without |
21 | | * specific prior written permission. |
22 | | * |
23 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
26 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
27 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
29 | | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
30 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
31 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
32 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
33 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | | */ |
35 | | |
36 | | /** |
37 | | * \file |
38 | | * |
39 | | * This file contains functions to enable RPZ service. |
40 | | */ |
41 | | |
42 | | #include "config.h" |
43 | | #include "services/rpz.h" |
44 | | #include "util/config_file.h" |
45 | | #include "sldns/wire2str.h" |
46 | | #include "sldns/str2wire.h" |
47 | | #include "util/data/dname.h" |
48 | | #include "util/net_help.h" |
49 | | #include "util/log.h" |
50 | | #include "util/data/dname.h" |
51 | | #include "util/locks.h" |
52 | | #include "util/regional.h" |
53 | | #include "util/data/msgencode.h" |
54 | | #include "services/cache/dns.h" |
55 | | #include "iterator/iterator.h" |
56 | | #include "iterator/iter_delegpt.h" |
57 | | #include "daemon/worker.h" |
58 | | |
59 | | typedef struct resp_addr rpz_aclnode_type; |
60 | | |
61 | | struct matched_delegation_point { |
62 | | uint8_t* dname; |
63 | | size_t dname_len; |
64 | | }; |
65 | | |
66 | | /** string for RPZ action enum */ |
67 | | const char* |
68 | | rpz_action_to_string(enum rpz_action a) |
69 | 0 | { |
70 | 0 | switch(a) { |
71 | 0 | case RPZ_NXDOMAIN_ACTION: return "rpz-nxdomain"; |
72 | 0 | case RPZ_NODATA_ACTION: return "rpz-nodata"; |
73 | 0 | case RPZ_PASSTHRU_ACTION: return "rpz-passthru"; |
74 | 0 | case RPZ_DROP_ACTION: return "rpz-drop"; |
75 | 0 | case RPZ_TCP_ONLY_ACTION: return "rpz-tcp-only"; |
76 | 0 | case RPZ_INVALID_ACTION: return "rpz-invalid"; |
77 | 0 | case RPZ_LOCAL_DATA_ACTION: return "rpz-local-data"; |
78 | 0 | case RPZ_DISABLED_ACTION: return "rpz-disabled"; |
79 | 0 | case RPZ_CNAME_OVERRIDE_ACTION: return "rpz-cname-override"; |
80 | 0 | case RPZ_NO_OVERRIDE_ACTION: return "rpz-no-override"; |
81 | 0 | default: return "rpz-unknown-action"; |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | | /** RPZ action enum for config string */ |
86 | | static enum rpz_action |
87 | | rpz_config_to_action(char* a) |
88 | 0 | { |
89 | 0 | if(strcmp(a, "nxdomain") == 0) return RPZ_NXDOMAIN_ACTION; |
90 | 0 | else if(strcmp(a, "nodata") == 0) return RPZ_NODATA_ACTION; |
91 | 0 | else if(strcmp(a, "passthru") == 0) return RPZ_PASSTHRU_ACTION; |
92 | 0 | else if(strcmp(a, "drop") == 0) return RPZ_DROP_ACTION; |
93 | 0 | else if(strcmp(a, "tcp_only") == 0) return RPZ_TCP_ONLY_ACTION; |
94 | 0 | else if(strcmp(a, "cname") == 0) return RPZ_CNAME_OVERRIDE_ACTION; |
95 | 0 | else if(strcmp(a, "disabled") == 0) return RPZ_DISABLED_ACTION; |
96 | 0 | else return RPZ_INVALID_ACTION; |
97 | 0 | } |
98 | | |
99 | | /** string for RPZ trigger enum */ |
100 | | static const char* |
101 | | rpz_trigger_to_string(enum rpz_trigger r) |
102 | 0 | { |
103 | 0 | switch(r) { |
104 | 0 | case RPZ_QNAME_TRIGGER: return "rpz-qname"; |
105 | 0 | case RPZ_CLIENT_IP_TRIGGER: return "rpz-client-ip"; |
106 | 0 | case RPZ_RESPONSE_IP_TRIGGER: return "rpz-response-ip"; |
107 | 0 | case RPZ_NSDNAME_TRIGGER: return "rpz-nsdname"; |
108 | 0 | case RPZ_NSIP_TRIGGER: return "rpz-nsip"; |
109 | 0 | case RPZ_INVALID_TRIGGER: return "rpz-invalid"; |
110 | 0 | default: return "rpz-unknown-trigger"; |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | | /** |
115 | | * Get the label that is just before the root label. |
116 | | * @param dname: dname to work on |
117 | | * @param maxdnamelen: maximum length of the dname |
118 | | * @return: pointer to TLD label, NULL if not found or invalid dname |
119 | | */ |
120 | | static uint8_t* |
121 | | get_tld_label(uint8_t* dname, size_t maxdnamelen) |
122 | 0 | { |
123 | 0 | uint8_t* prevlab = dname; |
124 | 0 | size_t dnamelen = 0; |
125 | | |
126 | | /* one byte needed for label length */ |
127 | 0 | if(dnamelen+1 > maxdnamelen) |
128 | 0 | return NULL; |
129 | | |
130 | | /* only root label */ |
131 | 0 | if(*dname == 0) |
132 | 0 | return NULL; |
133 | | |
134 | 0 | while(*dname) { |
135 | 0 | dnamelen += ((size_t)*dname)+1; |
136 | 0 | if(dnamelen+1 > maxdnamelen) |
137 | 0 | return NULL; |
138 | 0 | dname = dname+((size_t)*dname)+1; |
139 | 0 | if(*dname != 0) |
140 | 0 | prevlab = dname; |
141 | 0 | } |
142 | 0 | return prevlab; |
143 | 0 | } |
144 | | |
145 | | /** |
146 | | * The RR types that are to be ignored. |
147 | | * DNSSEC RRs at the apex, and SOA and NS are ignored. |
148 | | */ |
149 | | static int |
150 | | rpz_type_ignored(uint16_t rr_type) |
151 | 0 | { |
152 | 0 | switch(rr_type) { |
153 | 0 | case LDNS_RR_TYPE_SOA: |
154 | 0 | case LDNS_RR_TYPE_NS: |
155 | 0 | case LDNS_RR_TYPE_DNAME: |
156 | | /* all DNSSEC-related RRs must be ignored */ |
157 | 0 | case LDNS_RR_TYPE_DNSKEY: |
158 | 0 | case LDNS_RR_TYPE_DS: |
159 | 0 | case LDNS_RR_TYPE_RRSIG: |
160 | 0 | case LDNS_RR_TYPE_NSEC: |
161 | 0 | case LDNS_RR_TYPE_NSEC3: |
162 | 0 | case LDNS_RR_TYPE_NSEC3PARAM: |
163 | 0 | return 1; |
164 | 0 | default: |
165 | 0 | break; |
166 | 0 | } |
167 | 0 | return 0; |
168 | 0 | } |
169 | | |
170 | | /** |
171 | | * Classify RPZ action for RR type/rdata |
172 | | * @param rr_type: the RR type |
173 | | * @param rdatawl: RDATA with 2 bytes length |
174 | | * @param rdatalen: the length of rdatawl (including its 2 bytes length) |
175 | | * @return: the RPZ action |
176 | | */ |
177 | | static enum rpz_action |
178 | | rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) |
179 | 0 | { |
180 | 0 | char* endptr; |
181 | 0 | uint8_t* rdata; |
182 | 0 | int rdatalabs; |
183 | 0 | uint8_t* tldlab = NULL; |
184 | |
|
185 | 0 | switch(rr_type) { |
186 | 0 | case LDNS_RR_TYPE_SOA: |
187 | 0 | case LDNS_RR_TYPE_NS: |
188 | 0 | case LDNS_RR_TYPE_DNAME: |
189 | | /* all DNSSEC-related RRs must be ignored */ |
190 | 0 | case LDNS_RR_TYPE_DNSKEY: |
191 | 0 | case LDNS_RR_TYPE_DS: |
192 | 0 | case LDNS_RR_TYPE_RRSIG: |
193 | 0 | case LDNS_RR_TYPE_NSEC: |
194 | 0 | case LDNS_RR_TYPE_NSEC3: |
195 | 0 | case LDNS_RR_TYPE_NSEC3PARAM: |
196 | 0 | return RPZ_INVALID_ACTION; |
197 | 0 | case LDNS_RR_TYPE_CNAME: |
198 | 0 | break; |
199 | 0 | default: |
200 | 0 | return RPZ_LOCAL_DATA_ACTION; |
201 | 0 | } |
202 | | |
203 | | /* use CNAME target to determine RPZ action */ |
204 | 0 | log_assert(rr_type == LDNS_RR_TYPE_CNAME); |
205 | 0 | if(rdatalen < 3) |
206 | 0 | return RPZ_INVALID_ACTION; |
207 | | |
208 | 0 | rdata = rdatawl + 2; /* 2 bytes of rdata length */ |
209 | 0 | if(dname_valid(rdata, rdatalen-2) != rdatalen-2) |
210 | 0 | return RPZ_INVALID_ACTION; |
211 | | |
212 | 0 | rdatalabs = dname_count_labels(rdata); |
213 | 0 | if(rdatalabs == 1) |
214 | 0 | return RPZ_NXDOMAIN_ACTION; |
215 | 0 | else if(rdatalabs == 2) { |
216 | 0 | if(dname_subdomain_c(rdata, (uint8_t*)&"\001*\000")) |
217 | 0 | return RPZ_NODATA_ACTION; |
218 | 0 | else if(dname_subdomain_c(rdata, |
219 | 0 | (uint8_t*)&"\014rpz-passthru\000")) |
220 | 0 | return RPZ_PASSTHRU_ACTION; |
221 | 0 | else if(dname_subdomain_c(rdata, (uint8_t*)&"\010rpz-drop\000")) |
222 | 0 | return RPZ_DROP_ACTION; |
223 | 0 | else if(dname_subdomain_c(rdata, |
224 | 0 | (uint8_t*)&"\014rpz-tcp-only\000")) |
225 | 0 | return RPZ_TCP_ONLY_ACTION; |
226 | 0 | } |
227 | | |
228 | | /* all other TLDs starting with "rpz-" are invalid */ |
229 | 0 | tldlab = get_tld_label(rdata, rdatalen-2); |
230 | 0 | if(tldlab && dname_lab_startswith(tldlab, "rpz-", &endptr)) |
231 | 0 | return RPZ_INVALID_ACTION; |
232 | | |
233 | | /* no special label found */ |
234 | 0 | return RPZ_LOCAL_DATA_ACTION; |
235 | 0 | } |
236 | | |
237 | | static enum localzone_type |
238 | | rpz_action_to_localzone_type(enum rpz_action a) |
239 | 0 | { |
240 | 0 | switch(a) { |
241 | 0 | case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain; |
242 | 0 | case RPZ_NODATA_ACTION: return local_zone_always_nodata; |
243 | 0 | case RPZ_DROP_ACTION: return local_zone_always_deny; |
244 | 0 | case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent; |
245 | 0 | case RPZ_LOCAL_DATA_ACTION: |
246 | 0 | ATTR_FALLTHROUGH |
247 | | /* fallthrough */ |
248 | 0 | case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect; |
249 | 0 | case RPZ_TCP_ONLY_ACTION: return local_zone_truncate; |
250 | 0 | case RPZ_INVALID_ACTION: |
251 | 0 | ATTR_FALLTHROUGH |
252 | | /* fallthrough */ |
253 | 0 | default: return local_zone_invalid; |
254 | 0 | } |
255 | 0 | } |
256 | | |
257 | | enum respip_action |
258 | | rpz_action_to_respip_action(enum rpz_action a) |
259 | 0 | { |
260 | 0 | switch(a) { |
261 | 0 | case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain; |
262 | 0 | case RPZ_NODATA_ACTION: return respip_always_nodata; |
263 | 0 | case RPZ_DROP_ACTION: return respip_always_deny; |
264 | 0 | case RPZ_PASSTHRU_ACTION: return respip_always_transparent; |
265 | 0 | case RPZ_LOCAL_DATA_ACTION: |
266 | 0 | ATTR_FALLTHROUGH |
267 | | /* fallthrough */ |
268 | 0 | case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect; |
269 | 0 | case RPZ_TCP_ONLY_ACTION: return respip_truncate; |
270 | 0 | case RPZ_INVALID_ACTION: |
271 | 0 | ATTR_FALLTHROUGH |
272 | | /* fallthrough */ |
273 | 0 | default: return respip_invalid; |
274 | 0 | } |
275 | 0 | } |
276 | | |
277 | | static enum rpz_action |
278 | | localzone_type_to_rpz_action(enum localzone_type lzt) |
279 | 0 | { |
280 | 0 | switch(lzt) { |
281 | 0 | case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION; |
282 | 0 | case local_zone_always_nodata: return RPZ_NODATA_ACTION; |
283 | 0 | case local_zone_always_deny: return RPZ_DROP_ACTION; |
284 | 0 | case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION; |
285 | 0 | case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION; |
286 | 0 | case local_zone_truncate: return RPZ_TCP_ONLY_ACTION; |
287 | 0 | case local_zone_invalid: |
288 | 0 | ATTR_FALLTHROUGH |
289 | | /* fallthrough */ |
290 | 0 | default: return RPZ_INVALID_ACTION; |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | enum rpz_action |
295 | | respip_action_to_rpz_action(enum respip_action a) |
296 | 0 | { |
297 | 0 | switch(a) { |
298 | 0 | case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION; |
299 | 0 | case respip_always_nodata: return RPZ_NODATA_ACTION; |
300 | 0 | case respip_always_deny: return RPZ_DROP_ACTION; |
301 | 0 | case respip_always_transparent: return RPZ_PASSTHRU_ACTION; |
302 | 0 | case respip_redirect: return RPZ_LOCAL_DATA_ACTION; |
303 | 0 | case respip_truncate: return RPZ_TCP_ONLY_ACTION; |
304 | 0 | case respip_invalid: |
305 | 0 | ATTR_FALLTHROUGH |
306 | | /* fallthrough */ |
307 | 0 | default: return RPZ_INVALID_ACTION; |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | | /** |
312 | | * Get RPZ trigger for dname |
313 | | * @param dname: dname containing RPZ trigger |
314 | | * @param dname_len: length of the dname |
315 | | * @return: RPZ trigger enum |
316 | | */ |
317 | | static enum rpz_trigger |
318 | | rpz_dname_to_trigger(uint8_t* dname, size_t dname_len) |
319 | 0 | { |
320 | 0 | uint8_t* tldlab; |
321 | 0 | char* endptr; |
322 | |
|
323 | 0 | if(dname_valid(dname, dname_len) != dname_len) |
324 | 0 | return RPZ_INVALID_TRIGGER; |
325 | | |
326 | 0 | tldlab = get_tld_label(dname, dname_len); |
327 | 0 | if(!tldlab || !dname_lab_startswith(tldlab, "rpz-", &endptr)) |
328 | 0 | return RPZ_QNAME_TRIGGER; |
329 | | |
330 | 0 | if(dname_subdomain_c(tldlab, |
331 | 0 | (uint8_t*)&"\015rpz-client-ip\000")) |
332 | 0 | return RPZ_CLIENT_IP_TRIGGER; |
333 | 0 | else if(dname_subdomain_c(tldlab, (uint8_t*)&"\006rpz-ip\000")) |
334 | 0 | return RPZ_RESPONSE_IP_TRIGGER; |
335 | 0 | else if(dname_subdomain_c(tldlab, (uint8_t*)&"\013rpz-nsdname\000")) |
336 | 0 | return RPZ_NSDNAME_TRIGGER; |
337 | 0 | else if(dname_subdomain_c(tldlab, (uint8_t*)&"\010rpz-nsip\000")) |
338 | 0 | return RPZ_NSIP_TRIGGER; |
339 | | |
340 | 0 | return RPZ_QNAME_TRIGGER; |
341 | 0 | } |
342 | | |
343 | | static inline struct clientip_synthesized_rrset* |
344 | | rpz_clientip_synthesized_set_create(void) |
345 | 0 | { |
346 | 0 | struct clientip_synthesized_rrset* set = calloc(1, sizeof(*set)); |
347 | 0 | if(set == NULL) { |
348 | 0 | return NULL; |
349 | 0 | } |
350 | 0 | set->region = regional_create(); |
351 | 0 | if(set->region == NULL) { |
352 | 0 | free(set); |
353 | 0 | return NULL; |
354 | 0 | } |
355 | 0 | addr_tree_init(&set->entries); |
356 | 0 | lock_rw_init(&set->lock); |
357 | 0 | return set; |
358 | 0 | } |
359 | | |
360 | | static void |
361 | | rpz_clientip_synthesized_rr_delete(rbnode_type* n, void* ATTR_UNUSED(arg)) |
362 | 0 | { |
363 | 0 | struct clientip_synthesized_rr* r = (struct clientip_synthesized_rr*)n->key; |
364 | 0 | lock_rw_destroy(&r->lock); |
365 | | #ifdef THREADS_DISABLED |
366 | | (void)r; |
367 | | #endif |
368 | 0 | } |
369 | | |
370 | | static inline void |
371 | | rpz_clientip_synthesized_set_delete(struct clientip_synthesized_rrset* set) |
372 | 0 | { |
373 | 0 | if(set == NULL) { |
374 | 0 | return; |
375 | 0 | } |
376 | 0 | lock_rw_destroy(&set->lock); |
377 | 0 | traverse_postorder(&set->entries, rpz_clientip_synthesized_rr_delete, NULL); |
378 | 0 | regional_destroy(set->region); |
379 | 0 | free(set); |
380 | 0 | } |
381 | | |
382 | | void |
383 | | rpz_delete(struct rpz* r) |
384 | 0 | { |
385 | 0 | if(!r) |
386 | 0 | return; |
387 | 0 | local_zones_delete(r->local_zones); |
388 | 0 | local_zones_delete(r->nsdname_zones); |
389 | 0 | respip_set_delete(r->respip_set); |
390 | 0 | rpz_clientip_synthesized_set_delete(r->client_set); |
391 | 0 | rpz_clientip_synthesized_set_delete(r->ns_set); |
392 | 0 | regional_destroy(r->region); |
393 | 0 | free(r->taglist); |
394 | 0 | free(r->log_name); |
395 | 0 | free(r); |
396 | 0 | } |
397 | | |
398 | | int |
399 | | rpz_clear(struct rpz* r) |
400 | 0 | { |
401 | | /* must hold write lock on auth_zone */ |
402 | 0 | local_zones_delete(r->local_zones); |
403 | 0 | r->local_zones = NULL; |
404 | 0 | local_zones_delete(r->nsdname_zones); |
405 | 0 | r->nsdname_zones = NULL; |
406 | 0 | respip_set_delete(r->respip_set); |
407 | 0 | r->respip_set = NULL; |
408 | 0 | rpz_clientip_synthesized_set_delete(r->client_set); |
409 | 0 | r->client_set = NULL; |
410 | 0 | rpz_clientip_synthesized_set_delete(r->ns_set); |
411 | 0 | r->ns_set = NULL; |
412 | 0 | if(!(r->local_zones = local_zones_create())){ |
413 | 0 | return 0; |
414 | 0 | } |
415 | 0 | r->nsdname_zones = local_zones_create(); |
416 | 0 | if(r->nsdname_zones == NULL) { |
417 | 0 | return 0; |
418 | 0 | } |
419 | 0 | if(!(r->respip_set = respip_set_create())) { |
420 | 0 | return 0; |
421 | 0 | } |
422 | 0 | if(!(r->client_set = rpz_clientip_synthesized_set_create())) { |
423 | 0 | return 0; |
424 | 0 | } |
425 | 0 | if(!(r->ns_set = rpz_clientip_synthesized_set_create())) { |
426 | 0 | return 0; |
427 | 0 | } |
428 | 0 | return 1; |
429 | 0 | } |
430 | | |
431 | | void |
432 | | rpz_finish_config(struct rpz* r) |
433 | 0 | { |
434 | 0 | lock_rw_wrlock(&r->respip_set->lock); |
435 | 0 | addr_tree_init_parents(&r->respip_set->ip_tree); |
436 | 0 | lock_rw_unlock(&r->respip_set->lock); |
437 | |
|
438 | 0 | lock_rw_wrlock(&r->client_set->lock); |
439 | 0 | addr_tree_init_parents(&r->client_set->entries); |
440 | 0 | lock_rw_unlock(&r->client_set->lock); |
441 | |
|
442 | 0 | lock_rw_wrlock(&r->ns_set->lock); |
443 | 0 | addr_tree_init_parents(&r->ns_set->entries); |
444 | 0 | lock_rw_unlock(&r->ns_set->lock); |
445 | 0 | } |
446 | | |
447 | | /** new rrset containing CNAME override, does not yet contain a dname */ |
448 | | static struct ub_packed_rrset_key* |
449 | | new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen) |
450 | 0 | { |
451 | 0 | struct ub_packed_rrset_key* rrset; |
452 | 0 | struct packed_rrset_data* pd; |
453 | 0 | uint16_t rdlength = htons(ctlen); |
454 | 0 | rrset = (struct ub_packed_rrset_key*)regional_alloc_zero(region, |
455 | 0 | sizeof(*rrset)); |
456 | 0 | if(!rrset) { |
457 | 0 | log_err("out of memory"); |
458 | 0 | return NULL; |
459 | 0 | } |
460 | 0 | rrset->entry.key = rrset; |
461 | 0 | pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd)); |
462 | 0 | if(!pd) { |
463 | 0 | log_err("out of memory"); |
464 | 0 | return NULL; |
465 | 0 | } |
466 | 0 | pd->trust = rrset_trust_prim_noglue; |
467 | 0 | pd->security = sec_status_insecure; |
468 | |
|
469 | 0 | pd->count = 1; |
470 | 0 | pd->rr_len = regional_alloc_zero(region, sizeof(*pd->rr_len)); |
471 | 0 | pd->rr_ttl = regional_alloc_zero(region, sizeof(*pd->rr_ttl)); |
472 | 0 | pd->rr_data = regional_alloc_zero(region, sizeof(*pd->rr_data)); |
473 | 0 | if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) { |
474 | 0 | log_err("out of memory"); |
475 | 0 | return NULL; |
476 | 0 | } |
477 | 0 | pd->rr_len[0] = ctlen+2; |
478 | 0 | pd->rr_ttl[0] = 3600; |
479 | 0 | pd->rr_data[0] = regional_alloc_zero(region, 2 /* rdlength */ + ctlen); |
480 | 0 | if(!pd->rr_data[0]) { |
481 | 0 | log_err("out of memory"); |
482 | 0 | return NULL; |
483 | 0 | } |
484 | 0 | memmove(pd->rr_data[0], &rdlength, 2); |
485 | 0 | memmove(pd->rr_data[0]+2, ct, ctlen); |
486 | |
|
487 | 0 | rrset->entry.data = pd; |
488 | 0 | rrset->rk.type = htons(LDNS_RR_TYPE_CNAME); |
489 | 0 | rrset->rk.rrset_class = htons(LDNS_RR_CLASS_IN); |
490 | 0 | return rrset; |
491 | 0 | } |
492 | | |
493 | | /** delete the cname override */ |
494 | | static void |
495 | | delete_cname_override(struct rpz* r) |
496 | 0 | { |
497 | 0 | if(r->cname_override) { |
498 | | /* The cname override is what is allocated in the region. */ |
499 | 0 | regional_free_all(r->region); |
500 | 0 | r->cname_override = NULL; |
501 | 0 | } |
502 | 0 | } |
503 | | |
504 | | /** Apply rpz config elements to the rpz structure, false on failure. */ |
505 | | static int |
506 | | rpz_apply_cfg_elements(struct rpz* r, struct config_auth* p) |
507 | 0 | { |
508 | 0 | if(p->rpz_taglist && p->rpz_taglistlen) { |
509 | 0 | r->taglistlen = p->rpz_taglistlen; |
510 | 0 | r->taglist = memdup(p->rpz_taglist, r->taglistlen); |
511 | 0 | if(!r->taglist) { |
512 | 0 | log_err("malloc failure on RPZ taglist alloc"); |
513 | 0 | return 0; |
514 | 0 | } |
515 | 0 | } |
516 | | |
517 | 0 | if(p->rpz_action_override) { |
518 | 0 | r->action_override = rpz_config_to_action(p->rpz_action_override); |
519 | 0 | } |
520 | 0 | else |
521 | 0 | r->action_override = RPZ_NO_OVERRIDE_ACTION; |
522 | |
|
523 | 0 | if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { |
524 | 0 | uint8_t nm[LDNS_MAX_DOMAINLEN+1]; |
525 | 0 | size_t nmlen = sizeof(nm); |
526 | |
|
527 | 0 | if(!p->rpz_cname) { |
528 | 0 | log_err("rpz: override with cname action found, but no " |
529 | 0 | "rpz-cname-override configured"); |
530 | 0 | return 0; |
531 | 0 | } |
532 | | |
533 | 0 | if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) { |
534 | 0 | log_err("rpz: cannot parse cname override: %s", |
535 | 0 | p->rpz_cname); |
536 | 0 | return 0; |
537 | 0 | } |
538 | 0 | r->cname_override = new_cname_override(r->region, nm, nmlen); |
539 | 0 | if(!r->cname_override) { |
540 | 0 | return 0; |
541 | 0 | } |
542 | 0 | } |
543 | 0 | r->log = p->rpz_log; |
544 | 0 | r->signal_nxdomain_ra = p->rpz_signal_nxdomain_ra; |
545 | 0 | if(p->rpz_log_name) { |
546 | 0 | if(!(r->log_name = strdup(p->rpz_log_name))) { |
547 | 0 | log_err("malloc failure on RPZ log_name strdup"); |
548 | 0 | return 0; |
549 | 0 | } |
550 | 0 | } |
551 | 0 | return 1; |
552 | 0 | } |
553 | | |
554 | | struct rpz* |
555 | | rpz_create(struct config_auth* p) |
556 | 0 | { |
557 | 0 | struct rpz* r = calloc(1, sizeof(*r)); |
558 | 0 | if(!r) |
559 | 0 | goto err; |
560 | | |
561 | 0 | r->region = regional_create_custom(sizeof(struct regional)); |
562 | 0 | if(!r->region) { |
563 | 0 | goto err; |
564 | 0 | } |
565 | | |
566 | 0 | if(!(r->local_zones = local_zones_create())){ |
567 | 0 | goto err; |
568 | 0 | } |
569 | | |
570 | 0 | r->nsdname_zones = local_zones_create(); |
571 | 0 | if(r->local_zones == NULL){ |
572 | 0 | goto err; |
573 | 0 | } |
574 | | |
575 | 0 | if(!(r->respip_set = respip_set_create())) { |
576 | 0 | goto err; |
577 | 0 | } |
578 | | |
579 | 0 | r->client_set = rpz_clientip_synthesized_set_create(); |
580 | 0 | if(r->client_set == NULL) { |
581 | 0 | goto err; |
582 | 0 | } |
583 | | |
584 | 0 | r->ns_set = rpz_clientip_synthesized_set_create(); |
585 | 0 | if(r->ns_set == NULL) { |
586 | 0 | goto err; |
587 | 0 | } |
588 | | |
589 | 0 | if(!rpz_apply_cfg_elements(r, p)) |
590 | 0 | goto err; |
591 | 0 | return r; |
592 | 0 | err: |
593 | 0 | if(r) { |
594 | 0 | if(r->local_zones) |
595 | 0 | local_zones_delete(r->local_zones); |
596 | 0 | if(r->nsdname_zones) |
597 | 0 | local_zones_delete(r->nsdname_zones); |
598 | 0 | if(r->respip_set) |
599 | 0 | respip_set_delete(r->respip_set); |
600 | 0 | if(r->client_set != NULL) |
601 | 0 | rpz_clientip_synthesized_set_delete(r->client_set); |
602 | 0 | if(r->ns_set != NULL) |
603 | 0 | rpz_clientip_synthesized_set_delete(r->ns_set); |
604 | 0 | if(r->taglist) |
605 | 0 | free(r->taglist); |
606 | 0 | if(r->region) |
607 | 0 | regional_destroy(r->region); |
608 | 0 | free(r); |
609 | 0 | } |
610 | 0 | return NULL; |
611 | 0 | } |
612 | | |
613 | | int |
614 | | rpz_config(struct rpz* r, struct config_auth* p) |
615 | 0 | { |
616 | | /* If the zonefile changes, it is read later, after which |
617 | | * rpz_clear and rpz_finish_config is called. */ |
618 | | |
619 | | /* free taglist, if any */ |
620 | 0 | if(r->taglist) { |
621 | 0 | free(r->taglist); |
622 | 0 | r->taglist = NULL; |
623 | 0 | r->taglistlen = 0; |
624 | 0 | } |
625 | | |
626 | | /* free logname, if any */ |
627 | 0 | if(r->log_name) { |
628 | 0 | free(r->log_name); |
629 | 0 | r->log_name = NULL; |
630 | 0 | } |
631 | |
|
632 | 0 | delete_cname_override(r); |
633 | |
|
634 | 0 | if(!rpz_apply_cfg_elements(r, p)) |
635 | 0 | return 0; |
636 | 0 | return 1; |
637 | 0 | } |
638 | | |
639 | | /** |
640 | | * Remove RPZ zone name from dname |
641 | | * Copy dname to newdname, without the originlen number of trailing bytes |
642 | | */ |
643 | | static size_t |
644 | | strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen, |
645 | | uint8_t* newdname, size_t maxnewdnamelen) |
646 | 0 | { |
647 | 0 | size_t newdnamelen; |
648 | 0 | if(dnamelen < originlen) |
649 | 0 | return 0; |
650 | 0 | newdnamelen = dnamelen - originlen; |
651 | 0 | if(newdnamelen+1 > maxnewdnamelen) |
652 | 0 | return 0; |
653 | 0 | memmove(newdname, dname, newdnamelen); |
654 | 0 | newdname[newdnamelen] = 0; |
655 | 0 | return newdnamelen + 1; /* + 1 for root label */ |
656 | 0 | } |
657 | | |
658 | | static void |
659 | | rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, |
660 | | size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, |
661 | | uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) |
662 | 0 | { |
663 | 0 | struct local_zone* z; |
664 | 0 | enum localzone_type tp = local_zone_always_transparent; |
665 | 0 | int dnamelabs = dname_count_labels(dname); |
666 | 0 | int newzone = 0; |
667 | |
|
668 | 0 | if(a == RPZ_INVALID_ACTION) { |
669 | 0 | char str[LDNS_MAX_DOMAINLEN]; |
670 | 0 | if(rrtype == LDNS_RR_TYPE_SOA || rrtype == LDNS_RR_TYPE_NS || |
671 | 0 | rrtype == LDNS_RR_TYPE_DNAME || |
672 | 0 | rrtype == LDNS_RR_TYPE_DNSKEY || |
673 | 0 | rrtype == LDNS_RR_TYPE_RRSIG || |
674 | 0 | rrtype == LDNS_RR_TYPE_NSEC || |
675 | 0 | rrtype == LDNS_RR_TYPE_NSEC3PARAM || |
676 | 0 | rrtype == LDNS_RR_TYPE_NSEC3 || |
677 | 0 | rrtype == LDNS_RR_TYPE_DS) { |
678 | 0 | free(dname); |
679 | 0 | return; /* no need to log these types as unsupported */ |
680 | 0 | } |
681 | 0 | dname_str(dname, str); |
682 | 0 | verbose(VERB_ALGO, "rpz: qname trigger, %s skipping unsupported action: %s", |
683 | 0 | str, rpz_action_to_string(a)); |
684 | 0 | free(dname); |
685 | 0 | return; |
686 | 0 | } |
687 | | |
688 | 0 | lock_rw_wrlock(&lz->lock); |
689 | | /* exact match */ |
690 | 0 | z = local_zones_find(lz, dname, dnamelen, dnamelabs, LDNS_RR_CLASS_IN); |
691 | 0 | if(z != NULL && a != RPZ_LOCAL_DATA_ACTION) { |
692 | 0 | char* rrstr = sldns_wire2str_rr(rr, rr_len); |
693 | 0 | if(rrstr == NULL) { |
694 | 0 | log_err("malloc error while inserting rpz nsdname trigger"); |
695 | 0 | free(dname); |
696 | 0 | lock_rw_unlock(&lz->lock); |
697 | 0 | return; |
698 | 0 | } |
699 | 0 | if(rrstr[0]) |
700 | 0 | rrstr[strlen(rrstr)-1]=0; /* remove newline */ |
701 | 0 | verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", rrstr); |
702 | 0 | free(rrstr); |
703 | 0 | free(dname); |
704 | 0 | lock_rw_unlock(&lz->lock); |
705 | 0 | return; |
706 | 0 | } |
707 | 0 | if(z == NULL) { |
708 | 0 | tp = rpz_action_to_localzone_type(a); |
709 | 0 | z = local_zones_add_zone(lz, dname, dnamelen, |
710 | 0 | dnamelabs, rrclass, tp); |
711 | 0 | if(z == NULL) { |
712 | 0 | log_warn("rpz: create failed"); |
713 | 0 | lock_rw_unlock(&lz->lock); |
714 | | /* dname will be free'd in failed local_zone_create() */ |
715 | 0 | return; |
716 | 0 | } |
717 | 0 | newzone = 1; |
718 | 0 | } |
719 | 0 | if(a == RPZ_LOCAL_DATA_ACTION) { |
720 | 0 | char* rrstr = sldns_wire2str_rr(rr, rr_len); |
721 | 0 | if(rrstr == NULL) { |
722 | 0 | log_err("malloc error while inserting rpz nsdname trigger"); |
723 | 0 | free(dname); |
724 | 0 | lock_rw_unlock(&lz->lock); |
725 | 0 | return; |
726 | 0 | } |
727 | 0 | lock_rw_wrlock(&z->lock); |
728 | 0 | local_zone_enter_rr(z, dname, dnamelen, dnamelabs, rrtype, |
729 | 0 | rrclass, ttl, rdata, rdata_len, rrstr); |
730 | 0 | lock_rw_unlock(&z->lock); |
731 | 0 | free(rrstr); |
732 | 0 | } |
733 | 0 | if(!newzone) { |
734 | 0 | free(dname); |
735 | 0 | } |
736 | 0 | lock_rw_unlock(&lz->lock); |
737 | 0 | } |
738 | | |
739 | | static void |
740 | | rpz_log_dname(char const* msg, uint8_t* dname, size_t dname_len) |
741 | 0 | { |
742 | 0 | char buf[LDNS_MAX_DOMAINLEN]; |
743 | 0 | (void)dname_len; |
744 | 0 | dname_str(dname, buf); |
745 | 0 | verbose(VERB_ALGO, "rpz: %s: <%s>", msg, buf); |
746 | 0 | } |
747 | | |
748 | | static void |
749 | | rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
750 | | enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, |
751 | | uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) |
752 | 0 | { |
753 | 0 | if(a == RPZ_INVALID_ACTION) { |
754 | 0 | verbose(VERB_ALGO, "rpz: skipping invalid action"); |
755 | 0 | free(dname); |
756 | 0 | return; |
757 | 0 | } |
758 | | |
759 | 0 | rpz_insert_local_zones_trigger(r->local_zones, dname, dnamelen, a, rrtype, |
760 | 0 | rrclass, ttl, rdata, rdata_len, rr, rr_len); |
761 | 0 | } |
762 | | |
763 | | static int |
764 | | rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen, |
765 | | uint8_t** stripdname, size_t* stripdnamelen) |
766 | 0 | { |
767 | 0 | uint8_t* tldstart = get_tld_label(dname, maxdnamelen); |
768 | 0 | uint8_t swap; |
769 | 0 | if(tldstart == NULL) { |
770 | 0 | if(dname == NULL) { |
771 | 0 | *stripdname = NULL; |
772 | 0 | *stripdnamelen = 0; |
773 | 0 | return 0; |
774 | 0 | } |
775 | 0 | *stripdname = memdup(dname, maxdnamelen); |
776 | 0 | if(!*stripdname) { |
777 | 0 | *stripdnamelen = 0; |
778 | 0 | log_err("malloc failure for rpz strip suffix"); |
779 | 0 | return 0; |
780 | 0 | } |
781 | 0 | *stripdnamelen = maxdnamelen; |
782 | 0 | return 1; |
783 | 0 | } |
784 | | /* shorten the domain name briefly, |
785 | | * then we allocate a new name with the correct length */ |
786 | 0 | swap = *tldstart; |
787 | 0 | *tldstart = 0; |
788 | 0 | (void)dname_count_size_labels(dname, stripdnamelen); |
789 | 0 | *stripdname = memdup(dname, *stripdnamelen); |
790 | 0 | *tldstart = swap; |
791 | 0 | if(!*stripdname) { |
792 | 0 | *stripdnamelen = 0; |
793 | 0 | log_err("malloc failure for rpz strip suffix"); |
794 | 0 | return 0; |
795 | 0 | } |
796 | 0 | return 1; |
797 | 0 | } |
798 | | |
799 | | static void |
800 | | rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
801 | | enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, |
802 | | uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) |
803 | 0 | { |
804 | 0 | uint8_t* dname_stripped = NULL; |
805 | 0 | size_t dnamelen_stripped = 0; |
806 | |
|
807 | 0 | rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped, |
808 | 0 | &dnamelen_stripped); |
809 | 0 | if(a == RPZ_INVALID_ACTION) { |
810 | 0 | verbose(VERB_ALGO, "rpz: skipping invalid action"); |
811 | 0 | free(dname_stripped); |
812 | 0 | return; |
813 | 0 | } |
814 | | |
815 | | /* dname_stripped is consumed or freed by the insert routine */ |
816 | 0 | rpz_insert_local_zones_trigger(r->nsdname_zones, dname_stripped, |
817 | 0 | dnamelen_stripped, a, rrtype, rrclass, ttl, rdata, rdata_len, |
818 | 0 | rr, rr_len); |
819 | 0 | } |
820 | | |
821 | | static int |
822 | | rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* addr, |
823 | | socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype, |
824 | | uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, |
825 | | uint8_t* rr, size_t rr_len) |
826 | 0 | { |
827 | 0 | struct resp_addr* node; |
828 | 0 | char* rrstr; |
829 | 0 | enum respip_action respa = rpz_action_to_respip_action(a); |
830 | |
|
831 | 0 | lock_rw_wrlock(&set->lock); |
832 | 0 | rrstr = sldns_wire2str_rr(rr, rr_len); |
833 | 0 | if(rrstr == NULL) { |
834 | 0 | log_err("malloc error while inserting rpz ipaddr based trigger"); |
835 | 0 | lock_rw_unlock(&set->lock); |
836 | 0 | return 0; |
837 | 0 | } |
838 | | |
839 | 0 | node = respip_sockaddr_find_or_create(set, addr, addrlen, net, 1, rrstr); |
840 | 0 | if(node == NULL) { |
841 | 0 | lock_rw_unlock(&set->lock); |
842 | 0 | free(rrstr); |
843 | 0 | return 0; |
844 | 0 | } |
845 | | |
846 | 0 | lock_rw_wrlock(&node->lock); |
847 | 0 | lock_rw_unlock(&set->lock); |
848 | |
|
849 | 0 | node->action = respa; |
850 | |
|
851 | 0 | if(a == RPZ_LOCAL_DATA_ACTION) { |
852 | 0 | respip_enter_rr(set->region, node, rrtype, |
853 | 0 | rrclass, ttl, rdata, rdata_len, rrstr, ""); |
854 | 0 | } |
855 | |
|
856 | 0 | lock_rw_unlock(&node->lock); |
857 | 0 | free(rrstr); |
858 | 0 | return 1; |
859 | 0 | } |
860 | | |
861 | | static inline struct clientip_synthesized_rr* |
862 | | rpz_clientip_ensure_entry(struct clientip_synthesized_rrset* set, |
863 | | struct sockaddr_storage* addr, socklen_t addrlen, int net) |
864 | 0 | { |
865 | 0 | int insert_ok; |
866 | 0 | struct clientip_synthesized_rr* node = |
867 | 0 | (struct clientip_synthesized_rr*)addr_tree_find(&set->entries, |
868 | 0 | addr, addrlen, net); |
869 | |
|
870 | 0 | if(node != NULL) { return node; } |
871 | | |
872 | | /* node does not yet exist => allocate one */ |
873 | 0 | node = regional_alloc_zero(set->region, sizeof(*node)); |
874 | 0 | if(node == NULL) { |
875 | 0 | log_err("out of memory"); |
876 | 0 | return NULL; |
877 | 0 | } |
878 | | |
879 | 0 | lock_rw_init(&node->lock); |
880 | 0 | node->action = RPZ_INVALID_ACTION; |
881 | 0 | insert_ok = addr_tree_insert(&set->entries, &node->node, |
882 | 0 | addr, addrlen, net); |
883 | 0 | if (!insert_ok) { |
884 | 0 | log_warn("rpz: unexpected: unable to insert clientip address node"); |
885 | | /* we can not free the just allocated node. |
886 | | * theoretically a memleak */ |
887 | 0 | return NULL; |
888 | 0 | } |
889 | | |
890 | 0 | return node; |
891 | 0 | } |
892 | | |
893 | | static void |
894 | 0 | rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) { |
895 | 0 | char* rrstr = sldns_wire2str_rr(rr, rr_len); |
896 | 0 | if(rrstr == NULL) { |
897 | 0 | log_err("malloc error while inserting rpz clientip based record"); |
898 | 0 | return; |
899 | 0 | } |
900 | 0 | log_err("rpz: unexpected: unable to insert %s: %s", msg, rrstr); |
901 | 0 | free(rrstr); |
902 | 0 | } |
903 | | |
904 | | /* from localzone.c; difference is we don't have a dname */ |
905 | | static struct local_rrset* |
906 | | rpz_clientip_new_rrset(struct regional* region, |
907 | | struct clientip_synthesized_rr* raddr, uint16_t rrtype, uint16_t rrclass) |
908 | 0 | { |
909 | 0 | struct packed_rrset_data* pd; |
910 | 0 | struct local_rrset* rrset = (struct local_rrset*) |
911 | 0 | regional_alloc_zero(region, sizeof(*rrset)); |
912 | 0 | if(rrset == NULL) { |
913 | 0 | log_err("out of memory"); |
914 | 0 | return NULL; |
915 | 0 | } |
916 | 0 | rrset->next = raddr->data; |
917 | 0 | raddr->data = rrset; |
918 | 0 | rrset->rrset = (struct ub_packed_rrset_key*) |
919 | 0 | regional_alloc_zero(region, sizeof(*rrset->rrset)); |
920 | 0 | if(rrset->rrset == NULL) { |
921 | 0 | log_err("out of memory"); |
922 | 0 | return NULL; |
923 | 0 | } |
924 | 0 | rrset->rrset->entry.key = rrset->rrset; |
925 | 0 | pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd)); |
926 | 0 | if(pd == NULL) { |
927 | 0 | log_err("out of memory"); |
928 | 0 | return NULL; |
929 | 0 | } |
930 | 0 | pd->trust = rrset_trust_prim_noglue; |
931 | 0 | pd->security = sec_status_insecure; |
932 | 0 | rrset->rrset->entry.data = pd; |
933 | 0 | rrset->rrset->rk.type = htons(rrtype); |
934 | 0 | rrset->rrset->rk.rrset_class = htons(rrclass); |
935 | 0 | rrset->rrset->rk.dname = regional_alloc_zero(region, 1); |
936 | 0 | if(rrset->rrset->rk.dname == NULL) { |
937 | 0 | log_err("out of memory"); |
938 | 0 | return NULL; |
939 | 0 | } |
940 | 0 | rrset->rrset->rk.dname_len = 1; |
941 | 0 | return rrset; |
942 | 0 | } |
943 | | |
944 | | static int |
945 | | rpz_clientip_enter_rr(struct regional* region, struct clientip_synthesized_rr* raddr, |
946 | | uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata, |
947 | | size_t rdata_len) |
948 | 0 | { |
949 | 0 | struct local_rrset* rrset; |
950 | 0 | if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data != NULL) { |
951 | 0 | log_err("CNAME response-ip data can not co-exist with other " |
952 | 0 | "client-ip data"); |
953 | 0 | return 0; |
954 | 0 | } |
955 | | |
956 | 0 | rrset = rpz_clientip_new_rrset(region, raddr, rrtype, rrclass); |
957 | 0 | if(raddr->data == NULL) { |
958 | 0 | return 0; |
959 | 0 | } |
960 | | |
961 | 0 | return rrset_insert_rr(region, rrset->rrset->entry.data, rdata, rdata_len, ttl, ""); |
962 | 0 | } |
963 | | |
964 | | static int |
965 | | rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct sockaddr_storage* addr, |
966 | | socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype, |
967 | | uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, |
968 | | uint8_t* rr, size_t rr_len) |
969 | 0 | { |
970 | 0 | struct clientip_synthesized_rr* node; |
971 | |
|
972 | 0 | lock_rw_wrlock(&set->lock); |
973 | |
|
974 | 0 | node = rpz_clientip_ensure_entry(set, addr, addrlen, net); |
975 | 0 | if(node == NULL) { |
976 | 0 | lock_rw_unlock(&set->lock); |
977 | 0 | rpz_report_rrset_error("client ip address", rr, rr_len); |
978 | 0 | return 0; |
979 | 0 | } |
980 | | |
981 | 0 | lock_rw_wrlock(&node->lock); |
982 | 0 | lock_rw_unlock(&set->lock); |
983 | |
|
984 | 0 | node->action = a; |
985 | 0 | if(a == RPZ_LOCAL_DATA_ACTION) { |
986 | 0 | if(!rpz_clientip_enter_rr(set->region, node, rrtype, |
987 | 0 | rrclass, ttl, rdata, rdata_len)) { |
988 | 0 | verbose(VERB_ALGO, "rpz: unable to insert clientip rr"); |
989 | 0 | lock_rw_unlock(&node->lock); |
990 | 0 | return 0; |
991 | 0 | } |
992 | |
|
993 | 0 | } |
994 | | |
995 | 0 | lock_rw_unlock(&node->lock); |
996 | |
|
997 | 0 | return 1; |
998 | 0 | } |
999 | | |
1000 | | static int |
1001 | | rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
1002 | | enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, |
1003 | | uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) |
1004 | 0 | { |
1005 | 0 | struct sockaddr_storage addr; |
1006 | 0 | socklen_t addrlen; |
1007 | 0 | int net, af; |
1008 | |
|
1009 | 0 | if(a == RPZ_INVALID_ACTION) { |
1010 | 0 | return 0; |
1011 | 0 | } |
1012 | | |
1013 | 0 | if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) { |
1014 | 0 | verbose(VERB_ALGO, "rpz: unable to parse client ip"); |
1015 | 0 | return 0; |
1016 | 0 | } |
1017 | | |
1018 | 0 | return rpz_clientip_insert_trigger_rr(r->client_set, &addr, addrlen, net, |
1019 | 0 | a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); |
1020 | 0 | } |
1021 | | |
1022 | | static int |
1023 | | rpz_insert_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
1024 | | enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, |
1025 | | uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) |
1026 | 0 | { |
1027 | 0 | struct sockaddr_storage addr; |
1028 | 0 | socklen_t addrlen; |
1029 | 0 | int net, af; |
1030 | |
|
1031 | 0 | if(a == RPZ_INVALID_ACTION) { |
1032 | 0 | return 0; |
1033 | 0 | } |
1034 | | |
1035 | 0 | if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) { |
1036 | 0 | verbose(VERB_ALGO, "rpz: unable to parse ns ip"); |
1037 | 0 | return 0; |
1038 | 0 | } |
1039 | | |
1040 | 0 | return rpz_clientip_insert_trigger_rr(r->ns_set, &addr, addrlen, net, |
1041 | 0 | a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); |
1042 | 0 | } |
1043 | | |
1044 | | /** Insert RR into RPZ's respip_set */ |
1045 | | static int |
1046 | | rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
1047 | | enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, |
1048 | | uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) |
1049 | 0 | { |
1050 | 0 | struct sockaddr_storage addr; |
1051 | 0 | socklen_t addrlen; |
1052 | 0 | int net, af; |
1053 | |
|
1054 | 0 | if(a == RPZ_INVALID_ACTION) { |
1055 | 0 | return 0; |
1056 | 0 | } |
1057 | | |
1058 | 0 | if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) { |
1059 | 0 | verbose(VERB_ALGO, "rpz: unable to parse response ip"); |
1060 | 0 | return 0; |
1061 | 0 | } |
1062 | | |
1063 | 0 | if(a == RPZ_INVALID_ACTION || |
1064 | 0 | rpz_action_to_respip_action(a) == respip_invalid) { |
1065 | 0 | char str[LDNS_MAX_DOMAINLEN]; |
1066 | 0 | dname_str(dname, str); |
1067 | 0 | verbose(VERB_ALGO, "rpz: respip trigger, %s skipping unsupported action: %s", |
1068 | 0 | str, rpz_action_to_string(a)); |
1069 | 0 | return 0; |
1070 | 0 | } |
1071 | | |
1072 | 0 | return rpz_insert_ipaddr_based_trigger(r->respip_set, &addr, addrlen, net, |
1073 | 0 | a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); |
1074 | 0 | } |
1075 | | |
1076 | | int |
1077 | | rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, |
1078 | | size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl, |
1079 | | uint8_t* rdatawl, size_t rdatalen, uint8_t* rr, size_t rr_len) |
1080 | 0 | { |
1081 | 0 | size_t policydnamelen; |
1082 | | /* name is free'd in local_zone delete */ |
1083 | 0 | enum rpz_trigger t; |
1084 | 0 | enum rpz_action a; |
1085 | 0 | uint8_t* policydname; |
1086 | |
|
1087 | 0 | if(rpz_type_ignored(rr_type)) { |
1088 | | /* this rpz action is not valid, eg. this is the SOA or NS RR */ |
1089 | 0 | return 1; |
1090 | 0 | } |
1091 | 0 | if(!dname_subdomain_c(dname, azname)) { |
1092 | 0 | char* dname_str = sldns_wire2str_dname(dname, dnamelen); |
1093 | 0 | char* azname_str = sldns_wire2str_dname(azname, aznamelen); |
1094 | 0 | if(dname_str && azname_str) { |
1095 | 0 | log_err("rpz: name of record (%s) to insert into RPZ is not a " |
1096 | 0 | "subdomain of the configured name of the RPZ zone (%s)", |
1097 | 0 | dname_str, azname_str); |
1098 | 0 | } else { |
1099 | 0 | log_err("rpz: name of record to insert into RPZ is not a " |
1100 | 0 | "subdomain of the configured name of the RPZ zone"); |
1101 | 0 | } |
1102 | 0 | free(dname_str); |
1103 | 0 | free(azname_str); |
1104 | 0 | return 0; |
1105 | 0 | } |
1106 | | |
1107 | 0 | log_assert(dnamelen >= aznamelen); |
1108 | 0 | if(!(policydname = calloc(1, (dnamelen-aznamelen)+1))) { |
1109 | 0 | log_err("malloc error while inserting RPZ RR"); |
1110 | 0 | return 0; |
1111 | 0 | } |
1112 | | |
1113 | 0 | a = rpz_rr_to_action(rr_type, rdatawl, rdatalen); |
1114 | 0 | if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen, |
1115 | 0 | policydname, (dnamelen-aznamelen)+1))) { |
1116 | 0 | free(policydname); |
1117 | 0 | return 0; |
1118 | 0 | } |
1119 | 0 | t = rpz_dname_to_trigger(policydname, policydnamelen); |
1120 | 0 | if(t == RPZ_INVALID_TRIGGER) { |
1121 | 0 | free(policydname); |
1122 | 0 | verbose(VERB_ALGO, "rpz: skipping invalid trigger"); |
1123 | 0 | return 1; |
1124 | 0 | } |
1125 | 0 | if(t == RPZ_QNAME_TRIGGER) { |
1126 | | /* policydname will be consumed, no free */ |
1127 | 0 | rpz_insert_qname_trigger(r, policydname, policydnamelen, |
1128 | 0 | a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, |
1129 | 0 | rr_len); |
1130 | 0 | } else if(t == RPZ_RESPONSE_IP_TRIGGER) { |
1131 | 0 | rpz_insert_response_ip_trigger(r, policydname, policydnamelen, |
1132 | 0 | a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, |
1133 | 0 | rr_len); |
1134 | 0 | free(policydname); |
1135 | 0 | } else if(t == RPZ_CLIENT_IP_TRIGGER) { |
1136 | 0 | rpz_insert_clientip_trigger(r, policydname, policydnamelen, |
1137 | 0 | a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, |
1138 | 0 | rr_len); |
1139 | 0 | free(policydname); |
1140 | 0 | } else if(t == RPZ_NSIP_TRIGGER) { |
1141 | 0 | rpz_insert_nsip_trigger(r, policydname, policydnamelen, |
1142 | 0 | a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, |
1143 | 0 | rr_len); |
1144 | 0 | free(policydname); |
1145 | 0 | } else if(t == RPZ_NSDNAME_TRIGGER) { |
1146 | 0 | rpz_insert_nsdname_trigger(r, policydname, policydnamelen, |
1147 | 0 | a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, |
1148 | 0 | rr_len); |
1149 | 0 | free(policydname); |
1150 | 0 | } else { |
1151 | 0 | free(policydname); |
1152 | 0 | verbose(VERB_ALGO, "rpz: skipping unsupported trigger: %s", |
1153 | 0 | rpz_trigger_to_string(t)); |
1154 | 0 | } |
1155 | 0 | return 1; |
1156 | 0 | } |
1157 | | |
1158 | | /** |
1159 | | * Find RPZ local-zone by qname. |
1160 | | * @param zones: local-zone tree |
1161 | | * @param qname: qname |
1162 | | * @param qname_len: length of qname |
1163 | | * @param qclass: qclass |
1164 | | * @param only_exact: if 1 only exact (non wildcard) matches are returned |
1165 | | * @param wr: get write lock for local-zone if 1, read lock if 0 |
1166 | | * @param zones_keep_lock: if set do not release the r->local_zones lock, this |
1167 | | * makes the caller of this function responsible for releasing the lock. |
1168 | | * @return: NULL or local-zone holding rd or wr lock |
1169 | | */ |
1170 | | static struct local_zone* |
1171 | | rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint16_t qclass, |
1172 | | int only_exact, int wr, int zones_keep_lock) |
1173 | 0 | { |
1174 | 0 | uint8_t* ce; |
1175 | 0 | size_t ce_len; |
1176 | 0 | int ce_labs; |
1177 | 0 | uint8_t wc[LDNS_MAX_DOMAINLEN+1]; |
1178 | 0 | int exact; |
1179 | 0 | struct local_zone* z = NULL; |
1180 | |
|
1181 | 0 | if(wr) { |
1182 | 0 | lock_rw_wrlock(&zones->lock); |
1183 | 0 | } else { |
1184 | 0 | lock_rw_rdlock(&zones->lock); |
1185 | 0 | } |
1186 | 0 | z = local_zones_find_le(zones, qname, qname_len, |
1187 | 0 | dname_count_labels(qname), |
1188 | 0 | LDNS_RR_CLASS_IN, &exact); |
1189 | 0 | if(!z || (only_exact && !exact)) { |
1190 | 0 | if(!zones_keep_lock) { |
1191 | 0 | lock_rw_unlock(&zones->lock); |
1192 | 0 | } |
1193 | 0 | return NULL; |
1194 | 0 | } |
1195 | 0 | if(wr) { |
1196 | 0 | lock_rw_wrlock(&z->lock); |
1197 | 0 | } else { |
1198 | 0 | lock_rw_rdlock(&z->lock); |
1199 | 0 | } |
1200 | 0 | if(!zones_keep_lock) { |
1201 | 0 | lock_rw_unlock(&zones->lock); |
1202 | 0 | } |
1203 | |
|
1204 | 0 | if(exact) |
1205 | 0 | return z; |
1206 | | |
1207 | | /* No exact match found, lookup wildcard. closest encloser must |
1208 | | * be the shared parent between the qname and the best local |
1209 | | * zone match, append '*' to that and do another lookup. */ |
1210 | | |
1211 | 0 | ce = dname_get_shared_topdomain(z->name, qname); |
1212 | 0 | if(!ce /* should not happen */) { |
1213 | 0 | lock_rw_unlock(&z->lock); |
1214 | 0 | if(zones_keep_lock) { |
1215 | 0 | lock_rw_unlock(&zones->lock); |
1216 | 0 | } |
1217 | 0 | return NULL; |
1218 | 0 | } |
1219 | 0 | ce_labs = dname_count_size_labels(ce, &ce_len); |
1220 | 0 | if(ce_len+2 > sizeof(wc)) { |
1221 | 0 | lock_rw_unlock(&z->lock); |
1222 | 0 | if(zones_keep_lock) { |
1223 | 0 | lock_rw_unlock(&zones->lock); |
1224 | 0 | } |
1225 | 0 | return NULL; |
1226 | 0 | } |
1227 | 0 | wc[0] = 1; /* length of wildcard label */ |
1228 | 0 | wc[1] = (uint8_t)'*'; /* wildcard label */ |
1229 | 0 | memmove(wc+2, ce, ce_len); |
1230 | 0 | lock_rw_unlock(&z->lock); |
1231 | |
|
1232 | 0 | if(!zones_keep_lock) { |
1233 | 0 | if(wr) { |
1234 | 0 | lock_rw_wrlock(&zones->lock); |
1235 | 0 | } else { |
1236 | 0 | lock_rw_rdlock(&zones->lock); |
1237 | 0 | } |
1238 | 0 | } |
1239 | 0 | z = local_zones_find_le(zones, wc, |
1240 | 0 | ce_len+2, ce_labs+1, qclass, &exact); |
1241 | 0 | if(!z || !exact) { |
1242 | 0 | lock_rw_unlock(&zones->lock); |
1243 | 0 | return NULL; |
1244 | 0 | } |
1245 | 0 | if(wr) { |
1246 | 0 | lock_rw_wrlock(&z->lock); |
1247 | 0 | } else { |
1248 | 0 | lock_rw_rdlock(&z->lock); |
1249 | 0 | } |
1250 | 0 | if(!zones_keep_lock) { |
1251 | 0 | lock_rw_unlock(&zones->lock); |
1252 | 0 | } |
1253 | 0 | return z; |
1254 | 0 | } |
1255 | | |
1256 | | /** Find entry for RR type in the list of rrsets for the clientip. */ |
1257 | | static struct local_rrset* |
1258 | | rpz_find_synthesized_rrset(uint16_t qtype, |
1259 | | struct clientip_synthesized_rr* data, int alias_ok) |
1260 | 0 | { |
1261 | 0 | struct local_rrset* cursor = data->data, *cname = NULL; |
1262 | 0 | while( cursor != NULL) { |
1263 | 0 | struct packed_rrset_key* packed_rrset = &cursor->rrset->rk; |
1264 | 0 | if(htons(qtype) == packed_rrset->type) { |
1265 | 0 | return cursor; |
1266 | 0 | } |
1267 | 0 | if(ntohs(packed_rrset->type) == LDNS_RR_TYPE_CNAME && alias_ok) |
1268 | 0 | cname = cursor; |
1269 | 0 | cursor = cursor->next; |
1270 | 0 | } |
1271 | 0 | if(alias_ok) |
1272 | 0 | return cname; |
1273 | 0 | return NULL; |
1274 | 0 | } |
1275 | | |
1276 | | /** |
1277 | | * Remove RR from RPZ's local-data |
1278 | | * @param z: local-zone for RPZ, holding write lock |
1279 | | * @param policydname: dname of RR to remove |
1280 | | * @param policydnamelen: length of policydname |
1281 | | * @param rr_type: RR type of RR to remove |
1282 | | * @param rdata: rdata of RR to remove |
1283 | | * @param rdatalen: length of rdata |
1284 | | * @return: 1 if zone must be removed after RR deletion |
1285 | | */ |
1286 | | static int |
1287 | | rpz_data_delete_rr(struct local_zone* z, uint8_t* policydname, |
1288 | | size_t policydnamelen, uint16_t rr_type, uint8_t* rdata, |
1289 | | size_t rdatalen) |
1290 | 0 | { |
1291 | 0 | struct local_data* ld; |
1292 | 0 | struct packed_rrset_data* d; |
1293 | 0 | size_t index; |
1294 | 0 | ld = local_zone_find_data(z, policydname, policydnamelen, |
1295 | 0 | dname_count_labels(policydname)); |
1296 | 0 | if(ld) { |
1297 | 0 | struct local_rrset* prev=NULL, *p=ld->rrsets; |
1298 | 0 | while(p && ntohs(p->rrset->rk.type) != rr_type) { |
1299 | 0 | prev = p; |
1300 | 0 | p = p->next; |
1301 | 0 | } |
1302 | 0 | if(!p) |
1303 | 0 | return 0; |
1304 | 0 | d = (struct packed_rrset_data*)p->rrset->entry.data; |
1305 | 0 | if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) { |
1306 | 0 | if(d->count == 1) { |
1307 | | /* no memory recycling for zone deletions ... */ |
1308 | 0 | if(prev) prev->next = p->next; |
1309 | 0 | else ld->rrsets = p->next; |
1310 | 0 | } |
1311 | 0 | if(d->count > 1) { |
1312 | 0 | if(!local_rrset_remove_rr(d, index)) |
1313 | 0 | return 0; |
1314 | 0 | } |
1315 | 0 | } |
1316 | 0 | } |
1317 | 0 | if(ld && ld->rrsets) |
1318 | 0 | return 0; |
1319 | 0 | return 1; |
1320 | 0 | } |
1321 | | |
1322 | | /** |
1323 | | * Remove RR from RPZ's respip set |
1324 | | * @param raddr: respip node |
1325 | | * @param rr_type: RR type of RR to remove |
1326 | | * @param rdata: rdata of RR to remove |
1327 | | * @param rdatalen: length of rdata |
1328 | | * @return: 1 if zone must be removed after RR deletion |
1329 | | */ |
1330 | | static int |
1331 | | rpz_rrset_delete_rr(struct resp_addr* raddr, uint16_t rr_type, uint8_t* rdata, |
1332 | | size_t rdatalen) |
1333 | 0 | { |
1334 | 0 | size_t index; |
1335 | 0 | struct packed_rrset_data* d; |
1336 | 0 | if(!raddr->data) |
1337 | 0 | return 1; |
1338 | 0 | d = raddr->data->entry.data; |
1339 | 0 | if(ntohs(raddr->data->rk.type) != rr_type) { |
1340 | 0 | return 0; |
1341 | 0 | } |
1342 | 0 | if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) { |
1343 | 0 | if(d->count == 1) { |
1344 | | /* regional alloc'd */ |
1345 | 0 | raddr->data->entry.data = NULL; |
1346 | 0 | raddr->data = NULL; |
1347 | 0 | return 1; |
1348 | 0 | } |
1349 | 0 | if(d->count > 1) { |
1350 | 0 | if(!local_rrset_remove_rr(d, index)) |
1351 | 0 | return 0; |
1352 | 0 | } |
1353 | 0 | } |
1354 | 0 | return 0; |
1355 | |
|
1356 | 0 | } |
1357 | | |
1358 | | /** Remove RR from rpz localzones structure */ |
1359 | | static void |
1360 | | rpz_remove_local_zones_trigger(struct local_zones* zones, uint8_t* dname, |
1361 | | size_t dnamelen, enum rpz_action a, uint16_t rr_type, |
1362 | | uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen) |
1363 | 0 | { |
1364 | 0 | struct local_zone* z; |
1365 | 0 | int delete_zone = 1; |
1366 | 0 | z = rpz_find_zone(zones, dname, dnamelen, rr_class, |
1367 | 0 | 1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/); |
1368 | 0 | if(!z) { |
1369 | 0 | verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, " |
1370 | 0 | "RPZ domain not found"); |
1371 | 0 | return; |
1372 | 0 | } |
1373 | 0 | if(a == RPZ_LOCAL_DATA_ACTION) |
1374 | 0 | delete_zone = rpz_data_delete_rr(z, dname, |
1375 | 0 | dnamelen, rr_type, rdatawl, rdatalen); |
1376 | 0 | else if(a != localzone_type_to_rpz_action(z->type)) { |
1377 | 0 | lock_rw_unlock(&z->lock); |
1378 | 0 | lock_rw_unlock(&zones->lock); |
1379 | 0 | return; |
1380 | 0 | } |
1381 | 0 | lock_rw_unlock(&z->lock); |
1382 | 0 | if(delete_zone) { |
1383 | 0 | local_zones_del_zone(zones, z); |
1384 | 0 | } |
1385 | 0 | lock_rw_unlock(&zones->lock); |
1386 | 0 | } |
1387 | | |
1388 | | /** Remove RR from RPZ's local-zone */ |
1389 | | static void |
1390 | | rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
1391 | | enum rpz_action a, uint16_t rr_type, uint16_t rr_class, |
1392 | | uint8_t* rdatawl, size_t rdatalen) |
1393 | 0 | { |
1394 | 0 | rpz_remove_local_zones_trigger(r->local_zones, dname, dnamelen, |
1395 | 0 | a, rr_type, rr_class, rdatawl, rdatalen); |
1396 | 0 | } |
1397 | | |
1398 | | static void |
1399 | | rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
1400 | | enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) |
1401 | 0 | { |
1402 | 0 | struct resp_addr* node; |
1403 | 0 | struct sockaddr_storage addr; |
1404 | 0 | socklen_t addrlen; |
1405 | 0 | int net, af; |
1406 | 0 | int delete_respip = 1; |
1407 | |
|
1408 | 0 | if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) |
1409 | 0 | return; |
1410 | | |
1411 | 0 | lock_rw_wrlock(&r->respip_set->lock); |
1412 | 0 | if(!(node = (struct resp_addr*)addr_tree_find( |
1413 | 0 | &r->respip_set->ip_tree, &addr, addrlen, net))) { |
1414 | 0 | verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, " |
1415 | 0 | "RPZ domain not found"); |
1416 | 0 | lock_rw_unlock(&r->respip_set->lock); |
1417 | 0 | return; |
1418 | 0 | } |
1419 | | |
1420 | 0 | lock_rw_wrlock(&node->lock); |
1421 | 0 | if(a == RPZ_LOCAL_DATA_ACTION) { |
1422 | | /* remove RR, signal whether RR can be removed */ |
1423 | 0 | delete_respip = rpz_rrset_delete_rr(node, rr_type, rdatawl, |
1424 | 0 | rdatalen); |
1425 | 0 | } |
1426 | 0 | lock_rw_unlock(&node->lock); |
1427 | 0 | if(delete_respip) |
1428 | 0 | respip_sockaddr_delete(r->respip_set, node); |
1429 | 0 | lock_rw_unlock(&r->respip_set->lock); |
1430 | 0 | } |
1431 | | |
1432 | | /** find and remove type from list of local_rrset entries*/ |
1433 | | static void |
1434 | | del_local_rrset_from_list(struct local_rrset** list_head, uint16_t dtype) |
1435 | 0 | { |
1436 | 0 | struct local_rrset* prev=NULL, *p=*list_head; |
1437 | 0 | while(p && ntohs(p->rrset->rk.type) != dtype) { |
1438 | 0 | prev = p; |
1439 | 0 | p = p->next; |
1440 | 0 | } |
1441 | 0 | if(!p) |
1442 | 0 | return; /* rrset type not found */ |
1443 | | /* unlink it */ |
1444 | 0 | if(prev) prev->next = p->next; |
1445 | 0 | else *list_head = p->next; |
1446 | | /* no memory recycling for zone deletions ... */ |
1447 | 0 | } |
1448 | | |
1449 | | /** Delete client-ip trigger RR from its RRset and perhaps also the rrset |
1450 | | * from the linked list. Returns if the local data is empty and the node can |
1451 | | * be deleted too, or not. */ |
1452 | | static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node, |
1453 | | uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) |
1454 | 0 | { |
1455 | 0 | struct local_rrset* rrset; |
1456 | 0 | struct packed_rrset_data* d; |
1457 | 0 | size_t index; |
1458 | 0 | rrset = rpz_find_synthesized_rrset(rr_type, node, 0); |
1459 | 0 | if(rrset == NULL) |
1460 | 0 | return 0; /* type not found, ignore */ |
1461 | 0 | d = (struct packed_rrset_data*)rrset->rrset->entry.data; |
1462 | 0 | if(!packed_rrset_find_rr(d, rdatawl, rdatalen, &index)) |
1463 | 0 | return 0; /* RR not found, ignore */ |
1464 | 0 | if(d->count == 1) { |
1465 | | /* regional alloc'd */ |
1466 | | /* delete the type entry from the list */ |
1467 | 0 | del_local_rrset_from_list(&node->data, rr_type); |
1468 | | /* if the list is empty, the node can be removed too */ |
1469 | 0 | if(node->data == NULL) |
1470 | 0 | return 1; |
1471 | 0 | } else if (d->count > 1) { |
1472 | 0 | if(!local_rrset_remove_rr(d, index)) |
1473 | 0 | return 0; |
1474 | 0 | } |
1475 | 0 | return 0; |
1476 | 0 | } |
1477 | | |
1478 | | /** remove trigger RR from clientip_syntheized set tree. */ |
1479 | | static void |
1480 | | rpz_clientip_remove_trigger_rr(struct clientip_synthesized_rrset* set, |
1481 | | struct sockaddr_storage* addr, socklen_t addrlen, int net, |
1482 | | enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) |
1483 | 0 | { |
1484 | 0 | struct clientip_synthesized_rr* node; |
1485 | 0 | int delete_node = 1; |
1486 | |
|
1487 | 0 | lock_rw_wrlock(&set->lock); |
1488 | 0 | node = (struct clientip_synthesized_rr*)addr_tree_find(&set->entries, |
1489 | 0 | addr, addrlen, net); |
1490 | 0 | if(node == NULL) { |
1491 | | /* netblock not found */ |
1492 | 0 | verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, " |
1493 | 0 | "RPZ address, netblock not found"); |
1494 | 0 | lock_rw_unlock(&set->lock); |
1495 | 0 | return; |
1496 | 0 | } |
1497 | 0 | lock_rw_wrlock(&node->lock); |
1498 | 0 | if(a == RPZ_LOCAL_DATA_ACTION) { |
1499 | | /* remove RR, signal whether entry can be removed */ |
1500 | 0 | delete_node = rpz_remove_clientip_rr(node, rr_type, rdatawl, |
1501 | 0 | rdatalen); |
1502 | 0 | } else if(a != node->action) { |
1503 | | /* ignore the RR with different action specification */ |
1504 | 0 | delete_node = 0; |
1505 | 0 | } |
1506 | 0 | if(delete_node) { |
1507 | 0 | rbtree_delete(&set->entries, node->node.node.key); |
1508 | 0 | } |
1509 | 0 | lock_rw_unlock(&set->lock); |
1510 | 0 | lock_rw_unlock(&node->lock); |
1511 | 0 | if(delete_node) { |
1512 | 0 | lock_rw_destroy(&node->lock); |
1513 | 0 | } |
1514 | 0 | } |
1515 | | |
1516 | | /** Remove clientip trigger RR from RPZ. */ |
1517 | | static void |
1518 | | rpz_remove_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
1519 | | enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) |
1520 | 0 | { |
1521 | 0 | struct sockaddr_storage addr; |
1522 | 0 | socklen_t addrlen; |
1523 | 0 | int net, af; |
1524 | 0 | if(a == RPZ_INVALID_ACTION) |
1525 | 0 | return; |
1526 | 0 | if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) |
1527 | 0 | return; |
1528 | 0 | rpz_clientip_remove_trigger_rr(r->client_set, &addr, addrlen, net, |
1529 | 0 | a, rr_type, rdatawl, rdatalen); |
1530 | 0 | } |
1531 | | |
1532 | | /** Remove nsip trigger RR from RPZ. */ |
1533 | | static void |
1534 | | rpz_remove_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
1535 | | enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) |
1536 | 0 | { |
1537 | 0 | struct sockaddr_storage addr; |
1538 | 0 | socklen_t addrlen; |
1539 | 0 | int net, af; |
1540 | 0 | if(a == RPZ_INVALID_ACTION) |
1541 | 0 | return; |
1542 | 0 | if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) |
1543 | 0 | return; |
1544 | 0 | rpz_clientip_remove_trigger_rr(r->ns_set, &addr, addrlen, net, |
1545 | 0 | a, rr_type, rdatawl, rdatalen); |
1546 | 0 | } |
1547 | | |
1548 | | /** Remove nsdname trigger RR from RPZ. */ |
1549 | | static void |
1550 | | rpz_remove_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, |
1551 | | enum rpz_action a, uint16_t rr_type, uint16_t rr_class, |
1552 | | uint8_t* rdatawl, size_t rdatalen) |
1553 | 0 | { |
1554 | 0 | uint8_t* dname_stripped = NULL; |
1555 | 0 | size_t dnamelen_stripped = 0; |
1556 | 0 | if(a == RPZ_INVALID_ACTION) |
1557 | 0 | return; |
1558 | 0 | if(!rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped, |
1559 | 0 | &dnamelen_stripped)) |
1560 | 0 | return; |
1561 | 0 | rpz_remove_local_zones_trigger(r->nsdname_zones, dname_stripped, |
1562 | 0 | dnamelen_stripped, a, rr_type, rr_class, rdatawl, rdatalen); |
1563 | 0 | free(dname_stripped); |
1564 | 0 | } |
1565 | | |
1566 | | void |
1567 | | rpz_remove_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, |
1568 | | size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl, |
1569 | | size_t rdatalen) |
1570 | 0 | { |
1571 | 0 | size_t policydnamelen; |
1572 | 0 | enum rpz_trigger t; |
1573 | 0 | enum rpz_action a; |
1574 | 0 | uint8_t* policydname; |
1575 | |
|
1576 | 0 | if(rpz_type_ignored(rr_type)) { |
1577 | | /* this rpz action is not valid, eg. this is the SOA or NS RR */ |
1578 | 0 | return; |
1579 | 0 | } |
1580 | 0 | if(!dname_subdomain_c(dname, azname)) { |
1581 | | /* not subdomain of the RPZ zone. */ |
1582 | 0 | return; |
1583 | 0 | } |
1584 | | |
1585 | 0 | if(!(policydname = calloc(1, LDNS_MAX_DOMAINLEN + 1))) |
1586 | 0 | return; |
1587 | | |
1588 | 0 | a = rpz_rr_to_action(rr_type, rdatawl, rdatalen); |
1589 | 0 | if(a == RPZ_INVALID_ACTION) { |
1590 | 0 | free(policydname); |
1591 | 0 | return; |
1592 | 0 | } |
1593 | 0 | if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen, |
1594 | 0 | policydname, LDNS_MAX_DOMAINLEN + 1))) { |
1595 | 0 | free(policydname); |
1596 | 0 | return; |
1597 | 0 | } |
1598 | 0 | t = rpz_dname_to_trigger(policydname, policydnamelen); |
1599 | 0 | if(t == RPZ_INVALID_TRIGGER) { |
1600 | | /* skipping invalid trigger */ |
1601 | 0 | free(policydname); |
1602 | 0 | return; |
1603 | 0 | } |
1604 | 0 | if(t == RPZ_QNAME_TRIGGER) { |
1605 | 0 | rpz_remove_qname_trigger(r, policydname, policydnamelen, a, |
1606 | 0 | rr_type, rr_class, rdatawl, rdatalen); |
1607 | 0 | } else if(t == RPZ_RESPONSE_IP_TRIGGER) { |
1608 | 0 | rpz_remove_response_ip_trigger(r, policydname, policydnamelen, |
1609 | 0 | a, rr_type, rdatawl, rdatalen); |
1610 | 0 | } else if(t == RPZ_CLIENT_IP_TRIGGER) { |
1611 | 0 | rpz_remove_clientip_trigger(r, policydname, policydnamelen, a, |
1612 | 0 | rr_type, rdatawl, rdatalen); |
1613 | 0 | } else if(t == RPZ_NSIP_TRIGGER) { |
1614 | 0 | rpz_remove_nsip_trigger(r, policydname, policydnamelen, a, |
1615 | 0 | rr_type, rdatawl, rdatalen); |
1616 | 0 | } else if(t == RPZ_NSDNAME_TRIGGER) { |
1617 | 0 | rpz_remove_nsdname_trigger(r, policydname, policydnamelen, a, |
1618 | 0 | rr_type, rr_class, rdatawl, rdatalen); |
1619 | 0 | } |
1620 | | /* else it was an unsupported trigger, also skipped. */ |
1621 | 0 | free(policydname); |
1622 | 0 | } |
1623 | | |
1624 | | /** print log information for an applied RPZ policy. Based on local-zone's |
1625 | | * lz_inform_print(). |
1626 | | * The repinfo contains the reply address. If it is NULL, the module |
1627 | | * state is used to report the first IP address (if any). |
1628 | | * The dname is used, for the applied rpz, if NULL, addrnode is used. |
1629 | | */ |
1630 | | static void |
1631 | | log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode, |
1632 | | enum rpz_action a, struct query_info* qinfo, |
1633 | | struct comm_reply* repinfo, struct module_qstate* ms, char* log_name) |
1634 | 0 | { |
1635 | 0 | char ip[128], txt[512], portstr[32]; |
1636 | 0 | char dnamestr[LDNS_MAX_DOMAINLEN]; |
1637 | 0 | uint16_t port = 0; |
1638 | 0 | if(dname) { |
1639 | 0 | dname_str(dname, dnamestr); |
1640 | 0 | } else if(addrnode) { |
1641 | 0 | char addrbuf[128]; |
1642 | 0 | addr_to_str(&addrnode->addr, addrnode->addrlen, addrbuf, sizeof(addrbuf)); |
1643 | 0 | snprintf(dnamestr, sizeof(dnamestr), "%s/%d", addrbuf, addrnode->net); |
1644 | 0 | } else { |
1645 | 0 | dnamestr[0]=0; |
1646 | 0 | } |
1647 | 0 | if(repinfo) { |
1648 | 0 | addr_to_str(&repinfo->client_addr, repinfo->client_addrlen, ip, sizeof(ip)); |
1649 | 0 | port = ntohs(((struct sockaddr_in*)&repinfo->client_addr)->sin_port); |
1650 | 0 | } else if(ms && ms->mesh_info && ms->mesh_info->reply_list) { |
1651 | 0 | addr_to_str(&ms->mesh_info->reply_list->query_reply.client_addr, |
1652 | 0 | ms->mesh_info->reply_list->query_reply.client_addrlen, |
1653 | 0 | ip, sizeof(ip)); |
1654 | 0 | port = ntohs(((struct sockaddr_in*)&ms->mesh_info->reply_list->query_reply.client_addr)->sin_port); |
1655 | 0 | } else { |
1656 | 0 | ip[0]=0; |
1657 | 0 | port = 0; |
1658 | 0 | } |
1659 | 0 | snprintf(portstr, sizeof(portstr), "@%u", (unsigned)port); |
1660 | 0 | snprintf(txt, sizeof(txt), "rpz: applied %s%s%s%s%s%s %s %s%s", |
1661 | 0 | (log_name?"[":""), (log_name?log_name:""), (log_name?"] ":""), |
1662 | 0 | (strcmp(trigger,"qname")==0?"":trigger), |
1663 | 0 | (strcmp(trigger,"qname")==0?"":" "), |
1664 | 0 | dnamestr, rpz_action_to_string(a), |
1665 | 0 | (ip[0]?ip:""), (ip[0]?portstr:"")); |
1666 | 0 | log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); |
1667 | 0 | } |
1668 | | |
1669 | | static struct clientip_synthesized_rr* |
1670 | | rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, |
1671 | | struct sockaddr_storage* addr, socklen_t addrlen, char* triggername) |
1672 | 0 | { |
1673 | 0 | struct clientip_synthesized_rr* raddr = NULL; |
1674 | 0 | enum rpz_action action = RPZ_INVALID_ACTION; |
1675 | |
|
1676 | 0 | lock_rw_rdlock(&set->lock); |
1677 | |
|
1678 | 0 | raddr = (struct clientip_synthesized_rr*)addr_tree_lookup(&set->entries, |
1679 | 0 | addr, addrlen); |
1680 | 0 | if(raddr != NULL) { |
1681 | 0 | lock_rw_rdlock(&raddr->lock); |
1682 | 0 | action = raddr->action; |
1683 | 0 | if(verbosity >= VERB_ALGO) { |
1684 | 0 | char ip[256], net[256]; |
1685 | 0 | addr_to_str(addr, addrlen, ip, sizeof(ip)); |
1686 | 0 | addr_to_str(&raddr->node.addr, raddr->node.addrlen, |
1687 | 0 | net, sizeof(net)); |
1688 | 0 | verbose(VERB_ALGO, "rpz: trigger %s %s/%d on %s action=%s", |
1689 | 0 | triggername, net, raddr->node.net, ip, rpz_action_to_string(action)); |
1690 | 0 | } |
1691 | 0 | } |
1692 | 0 | lock_rw_unlock(&set->lock); |
1693 | |
|
1694 | 0 | return raddr; |
1695 | 0 | } |
1696 | | |
1697 | | static inline |
1698 | | struct clientip_synthesized_rr* |
1699 | | rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo, |
1700 | | struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, |
1701 | | struct ub_server_stats* stats, |
1702 | | /* output parameters */ |
1703 | | struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out) |
1704 | 0 | { |
1705 | 0 | struct clientip_synthesized_rr* node = NULL; |
1706 | 0 | struct auth_zone* a = NULL; |
1707 | 0 | struct rpz* r = NULL; |
1708 | 0 | struct local_zone* z = NULL; |
1709 | |
|
1710 | 0 | lock_rw_rdlock(&az->rpz_lock); |
1711 | |
|
1712 | 0 | for(a = az->rpz_first; a; a = a->rpz_az_next) { |
1713 | 0 | lock_rw_rdlock(&a->lock); |
1714 | 0 | r = a->rpz; |
1715 | 0 | if(r->disabled) { |
1716 | 0 | lock_rw_unlock(&a->lock); |
1717 | 0 | continue; |
1718 | 0 | } |
1719 | 0 | if(r->taglist && !taglist_intersect(r->taglist, |
1720 | 0 | r->taglistlen, taglist, taglen)) { |
1721 | 0 | lock_rw_unlock(&a->lock); |
1722 | 0 | continue; |
1723 | 0 | } |
1724 | 0 | z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len, |
1725 | 0 | qinfo->qclass, 0, 0, 0); |
1726 | 0 | node = rpz_ipbased_trigger_lookup(r->client_set, |
1727 | 0 | &repinfo->client_addr, repinfo->client_addrlen, |
1728 | 0 | "clientip"); |
1729 | 0 | if((z || node) && r->action_override == RPZ_DISABLED_ACTION) { |
1730 | 0 | if(r->log) |
1731 | 0 | log_rpz_apply((node?"clientip":"qname"), |
1732 | 0 | (z?z->name:NULL), |
1733 | 0 | (node?&node->node:NULL), |
1734 | 0 | r->action_override, |
1735 | 0 | qinfo, repinfo, NULL, r->log_name); |
1736 | 0 | stats->rpz_action[r->action_override]++; |
1737 | 0 | if(z != NULL) { |
1738 | 0 | lock_rw_unlock(&z->lock); |
1739 | 0 | z = NULL; |
1740 | 0 | } |
1741 | 0 | if(node != NULL) { |
1742 | 0 | lock_rw_unlock(&node->lock); |
1743 | 0 | node = NULL; |
1744 | 0 | } |
1745 | 0 | } |
1746 | 0 | if(z || node) { |
1747 | 0 | break; |
1748 | 0 | } |
1749 | | /* not found in this auth_zone */ |
1750 | 0 | lock_rw_unlock(&a->lock); |
1751 | 0 | } |
1752 | |
|
1753 | 0 | lock_rw_unlock(&az->rpz_lock); |
1754 | |
|
1755 | 0 | *r_out = r; |
1756 | 0 | *a_out = a; |
1757 | 0 | *z_out = z; |
1758 | |
|
1759 | 0 | return node; |
1760 | 0 | } |
1761 | | |
1762 | | static inline int |
1763 | 0 | rpz_is_udp_query(struct comm_reply* repinfo) { |
1764 | 0 | return repinfo != NULL |
1765 | 0 | ? (repinfo->c != NULL |
1766 | 0 | ? repinfo->c->type == comm_udp |
1767 | 0 | : 0) |
1768 | 0 | : 0; |
1769 | 0 | } |
1770 | | |
1771 | | /** encode answer consisting of 1 rrset */ |
1772 | | static int |
1773 | | rpz_local_encode(struct module_env* env, struct query_info* qinfo, |
1774 | | struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, |
1775 | | struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, |
1776 | | int rcode, struct ub_packed_rrset_key* soa_rrset) |
1777 | 0 | { |
1778 | 0 | struct reply_info rep; |
1779 | 0 | uint16_t udpsize; |
1780 | 0 | struct ub_packed_rrset_key* rrsetlist[3]; |
1781 | |
|
1782 | 0 | memset(&rep, 0, sizeof(rep)); |
1783 | 0 | rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); |
1784 | 0 | rep.qdcount = 1; |
1785 | 0 | rep.rrset_count = ansec; |
1786 | 0 | rep.rrsets = rrsetlist; |
1787 | 0 | if(ansec > 0) { |
1788 | 0 | rep.an_numrrsets = 1; |
1789 | 0 | rep.rrsets[0] = rrset; |
1790 | 0 | rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0]; |
1791 | 0 | } |
1792 | 0 | if(soa_rrset != NULL) { |
1793 | 0 | rep.ar_numrrsets = 1; |
1794 | 0 | rep.rrsets[rep.rrset_count] = soa_rrset; |
1795 | 0 | rep.rrset_count ++; |
1796 | 0 | if(rep.ttl < ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]) { |
1797 | 0 | rep.ttl = ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]; |
1798 | 0 | } |
1799 | 0 | } |
1800 | |
|
1801 | 0 | udpsize = edns->udp_size; |
1802 | 0 | edns->edns_version = EDNS_ADVERTISED_VERSION; |
1803 | 0 | edns->udp_size = EDNS_ADVERTISED_SIZE; |
1804 | 0 | edns->ext_rcode = 0; |
1805 | 0 | edns->bits &= EDNS_DO; |
1806 | 0 | if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, |
1807 | 0 | repinfo, temp, env->now_tv) || |
1808 | 0 | !reply_info_answer_encode(qinfo, &rep, |
1809 | 0 | *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), |
1810 | 0 | buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { |
1811 | 0 | error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, |
1812 | 0 | *(uint16_t*)sldns_buffer_begin(buf), |
1813 | 0 | sldns_buffer_read_u16_at(buf, 2), edns); |
1814 | 0 | } |
1815 | |
|
1816 | 0 | return 1; |
1817 | 0 | } |
1818 | | |
1819 | | /** allocate SOA record ubrrsetkey in region */ |
1820 | | static struct ub_packed_rrset_key* |
1821 | | make_soa_ubrrset(struct auth_zone* auth_zone, struct auth_rrset* soa, |
1822 | | struct regional* temp) |
1823 | 0 | { |
1824 | 0 | struct ub_packed_rrset_key csoa; |
1825 | 0 | if(!soa) |
1826 | 0 | return NULL; |
1827 | 0 | memset(&csoa, 0, sizeof(csoa)); |
1828 | 0 | csoa.entry.key = &csoa; |
1829 | 0 | csoa.rk.rrset_class = htons(LDNS_RR_CLASS_IN); |
1830 | 0 | csoa.rk.type = htons(LDNS_RR_TYPE_SOA); |
1831 | 0 | csoa.rk.flags |= PACKED_RRSET_FIXEDTTL |
1832 | 0 | | PACKED_RRSET_RPZ; |
1833 | 0 | csoa.rk.dname = auth_zone->name; |
1834 | 0 | csoa.rk.dname_len = auth_zone->namelen; |
1835 | 0 | csoa.entry.hash = rrset_key_hash(&csoa.rk); |
1836 | 0 | csoa.entry.data = soa->data; |
1837 | 0 | return respip_copy_rrset(&csoa, temp); |
1838 | 0 | } |
1839 | | |
1840 | | static void |
1841 | | rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, |
1842 | | struct module_env* env, struct query_info* qinfo, |
1843 | | struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, |
1844 | | struct regional* temp, struct auth_zone* auth_zone) |
1845 | 0 | { |
1846 | 0 | struct local_rrset* rrset; |
1847 | 0 | enum rpz_action action = RPZ_INVALID_ACTION; |
1848 | 0 | struct ub_packed_rrset_key* rp = NULL; |
1849 | 0 | struct ub_packed_rrset_key* rsoa = NULL; |
1850 | 0 | int rcode = LDNS_RCODE_NOERROR|BIT_AA; |
1851 | 0 | int rrset_count = 1; |
1852 | | |
1853 | | /* prepare synthesized answer for client */ |
1854 | 0 | action = raddr->action; |
1855 | 0 | if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) { |
1856 | 0 | verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); |
1857 | 0 | return; |
1858 | 0 | } |
1859 | | |
1860 | | /* check query type / rr type */ |
1861 | 0 | rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr, 1); |
1862 | 0 | if(rrset == NULL) { |
1863 | 0 | verbose(VERB_ALGO, "rpz: unable to find local-data for query"); |
1864 | 0 | rrset_count = 0; |
1865 | 0 | goto nodata; |
1866 | 0 | } |
1867 | | |
1868 | 0 | rp = respip_copy_rrset(rrset->rrset, temp); |
1869 | 0 | if(!rp) { |
1870 | 0 | verbose(VERB_ALGO, "rpz: local data action: out of memory"); |
1871 | 0 | return; |
1872 | 0 | } |
1873 | | |
1874 | 0 | rp->rk.flags |= PACKED_RRSET_FIXEDTTL | PACKED_RRSET_RPZ; |
1875 | 0 | rp->rk.dname = qinfo->qname; |
1876 | 0 | rp->rk.dname_len = qinfo->qname_len; |
1877 | 0 | rp->entry.hash = rrset_key_hash(&rp->rk); |
1878 | 0 | nodata: |
1879 | 0 | if(auth_zone) { |
1880 | 0 | struct auth_rrset* soa = NULL; |
1881 | 0 | soa = auth_zone_get_soa_rrset(auth_zone); |
1882 | 0 | if(soa) { |
1883 | 0 | rsoa = make_soa_ubrrset(auth_zone, soa, temp); |
1884 | 0 | if(!rsoa) { |
1885 | 0 | verbose(VERB_ALGO, "rpz: local data action soa: out of memory"); |
1886 | 0 | return; |
1887 | 0 | } |
1888 | 0 | } |
1889 | 0 | } |
1890 | | |
1891 | 0 | rpz_local_encode(env, qinfo, edns, repinfo, buf, temp, rp, |
1892 | 0 | rrset_count, rcode, rsoa); |
1893 | 0 | } |
1894 | | |
1895 | | /** Apply the cname override action, during worker request callback. |
1896 | | * false on failure. */ |
1897 | | static int |
1898 | | rpz_apply_cname_override_action(struct rpz* r, |
1899 | | struct query_info* qinfo, struct regional* temp) |
1900 | 0 | { |
1901 | 0 | if(!r) |
1902 | 0 | return 0; |
1903 | 0 | qinfo->local_alias = regional_alloc_zero(temp, |
1904 | 0 | sizeof(struct local_rrset)); |
1905 | 0 | if(qinfo->local_alias == NULL) |
1906 | 0 | return 0; /* out of memory */ |
1907 | 0 | qinfo->local_alias->rrset = respip_copy_rrset(r->cname_override, temp); |
1908 | 0 | if(qinfo->local_alias->rrset == NULL) { |
1909 | 0 | qinfo->local_alias = NULL; |
1910 | 0 | return 0; /* out of memory */ |
1911 | 0 | } |
1912 | 0 | qinfo->local_alias->rrset->rk.dname = qinfo->qname; |
1913 | 0 | qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; |
1914 | 0 | return 1; |
1915 | 0 | } |
1916 | | |
1917 | | /** add additional section SOA record to the reply. |
1918 | | * Since this gets fed into the normal iterator answer creation, it |
1919 | | * gets minimal-responses applied to it, that can remove the additional SOA |
1920 | | * again. */ |
1921 | | static int |
1922 | | rpz_add_soa(struct reply_info* rep, struct module_qstate* ms, |
1923 | | struct auth_zone* az) |
1924 | 0 | { |
1925 | 0 | struct auth_rrset* soa = NULL; |
1926 | 0 | struct ub_packed_rrset_key* rsoa = NULL; |
1927 | 0 | struct ub_packed_rrset_key** prevrrsets; |
1928 | 0 | if(!az) return 1; |
1929 | 0 | soa = auth_zone_get_soa_rrset(az); |
1930 | 0 | if(!soa) return 1; |
1931 | 0 | if(!rep) return 0; |
1932 | 0 | rsoa = make_soa_ubrrset(az, soa, ms->region); |
1933 | 0 | if(!rsoa) return 0; |
1934 | 0 | prevrrsets = rep->rrsets; |
1935 | 0 | rep->rrsets = regional_alloc_zero(ms->region, |
1936 | 0 | sizeof(*rep->rrsets)*(rep->rrset_count+1)); |
1937 | 0 | if(!rep->rrsets) |
1938 | 0 | return 0; |
1939 | 0 | if(prevrrsets && rep->rrset_count > 0) |
1940 | 0 | memcpy(rep->rrsets, prevrrsets, rep->rrset_count*sizeof(*rep->rrsets)); |
1941 | 0 | rep->rrset_count++; |
1942 | 0 | rep->ar_numrrsets++; |
1943 | 0 | rep->rrsets[rep->rrset_count-1] = rsoa; |
1944 | 0 | return 1; |
1945 | 0 | } |
1946 | | |
1947 | | static inline struct dns_msg* |
1948 | | rpz_dns_msg_new(struct regional* region) |
1949 | 0 | { |
1950 | 0 | struct dns_msg* msg = |
1951 | 0 | (struct dns_msg*)regional_alloc(region, |
1952 | 0 | sizeof(struct dns_msg)); |
1953 | 0 | if(msg == NULL) { return NULL; } |
1954 | 0 | memset(msg, 0, sizeof(struct dns_msg)); |
1955 | |
|
1956 | 0 | return msg; |
1957 | 0 | } |
1958 | | |
1959 | | static inline struct dns_msg* |
1960 | | rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, |
1961 | | struct query_info* qinfo, struct auth_zone* az) |
1962 | 0 | { |
1963 | 0 | struct dns_msg* msg = rpz_dns_msg_new(ms->region); |
1964 | 0 | if(msg == NULL) { return msg; } |
1965 | 0 | msg->qinfo = *qinfo; |
1966 | 0 | msg->rep = construct_reply_info_base(ms->region, |
1967 | 0 | LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, |
1968 | 0 | 1, /* qd */ |
1969 | 0 | 0, /* ttl */ |
1970 | 0 | 0, /* prettl */ |
1971 | 0 | 0, /* expttl */ |
1972 | 0 | 0, /* norecttl */ |
1973 | 0 | 0, /* an */ |
1974 | 0 | 0, /* ns */ |
1975 | 0 | 0, /* ar */ |
1976 | 0 | 0, /* total */ |
1977 | 0 | sec_status_insecure, |
1978 | 0 | LDNS_EDE_NONE); |
1979 | 0 | if(msg->rep) |
1980 | 0 | msg->rep->authoritative = 1; |
1981 | 0 | if(!rpz_add_soa(msg->rep, ms, az)) |
1982 | 0 | return NULL; |
1983 | 0 | return msg; |
1984 | 0 | } |
1985 | | |
1986 | | static inline struct dns_msg* |
1987 | | rpz_synthesize_nxdomain(struct rpz* r, struct module_qstate* ms, |
1988 | | struct query_info* qinfo, struct auth_zone* az) |
1989 | 0 | { |
1990 | 0 | struct dns_msg* msg = rpz_dns_msg_new(ms->region); |
1991 | 0 | uint16_t flags; |
1992 | 0 | if(msg == NULL) { return msg; } |
1993 | 0 | msg->qinfo = *qinfo; |
1994 | 0 | flags = LDNS_RCODE_NXDOMAIN | BIT_QR | BIT_AA | BIT_RA; |
1995 | 0 | if(r->signal_nxdomain_ra) |
1996 | 0 | flags &= ~BIT_RA; |
1997 | 0 | msg->rep = construct_reply_info_base(ms->region, |
1998 | 0 | flags, |
1999 | 0 | 1, /* qd */ |
2000 | 0 | 0, /* ttl */ |
2001 | 0 | 0, /* prettl */ |
2002 | 0 | 0, /* expttl */ |
2003 | 0 | 0, /* norecttl */ |
2004 | 0 | 0, /* an */ |
2005 | 0 | 0, /* ns */ |
2006 | 0 | 0, /* ar */ |
2007 | 0 | 0, /* total */ |
2008 | 0 | sec_status_insecure, |
2009 | 0 | LDNS_EDE_NONE); |
2010 | 0 | if(msg->rep) |
2011 | 0 | msg->rep->authoritative = 1; |
2012 | 0 | if(!rpz_add_soa(msg->rep, ms, az)) |
2013 | 0 | return NULL; |
2014 | 0 | return msg; |
2015 | 0 | } |
2016 | | |
2017 | | static inline struct dns_msg* |
2018 | | rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, |
2019 | | struct query_info* qi, struct local_rrset* rrset, struct auth_zone* az) |
2020 | 0 | { |
2021 | 0 | struct dns_msg* msg = NULL; |
2022 | 0 | struct reply_info* new_reply_info; |
2023 | 0 | struct ub_packed_rrset_key* rp; |
2024 | | |
2025 | |
|
2026 | 0 | msg = rpz_dns_msg_new(ms->region); |
2027 | 0 | if(msg == NULL) { return NULL; } |
2028 | | |
2029 | 0 | msg->qinfo = *qi; |
2030 | 0 | new_reply_info = construct_reply_info_base(ms->region, |
2031 | 0 | LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, |
2032 | 0 | 1, /* qd */ |
2033 | 0 | 0, /* ttl */ |
2034 | 0 | 0, /* prettl */ |
2035 | 0 | 0, /* expttl */ |
2036 | 0 | 0, /* norecttl */ |
2037 | 0 | 1, /* an */ |
2038 | 0 | 0, /* ns */ |
2039 | 0 | 0, /* ar */ |
2040 | 0 | 1, /* total */ |
2041 | 0 | sec_status_insecure, |
2042 | 0 | LDNS_EDE_NONE); |
2043 | 0 | if(new_reply_info == NULL) { |
2044 | 0 | log_err("out of memory"); |
2045 | 0 | return NULL; |
2046 | 0 | } |
2047 | 0 | new_reply_info->authoritative = 1; |
2048 | 0 | rp = respip_copy_rrset(rrset->rrset, ms->region); |
2049 | 0 | if(rp == NULL) { |
2050 | 0 | log_err("out of memory"); |
2051 | 0 | return NULL; |
2052 | 0 | } |
2053 | 0 | rp->rk.dname = qi->qname; |
2054 | 0 | rp->rk.dname_len = qi->qname_len; |
2055 | | /* this rrset is from the rpz data, or synthesized. |
2056 | | * It is not actually from the network, so we flag it with this |
2057 | | * flags as a fake RRset. If later the cache is used to look up |
2058 | | * rrsets, then the fake ones are not returned (if you look without |
2059 | | * the flag). For like CNAME lookups from the iterator or A, AAAA |
2060 | | * lookups for nameserver targets, it would use the without flag |
2061 | | * actual data. So that the actual network data and fake data |
2062 | | * are kept track of separately. */ |
2063 | 0 | rp->rk.flags |= PACKED_RRSET_RPZ; |
2064 | 0 | new_reply_info->rrsets[0] = rp; |
2065 | 0 | msg->rep = new_reply_info; |
2066 | 0 | if(!rpz_add_soa(msg->rep, ms, az)) |
2067 | 0 | return NULL; |
2068 | 0 | return msg; |
2069 | 0 | } |
2070 | | |
2071 | | static inline struct dns_msg* |
2072 | | rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, |
2073 | | struct query_info* qi, struct clientip_synthesized_rr* data, |
2074 | | struct auth_zone* az) |
2075 | 0 | { |
2076 | 0 | struct local_rrset* rrset; |
2077 | |
|
2078 | 0 | rrset = rpz_find_synthesized_rrset(qi->qtype, data, 1); |
2079 | 0 | if(rrset == NULL) { |
2080 | 0 | verbose(VERB_ALGO, "rpz: nsip: no matching local data found"); |
2081 | 0 | return NULL; |
2082 | 0 | } |
2083 | | |
2084 | 0 | return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); |
2085 | 0 | } |
2086 | | |
2087 | | /* copy'n'paste from localzone.c */ |
2088 | | static struct local_rrset* |
2089 | | local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) |
2090 | 0 | { |
2091 | 0 | struct local_rrset* p, *cname = NULL; |
2092 | 0 | type = htons(type); |
2093 | 0 | for(p = data->rrsets; p; p = p->next) { |
2094 | 0 | if(p->rrset->rk.type == type) |
2095 | 0 | return p; |
2096 | 0 | if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) |
2097 | 0 | cname = p; |
2098 | 0 | } |
2099 | 0 | if(alias_ok) |
2100 | 0 | return cname; |
2101 | 0 | return NULL; |
2102 | 0 | } |
2103 | | |
2104 | | /* based on localzone.c:local_data_answer() */ |
2105 | | static inline struct dns_msg* |
2106 | | rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, |
2107 | | struct query_info* qi, struct local_zone* z, |
2108 | | struct matched_delegation_point const* match, struct auth_zone* az) |
2109 | 0 | { |
2110 | 0 | struct local_data key; |
2111 | 0 | struct local_data* ld; |
2112 | 0 | struct local_rrset* rrset; |
2113 | |
|
2114 | 0 | if(match->dname == NULL) { return NULL; } |
2115 | | |
2116 | 0 | key.node.key = &key; |
2117 | 0 | key.name = match->dname; |
2118 | 0 | key.namelen = match->dname_len; |
2119 | 0 | key.namelabs = dname_count_labels(match->dname); |
2120 | |
|
2121 | 0 | rpz_log_dname("nsdname local data", key.name, key.namelen); |
2122 | |
|
2123 | 0 | ld = (struct local_data*)rbtree_search(&z->data, &key.node); |
2124 | 0 | if(ld == NULL) { |
2125 | 0 | verbose(VERB_ALGO, "rpz: nsdname: impossible: qname not found"); |
2126 | 0 | return NULL; |
2127 | 0 | } |
2128 | | |
2129 | 0 | rrset = local_data_find_type(ld, qi->qtype, 1); |
2130 | 0 | if(rrset == NULL) { |
2131 | 0 | verbose(VERB_ALGO, "rpz: nsdname: no matching local data found"); |
2132 | 0 | return NULL; |
2133 | 0 | } |
2134 | | |
2135 | 0 | return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); |
2136 | 0 | } |
2137 | | |
2138 | | /* like local_data_answer for qname triggers after a cname */ |
2139 | | static struct dns_msg* |
2140 | | rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms, |
2141 | | struct query_info* qinfo, struct local_zone* z, struct auth_zone* az) |
2142 | 0 | { |
2143 | 0 | struct local_data key; |
2144 | 0 | struct local_data* ld; |
2145 | 0 | struct local_rrset* rrset; |
2146 | 0 | key.node.key = &key; |
2147 | 0 | key.name = qinfo->qname; |
2148 | 0 | key.namelen = qinfo->qname_len; |
2149 | 0 | key.namelabs = dname_count_labels(qinfo->qname); |
2150 | 0 | ld = (struct local_data*)rbtree_search(&z->data, &key.node); |
2151 | 0 | if(ld == NULL) { |
2152 | 0 | verbose(VERB_ALGO, "rpz: qname: name not found"); |
2153 | 0 | return NULL; |
2154 | 0 | } |
2155 | 0 | rrset = local_data_find_type(ld, qinfo->qtype, 1); |
2156 | 0 | if(rrset == NULL) { |
2157 | 0 | verbose(VERB_ALGO, "rpz: qname: type not found"); |
2158 | 0 | return NULL; |
2159 | 0 | } |
2160 | 0 | return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az); |
2161 | 0 | } |
2162 | | |
2163 | | /** Synthesize a CNAME message for RPZ action override */ |
2164 | | static struct dns_msg* |
2165 | | rpz_synthesize_cname_override_msg(struct rpz* r, struct module_qstate* ms, |
2166 | | struct query_info* qinfo) |
2167 | 0 | { |
2168 | 0 | struct dns_msg* msg = NULL; |
2169 | 0 | struct reply_info* new_reply_info; |
2170 | 0 | struct ub_packed_rrset_key* rp; |
2171 | |
|
2172 | 0 | msg = rpz_dns_msg_new(ms->region); |
2173 | 0 | if(msg == NULL) { return NULL; } |
2174 | | |
2175 | 0 | msg->qinfo = *qinfo; |
2176 | 0 | new_reply_info = construct_reply_info_base(ms->region, |
2177 | 0 | LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, |
2178 | 0 | 1, /* qd */ |
2179 | 0 | 0, /* ttl */ |
2180 | 0 | 0, /* prettl */ |
2181 | 0 | 0, /* expttl */ |
2182 | 0 | 0, /* norecttl */ |
2183 | 0 | 1, /* an */ |
2184 | 0 | 0, /* ns */ |
2185 | 0 | 0, /* ar */ |
2186 | 0 | 1, /* total */ |
2187 | 0 | sec_status_insecure, |
2188 | 0 | LDNS_EDE_NONE); |
2189 | 0 | if(new_reply_info == NULL) { |
2190 | 0 | log_err("out of memory"); |
2191 | 0 | return NULL; |
2192 | 0 | } |
2193 | 0 | new_reply_info->authoritative = 1; |
2194 | |
|
2195 | 0 | rp = respip_copy_rrset(r->cname_override, ms->region); |
2196 | 0 | if(rp == NULL) { |
2197 | 0 | log_err("out of memory"); |
2198 | 0 | return NULL; |
2199 | 0 | } |
2200 | 0 | rp->rk.dname = qinfo->qname; |
2201 | 0 | rp->rk.dname_len = qinfo->qname_len; |
2202 | | /* this rrset is from the rpz data, or synthesized. |
2203 | | * It is not actually from the network, so we flag it with this |
2204 | | * flags as a fake RRset. If later the cache is used to look up |
2205 | | * rrsets, then the fake ones are not returned (if you look without |
2206 | | * the flag). For like CNAME lookups from the iterator or A, AAAA |
2207 | | * lookups for nameserver targets, it would use the without flag |
2208 | | * actual data. So that the actual network data and fake data |
2209 | | * are kept track of separately. */ |
2210 | 0 | rp->rk.flags |= PACKED_RRSET_RPZ; |
2211 | 0 | new_reply_info->rrsets[0] = rp; |
2212 | |
|
2213 | 0 | msg->rep = new_reply_info; |
2214 | 0 | return msg; |
2215 | 0 | } |
2216 | | |
2217 | | static int |
2218 | | rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, |
2219 | | struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo, |
2220 | | struct edns_data* edns, sldns_buffer* buf, struct regional* temp, |
2221 | | struct comm_reply* repinfo, struct ub_server_stats* stats) |
2222 | 0 | { |
2223 | 0 | struct local_data* ld = NULL; |
2224 | 0 | int ret = 0; |
2225 | 0 | if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { |
2226 | 0 | if(!rpz_apply_cname_override_action(r, qinfo, temp)) |
2227 | 0 | return 0; |
2228 | 0 | if(r->log) { |
2229 | 0 | log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION, |
2230 | 0 | qinfo, repinfo, NULL, r->log_name); |
2231 | 0 | } |
2232 | 0 | stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; |
2233 | 0 | return 0; |
2234 | 0 | } |
2235 | | |
2236 | 0 | if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo, |
2237 | 0 | edns, repinfo, buf, temp, dname_count_labels(qinfo->qname), |
2238 | 0 | &ld, lzt, -1, NULL, 0, NULL, 0)) { |
2239 | 0 | if(r->log) { |
2240 | 0 | log_rpz_apply("qname", z->name, NULL, |
2241 | 0 | localzone_type_to_rpz_action(lzt), qinfo, |
2242 | 0 | repinfo, NULL, r->log_name); |
2243 | 0 | } |
2244 | 0 | stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; |
2245 | 0 | return !qinfo->local_alias; |
2246 | 0 | } |
2247 | | |
2248 | 0 | ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, |
2249 | 0 | 0 /* no local data used */, lzt); |
2250 | 0 | if(r->signal_nxdomain_ra && LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) |
2251 | 0 | == LDNS_RCODE_NXDOMAIN) |
2252 | 0 | LDNS_RA_CLR(sldns_buffer_begin(buf)); |
2253 | 0 | if(r->log) { |
2254 | 0 | log_rpz_apply("qname", z->name, NULL, localzone_type_to_rpz_action(lzt), |
2255 | 0 | qinfo, repinfo, NULL, r->log_name); |
2256 | 0 | } |
2257 | 0 | stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; |
2258 | 0 | return ret; |
2259 | 0 | } |
2260 | | |
2261 | | static struct clientip_synthesized_rr* |
2262 | | rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* is) |
2263 | 0 | { |
2264 | 0 | struct delegpt_addr* cursor; |
2265 | 0 | struct clientip_synthesized_rr* action = NULL; |
2266 | 0 | if(is->dp == NULL) { return NULL; } |
2267 | 0 | for(cursor = is->dp->target_list; |
2268 | 0 | cursor != NULL; |
2269 | 0 | cursor = cursor->next_target) { |
2270 | 0 | if(cursor->bogus) { continue; } |
2271 | 0 | action = rpz_ipbased_trigger_lookup(rpz->ns_set, &cursor->addr, |
2272 | 0 | cursor->addrlen, "nsip"); |
2273 | 0 | if(action != NULL) { return action; } |
2274 | 0 | } |
2275 | 0 | return NULL; |
2276 | 0 | } |
2277 | | |
2278 | | static struct dns_msg* |
2279 | | rpz_apply_nsip_trigger(struct module_qstate* ms, struct query_info* qchase, |
2280 | | struct rpz* r, struct clientip_synthesized_rr* raddr, |
2281 | | struct auth_zone* az) |
2282 | 0 | { |
2283 | 0 | enum rpz_action action = raddr->action; |
2284 | 0 | struct dns_msg* ret = NULL; |
2285 | |
|
2286 | 0 | if(r->action_override != RPZ_NO_OVERRIDE_ACTION) { |
2287 | 0 | verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)", |
2288 | 0 | rpz_action_to_string(r->action_override), rpz_action_to_string(action)); |
2289 | 0 | action = r->action_override; |
2290 | 0 | } |
2291 | |
|
2292 | 0 | if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { |
2293 | 0 | verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); |
2294 | 0 | ret = rpz_synthesize_nodata(r, ms, qchase, az); |
2295 | 0 | ms->rpz_applied = 1; |
2296 | 0 | goto done; |
2297 | 0 | } |
2298 | | |
2299 | 0 | switch(action) { |
2300 | 0 | case RPZ_NXDOMAIN_ACTION: |
2301 | 0 | ret = rpz_synthesize_nxdomain(r, ms, qchase, az); |
2302 | 0 | ms->rpz_applied = 1; |
2303 | 0 | break; |
2304 | 0 | case RPZ_NODATA_ACTION: |
2305 | 0 | ret = rpz_synthesize_nodata(r, ms, qchase, az); |
2306 | 0 | ms->rpz_applied = 1; |
2307 | 0 | break; |
2308 | 0 | case RPZ_TCP_ONLY_ACTION: |
2309 | | /* basically a passthru here but the tcp-only will be |
2310 | | * honored before the query gets sent. */ |
2311 | 0 | ms->tcp_required = 1; |
2312 | 0 | ret = NULL; |
2313 | 0 | break; |
2314 | 0 | case RPZ_DROP_ACTION: |
2315 | 0 | ret = rpz_synthesize_nodata(r, ms, qchase, az); |
2316 | 0 | ms->rpz_applied = 1; |
2317 | 0 | ms->is_drop = 1; |
2318 | 0 | break; |
2319 | 0 | case RPZ_LOCAL_DATA_ACTION: |
2320 | 0 | ret = rpz_synthesize_nsip_localdata(r, ms, qchase, raddr, az); |
2321 | 0 | if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } |
2322 | 0 | ms->rpz_applied = 1; |
2323 | 0 | break; |
2324 | 0 | case RPZ_PASSTHRU_ACTION: |
2325 | 0 | ret = NULL; |
2326 | 0 | ms->rpz_passthru = 1; |
2327 | 0 | break; |
2328 | 0 | case RPZ_CNAME_OVERRIDE_ACTION: |
2329 | 0 | ret = rpz_synthesize_cname_override_msg(r, ms, qchase); |
2330 | 0 | ms->rpz_applied = 1; |
2331 | 0 | break; |
2332 | 0 | default: |
2333 | 0 | verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", |
2334 | 0 | rpz_action_to_string(action)); |
2335 | 0 | ret = NULL; |
2336 | 0 | } |
2337 | | |
2338 | 0 | done: |
2339 | 0 | if(r->log) |
2340 | 0 | log_rpz_apply("nsip", NULL, &raddr->node, |
2341 | 0 | action, &ms->qinfo, NULL, ms, r->log_name); |
2342 | 0 | if(ms->env->worker) |
2343 | 0 | ms->env->worker->stats.rpz_action[action]++; |
2344 | 0 | lock_rw_unlock(&raddr->lock); |
2345 | 0 | return ret; |
2346 | 0 | } |
2347 | | |
2348 | | static struct dns_msg* |
2349 | | rpz_apply_nsdname_trigger(struct module_qstate* ms, struct query_info* qchase, |
2350 | | struct rpz* r, struct local_zone* z, |
2351 | | struct matched_delegation_point const* match, struct auth_zone* az) |
2352 | 0 | { |
2353 | 0 | struct dns_msg* ret = NULL; |
2354 | 0 | enum rpz_action action = localzone_type_to_rpz_action(z->type); |
2355 | |
|
2356 | 0 | if(r->action_override != RPZ_NO_OVERRIDE_ACTION) { |
2357 | 0 | verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)", |
2358 | 0 | rpz_action_to_string(r->action_override), rpz_action_to_string(action)); |
2359 | 0 | action = r->action_override; |
2360 | 0 | } |
2361 | |
|
2362 | 0 | switch(action) { |
2363 | 0 | case RPZ_NXDOMAIN_ACTION: |
2364 | 0 | ret = rpz_synthesize_nxdomain(r, ms, qchase, az); |
2365 | 0 | ms->rpz_applied = 1; |
2366 | 0 | break; |
2367 | 0 | case RPZ_NODATA_ACTION: |
2368 | 0 | ret = rpz_synthesize_nodata(r, ms, qchase, az); |
2369 | 0 | ms->rpz_applied = 1; |
2370 | 0 | break; |
2371 | 0 | case RPZ_TCP_ONLY_ACTION: |
2372 | | /* basically a passthru here but the tcp-only will be |
2373 | | * honored before the query gets sent. */ |
2374 | 0 | ms->tcp_required = 1; |
2375 | 0 | ret = NULL; |
2376 | 0 | break; |
2377 | 0 | case RPZ_DROP_ACTION: |
2378 | 0 | ret = rpz_synthesize_nodata(r, ms, qchase, az); |
2379 | 0 | ms->rpz_applied = 1; |
2380 | 0 | ms->is_drop = 1; |
2381 | 0 | break; |
2382 | 0 | case RPZ_LOCAL_DATA_ACTION: |
2383 | 0 | ret = rpz_synthesize_nsdname_localdata(r, ms, qchase, z, match, az); |
2384 | 0 | if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } |
2385 | 0 | ms->rpz_applied = 1; |
2386 | 0 | break; |
2387 | 0 | case RPZ_PASSTHRU_ACTION: |
2388 | 0 | ret = NULL; |
2389 | 0 | ms->rpz_passthru = 1; |
2390 | 0 | break; |
2391 | 0 | case RPZ_CNAME_OVERRIDE_ACTION: |
2392 | 0 | ret = rpz_synthesize_cname_override_msg(r, ms, qchase); |
2393 | 0 | ms->rpz_applied = 1; |
2394 | 0 | break; |
2395 | 0 | default: |
2396 | 0 | verbose(VERB_ALGO, "rpz: nsdname: bug: unhandled or invalid action: '%s'", |
2397 | 0 | rpz_action_to_string(action)); |
2398 | 0 | ret = NULL; |
2399 | 0 | } |
2400 | | |
2401 | 0 | if(r->log) |
2402 | 0 | log_rpz_apply("nsdname", match->dname, NULL, |
2403 | 0 | action, &ms->qinfo, NULL, ms, r->log_name); |
2404 | 0 | if(ms->env->worker) |
2405 | 0 | ms->env->worker->stats.rpz_action[action]++; |
2406 | 0 | lock_rw_unlock(&z->lock); |
2407 | 0 | return ret; |
2408 | 0 | } |
2409 | | |
2410 | | static struct local_zone* |
2411 | | rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, |
2412 | | uint16_t qclass, |
2413 | | /* output parameter */ |
2414 | | struct matched_delegation_point* match) |
2415 | 0 | { |
2416 | 0 | struct delegpt_ns* nameserver; |
2417 | 0 | struct local_zone* z = NULL; |
2418 | | |
2419 | | /* the rpz specs match the nameserver names (NS records), not the |
2420 | | * name of the delegation point itself, to the nsdname triggers */ |
2421 | 0 | for(nameserver = dp->nslist; |
2422 | 0 | nameserver != NULL; |
2423 | 0 | nameserver = nameserver->next) { |
2424 | 0 | z = rpz_find_zone(zones, nameserver->name, nameserver->namelen, |
2425 | 0 | qclass, 0, 0, 0); |
2426 | 0 | if(z != NULL) { |
2427 | 0 | match->dname = nameserver->name; |
2428 | 0 | match->dname_len = nameserver->namelen; |
2429 | 0 | if(verbosity >= VERB_ALGO) { |
2430 | 0 | char nm[LDNS_MAX_DOMAINLEN]; |
2431 | 0 | char zn[LDNS_MAX_DOMAINLEN]; |
2432 | 0 | dname_str(match->dname, nm); |
2433 | 0 | dname_str(z->name, zn); |
2434 | 0 | if(strcmp(nm, zn) != 0) |
2435 | 0 | verbose(VERB_ALGO, "rpz: trigger nsdname %s on %s action=%s", |
2436 | 0 | zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type))); |
2437 | 0 | else |
2438 | 0 | verbose(VERB_ALGO, "rpz: trigger nsdname %s action=%s", |
2439 | 0 | nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type))); |
2440 | 0 | } |
2441 | 0 | break; |
2442 | 0 | } |
2443 | 0 | } |
2444 | |
|
2445 | 0 | return z; |
2446 | 0 | } |
2447 | | |
2448 | | struct dns_msg* |
2449 | | rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* is) |
2450 | 0 | { |
2451 | 0 | struct auth_zones* az; |
2452 | 0 | struct auth_zone* a; |
2453 | 0 | struct clientip_synthesized_rr* raddr = NULL; |
2454 | 0 | struct rpz* r = NULL; |
2455 | 0 | struct local_zone* z = NULL; |
2456 | 0 | struct matched_delegation_point match = {0}; |
2457 | |
|
2458 | 0 | if(ms->rpz_passthru) { |
2459 | 0 | verbose(VERB_ALGO, "query is rpz_passthru, no further processing"); |
2460 | 0 | return NULL; |
2461 | 0 | } |
2462 | | |
2463 | 0 | if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } |
2464 | | |
2465 | 0 | az = ms->env->auth_zones; |
2466 | 0 | lock_rw_rdlock(&az->rpz_lock); |
2467 | |
|
2468 | 0 | verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL); |
2469 | | |
2470 | | /* precedence of RPZ works, loosely, like this: |
2471 | | * CNAMEs in order of the CNAME chain. rpzs in the order they are |
2472 | | * configured. In an RPZ: first client-IP addr, then QNAME, then |
2473 | | * response IP, then NSDNAME, then NSIP. Longest match first. Smallest |
2474 | | * one from a set. */ |
2475 | | /* we use the precedence rules for the topics and triggers that |
2476 | | * are pertinent at this stage of the resolve processing */ |
2477 | 0 | for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) { |
2478 | 0 | lock_rw_rdlock(&a->lock); |
2479 | 0 | r = a->rpz; |
2480 | 0 | if(r->disabled) { |
2481 | 0 | lock_rw_unlock(&a->lock); |
2482 | 0 | continue; |
2483 | 0 | } |
2484 | 0 | if(r->taglist && (!ms->client_info || |
2485 | 0 | !taglist_intersect(r->taglist, r->taglistlen, |
2486 | 0 | ms->client_info->taglist, |
2487 | 0 | ms->client_info->taglen))) { |
2488 | 0 | lock_rw_unlock(&a->lock); |
2489 | 0 | continue; |
2490 | 0 | } |
2491 | | |
2492 | | /* the nsdname has precedence over the nsip triggers */ |
2493 | 0 | z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, |
2494 | 0 | is->qchase.qclass, &match); |
2495 | 0 | if(z != NULL) { |
2496 | 0 | lock_rw_unlock(&a->lock); |
2497 | 0 | break; |
2498 | 0 | } |
2499 | | |
2500 | 0 | raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is); |
2501 | 0 | if(raddr != NULL) { |
2502 | 0 | lock_rw_unlock(&a->lock); |
2503 | 0 | break; |
2504 | 0 | } |
2505 | 0 | lock_rw_unlock(&a->lock); |
2506 | 0 | } |
2507 | |
|
2508 | 0 | lock_rw_unlock(&az->rpz_lock); |
2509 | |
|
2510 | 0 | if(raddr == NULL && z == NULL) |
2511 | 0 | return NULL; |
2512 | | |
2513 | 0 | if(raddr != NULL) { |
2514 | 0 | if(z) { |
2515 | 0 | lock_rw_unlock(&z->lock); |
2516 | 0 | } |
2517 | 0 | return rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a); |
2518 | 0 | } |
2519 | 0 | return rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a); |
2520 | 0 | } |
2521 | | |
2522 | | struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, |
2523 | | struct iter_qstate* is) |
2524 | 0 | { |
2525 | 0 | struct auth_zones* az; |
2526 | 0 | struct auth_zone* a = NULL; |
2527 | 0 | struct rpz* r = NULL; |
2528 | 0 | struct local_zone* z = NULL; |
2529 | 0 | enum localzone_type lzt; |
2530 | 0 | struct dns_msg* ret = NULL; |
2531 | |
|
2532 | 0 | if(ms->rpz_passthru) { |
2533 | 0 | verbose(VERB_ALGO, "query is rpz_passthru, no further processing"); |
2534 | 0 | return NULL; |
2535 | 0 | } |
2536 | | |
2537 | 0 | if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } |
2538 | 0 | az = ms->env->auth_zones; |
2539 | |
|
2540 | 0 | lock_rw_rdlock(&az->rpz_lock); |
2541 | |
|
2542 | 0 | for(a = az->rpz_first; a; a = a->rpz_az_next) { |
2543 | 0 | lock_rw_rdlock(&a->lock); |
2544 | 0 | r = a->rpz; |
2545 | 0 | if(r->disabled) { |
2546 | 0 | lock_rw_unlock(&a->lock); |
2547 | 0 | continue; |
2548 | 0 | } |
2549 | 0 | if(r->taglist && (!ms->client_info || |
2550 | 0 | !taglist_intersect(r->taglist, r->taglistlen, |
2551 | 0 | ms->client_info->taglist, |
2552 | 0 | ms->client_info->taglen))) { |
2553 | 0 | lock_rw_unlock(&a->lock); |
2554 | 0 | continue; |
2555 | 0 | } |
2556 | 0 | z = rpz_find_zone(r->local_zones, is->qchase.qname, |
2557 | 0 | is->qchase.qname_len, is->qchase.qclass, 0, 0, 0); |
2558 | 0 | if(z && r->action_override == RPZ_DISABLED_ACTION) { |
2559 | 0 | if(r->log) |
2560 | 0 | log_rpz_apply("qname", z->name, NULL, |
2561 | 0 | r->action_override, |
2562 | 0 | &ms->qinfo, NULL, ms, r->log_name); |
2563 | 0 | if(ms->env->worker) |
2564 | 0 | ms->env->worker->stats.rpz_action[r->action_override]++; |
2565 | 0 | lock_rw_unlock(&z->lock); |
2566 | 0 | z = NULL; |
2567 | 0 | } |
2568 | 0 | if(z) { |
2569 | 0 | break; |
2570 | 0 | } |
2571 | | /* not found in this auth_zone */ |
2572 | 0 | lock_rw_unlock(&a->lock); |
2573 | 0 | } |
2574 | 0 | lock_rw_unlock(&az->rpz_lock); |
2575 | |
|
2576 | 0 | if(z == NULL) |
2577 | 0 | return NULL; |
2578 | 0 | if(r->action_override == RPZ_NO_OVERRIDE_ACTION) { |
2579 | 0 | lzt = z->type; |
2580 | 0 | } else { |
2581 | 0 | lzt = rpz_action_to_localzone_type(r->action_override); |
2582 | 0 | } |
2583 | |
|
2584 | 0 | if(verbosity >= VERB_ALGO) { |
2585 | 0 | char nm[LDNS_MAX_DOMAINLEN], zn[LDNS_MAX_DOMAINLEN]; |
2586 | 0 | dname_str(is->qchase.qname, nm); |
2587 | 0 | dname_str(z->name, zn); |
2588 | 0 | if(strcmp(zn, nm) != 0) |
2589 | 0 | verbose(VERB_ALGO, "rpz: qname trigger %s on %s, with action=%s", |
2590 | 0 | zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); |
2591 | 0 | else |
2592 | 0 | verbose(VERB_ALGO, "rpz: qname trigger %s, with action=%s", |
2593 | 0 | nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); |
2594 | 0 | } |
2595 | 0 | switch(localzone_type_to_rpz_action(lzt)) { |
2596 | 0 | case RPZ_NXDOMAIN_ACTION: |
2597 | 0 | ret = rpz_synthesize_nxdomain(r, ms, &is->qchase, a); |
2598 | 0 | ms->rpz_applied = 1; |
2599 | 0 | break; |
2600 | 0 | case RPZ_NODATA_ACTION: |
2601 | 0 | ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); |
2602 | 0 | ms->rpz_applied = 1; |
2603 | 0 | break; |
2604 | 0 | case RPZ_TCP_ONLY_ACTION: |
2605 | | /* basically a passthru here but the tcp-only will be |
2606 | | * honored before the query gets sent. */ |
2607 | 0 | ms->tcp_required = 1; |
2608 | 0 | ret = NULL; |
2609 | 0 | break; |
2610 | 0 | case RPZ_DROP_ACTION: |
2611 | 0 | ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); |
2612 | 0 | ms->rpz_applied = 1; |
2613 | 0 | ms->is_drop = 1; |
2614 | 0 | break; |
2615 | 0 | case RPZ_LOCAL_DATA_ACTION: |
2616 | 0 | ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z, a); |
2617 | 0 | if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); } |
2618 | 0 | ms->rpz_applied = 1; |
2619 | 0 | break; |
2620 | 0 | case RPZ_PASSTHRU_ACTION: |
2621 | 0 | ret = NULL; |
2622 | 0 | ms->rpz_passthru = 1; |
2623 | 0 | break; |
2624 | 0 | default: |
2625 | 0 | verbose(VERB_ALGO, "rpz: qname trigger: bug: unhandled or invalid action: '%s'", |
2626 | 0 | rpz_action_to_string(localzone_type_to_rpz_action(lzt))); |
2627 | 0 | ret = NULL; |
2628 | 0 | } |
2629 | 0 | if(r->log) |
2630 | 0 | log_rpz_apply("qname", (z?z->name:NULL), NULL, |
2631 | 0 | localzone_type_to_rpz_action(lzt), |
2632 | 0 | &is->qchase, NULL, ms, r->log_name); |
2633 | 0 | lock_rw_unlock(&z->lock); |
2634 | 0 | lock_rw_unlock(&a->lock); |
2635 | 0 | return ret; |
2636 | 0 | } |
2637 | | |
2638 | | static int |
2639 | | rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, |
2640 | | struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, |
2641 | | uint8_t* taglist, size_t taglen, struct ub_server_stats* stats, |
2642 | | sldns_buffer* buf, struct regional* temp, |
2643 | | /* output parameters */ |
2644 | | struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out, |
2645 | | int* passthru) |
2646 | 0 | { |
2647 | 0 | int ret = 0; |
2648 | 0 | enum rpz_action client_action; |
2649 | 0 | struct clientip_synthesized_rr* node = rpz_resolve_client_action_and_zone( |
2650 | 0 | az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out); |
2651 | |
|
2652 | 0 | client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action); |
2653 | 0 | if(node != NULL && *r_out && |
2654 | 0 | (*r_out)->action_override != RPZ_NO_OVERRIDE_ACTION) { |
2655 | 0 | client_action = (*r_out)->action_override; |
2656 | 0 | } |
2657 | 0 | if(client_action == RPZ_PASSTHRU_ACTION) { |
2658 | 0 | if(*r_out && (*r_out)->log) |
2659 | 0 | log_rpz_apply( |
2660 | 0 | (node?"clientip":"qname"), |
2661 | 0 | ((*z_out)?(*z_out)->name:NULL), |
2662 | 0 | (node?&node->node:NULL), |
2663 | 0 | client_action, qinfo, repinfo, NULL, |
2664 | 0 | (*r_out)->log_name); |
2665 | 0 | *passthru = 1; |
2666 | 0 | ret = 0; |
2667 | 0 | goto done; |
2668 | 0 | } |
2669 | 0 | if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION && |
2670 | 0 | client_action != RPZ_PASSTHRU_ACTION)) { |
2671 | 0 | if(client_action == RPZ_PASSTHRU_ACTION |
2672 | 0 | || client_action == RPZ_INVALID_ACTION |
2673 | 0 | || (client_action == RPZ_TCP_ONLY_ACTION |
2674 | 0 | && !rpz_is_udp_query(repinfo))) { |
2675 | 0 | ret = 0; |
2676 | 0 | goto done; |
2677 | 0 | } |
2678 | 0 | stats->rpz_action[client_action]++; |
2679 | 0 | if(client_action == RPZ_LOCAL_DATA_ACTION) { |
2680 | 0 | rpz_apply_clientip_localdata_action(node, env, qinfo, |
2681 | 0 | edns, repinfo, buf, temp, *a_out); |
2682 | 0 | ret = 1; |
2683 | 0 | } else if(client_action == RPZ_CNAME_OVERRIDE_ACTION) { |
2684 | 0 | if(!rpz_apply_cname_override_action(*r_out, qinfo, |
2685 | 0 | temp)) { |
2686 | 0 | ret = 0; |
2687 | 0 | goto done; |
2688 | 0 | } |
2689 | 0 | ret = 0; |
2690 | 0 | } else { |
2691 | 0 | local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns, |
2692 | 0 | repinfo, buf, temp, 0 /* no local data used */, |
2693 | 0 | rpz_action_to_localzone_type(client_action)); |
2694 | 0 | if(*r_out && (*r_out)->signal_nxdomain_ra && |
2695 | 0 | LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) |
2696 | 0 | == LDNS_RCODE_NXDOMAIN) |
2697 | 0 | LDNS_RA_CLR(sldns_buffer_begin(buf)); |
2698 | 0 | ret = 1; |
2699 | 0 | } |
2700 | 0 | if(*r_out && (*r_out)->log) |
2701 | 0 | log_rpz_apply( |
2702 | 0 | (node?"clientip":"qname"), |
2703 | 0 | ((*z_out)?(*z_out)->name:NULL), |
2704 | 0 | (node?&node->node:NULL), |
2705 | 0 | client_action, qinfo, repinfo, NULL, |
2706 | 0 | (*r_out)->log_name); |
2707 | 0 | goto done; |
2708 | 0 | } |
2709 | 0 | ret = -1; |
2710 | 0 | done: |
2711 | 0 | if(node != NULL) { |
2712 | 0 | lock_rw_unlock(&node->lock); |
2713 | 0 | } |
2714 | 0 | return ret; |
2715 | 0 | } |
2716 | | |
2717 | | int |
2718 | | rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env, |
2719 | | struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, |
2720 | | struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist, |
2721 | | size_t taglen, struct ub_server_stats* stats, int* passthru) |
2722 | 0 | { |
2723 | 0 | struct rpz* r = NULL; |
2724 | 0 | struct auth_zone* a = NULL; |
2725 | 0 | struct local_zone* z = NULL; |
2726 | 0 | int ret; |
2727 | 0 | enum localzone_type lzt; |
2728 | |
|
2729 | 0 | int clientip_trigger = rpz_apply_maybe_clientip_trigger(az, env, qinfo, |
2730 | 0 | edns, repinfo, taglist, taglen, stats, buf, temp, &z, &a, &r, |
2731 | 0 | passthru); |
2732 | 0 | if(clientip_trigger >= 0) { |
2733 | 0 | if(a) { |
2734 | 0 | lock_rw_unlock(&a->lock); |
2735 | 0 | } |
2736 | 0 | if(z) { |
2737 | 0 | lock_rw_unlock(&z->lock); |
2738 | 0 | } |
2739 | 0 | return clientip_trigger; |
2740 | 0 | } |
2741 | | |
2742 | 0 | if(z == NULL) { |
2743 | 0 | if(a) { |
2744 | 0 | lock_rw_unlock(&a->lock); |
2745 | 0 | } |
2746 | 0 | return 0; |
2747 | 0 | } |
2748 | | |
2749 | 0 | log_assert(r); |
2750 | |
|
2751 | 0 | if(r->action_override == RPZ_NO_OVERRIDE_ACTION) { |
2752 | 0 | lzt = z->type; |
2753 | 0 | } else { |
2754 | 0 | lzt = rpz_action_to_localzone_type(r->action_override); |
2755 | 0 | } |
2756 | 0 | if(r->action_override == RPZ_PASSTHRU_ACTION || |
2757 | 0 | lzt == local_zone_always_transparent /* RPZ_PASSTHRU_ACTION */) { |
2758 | 0 | *passthru = 1; |
2759 | 0 | } |
2760 | |
|
2761 | 0 | if(verbosity >= VERB_ALGO) { |
2762 | 0 | char nm[LDNS_MAX_DOMAINLEN], zn[LDNS_MAX_DOMAINLEN]; |
2763 | 0 | dname_str(qinfo->qname, nm); |
2764 | 0 | dname_str(z->name, zn); |
2765 | 0 | if(strcmp(zn, nm) != 0) |
2766 | 0 | verbose(VERB_ALGO, "rpz: qname trigger %s on %s with action=%s", |
2767 | 0 | zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); |
2768 | 0 | else |
2769 | 0 | verbose(VERB_ALGO, "rpz: qname trigger %s with action=%s", |
2770 | 0 | nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); |
2771 | 0 | } |
2772 | |
|
2773 | 0 | ret = rpz_synthesize_qname_localdata(env, r, z, lzt, qinfo, edns, buf, temp, |
2774 | 0 | repinfo, stats); |
2775 | |
|
2776 | 0 | lock_rw_unlock(&z->lock); |
2777 | 0 | lock_rw_unlock(&a->lock); |
2778 | |
|
2779 | 0 | return ret; |
2780 | 0 | } |
2781 | | |
2782 | | void rpz_enable(struct rpz* r) |
2783 | 0 | { |
2784 | 0 | if(!r) |
2785 | 0 | return; |
2786 | 0 | r->disabled = 0; |
2787 | 0 | } |
2788 | | |
2789 | | void rpz_disable(struct rpz* r) |
2790 | 0 | { |
2791 | 0 | if(!r) |
2792 | 0 | return; |
2793 | 0 | r->disabled = 1; |
2794 | 0 | } |
2795 | | |
2796 | | /** Get memory usage for clientip_synthesized_rrset. Ignores memory usage |
2797 | | * of locks. */ |
2798 | | static size_t |
2799 | | rpz_clientip_synthesized_set_get_mem(struct clientip_synthesized_rrset* set) |
2800 | 0 | { |
2801 | 0 | size_t m = sizeof(*set); |
2802 | 0 | lock_rw_rdlock(&set->lock); |
2803 | 0 | m += regional_get_mem(set->region); |
2804 | 0 | lock_rw_unlock(&set->lock); |
2805 | 0 | return m; |
2806 | 0 | } |
2807 | | |
2808 | | size_t rpz_get_mem(struct rpz* r) |
2809 | 0 | { |
2810 | 0 | size_t m = sizeof(*r); |
2811 | 0 | if(r->taglist) |
2812 | 0 | m += r->taglistlen; |
2813 | 0 | if(r->log_name) |
2814 | 0 | m += strlen(r->log_name) + 1; |
2815 | 0 | m += regional_get_mem(r->region); |
2816 | 0 | m += local_zones_get_mem(r->local_zones); |
2817 | 0 | m += local_zones_get_mem(r->nsdname_zones); |
2818 | 0 | m += respip_set_get_mem(r->respip_set); |
2819 | 0 | m += rpz_clientip_synthesized_set_get_mem(r->client_set); |
2820 | 0 | m += rpz_clientip_synthesized_set_get_mem(r->ns_set); |
2821 | 0 | return m; |
2822 | 0 | } |