]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/shuffle.cc
64c4bba7dda401d4dfc4fc0725094c922382f4d4
[thirdparty/pdns.git] / pdns / shuffle.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <string>
27
28 #include "shuffle.hh"
29 #include "dns_random.hh"
30 #include "dnsparser.hh"
31
32 // shuffle, maintaining some semblance of order
33 void pdns::shuffle(std::vector<DNSZoneRecord>& rrs)
34 {
35 std::vector<DNSZoneRecord>::iterator first, second;
36
37 // We assume the CNAMES are listed firts in the ANSWWER section and the the other records
38 // and we want to shuffle the other records only
39
40 // First we scan for the first non-CNAME ANSWER record
41 for (first = rrs.begin(); first != rrs.end(); ++first) {
42 if (first->dr.d_place == DNSResourceRecord::ANSWER && first->dr.d_type != QType::CNAME) {
43 break;
44 }
45 }
46 // And then for one past the last ANSWER recordd
47 for (second = first; second != rrs.end(); ++second)
48 if (second->dr.d_place != DNSResourceRecord::ANSWER)
49 break;
50
51 // Now shuffle the non-CNAME ANSWER records
52 dns_random_engine r;
53 if (second - first > 1) {
54 shuffle(first, second, r);
55 }
56
57 // now shuffle the ADDITIONAL records in the same manner as the ANSWER records
58 for (first = second; first != rrs.end(); ++first) {
59 if (first->dr.d_place == DNSResourceRecord::ADDITIONAL && first->dr.d_type != QType::CNAME) {
60 break;
61 }
62 }
63 for (second = first; second != rrs.end(); ++second) {
64 if (second->dr.d_place != DNSResourceRecord::ADDITIONAL) {
65 break;
66 }
67 }
68
69 if (second - first > 1) {
70 shuffle(first, second, r);
71 }
72 // we don't shuffle the rest
73 }
74
75 // shuffle, maintaining some semblance of order
76 static void shuffle(std::vector<DNSRecord>& rrs)
77 {
78 // This shuffles in the same style as the above method, keeping CNAME in the front and RRSIGs at the end
79 std::vector<DNSRecord>::iterator first, second;
80 for (first = rrs.begin(); first != rrs.end(); ++first) {
81 if (first->d_place == DNSResourceRecord::ANSWER && first->d_type != QType::CNAME) {
82 break;
83 }
84 }
85 for (second = first; second != rrs.end(); ++second) {
86 if (second->d_place != DNSResourceRecord::ANSWER || second->d_type == QType::RRSIG) {
87 break;
88 }
89 }
90
91 pdns::dns_random_engine r;
92 if (second - first > 1) {
93 shuffle(first, second, r);
94 }
95
96 // now shuffle the additional records
97 for (first = second; first != rrs.end(); ++first) {
98 if (first->d_place == DNSResourceRecord::ADDITIONAL && first->d_type != QType::CNAME) {
99 break;
100 }
101 }
102 for (second = first; second != rrs.end(); ++second) {
103 if (second->d_place != DNSResourceRecord::ADDITIONAL) {
104 break;
105 }
106 }
107
108 if (second - first > 1) {
109 shuffle(first, second, r);
110 }
111 // we don't shuffle the rest
112 }
113
114 static uint16_t mapTypesToOrder(uint16_t type)
115 {
116 if (type == QType::CNAME)
117 return 0;
118 if (type == QType::RRSIG)
119 return 65535;
120 else
121 return 1;
122 }
123
124 // make sure rrs is sorted in d_place order to avoid surprises later
125 // then shuffle the parts that desire shuffling
126 void pdns::orderAndShuffle(vector<DNSRecord>& rrs)
127 {
128 std::stable_sort(rrs.begin(), rrs.end(), [](const DNSRecord& a, const DNSRecord& b) {
129 return std::make_tuple(a.d_place, mapTypesToOrder(a.d_type)) < std::make_tuple(b.d_place, mapTypesToOrder(b.d_type));
130 });
131 shuffle(rrs);
132 }