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