]>
Commit | Line | Data |
---|---|---|
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 | ||
23 | #include <cinttypes> | |
24 | #include <dirent.h> | |
25 | #include <errno.h> | |
26 | #include "ixfrutils.hh" | |
27 | #include "sstuff.hh" | |
28 | #include "dnssecinfra.hh" | |
29 | #include "zoneparser-tng.hh" | |
30 | #include "dnsparser.hh" | |
31 | ||
32 | uint32_t getSerialFromMaster(const ComboAddress& master, const DNSName& zone, shared_ptr<SOARecordContent>& sr, const TSIGTriplet& tt, const uint16_t timeout) | |
33 | { | |
34 | vector<uint8_t> packet; | |
35 | DNSPacketWriter pw(packet, zone, QType::SOA); | |
36 | if(!tt.algo.empty()) { | |
37 | TSIGRecordContent trc; | |
38 | trc.d_algoName = tt.algo; | |
39 | trc.d_time = time(nullptr); | |
40 | trc.d_fudge = 300; | |
41 | trc.d_origID=ntohs(pw.getHeader()->id); | |
42 | trc.d_eRcode=0; | |
43 | addTSIG(pw, trc, tt.name, tt.secret, "", false); | |
44 | } | |
45 | ||
46 | Socket s(master.sin4.sin_family, SOCK_DGRAM); | |
47 | s.connect(master); | |
48 | string msg((const char*)&packet[0], packet.size()); | |
49 | s.writen(msg); | |
50 | ||
51 | string reply; | |
52 | reply.resize(4096); | |
53 | // will throw a NetworkError on timeout | |
54 | ssize_t got = s.readWithTimeout(&reply[0], reply.size(), timeout); | |
55 | if (got < 0 || static_cast<size_t>(got) < sizeof(dnsheader)) { | |
56 | throw std::runtime_error("Invalid response size " + std::to_string(got)); | |
57 | } | |
58 | ||
59 | reply.resize(got); | |
60 | ||
61 | MOADNSParser mdp(false, reply); | |
62 | if(mdp.d_header.rcode) { | |
63 | throw std::runtime_error("RCODE from response is not NoError but " + RCode::to_s(mdp.d_header.rcode)); | |
64 | } | |
65 | for(const auto& r: mdp.d_answers) { | |
66 | if(r.first.d_type == QType::SOA) { | |
67 | sr = getRR<SOARecordContent>(r.first); | |
68 | if(sr != nullptr) { | |
69 | return sr->d_st.serial; | |
70 | } | |
71 | } | |
72 | } | |
73 | return 0; | |
74 | } | |
75 | ||
76 | uint32_t getSerialsFromDir(const std::string& dir) | |
77 | { | |
78 | uint32_t ret=0; | |
79 | DIR* dirhdl=opendir(dir.c_str()); | |
80 | if(!dirhdl) | |
81 | throw runtime_error("Could not open IXFR directory '" + dir + "': " + strerror(errno)); | |
82 | struct dirent *entry; | |
83 | ||
84 | while((entry = readdir(dirhdl))) { | |
85 | uint32_t num = atoi(entry->d_name); | |
86 | if(std::to_string(num) == entry->d_name) | |
87 | ret = max(num, ret); | |
88 | } | |
89 | closedir(dirhdl); | |
90 | return ret; | |
91 | } | |
92 | ||
93 | uint32_t getSerialFromRecords(const records_t& records, DNSRecord& soaret) | |
94 | { | |
95 | DNSName root("."); | |
96 | uint16_t t=QType::SOA; | |
97 | ||
98 | auto found = records.equal_range(tie(root, t)); | |
99 | ||
100 | for(auto iter = found.first; iter != found.second; ++iter) { | |
101 | auto soa = std::dynamic_pointer_cast<SOARecordContent>(iter->d_content); | |
102 | if (soa) { | |
103 | soaret = *iter; | |
104 | return soa->d_st.serial; | |
105 | } | |
106 | } | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static void writeRecords(FILE* fp, const records_t& records) | |
111 | { | |
112 | for(const auto& r: records) { | |
113 | fprintf(fp, "%s\t%" PRIu32 "\tIN\t%s\t%s\n", | |
114 | r.d_name.isRoot() ? "@" : r.d_name.toStringNoDot().c_str(), | |
115 | r.d_ttl, | |
116 | DNSRecordContent::NumberToType(r.d_type).c_str(), | |
117 | r.d_content->getZoneRepresentation().c_str()); | |
118 | } | |
119 | } | |
120 | ||
121 | void writeZoneToDisk(const records_t& records, const DNSName& zone, const std::string& directory) | |
122 | { | |
123 | DNSRecord soa; | |
124 | auto serial = getSerialFromRecords(records, soa); | |
125 | string fname=directory +"/"+std::to_string(serial); | |
126 | FILE* fp=fopen((fname+".partial").c_str(), "w"); | |
127 | if(!fp) | |
128 | throw runtime_error("Unable to open file '"+fname+".partial' for writing: "+string(strerror(errno))); | |
129 | ||
130 | records_t soarecord; | |
131 | soarecord.insert(soa); | |
132 | fprintf(fp, "$ORIGIN %s\n", zone.toString().c_str()); | |
133 | ||
134 | writeRecords(fp, soarecord); | |
135 | writeRecords(fp, records); | |
136 | writeRecords(fp, soarecord); | |
137 | ||
138 | fclose(fp); | |
139 | if (rename( (fname+".partial").c_str(), fname.c_str()) != 0) { | |
140 | throw std::runtime_error("Unable to move the zone file for " + zone.toLogString() + " from " + fname + ".partial to " + fname + ": " + string(strerror(errno))); | |
141 | } | |
142 | } | |
143 | ||
144 | void loadZoneFromDisk(records_t& records, const string& fname, const DNSName& zone) | |
145 | { | |
146 | ZoneParserTNG zpt(fname, zone); | |
147 | ||
148 | DNSResourceRecord rr; | |
149 | bool seenSOA=false; | |
150 | while(zpt.get(rr)) { | |
151 | if(rr.qtype.getCode() == QType::CNAME && rr.content.empty()) | |
152 | rr.content="."; | |
153 | rr.qname = rr.qname.makeRelative(zone); | |
154 | ||
155 | if(rr.qtype.getCode() != QType::SOA || seenSOA==false) | |
156 | records.insert(DNSRecord(rr)); | |
157 | if(rr.qtype.getCode() == QType::SOA) { | |
158 | seenSOA=true; | |
159 | } | |
160 | } | |
161 | if(!(rr.qtype.getCode() == QType::SOA && seenSOA)) { | |
162 | records.clear(); | |
163 | throw runtime_error("Zone not complete!"); | |
164 | } | |
165 | } | |
166 | ||
167 | /* | |
168 | * Load the zone `zone` from `fname` and put the first found SOA into `soa` | |
169 | * Does NOT check for nullptr | |
170 | */ | |
171 | void loadSOAFromDisk(const DNSName& zone, const string& fname, shared_ptr<SOARecordContent>& soa, uint32_t& soaTTL) | |
172 | { | |
173 | ZoneParserTNG zpt(fname, zone); | |
174 | DNSResourceRecord rr; | |
175 | ||
176 | while(zpt.get(rr)) { | |
177 | if (rr.qtype == QType::SOA) { | |
178 | soa = getRR<SOARecordContent>(DNSRecord(rr)); | |
179 | soaTTL = rr.ttl; | |
180 | return; | |
181 | } | |
182 | } | |
183 | } |