File: aaaa-filter-iterator.patch

package info (click to toggle)
unbound 1.6.0-2~bpo8%2B1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 20,444 kB
  • sloc: ansic: 79,862; sh: 5,040; yacc: 1,900; makefile: 1,315; python: 1,302; perl: 141
file content (413 lines) | stat: -rw-r--r-- 14,787 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
Index: trunk/doc/unbound.conf.5.in
===================================================================
--- trunk/doc/unbound.conf.5.in	(revision 3587)
+++ trunk/doc/unbound.conf.5.in	(working copy)
@@ -593,6 +593,13 @@
 possible. Best effort approach, full QNAME and original QTYPE will be sent when
 upstream replies with a RCODE other than NOERROR. Default is off.
 .TP
+.B aaaa\-filter: \fI<yes or no>
+Activate behavior similar to BIND's AAAA-filter.
+This forces the dropping of all AAAA records, unless in the case of
+explicit AAAA queries, when no A records have been confirmed.
+This also causes an additional A query to be sent for each AAAA query.
+This breaks DNSSEC!
+.TP
 .B private\-address: \fI<IP address or subnet>
 Give IPv4 of IPv6 addresses or classless subnets. These are addresses
 on your private network, and are not allowed to be returned for
Index: trunk/iterator/iter_scrub.c
===================================================================
--- trunk/iterator/iter_scrub.c	(revision 3587)
+++ trunk/iterator/iter_scrub.c	(working copy)
@@ -617,6 +617,32 @@
 }
 
 /**
+ * ASN: Lookup A records from rrset cache.
+ * @param qinfo: the question originally asked.
+ * @param env: module environment with config and cache.
+ * @param ie: iterator environment with private address data.
+ * @return 0 if no A record found, 1 if A record found.
+ */
+static int
+asn_lookup_a_record_from_cache(struct query_info* qinfo,
+	struct module_env* env, struct iter_env* ATTR_UNUSED(ie))
+{
+	struct ub_packed_rrset_key* akey;
+
+	/* get cached A records for queried name */
+	akey = rrset_cache_lookup(env->rrset_cache, qinfo->qname,
+		qinfo->qname_len, LDNS_RR_TYPE_A, qinfo->qclass,
+		0, *env->now, 0);
+	if(akey) { /* we had some. */
+		log_rrset_key(VERB_ALGO, "ASN-AAAA-filter: found A record",
+			      akey);
+		lock_rw_unlock(&akey->entry.lock);
+		return 1;
+	}
+	return 0;
+}
+
+/**
  * Given a response event, remove suspect RRsets from the response.
  * "Suspect" rrsets are potentially poison. Note that this routine expects
  * the response to be in a "normalized" state -- that is, all "irrelevant"
@@ -635,6 +661,7 @@
 	struct query_info* qinfo, uint8_t* zonename, struct module_env* env,
 	struct iter_env* ie)
 {
+	int found_a_record = 0; /* ASN: do we have a A record? */
 	int del_addi = 0; /* if additional-holding rrsets are deleted, we
 		do not trust the normalized additional-A-AAAA any more */
 	struct rrset_parse* rrset, *prev;
@@ -670,6 +697,13 @@
 		rrset = rrset->rrset_all_next;
 	}
 
+	/* ASN: Locate any A record we can find */
+	if((ie->aaaa_filter) && (qinfo->qtype == LDNS_RR_TYPE_AAAA)) {
+		found_a_record = asn_lookup_a_record_from_cache(qinfo,
+			env, ie);
+	}
+	/* ASN: End of added code */
+
 	/* At this point, we brutally remove ALL rrsets that aren't 
 	 * children of the originating zone. The idea here is that, 
 	 * as far as we know, the server that we contacted is ONLY 
@@ -681,6 +715,24 @@
 	rrset = msg->rrset_first;
 	while(rrset) {
 
+		/* ASN: For AAAA records only... */
+		if((ie->aaaa_filter) && (rrset->type == LDNS_RR_TYPE_AAAA)) {
+			/* ASN: If this is not a AAAA query, then remove AAAA
+			 * records, no questions asked. If this IS a AAAA query
+			 * then remove AAAA records if we have an A record.
+			 * Otherwise, leave things be. */
+			if((qinfo->qtype != LDNS_RR_TYPE_AAAA) ||
+				(found_a_record)) {
+				remove_rrset("ASN-AAAA-filter: removing AAAA "
+					"for record", pkt, msg, prev, &rrset);
+				continue;
+			}
+			log_nametypeclass(VERB_ALGO, "ASN-AAAA-filter: "
+				"keep AAAA for", zonename,
+				LDNS_RR_TYPE_AAAA, qinfo->qclass);
+		}
+		/* ASN: End of added code */
+
 		/* remove private addresses */
 		if( (rrset->type == LDNS_RR_TYPE_A || 
 			rrset->type == LDNS_RR_TYPE_AAAA)) {
Index: trunk/iterator/iter_utils.c
===================================================================
--- trunk/iterator/iter_utils.c	(revision 3587)
+++ trunk/iterator/iter_utils.c	(working copy)
@@ -175,6 +175,7 @@
 	}
 	iter_env->supports_ipv6 = cfg->do_ip6;
 	iter_env->supports_ipv4 = cfg->do_ip4;
+	iter_env->aaaa_filter = cfg->aaaa_filter;
 	return 1;
 }
 
Index: trunk/iterator/iterator.c
===================================================================
--- trunk/iterator/iterator.c	(revision 3587)
+++ trunk/iterator/iterator.c	(working copy)
@@ -1776,6 +1776,53 @@
 
 	return 0;
 }
+
+/**
+ * ASN: This event state was added as an intermediary step between
+ * QUERYTARGETS_STATE and the next step, in order to cast a subquery for the
+ * purpose of caching A records for the queried name.
+ * 
+ * @param qstate: query state.
+ * @param iq: iterator query state.
+ * @param ie: iterator shared global environment.
+ * @param id: module id.
+ * @return true if the event requires more request processing immediately,
+ *         false if not. This state only returns true when it is generating
+ *         a SERVFAIL response because the query has hit a dead end.
+ */
+static int
+asn_processQueryAAAA(struct module_qstate* qstate, struct iter_qstate* iq,
+	struct iter_env* ATTR_UNUSED(ie), int id)
+{
+	struct module_qstate* subq = NULL;
+
+	log_assert(iq->fetch_a_for_aaaa == 0);
+
+	/* flag the query properly in order to not loop */
+	iq->fetch_a_for_aaaa = 1;
+
+	/* re-throw same query, but with a different type */
+	if(!generate_sub_request(iq->qchase.qname,
+        	iq->qchase.qname_len, LDNS_RR_TYPE_A,
+		iq->qchase.qclass, qstate, id, iq,
+		INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) {
+		log_nametypeclass(VERB_ALGO, "ASN-AAAA-filter: failed "
+			"preloading of A record for",
+			iq->qchase.qname, LDNS_RR_TYPE_A,
+			iq->qchase.qclass);
+		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+	}
+	log_nametypeclass(VERB_ALGO, "ASN-AAAA-filter: "
+		"preloading records in cache for",
+		iq->qchase.qname, LDNS_RR_TYPE_A,
+		iq->qchase.qclass);
+
+	/* set this query as waiting */
+	qstate->ext_state[id] = module_wait_subquery;
+	/* at this point break loop */
+	return 0;
+}
+/* ASN: End of added code */
 	
 /** 
  * This is the request event state where the request will be sent to one of
@@ -1823,6 +1870,13 @@
 		return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
 	}
 	
+	/* ASN: If we have a AAAA query, then also query for A records */
+	if((ie->aaaa_filter) && (iq->qchase.qtype == LDNS_RR_TYPE_AAAA) &&
+		(iq->fetch_a_for_aaaa == 0)) {
+		return next_state(iq, ASN_FETCH_A_FOR_AAAA_STATE);
+	}
+	/* ASN: End of added code */
+
 	/* Make sure we have a delegation point, otherwise priming failed
 	 * or another failure occurred */
 	if(!iq->dp) {
@@ -2922,6 +2976,61 @@
 	return 0;
 }
 
+/** 
+ * ASN: Do final processing on responses to A queries originated from AAAA
+ * queries. Events reach this state after the iterative resolution algorithm
+ * terminates.
+ * This is required down the road to decide whether to scrub AAAA records
+ * from the results or not.
+ *
+ * @param qstate: query state.
+ * @param id: module id.
+ * @param forq: super query state.
+ */
+static void
+asn_processAAAAResponse(struct module_qstate* qstate, int id,
+	struct module_qstate* super)
+{
+	/*struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];*/
+	struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id];
+	struct delegpt_ns* dpns = NULL;
+	int error = (qstate->return_rcode != LDNS_RCODE_NOERROR);
+
+	log_assert(super_iq->fetch_a_for_aaaa > 0);
+
+	/* let super go to evaluation of targets after this */
+	super_iq->state = QUERYTARGETS_STATE;
+
+	log_query_info(VERB_ALGO, "ASN-AAAA-filter: processAAAAResponse",
+		&qstate->qinfo);
+	log_query_info(VERB_ALGO, "ASN-AAAA-filter: processAAAAResponse super",
+		&super->qinfo);
+
+	if(super_iq->dp)
+		dpns = delegpt_find_ns(super_iq->dp,
+			qstate->qinfo.qname, qstate->qinfo.qname_len);
+	if (!dpns) {
+		/* not interested */
+		verbose(VERB_ALGO, "ASN-AAAA-filter: subq: %s, but parent not "
+			"interested%s", (error ? "error, but" : "success"),
+			(super_iq->dp ? "anymore" : " (was reset)"));
+		log_query_info(VERB_ALGO, "ASN-AAAA-filter: superq", &super->qinfo);
+		if(super_iq->dp && error)
+			delegpt_log(VERB_ALGO, super_iq->dp);
+		return;
+	} else if (error) {
+		verbose(VERB_ALGO, "ASN-AAAA-filter: mark as failed, "
+			"and go to target query.");
+		/* see if the failure did get (parent-lame) info */
+		if(!cache_fill_missing(super->env,
+			super_iq->qchase.qclass, super->region,
+			super_iq->dp))
+		log_err("ASN-AAAA-filter: out of memory adding missing");
+		dpns->resolved = 1; /* mark as failed */
+	}
+}
+/* ASN: End of added code */
+
 /*
  * Return priming query results to interestes super querystates.
  * 
@@ -2941,6 +3050,9 @@
 	else if(super->qinfo.qtype == LDNS_RR_TYPE_DS && ((struct iter_qstate*)
 		super->minfo[id])->state == DSNS_FIND_STATE)
 		processDSNSResponse(qstate, id, super);
+	else if (super->qinfo.qtype == LDNS_RR_TYPE_AAAA && ((struct iter_qstate*)
+		super->minfo[id])->state == ASN_FETCH_A_FOR_AAAA_STATE)
+		asn_processAAAAResponse(qstate, id, super);
 	else if(qstate->return_rcode != LDNS_RCODE_NOERROR)
 		error_supers(qstate, id, super);
 	else if(qstate->is_priming)
@@ -2978,6 +3090,9 @@
 			case INIT_REQUEST_3_STATE:
 				cont = processInitRequest3(qstate, iq, id);
 				break;
+			case ASN_FETCH_A_FOR_AAAA_STATE:
+				cont = asn_processQueryAAAA(qstate, iq, ie, id);
+				break;
 			case QUERYTARGETS_STATE:
 				cont = processQueryTargets(qstate, iq, ie, id);
 				break;
@@ -3270,6 +3385,8 @@
 		return "INIT REQUEST STATE (stage 2)";
 	case INIT_REQUEST_3_STATE:
 		return "INIT REQUEST STATE (stage 3)";
+	case ASN_FETCH_A_FOR_AAAA_STATE:
+		return "ASN_FETCH_A_FOR_AAAA_STATE";
 	case QUERYTARGETS_STATE :
 		return "QUERY TARGETS STATE";
 	case PRIME_RESP_STATE :
@@ -3294,6 +3411,7 @@
 		case INIT_REQUEST_STATE :
 		case INIT_REQUEST_2_STATE :
 		case INIT_REQUEST_3_STATE :
+		case ASN_FETCH_A_FOR_AAAA_STATE :
 		case QUERYTARGETS_STATE :
 		case COLLECT_CLASS_STATE :
 			return 0;
Index: trunk/iterator/iterator.h
===================================================================
--- trunk/iterator/iterator.h	(revision 3587)
+++ trunk/iterator/iterator.h	(working copy)
@@ -113,6 +113,9 @@
 	 */
 	int* target_fetch_policy;
 
+	/** ASN: AAAA-filter flag */
+	int aaaa_filter;
+
 	/** ip6.arpa dname in wireformat, used for qname-minimisation */
 	uint8_t* ip6arpa_dname;
 };
@@ -163,6 +166,14 @@
 	INIT_REQUEST_3_STATE,
 
 	/**
+	 * This state is responsible for intercepting AAAA queries,
+	 * and launch a A subquery on the same target, to populate the
+	 * cache with A records, so the AAAA filter scrubbing logic can
+	 * work.
+	 */
+	ASN_FETCH_A_FOR_AAAA_STATE,
+
+	/**
 	 * Each time a delegation point changes for a given query or a 
 	 * query times out and/or wakes up, this state is (re)visited. 
 	 * This state is reponsible for iterating through a list of 
@@ -346,6 +357,13 @@
 	 */
 	int refetch_glue;
 
+	/**
+	 * ASN: This is a flag that, if true, means that this query is
+	 * for fetching A records to populate cache and determine if we must
+	 * return AAAA records or not.
+	 */
+	int fetch_a_for_aaaa;
+
 	/** list of pending queries to authoritative servers. */
 	struct outbound_list outlist;
 
Index: trunk/pythonmod/interface.i
===================================================================
--- trunk/pythonmod/interface.i	(revision 3587)
+++ trunk/pythonmod/interface.i	(working copy)
@@ -632,6 +632,7 @@
    int harden_dnssec_stripped;
    int harden_referral_path;
    int use_caps_bits_for_id;
+   int aaaa_filter; /* ASN */
    struct config_strlist* private_address;
    struct config_strlist* private_domain;
    size_t unwanted_threshold;
Index: trunk/util/config_file.c
===================================================================
--- trunk/util/config_file.c	(revision 3587)
+++ trunk/util/config_file.c	(working copy)
@@ -176,6 +176,7 @@
 	cfg->harden_referral_path = 0;
 	cfg->harden_algo_downgrade = 0;
 	cfg->use_caps_bits_for_id = 0;
+	cfg->aaaa_filter = 0; /* ASN: default is disabled */
 	cfg->caps_whitelist = NULL;
 	cfg->private_address = NULL;
 	cfg->private_domain = NULL;
Index: trunk/util/config_file.h
===================================================================
--- trunk/util/config_file.h	(revision 3587)
+++ trunk/util/config_file.h	(working copy)
@@ -179,6 +179,8 @@
 	int harden_algo_downgrade;
 	/** use 0x20 bits in query as random ID bits */
 	int use_caps_bits_for_id;
+	/** ASN: enable AAAA filter? */
+	int aaaa_filter;
 	/** 0x20 whitelist, domains that do not use capsforid */
 	struct config_strlist* caps_whitelist;
 	/** strip away these private addrs from answers, no DNS Rebinding */
Index: trunk/util/configlexer.lex
===================================================================
--- trunk/util/configlexer.lex	(revision 3587)
+++ trunk/util/configlexer.lex	(working copy)
@@ -267,6 +267,7 @@
 use-caps-for-id{COLON}		{ YDVAR(1, VAR_USE_CAPS_FOR_ID) }
 caps-whitelist{COLON}		{ YDVAR(1, VAR_CAPS_WHITELIST) }
 unwanted-reply-threshold{COLON}	{ YDVAR(1, VAR_UNWANTED_REPLY_THRESHOLD) }
+aaaa-filter{COLON}		{ YDVAR(1, VAR_AAAA_FILTER) }
 private-address{COLON}		{ YDVAR(1, VAR_PRIVATE_ADDRESS) }
 private-domain{COLON}		{ YDVAR(1, VAR_PRIVATE_DOMAIN) }
 prefetch-key{COLON}		{ YDVAR(1, VAR_PREFETCH_KEY) }
Index: trunk/util/configparser.y
===================================================================
--- trunk/util/configparser.y	(revision 3587)
+++ trunk/util/configparser.y	(working copy)
@@ -92,6 +92,7 @@
 %token VAR_STATISTICS_CUMULATIVE VAR_OUTGOING_PORT_PERMIT 
 %token VAR_OUTGOING_PORT_AVOID VAR_DLV_ANCHOR_FILE VAR_DLV_ANCHOR
 %token VAR_NEG_CACHE_SIZE VAR_HARDEN_REFERRAL_PATH VAR_PRIVATE_ADDRESS
+%token VAR_AAAA_FILTER
 %token VAR_PRIVATE_DOMAIN VAR_REMOTE_CONTROL VAR_CONTROL_ENABLE
 %token VAR_CONTROL_INTERFACE VAR_CONTROL_PORT VAR_SERVER_KEY_FILE
 %token VAR_SERVER_CERT_FILE VAR_CONTROL_KEY_FILE VAR_CONTROL_CERT_FILE
@@ -169,6 +170,7 @@
 	server_dlv_anchor_file | server_dlv_anchor | server_neg_cache_size |
 	server_harden_referral_path | server_private_address |
 	server_private_domain | server_extended_statistics | 
+	server_aaaa_filter |
 	server_local_data_ptr | server_jostle_timeout | 
 	server_unwanted_reply_threshold | server_log_time_ascii | 
 	server_domain_insecure | server_val_sig_skew_min | 
@@ -893,6 +895,15 @@
 			yyerror("out of memory");
 	}
 	;
+server_aaaa_filter: VAR_AAAA_FILTER STRING_ARG
+	{
+		OUTYY(("P(server_aaaa_filter:%s)\n", $2));
+		if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+			yyerror("expected yes or no.");
+		else cfg_parser->cfg->aaaa_filter = (strcmp($2, "yes")==0);
+		free($2);
+	}
+	;
 server_private_address: VAR_PRIVATE_ADDRESS STRING_ARG
 	{
 		OUTYY(("P(server_private_address:%s)\n", $2));