Coverage Report

Created: 2026-03-15 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/unbound/iterator/iter_scrub.c
Line
Count
Source
1
/*
2
 * iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs.
3
 *
4
 * Copyright (c) 2007, 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 has routine(s) for cleaning up incoming DNS messages from 
40
 * possible useless or malicious junk in it.
41
 */
42
#include "config.h"
43
#include "iterator/iter_scrub.h"
44
#include "iterator/iterator.h"
45
#include "iterator/iter_priv.h"
46
#include "services/cache/rrset.h"
47
#include "util/log.h"
48
#include "util/net_help.h"
49
#include "util/regional.h"
50
#include "util/config_file.h"
51
#include "util/module.h"
52
#include "util/data/msgparse.h"
53
#include "util/data/dname.h"
54
#include "util/data/msgreply.h"
55
#include "util/alloc.h"
56
#include "sldns/sbuffer.h"
57
58
/** RRset flag used during scrubbing. The RRset is OK. */
59
2.80k
#define RRSET_SCRUB_OK  0x80
60
61
/** remove rrset, update loop variables */
62
static void
63
remove_rrset(const char* str, sldns_buffer* pkt, struct msg_parse* msg, 
64
  struct rrset_parse* prev, struct rrset_parse** rrset)
65
82.4k
{
66
82.4k
  if(verbosity >= VERB_QUERY && str
67
0
    && (*rrset)->dname_len <= LDNS_MAX_DOMAINLEN) {
68
0
    uint8_t buf[LDNS_MAX_DOMAINLEN+1];
69
0
    dname_pkt_copy(pkt, buf, (*rrset)->dname);
70
0
    log_nametypeclass(VERB_QUERY, str, buf, 
71
0
      (*rrset)->type, ntohs((*rrset)->rrset_class));
72
0
  }
73
82.4k
  if(prev)
74
26.6k
    prev->rrset_all_next = (*rrset)->rrset_all_next;
75
55.8k
  else  msg->rrset_first = (*rrset)->rrset_all_next;
76
82.4k
  if(msg->rrset_last == *rrset)
77
2.68k
    msg->rrset_last = prev;
78
82.4k
  msg->rrset_count --;
79
82.4k
  switch((*rrset)->section) {
80
10.6k
    case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
81
41.6k
    case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
82
30.2k
    case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
83
0
    default: log_assert(0);
84
82.4k
  }
85
82.4k
  msgparse_bucket_remove(msg, *rrset);
86
82.4k
  *rrset = (*rrset)->rrset_all_next;
87
82.4k
}
88
89
/** return true if rr type has additional names in it */
90
static int
91
has_additional(uint16_t t)
92
69.8k
{
93
69.8k
  switch(t) {
94
2.26k
    case LDNS_RR_TYPE_MB:
95
4.73k
    case LDNS_RR_TYPE_MD:
96
7.52k
    case LDNS_RR_TYPE_MF:
97
10.6k
    case LDNS_RR_TYPE_NS:
98
11.4k
    case LDNS_RR_TYPE_MX:
99
12.4k
    case LDNS_RR_TYPE_KX:
100
13.4k
    case LDNS_RR_TYPE_SRV:
101
13.4k
      return 1;
102
228
    case LDNS_RR_TYPE_NAPTR:
103
      /* TODO: NAPTR not supported, glue stripped off */
104
228
      return 0;
105
69.8k
  }
106
56.0k
  return 0;
107
69.8k
}
108
109
/** get additional name from rrset RR, return false if no name present */
110
static int
111
get_additional_name(struct rrset_parse* rrset, struct rr_parse* rr, 
112
  uint8_t** nm, size_t* nmlen, sldns_buffer* pkt) 
113
12.5k
{
114
12.5k
  size_t offset = 0;
115
12.5k
  size_t len, oldpos;
116
12.5k
  switch(rrset->type) {
117
3.68k
    case LDNS_RR_TYPE_MB:
118
6.08k
    case LDNS_RR_TYPE_MD:
119
10.1k
    case LDNS_RR_TYPE_MF:
120
10.1k
    case LDNS_RR_TYPE_NS:
121
10.1k
      offset = 0;
122
10.1k
      break;
123
859
    case LDNS_RR_TYPE_MX:
124
1.70k
    case LDNS_RR_TYPE_KX:
125
1.70k
      offset = 2;
126
1.70k
      break;
127
688
    case LDNS_RR_TYPE_SRV:
128
688
      offset = 6;
129
688
      break;
130
0
    case LDNS_RR_TYPE_NAPTR:
131
      /* TODO: NAPTR not supported, glue stripped off */
132
0
      return 0;
133
0
    default:
134
0
      return 0;
135
12.5k
  }
136
12.5k
  len = sldns_read_uint16(rr->ttl_data+sizeof(uint32_t));
137
12.5k
  if(len < offset+1)
138
7.58k
    return 0; /* rdata field too small */
139
4.94k
  *nm = rr->ttl_data+sizeof(uint32_t)+sizeof(uint16_t)+offset;
140
4.94k
  oldpos = sldns_buffer_position(pkt);
141
4.94k
  sldns_buffer_set_position(pkt, (size_t)(*nm - sldns_buffer_begin(pkt)));
142
4.94k
  *nmlen = pkt_dname_len(pkt);
143
4.94k
  sldns_buffer_set_position(pkt, oldpos);
144
4.94k
  if(*nmlen == 0)
145
0
    return 0;
146
4.94k
  return 1;
147
4.94k
}
148
149
/** Place mark on rrsets in additional section they are OK */
150
static void
151
mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg, 
152
  struct rrset_parse* rrset)
153
15.4k
{
154
  /* Mark A and AAAA for NS as appropriate additional section info. */
155
15.4k
  uint8_t* nm = NULL;
156
15.4k
  size_t nmlen = 0;
157
15.4k
  struct rr_parse* rr;
158
159
15.4k
  if(!has_additional(rrset->type))
160
11.5k
    return;
161
16.4k
  for(rr = rrset->rr_first; rr; rr = rr->next) {
162
12.5k
    if(get_additional_name(rrset, rr, &nm, &nmlen, pkt)) {
163
      /* mark A */
164
4.94k
      hashvalue_type h = pkt_hash_rrset(pkt, nm,
165
4.94k
        LDNS_RR_TYPE_A, rrset->rrset_class, 0);
166
4.94k
      struct rrset_parse* r = msgparse_hashtable_lookup(
167
4.94k
        msg, pkt, h, 0, nm, nmlen, 
168
4.94k
        LDNS_RR_TYPE_A, rrset->rrset_class);
169
4.94k
      if(r && r->section == LDNS_SECTION_ADDITIONAL) {
170
139
        r->flags |= RRSET_SCRUB_OK;
171
139
      }
172
      
173
      /* mark AAAA */
174
4.94k
      h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_AAAA, 
175
4.94k
        rrset->rrset_class, 0);
176
4.94k
      r = msgparse_hashtable_lookup(msg, pkt, h, 0, nm, 
177
4.94k
        nmlen, LDNS_RR_TYPE_AAAA, rrset->rrset_class);
178
4.94k
      if(r && r->section == LDNS_SECTION_ADDITIONAL) {
179
85
        r->flags |= RRSET_SCRUB_OK;
180
85
      }
181
4.94k
    }
182
12.5k
  }
183
3.90k
}
184
185
/** Get target name of a CNAME */
186
static int
187
parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname, 
188
  size_t* snamelen, sldns_buffer* pkt)
189
130
{
190
130
  size_t oldpos, dlen;
191
130
  if(rrset->rr_count != 1) {
192
40
    struct rr_parse* sig;
193
40
    verbose(VERB_ALGO, "Found CNAME rrset with "
194
40
      "size > 1: %u", (unsigned)rrset->rr_count);
195
    /* use the first CNAME! */
196
40
    rrset->rr_count = 1;
197
40
    rrset->size = rrset->rr_first->size;
198
584
    for(sig=rrset->rrsig_first; sig; sig=sig->next)
199
544
      rrset->size += sig->size;
200
40
    rrset->rr_last = rrset->rr_first;
201
40
    rrset->rr_first->next = NULL;
202
40
  }
203
130
  if(rrset->rr_first->size < sizeof(uint16_t)+1)
204
60
    return 0; /* CNAME rdata too small */
205
70
  *sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
206
70
    + sizeof(uint16_t); /* skip ttl, rdatalen */
207
70
  *snamelen = rrset->rr_first->size - sizeof(uint16_t);
208
209
70
  if(rrset->rr_first->outside_packet) {
210
0
    if(!dname_valid(*sname, *snamelen))
211
0
      return 0;
212
0
    return 1;
213
0
  }
214
70
  oldpos = sldns_buffer_position(pkt);
215
70
  sldns_buffer_set_position(pkt, (size_t)(*sname - sldns_buffer_begin(pkt)));
216
70
  dlen = pkt_dname_len(pkt);
217
70
  sldns_buffer_set_position(pkt, oldpos);
218
70
  if(dlen == 0)
219
0
    return 0; /* parse fail on the rdata name */
220
70
  *snamelen = dlen;
221
70
  return 1;
222
70
}
223
224
/** Synthesize CNAME from DNAME, false if too long */
225
static int 
226
synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset, 
227
  uint8_t* alias, size_t* aliaslen, sldns_buffer* pkt)
228
14
{
229
  /* we already know that sname is a strict subdomain of DNAME owner */
230
14
  uint8_t* dtarg = NULL;
231
14
  size_t dtarglen;
232
14
  if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen, pkt))
233
14
    return 0; 
234
0
  if(qnamelen <= dname_rrset->dname_len)
235
0
    return 0;
236
0
  if(qnamelen == 0)
237
0
    return 0;
238
0
  log_assert(qnamelen > dname_rrset->dname_len);
239
  /* DNAME from com. to net. with qname example.com. -> example.net. */
240
  /* so: \3com\0 to \3net\0 and qname \7example\3com\0 */
241
0
  *aliaslen = qnamelen + dtarglen - dname_rrset->dname_len;
242
0
  if(*aliaslen > LDNS_MAX_DOMAINLEN)
243
0
    return 0; /* should have been RCODE YXDOMAIN */
244
  /* decompress dnames into buffer, we know it fits */
245
0
  dname_pkt_copy(pkt, alias, qname);
246
0
  dname_pkt_copy(pkt, alias+(qnamelen-dname_rrset->dname_len), dtarg);
247
0
  return 1;
248
0
}
249
250
/** synthesize a CNAME rrset */
251
static struct rrset_parse*
252
synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias, 
253
  size_t aliaslen, struct regional* region, struct msg_parse* msg, 
254
  struct rrset_parse* rrset, struct rrset_parse* prev,
255
  struct rrset_parse* nx, sldns_buffer* pkt)
256
0
{
257
0
  struct rrset_parse* cn = (struct rrset_parse*)regional_alloc(region,
258
0
    sizeof(struct rrset_parse));
259
0
  if(!cn)
260
0
    return NULL;
261
0
  memset(cn, 0, sizeof(*cn));
262
0
  cn->rr_first = (struct rr_parse*)regional_alloc(region, 
263
0
    sizeof(struct rr_parse));
264
0
  if(!cn->rr_first)
265
0
    return NULL;
266
0
  cn->rr_last = cn->rr_first;
267
  /* CNAME from sname to alias */
268
0
  cn->dname = (uint8_t*)regional_alloc(region, *snamelen);
269
0
  if(!cn->dname)
270
0
    return NULL;
271
0
  dname_pkt_copy(pkt, cn->dname, *sname);
272
0
  cn->dname_len = *snamelen;
273
0
  cn->type = LDNS_RR_TYPE_CNAME;
274
0
  cn->section = rrset->section;
275
0
  cn->rrset_class = rrset->rrset_class;
276
0
  cn->rr_count = 1;
277
0
  cn->size = sizeof(uint16_t) + aliaslen;
278
0
  cn->hash=pkt_hash_rrset(pkt, cn->dname, cn->type, cn->rrset_class, 0);
279
  /* allocate TTL + rdatalen + uncompressed dname */
280
0
  memset(cn->rr_first, 0, sizeof(struct rr_parse));
281
0
  cn->rr_first->outside_packet = 1;
282
0
  cn->rr_first->ttl_data = (uint8_t*)regional_alloc(region, 
283
0
    sizeof(uint32_t)+sizeof(uint16_t)+aliaslen);
284
0
  if(!cn->rr_first->ttl_data)
285
0
    return NULL;
286
0
  memmove(cn->rr_first->ttl_data, rrset->rr_first->ttl_data,
287
0
    sizeof(uint32_t)); /* RFC6672: synth CNAME TTL == DNAME TTL */
288
  /* Apply cache TTL policy so DNAME and synthesized CNAME stay equal
289
   * and respect cache-min-ttl/cache-max-ttl (same as rdata_copy path). */
290
0
  if(!SERVE_ORIGINAL_TTL) {
291
0
    uint32_t ttl = sldns_read_uint32(cn->rr_first->ttl_data);
292
0
    time_t ttl_t = (time_t)ttl;
293
0
    if(ttl_t < MIN_TTL) ttl_t = MIN_TTL;
294
0
    if(ttl_t > MAX_TTL) ttl_t = MAX_TTL;
295
0
    ttl = (uint32_t)ttl_t;
296
0
    sldns_write_uint32(cn->rr_first->ttl_data, ttl);
297
0
    sldns_write_uint32(rrset->rr_first->ttl_data, ttl);
298
0
  }
299
0
  sldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen);
300
0
  memmove(cn->rr_first->ttl_data+6, alias, aliaslen);
301
0
  cn->rr_first->size = sizeof(uint16_t)+aliaslen;
302
303
  /* link it in */
304
0
  cn->rrset_all_next = nx;
305
0
  if(prev)
306
0
    prev->rrset_all_next = cn;
307
0
  else  msg->rrset_first = cn;
308
0
  if(nx == NULL)
309
0
    msg->rrset_last = cn;
310
0
  msg->rrset_count ++;
311
0
  msg->an_rrsets++;
312
  /* it is not inserted in the msg hashtable. */
313
314
0
  *sname = cn->rr_first->ttl_data + sizeof(uint32_t)+sizeof(uint16_t);
315
0
  *snamelen = aliaslen;
316
0
  return cn;
317
0
}
318
319
/** check if DNAME applies to a name */
320
static int
321
pkt_strict_sub(sldns_buffer* pkt, uint8_t* sname, uint8_t* dr)
322
825
{
323
825
  uint8_t buf1[LDNS_MAX_DOMAINLEN+1];
324
825
  uint8_t buf2[LDNS_MAX_DOMAINLEN+1];
325
  /* decompress names */
326
825
  dname_pkt_copy(pkt, buf1, sname);
327
825
  dname_pkt_copy(pkt, buf2, dr);
328
825
  return dname_strict_subdomain_c(buf1, buf2);
329
825
}
330
331
/** check subdomain with decompression */
332
static int
333
pkt_sub(sldns_buffer* pkt, uint8_t* comprname, uint8_t* zone)
334
64.9k
{
335
64.9k
  uint8_t buf[LDNS_MAX_DOMAINLEN+1];
336
64.9k
  dname_pkt_copy(pkt, buf, comprname);
337
64.9k
  return dname_subdomain_c(buf, zone);
338
64.9k
}
339
340
/** check subdomain with decompression, compressed is parent */
341
static int
342
sub_of_pkt(sldns_buffer* pkt, uint8_t* zone, uint8_t* comprname)
343
3.80k
{
344
3.80k
  uint8_t buf[LDNS_MAX_DOMAINLEN+1];
345
3.80k
  dname_pkt_copy(pkt, buf, comprname);
346
3.80k
  return dname_subdomain_c(zone, buf);
347
3.80k
}
348
349
/** Check if there are SOA records in the authority section (negative) */
350
static int
351
soa_in_auth(struct msg_parse* msg)
352
3.92k
{
353
3.92k
  struct rrset_parse* rrset;
354
276k
  for(rrset = msg->rrset_first; rrset; rrset = rrset->rrset_all_next)
355
273k
    if(rrset->type == LDNS_RR_TYPE_SOA &&
356
2.37k
      rrset->section == LDNS_SECTION_AUTHORITY) 
357
1.02k
      return 1;
358
2.90k
  return 0;
359
3.92k
}
360
361
/** Check if type is allowed in the authority section */
362
static int
363
type_allowed_in_authority_section(uint16_t tp)
364
0
{
365
0
  if(tp == LDNS_RR_TYPE_SOA || tp == LDNS_RR_TYPE_NS ||
366
0
    tp == LDNS_RR_TYPE_DS || tp == LDNS_RR_TYPE_NSEC ||
367
0
    tp == LDNS_RR_TYPE_NSEC3)
368
0
    return 1;
369
0
  return 0;
370
0
}
371
372
/** Check if type is allowed in the additional section */
373
static int
374
type_allowed_in_additional_section(uint16_t tp)
375
0
{
376
0
  if(tp == LDNS_RR_TYPE_A || tp == LDNS_RR_TYPE_AAAA)
377
0
    return 1;
378
0
  return 0;
379
0
}
380
381
/** Shorten RRset */
382
static void
383
shorten_rrset(sldns_buffer* pkt, struct rrset_parse* rrset, int count)
384
330
{
385
  /* The too large NS RRset is shortened. This is so that too large
386
   * content does not overwhelm the cache. It may make the rrset
387
   * bogus if it was signed, and then the domain is not resolved any
388
   * more, that is okay, the NS RRset was too large. During a referral
389
   * it can be shortened and then the first part of the list could
390
   * be used to resolve. The scrub continues to disallow glue for the
391
   * removed nameserver RRs and removes that too. Because the glue
392
   * is not marked as okay, since the RRs have been removed here. */
393
330
  int i;
394
330
  struct rr_parse* rr = rrset->rr_first, *prev = NULL;
395
330
  if(!rr)
396
0
    return;
397
330
  for(i=0; i<count; i++) {
398
0
    prev = rr;
399
0
    rr = rr->next;
400
0
    if(!rr)
401
0
      return; /* The RRset is already short. */
402
0
  }
403
330
  if(verbosity >= VERB_QUERY
404
0
    && rrset->dname_len <= LDNS_MAX_DOMAINLEN) {
405
0
    uint8_t buf[LDNS_MAX_DOMAINLEN+1];
406
0
    dname_pkt_copy(pkt, buf, rrset->dname);
407
0
    log_nametypeclass(VERB_QUERY, "normalize: shorten RRset:", buf,
408
0
      rrset->type, ntohs(rrset->rrset_class));
409
0
  }
410
  /* remove further rrs */
411
330
  rrset->rr_last = prev;
412
330
  rrset->rr_count = count;
413
4.76k
  while(rr) {
414
4.43k
    rrset->size -= rr->size;
415
4.43k
    rr = rr->next;
416
4.43k
  }
417
330
  if(rrset->rr_last)
418
0
    rrset->rr_last->next = NULL;
419
330
  else  rrset->rr_first = NULL;
420
330
}
421
422
/**
423
 * This routine normalizes a response. This includes removing "irrelevant"
424
 * records from the answer and additional sections and (re)synthesizing
425
 * CNAMEs from DNAMEs, if present.
426
 *
427
 * @param pkt: packet.
428
 * @param msg: msg to normalize.
429
 * @param qinfo: original query.
430
 * @param region: where to allocate synthesized CNAMEs.
431
 * @param env: module env with config options.
432
 * @param zonename: name of server zone.
433
 * @return 0 on error.
434
 */
435
static int
436
scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, 
437
  struct query_info* qinfo, struct regional* region,
438
  struct module_env* env, uint8_t* zonename)
439
2.97k
{
440
2.97k
  uint8_t* sname = qinfo->qname;
441
2.97k
  size_t snamelen = qinfo->qname_len;
442
2.97k
  struct rrset_parse* rrset, *prev, *nsset=NULL;
443
2.97k
  int cname_length = 0; /* number of CNAMEs, or DNAMEs */
444
445
2.97k
  if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
446
2.01k
    FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN &&
447
1.72k
    FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_YXDOMAIN)
448
1.54k
    return 1;
449
450
  /* For the ANSWER section, remove all "irrelevant" records and add
451
   * synthesized CNAMEs from DNAMEs
452
   * This will strip out-of-order CNAMEs as well. */
453
454
  /* walk through the parse packet rrset list, keep track of previous
455
   * for insert and delete ease, and examine every RRset */
456
1.42k
  prev = NULL;
457
1.42k
  rrset = msg->rrset_first;
458
5.84k
  while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
459
4.46k
    if(cname_length > env->cfg->iter_scrub_cname) {
460
      /* Too many CNAMEs, or DNAMEs, from the authority
461
       * server, scrub down the length to something
462
       * shorter. This deletes everything after the limit
463
       * is reached. The iterator is going to look up
464
       * the content one by one anyway. */
465
1.18k
      remove_rrset("normalize: removing because too many cnames:",
466
1.18k
        pkt, msg, prev, &rrset);
467
1.18k
      continue;
468
1.18k
    }
469
3.28k
    if(rrset->type == LDNS_RR_TYPE_DNAME &&
470
703
      pkt_strict_sub(pkt, sname, rrset->dname) &&
471
351
      pkt_sub(pkt, rrset->dname, zonename)) {
472
      /* check if next rrset is correct CNAME. else,
473
       * synthesize a CNAME */
474
14
      struct rrset_parse* nx = rrset->rrset_all_next;
475
14
      uint8_t alias[LDNS_MAX_DOMAINLEN+1];
476
14
      size_t aliaslen = 0;
477
14
      if(rrset->rr_count != 1) {
478
0
        verbose(VERB_ALGO, "Found DNAME rrset with "
479
0
          "size > 1: %u", 
480
0
          (unsigned)rrset->rr_count);
481
0
        return 0;
482
0
      }
483
14
      if(!synth_cname(sname, snamelen, rrset, alias, 
484
14
        &aliaslen, pkt)) {
485
14
        verbose(VERB_ALGO, "synthesized CNAME "
486
14
          "too long");
487
14
        if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_YXDOMAIN) {
488
14
          prev = rrset;
489
14
          rrset = rrset->rrset_all_next;
490
14
          continue;
491
14
        }
492
0
        return 0;
493
14
      }
494
0
      cname_length++;
495
0
      if(nx && nx->type == LDNS_RR_TYPE_CNAME && 
496
0
         dname_pkt_compare(pkt, sname, nx->dname) == 0) {
497
        /* check next cname */
498
0
        uint8_t* t = NULL;
499
0
        size_t tlen = 0;
500
0
        if(!parse_get_cname_target(nx, &t, &tlen, pkt))
501
0
          return 0;
502
0
        if(dname_pkt_compare(pkt, alias, t) == 0) {
503
          /* it's OK and better capitalized */
504
0
          prev = rrset;
505
0
          rrset = nx;
506
0
          continue;
507
0
        }
508
        /* synth ourselves */
509
0
      }
510
      /* synth a CNAME rrset */
511
0
      prev = synth_cname_rrset(&sname, &snamelen, alias, 
512
0
        aliaslen, region, msg, rrset, rrset, nx, pkt);
513
0
      if(!prev) {
514
0
        log_err("out of memory synthesizing CNAME");
515
0
        return 0;
516
0
      }
517
0
      rrset = nx;
518
0
      continue;
519
520
0
    }
521
522
    /* The only records in the ANSWER section not allowed to */
523
3.26k
    if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) {
524
2.56k
      remove_rrset("normalize: removing irrelevant RRset:", 
525
2.56k
        pkt, msg, prev, &rrset);
526
2.56k
      continue;
527
2.56k
    }
528
529
    /* Follow the CNAME chain. */
530
708
    if(rrset->type == LDNS_RR_TYPE_CNAME) {
531
116
      struct rrset_parse* nx = rrset->rrset_all_next;
532
116
      uint8_t* oldsname = sname;
533
116
      cname_length++;
534
      /* see if the next one is a DNAME, if so, swap them */
535
116
      if(nx && nx->section == LDNS_SECTION_ANSWER &&
536
83
        nx->type == LDNS_RR_TYPE_DNAME &&
537
24
        nx->rr_count == 1 &&
538
11
        pkt_strict_sub(pkt, sname, nx->dname) &&
539
9
        pkt_sub(pkt, nx->dname, zonename)) {
540
        /* there is a DNAME after this CNAME, it 
541
         * is in the ANSWER section, and the DNAME
542
         * applies to the name we cover */
543
        /* check if the alias of the DNAME equals
544
         * this CNAME */
545
0
        uint8_t alias[LDNS_MAX_DOMAINLEN+1];
546
0
        size_t aliaslen = 0;
547
0
        uint8_t* t = NULL;
548
0
        size_t tlen = 0;
549
0
        if(synth_cname(sname, snamelen, nx, alias,
550
0
          &aliaslen, pkt) &&
551
0
          parse_get_cname_target(rrset, &t, &tlen, pkt) &&
552
0
            dname_pkt_compare(pkt, alias, t) == 0) {
553
          /* the synthesized CNAME equals the
554
           * current CNAME.  This CNAME is the
555
           * one that the DNAME creates, and this
556
           * CNAME is better capitalised */
557
0
          verbose(VERB_ALGO, "normalize: re-order of DNAME and its CNAME");
558
0
          if(prev) prev->rrset_all_next = nx;
559
0
          else msg->rrset_first = nx;
560
0
          if(nx->rrset_all_next == NULL)
561
0
            msg->rrset_last = rrset;
562
0
          rrset->rrset_all_next =
563
0
            nx->rrset_all_next;
564
0
          nx->rrset_all_next = rrset;
565
          /* prev = nx; unused, enable if there
566
           * is other rrset removal code after
567
           * this */
568
0
        }
569
0
      }
570
571
      /* move to next name in CNAME chain */
572
116
      if(!parse_get_cname_target(rrset, &sname, &snamelen, pkt))
573
46
        return 0;
574
70
      prev = rrset;
575
70
      rrset = rrset->rrset_all_next;
576
      /* in CNAME ANY response, can have data after CNAME */
577
70
      if(qinfo->qtype == LDNS_RR_TYPE_ANY) {
578
0
        while(rrset && rrset->section ==
579
0
          LDNS_SECTION_ANSWER &&
580
0
          dname_pkt_compare(pkt, oldsname,
581
0
          rrset->dname) == 0) {
582
0
          if(rrset->type == LDNS_RR_TYPE_NS &&
583
0
            rrset->rr_count > env->cfg->iter_scrub_ns) {
584
0
            shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
585
0
          }
586
0
          prev = rrset;
587
0
          rrset = rrset->rrset_all_next;
588
0
        }
589
0
      }
590
70
      continue;
591
116
    }
592
593
    /* Otherwise, make sure that the RRset matches the qtype. */
594
592
    if(qinfo->qtype != LDNS_RR_TYPE_ANY && 
595
592
      qinfo->qtype != rrset->type) {
596
434
      remove_rrset("normalize: removing irrelevant RRset:", 
597
434
        pkt, msg, prev, &rrset);
598
434
      continue;
599
434
    }
600
601
158
    if(rrset->type == LDNS_RR_TYPE_NS &&
602
0
      rrset->rr_count > env->cfg->iter_scrub_ns) {
603
0
      shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
604
0
    }
605
606
    /* Mark the additional names from relevant rrset as OK. */
607
    /* only for RRsets that match the query name, other ones
608
     * will be removed by sanitize, so no additional for them */
609
158
    if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0)
610
158
      mark_additional_rrset(pkt, msg, rrset);
611
    
612
158
    prev = rrset;
613
158
    rrset = rrset->rrset_all_next;
614
158
  }
615
616
  /* Mark additional names from AUTHORITY */
617
22.5k
  while(rrset && rrset->section == LDNS_SECTION_AUTHORITY) {
618
    /* protect internals of recursor by making sure to del these */
619
21.1k
    if(rrset->type==LDNS_RR_TYPE_DNAME ||
620
20.9k
      rrset->type==LDNS_RR_TYPE_CNAME ||
621
20.7k
      rrset->type==LDNS_RR_TYPE_A ||
622
19.2k
      rrset->type==LDNS_RR_TYPE_AAAA) {
623
2.58k
      remove_rrset("normalize: removing irrelevant "
624
2.58k
        "RRset:", pkt, msg, prev, &rrset);
625
2.58k
      continue;
626
2.58k
    }
627
    /* Allowed list of types in the authority section */
628
18.5k
    if(env->cfg->harden_unknown_additional &&
629
0
      !type_allowed_in_authority_section(rrset->type)) {
630
0
      remove_rrset("normalize: removing irrelevant "
631
0
        "RRset:", pkt, msg, prev, &rrset);
632
0
      continue;
633
0
    }
634
    /* only one NS set allowed in authority section */
635
18.5k
    if(rrset->type==LDNS_RR_TYPE_NS) {
636
      /* NS set must be pertinent to the query */
637
3.59k
      if(!sub_of_pkt(pkt, qinfo->qname, rrset->dname)) {
638
231
        remove_rrset("normalize: removing irrelevant "
639
231
          "RRset:", pkt, msg, prev, &rrset);
640
231
        continue;
641
231
      }
642
      /* we don't want NS sets for NXDOMAIN answers,
643
       * because they could contain poisonous contents,
644
       * from. eg. fragmentation attacks, inserted after
645
       * long RRSIGs in the packet get to the packet
646
       * border and such */
647
      /* also for NODATA answers */
648
3.36k
      if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN ||
649
2.90k
         (FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR
650
2.36k
          && soa_in_auth(msg) && msg->an_rrsets == 0)) {
651
963
        remove_rrset("normalize: removing irrelevant "
652
963
          "RRset:", pkt, msg, prev, &rrset);
653
963
        continue;
654
963
      }
655
      /* If the NS set is a promiscuous NS set, scrub that
656
       * to remove potential for poisonous contents that
657
       * affects other names in the same zone. Remove
658
       * promiscuous NS sets in positive answers, that
659
       * thus have records in the answer section. Nodata
660
       * and nxdomain promiscuous NS sets have been removed
661
       * already. Since the NS rrset is scrubbed, its
662
       * address records are also not marked to be allowed
663
       * and are removed later. */
664
2.39k
      if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR &&
665
1.86k
        msg->an_rrsets != 0 &&
666
717
        env->cfg->iter_scrub_promiscuous) {
667
0
        remove_rrset("normalize: removing promiscuous "
668
0
          "RRset:", pkt, msg, prev, &rrset);
669
0
        continue;
670
0
      }
671
      /* Also delete promiscuous NS for other RCODEs */
672
2.39k
      if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR
673
539
        && env->cfg->iter_scrub_promiscuous) {
674
0
        remove_rrset("normalize: removing promiscuous "
675
0
          "RRset:", pkt, msg, prev, &rrset);
676
0
        continue;
677
0
      }
678
      /* Also delete promiscuous NS for NOERROR with nodata
679
       * for authoritative answers, not for delegations.
680
       * NOERROR with an_rrsets!=0 already handled.
681
       * Also NOERROR and soa_in_auth already handled.
682
       * NOERROR with an_rrsets==0, and not a referral.
683
       * referral is (NS not the zonename, noSOA).
684
       */
685
2.39k
      if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR
686
1.86k
        && msg->an_rrsets == 0
687
1.14k
        && !(dname_pkt_compare(pkt, rrset->dname,
688
1.14k
             zonename) != 0 && !soa_in_auth(msg))
689
41
        && env->cfg->iter_scrub_promiscuous) {
690
0
        remove_rrset("normalize: removing promiscuous "
691
0
          "RRset:", pkt, msg, prev, &rrset);
692
0
        continue;
693
0
      }
694
2.39k
      if(nsset == NULL) {
695
336
        nsset = rrset;
696
2.06k
      } else {
697
2.06k
        remove_rrset("normalize: removing irrelevant "
698
2.06k
          "RRset:", pkt, msg, prev, &rrset);
699
2.06k
        continue;
700
2.06k
      }
701
336
      if(rrset->rr_count > env->cfg->iter_scrub_ns) {
702
        /* If this is not a referral, and the NS RRset
703
         * is signed, then remove it entirely, so
704
         * that when it becomes bogus it does not
705
         * make the message that is otherwise fine
706
         * into a bogus message. */
707
336
        if(!(msg->an_rrsets == 0 &&
708
287
          FLAGS_GET_RCODE(msg->flags) ==
709
287
          LDNS_RCODE_NOERROR &&
710
247
          !soa_in_auth(msg) &&
711
247
          !(msg->flags & BIT_AA)) &&
712
228
          rrset->rrsig_count != 0) {
713
6
          remove_rrset("normalize: removing too large NS "
714
6
            "RRset:", pkt, msg, prev, &rrset);
715
6
          continue;
716
330
        } else {
717
330
          shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
718
330
        }
719
336
      }
720
336
    }
721
    /* if this is type DS and we query for type DS we just got
722
     * a referral answer for our type DS query, fix packet */
723
15.3k
    if(rrset->type==LDNS_RR_TYPE_DS &&
724
127
      qinfo->qtype == LDNS_RR_TYPE_DS &&
725
0
      dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0) {
726
0
      rrset->section = LDNS_SECTION_ANSWER;
727
0
      msg->ancount = rrset->rr_count + rrset->rrsig_count;
728
0
      msg->nscount = 0;
729
0
      msg->arcount = 0;
730
0
      msg->an_rrsets = 1;
731
0
      msg->ns_rrsets = 0;
732
0
      msg->ar_rrsets = 0;
733
0
      msg->rrset_count = 1;
734
0
      msg->rrset_first = rrset;
735
0
      msg->rrset_last = rrset;
736
0
      rrset->rrset_all_next = NULL;
737
0
      return 1;
738
0
    }
739
15.3k
    mark_additional_rrset(pkt, msg, rrset);
740
15.3k
    prev = rrset;
741
15.3k
    rrset = rrset->rrset_all_next;
742
15.3k
  }
743
744
  /* For each record in the additional section, remove it if it is an
745
   * address record and not in the collection of additional names 
746
   * found in ANSWER and AUTHORITY. */
747
  /* These records have not been marked OK previously */
748
9.14k
  while(rrset && rrset->section == LDNS_SECTION_ADDITIONAL) {
749
7.76k
    if(rrset->type==LDNS_RR_TYPE_A || 
750
6.19k
      rrset->type==LDNS_RR_TYPE_AAAA) 
751
2.35k
    {
752
2.35k
      if((rrset->flags & RRSET_SCRUB_OK)) {
753
        /* remove flag to clean up flags variable */
754
224
        rrset->flags &= ~RRSET_SCRUB_OK;
755
2.13k
      } else {
756
2.13k
        remove_rrset("normalize: removing irrelevant "
757
2.13k
          "RRset:", pkt, msg, prev, &rrset);
758
2.13k
        continue;
759
2.13k
      }
760
2.35k
    }
761
    /* protect internals of recursor by making sure to del these */
762
5.63k
    if(rrset->type==LDNS_RR_TYPE_DNAME || 
763
5.37k
      rrset->type==LDNS_RR_TYPE_CNAME ||
764
5.22k
      rrset->type==LDNS_RR_TYPE_NS) {
765
915
      remove_rrset("normalize: removing irrelevant "
766
915
        "RRset:", pkt, msg, prev, &rrset);
767
915
      continue;
768
915
    }
769
    /* Allowed list of types in the additional section */
770
4.71k
    if(env->cfg->harden_unknown_additional &&
771
0
      !type_allowed_in_additional_section(rrset->type)) {
772
0
      remove_rrset("normalize: removing irrelevant "
773
0
        "RRset:", pkt, msg, prev, &rrset);
774
0
      continue;
775
0
    }
776
4.71k
    prev = rrset;
777
4.71k
    rrset = rrset->rrset_all_next;
778
4.71k
  }
779
  
780
1.37k
  return 1;
781
1.37k
}
782
783
/**
784
 * Store potential poison in the cache (only if hardening disabled).
785
 * The rrset is stored in the cache but removed from the message.
786
 * So that it will be used for infrastructure purposes, but not be 
787
 * returned to the client.
788
 * @param pkt: packet
789
 * @param msg: message parsed
790
 * @param env: environment with cache
791
 * @param rrset: to store.
792
 */
793
static void
794
store_rrset(sldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
795
  struct rrset_parse* rrset)
796
8.40k
{
797
8.40k
  struct ub_packed_rrset_key* k;
798
8.40k
  struct packed_rrset_data* d;
799
8.40k
  struct rrset_ref ref;
800
8.40k
  time_t now = *env->now;
801
802
8.40k
  k = alloc_special_obtain(env->alloc);
803
8.40k
  if(!k)
804
0
    return;
805
8.40k
  k->entry.data = NULL;
806
8.40k
  if(!parse_copy_decompress_rrset(pkt, msg, rrset, NULL, k)) {
807
0
    alloc_special_release(env->alloc, k);
808
0
    return;
809
0
  }
810
8.40k
  d = (struct packed_rrset_data*)k->entry.data;
811
8.40k
  packed_rrset_ttl_add(d, now);
812
8.40k
  ref.key = k;
813
8.40k
  ref.id = k->id;
814
  /*ignore ret: it was in the cache, ref updated */
815
8.40k
  (void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, now);
816
8.40k
}
817
818
/**
819
 * Check if right hand name in NSEC is within zone
820
 * @param pkt: the packet buffer for decompression.
821
 * @param rrset: the NSEC rrset
822
 * @param zonename: the zone name.
823
 * @return true if BAD.
824
 */
825
static int sanitize_nsec_is_overreach(sldns_buffer* pkt,
826
  struct rrset_parse* rrset, uint8_t* zonename)
827
1.71k
{
828
1.71k
  struct rr_parse* rr;
829
1.71k
  uint8_t* rhs;
830
1.71k
  size_t len;
831
1.71k
  log_assert(rrset->type == LDNS_RR_TYPE_NSEC);
832
4.00k
  for(rr = rrset->rr_first; rr; rr = rr->next) {
833
3.04k
    size_t pos = sldns_buffer_position(pkt);
834
3.04k
    size_t rhspos;
835
3.04k
    rhs = rr->ttl_data+4+2;
836
3.04k
    len = sldns_read_uint16(rr->ttl_data+4);
837
3.04k
    rhspos = rhs-sldns_buffer_begin(pkt);
838
3.04k
    sldns_buffer_set_position(pkt, rhspos);
839
3.04k
    if(pkt_dname_len(pkt) == 0) {
840
      /* malformed */
841
32
      sldns_buffer_set_position(pkt, pos);
842
32
      return 1;
843
32
    }
844
3.01k
    if(sldns_buffer_position(pkt)-rhspos > len) {
845
      /* outside of rdata boundaries */
846
389
      sldns_buffer_set_position(pkt, pos);
847
389
      return 1;
848
389
    }
849
2.62k
    sldns_buffer_set_position(pkt, pos);
850
2.62k
    if(!pkt_sub(pkt, rhs, zonename)) {
851
      /* overreaching */
852
326
      return 1;
853
326
    }
854
2.62k
  }
855
  /* all NSEC RRs OK */
856
964
  return 0;
857
1.71k
}
858
859
/** Remove individual RRs, if the length is wrong. Returns true if the RRset
860
 * has been removed. */
861
static int
862
scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg,
863
  struct rrset_parse* prev, struct rrset_parse** rrset, int* added_ede,
864
  struct module_qstate* qstate)
865
14.3k
{
866
14.3k
  struct rr_parse* rr, *rr_prev = NULL;
867
259k
  for(rr = (*rrset)->rr_first; rr; rr = rr->next) {
868
869
    /* Sanity check for length of records
870
     * An A record should be 6 bytes only
871
     * (2 bytes for length and 4 for IPv4 addr)*/
872
250k
    if((*rrset)->type == LDNS_RR_TYPE_A && rr->size != 6 ) {
873
101k
      if(!*added_ede) {
874
371
        *added_ede = 1;
875
371
        errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.",
876
371
          LDNS_EDE_OTHER);
877
371
      }
878
101k
      if(msgparse_rrset_remove_rr("sanitize: removing type A RR of inappropriate length:",
879
101k
        pkt, *rrset, rr_prev, rr, NULL, 0)) {
880
3.73k
        remove_rrset("sanitize: removing type A RRset of inappropriate length:",
881
3.73k
          pkt, msg, prev, rrset);
882
3.73k
        return 1;
883
3.73k
      }
884
97.5k
      continue;
885
101k
    }
886
887
    /* Sanity check for length of records
888
     * An AAAA record should be 18 bytes only
889
     * (2 bytes for length and 16 for IPv6 addr)*/
890
149k
    if((*rrset)->type == LDNS_RR_TYPE_AAAA && rr->size != 18 ) {
891
3.62k
      if(!*added_ede) {
892
81
        *added_ede = 1;
893
81
        errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.",
894
81
          LDNS_EDE_OTHER);
895
81
      }
896
3.62k
      if(msgparse_rrset_remove_rr("sanitize: removing type AAAA RR of inappropriate length:",
897
3.62k
        pkt, *rrset, rr_prev, rr, NULL, 0)) {
898
1.73k
        remove_rrset("sanitize: removing type AAAA RRset of inappropriate length:",
899
1.73k
          pkt, msg, prev, rrset);
900
1.73k
        return 1;
901
1.73k
      }
902
1.89k
      continue;
903
3.62k
    }
904
145k
    rr_prev = rr;
905
145k
  }
906
8.92k
  return 0;
907
14.3k
}
908
909
/**
910
 * Given a response event, remove suspect RRsets from the response.
911
 * "Suspect" rrsets are potentially poison. Note that this routine expects
912
 * the response to be in a "normalized" state -- that is, all "irrelevant"
913
 * RRsets have already been removed, CNAMEs are in order, etc.
914
 *
915
 * @param pkt: packet.
916
 * @param msg: msg to normalize.
917
 * @param qinfo: the question originally asked.
918
 * @param zonename: name of server zone.
919
 * @param env: module environment with config and cache.
920
 * @param ie: iterator environment with private address data.
921
 * @param qstate: for setting errinf for EDE error messages.
922
 * @return 0 on error.
923
 */
924
static int
925
scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, 
926
  struct query_info* qinfo, uint8_t* zonename, struct module_env* env,
927
  struct iter_env* ie, struct module_qstate* qstate)
928
2.92k
{
929
2.92k
  int del_addi = 0; /* if additional-holding rrsets are deleted, we
930
    do not trust the normalized additional-A-AAAA any more */
931
2.92k
  uint8_t* ns_rrset_dname = NULL;
932
2.92k
  int added_rrlen_ede = 0;
933
2.92k
  struct rrset_parse* rrset, *prev;
934
2.92k
  prev = NULL;
935
2.92k
  rrset = msg->rrset_first;
936
937
  /* the first DNAME is allowed to stay. It needs checking before
938
   * it can be used from the cache. After normalization, an initial 
939
   * DNAME will have a correctly synthesized CNAME after it. */
940
2.92k
  if(rrset && rrset->type == LDNS_RR_TYPE_DNAME && 
941
140
    rrset->section == LDNS_SECTION_ANSWER &&
942
111
    pkt_strict_sub(pkt, qinfo->qname, rrset->dname) &&
943
51
    pkt_sub(pkt, rrset->dname, zonename)) {
944
40
    prev = rrset; /* DNAME allowed to stay in answer section */
945
40
    rrset = rrset->rrset_all_next;
946
40
  }
947
  
948
  /* remove all records from the answer section that are 
949
   * not the same domain name as the query domain name.
950
   * The answer section should contain rrsets with the same name
951
   * as the question. For DNAMEs a CNAME has been synthesized.
952
   * Wildcards have the query name in answer section.
953
   * ANY queries get query name in answer section.
954
   * Remainders of CNAME chains are cut off and resolved by iterator. */
955
10.3k
  while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
956
7.38k
    if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) != 0) {
957
6.23k
      if(has_additional(rrset->type)) del_addi = 1;
958
6.23k
      remove_rrset("sanitize: removing extraneous answer "
959
6.23k
        "RRset:", pkt, msg, prev, &rrset);
960
6.23k
      continue;
961
6.23k
    }
962
1.15k
    prev = rrset;
963
1.15k
    rrset = rrset->rrset_all_next;
964
1.15k
  }
965
966
  /* At this point, we brutally remove ALL rrsets that aren't 
967
   * children of the originating zone. The idea here is that, 
968
   * as far as we know, the server that we contacted is ONLY 
969
   * authoritative for the originating zone. It, of course, MAY 
970
   * be authoritative for any other zones, and of course, MAY 
971
   * NOT be authoritative for some subdomains of the originating 
972
   * zone. */
973
2.92k
  prev = NULL;
974
2.92k
  rrset = msg->rrset_first;
975
70.2k
  while(rrset) {
976
977
    /* Sanity check for length of records */
978
67.3k
    if(rrset->type == LDNS_RR_TYPE_A ||
979
55.7k
      rrset->type == LDNS_RR_TYPE_AAAA) {
980
14.3k
      if(scrub_sanitize_rr_length(pkt, msg, prev, &rrset,
981
14.3k
        &added_rrlen_ede, qstate))
982
5.46k
        continue;
983
14.3k
    }
984
985
    /* remove private addresses */
986
61.8k
    if(rrset->type == LDNS_RR_TYPE_A ||
987
54.0k
      rrset->type == LDNS_RR_TYPE_AAAA ||
988
52.9k
      rrset->type == LDNS_RR_TYPE_SVCB ||
989
52.7k
      rrset->type == LDNS_RR_TYPE_HTTPS) {
990
991
      /* do not set servfail since this leads to too
992
       * many drops of other people using rfc1918 space */
993
      /* also do not remove entire rrset, unless all records
994
       * in it are bad */
995
9.18k
      if(priv_rrset_bad(ie->priv, pkt, rrset)) {
996
0
        remove_rrset(NULL, pkt, msg, prev, &rrset);
997
0
        continue;
998
0
      }
999
9.18k
    }
1000
    
1001
    /* skip DNAME records -- they will always be followed by a 
1002
     * synthesized CNAME, which will be relevant.
1003
     * FIXME: should this do something differently with DNAME 
1004
     * rrsets NOT in Section.ANSWER? */
1005
    /* But since DNAME records are also subdomains of the zone,
1006
     * same check can be used */
1007
1008
61.8k
    if(!pkt_sub(pkt, rrset->dname, zonename)) {
1009
56.7k
      if(msg->an_rrsets == 0 && 
1010
51.7k
        rrset->type == LDNS_RR_TYPE_NS && 
1011
2.24k
        rrset->section == LDNS_SECTION_AUTHORITY &&
1012
1.15k
        FLAGS_GET_RCODE(msg->flags) == 
1013
1.15k
        LDNS_RCODE_NOERROR && !soa_in_auth(msg) &&
1014
211
        sub_of_pkt(pkt, zonename, rrset->dname)) {
1015
        /* noerror, nodata and this NS rrset is above
1016
         * the zone. This is LAME! 
1017
         * Leave in the NS for lame classification. */
1018
        /* remove everything from the additional
1019
         * (we dont want its glue that was approved
1020
         * during the normalize action) */
1021
211
        del_addi = 1;
1022
56.4k
      } else if(!env->cfg->harden_glue && (
1023
41.7k
        rrset->type == LDNS_RR_TYPE_A ||
1024
34.1k
        rrset->type == LDNS_RR_TYPE_AAAA)) {
1025
        /* store in cache! Since it is relevant
1026
         * (from normalize) it will be picked up 
1027
         * from the cache to be used later */
1028
8.40k
        store_rrset(pkt, msg, env, rrset);
1029
8.40k
        remove_rrset("sanitize: storing potential "
1030
8.40k
        "poison RRset:", pkt, msg, prev, &rrset);
1031
8.40k
        continue;
1032
48.0k
      } else {
1033
48.0k
        if(has_additional(rrset->type)) del_addi = 1;
1034
48.0k
        remove_rrset("sanitize: removing potential "
1035
48.0k
        "poison RRset:", pkt, msg, prev, &rrset);
1036
48.0k
        continue;
1037
48.0k
      }
1038
56.7k
    }
1039
5.38k
    if(rrset->type == LDNS_RR_TYPE_NS &&
1040
656
      (rrset->section == LDNS_SECTION_AUTHORITY ||
1041
524
      rrset->section == LDNS_SECTION_ANSWER)) {
1042
      /* If the type is NS, and we're in the
1043
       * answer or authority section, then
1044
       * store the dname so we can check
1045
       * against the glue records
1046
       * further down */
1047
524
      ns_rrset_dname = rrset->dname;
1048
524
    }
1049
5.38k
    if(del_addi && rrset->section == LDNS_SECTION_ADDITIONAL) {
1050
469
      remove_rrset("sanitize: removing potential "
1051
469
      "poison reference RRset:", pkt, msg, prev, &rrset);
1052
469
      continue;
1053
469
    }
1054
    /* check if right hand side of NSEC is within zone */
1055
4.91k
    if(rrset->type == LDNS_RR_TYPE_NSEC &&
1056
1.71k
      sanitize_nsec_is_overreach(pkt, rrset, zonename)) {
1057
747
      remove_rrset("sanitize: removing overreaching NSEC "
1058
747
        "RRset:", pkt, msg, prev, &rrset);
1059
747
      continue;
1060
747
    }
1061
4.16k
    if(env->cfg->harden_unverified_glue && ns_rrset_dname &&
1062
0
      rrset->section == LDNS_SECTION_ADDITIONAL &&
1063
0
      (rrset->type == LDNS_RR_TYPE_A || rrset->type == LDNS_RR_TYPE_AAAA) &&
1064
0
      !pkt_strict_sub(pkt, rrset->dname, ns_rrset_dname)) {
1065
      /* We're in the additional section, looking
1066
       * at an A/AAAA rrset, have a previous
1067
       * delegation point and we notice that
1068
       * the glue records are NOT for strict
1069
       * subdomains of the delegation. So set a
1070
       * flag, recompute the hash for the rrset
1071
       * and write the A/AAAA record to cache.
1072
       * It'll be retrieved if we can't separately
1073
       * resolve the glue */
1074
0
      rrset->flags = PACKED_RRSET_UNVERIFIED_GLUE;
1075
0
      rrset->hash = pkt_hash_rrset(pkt, rrset->dname, rrset->type, rrset->rrset_class, rrset->flags);
1076
0
      store_rrset(pkt, msg, env, rrset);
1077
0
      remove_rrset("sanitize: storing potential "
1078
0
      "unverified glue reference RRset:", pkt, msg, prev, &rrset);
1079
0
      continue;
1080
0
    }
1081
4.16k
    prev = rrset;
1082
4.16k
    rrset = rrset->rrset_all_next;
1083
4.16k
  }
1084
2.92k
  return 1;
1085
2.92k
}
1086
1087
int 
1088
scrub_message(sldns_buffer* pkt, struct msg_parse* msg, 
1089
  struct query_info* qinfo, uint8_t* zonename, struct regional* region,
1090
  struct module_env* env, struct module_qstate* qstate,
1091
  struct iter_env* ie)
1092
3.31k
{
1093
  /* basic sanity checks */
1094
3.31k
  log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS, 
1095
3.31k
    qinfo->qclass);
1096
3.31k
  if(msg->qdcount > 1)
1097
0
    return 0;
1098
3.31k
  if( !(msg->flags&BIT_QR) )
1099
118
    return 0;
1100
3.19k
  msg->flags &= ~(BIT_AD|BIT_Z); /* force off bit AD and Z */
1101
  
1102
  /* make sure that a query is echoed back when NOERROR or NXDOMAIN */
1103
  /* this is not required for basic operation but is a forgery 
1104
   * resistance (security) feature */
1105
3.19k
  if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR ||
1106
2.15k
    FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN ||
1107
1.81k
    FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_YXDOMAIN) &&
1108
1.58k
    msg->qdcount == 0)
1109
43
    return 0;
1110
1111
  /* if a query is echoed back, make sure it is correct. Otherwise,
1112
   * this may be not a reply to our query. */
1113
3.15k
  if(msg->qdcount == 1) {
1114
1.72k
    if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0)
1115
80
      return 0;
1116
1.64k
    if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass)
1117
98
      return 0;
1118
1.64k
  }
1119
1120
  /* normalize the response, this cleans up the additional.  */
1121
2.97k
  if(!scrub_normalize(pkt, msg, qinfo, region, env, zonename))
1122
46
    return 0;
1123
  /* delete all out-of-zone information */
1124
2.92k
  if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate))
1125
0
    return 0;
1126
2.92k
  return 1;
1127
2.92k
}