]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * PowerDNS BIND Zone to LDAP converter | |
3 | * Copyright (C) 2003 Norbert Sendetzky | |
4 | * Copyright (C) 2007 bert hubert | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 | |
8 | * the Free Software Foundation | |
9 | * | |
10 | * Additionally, the license of this program contains a special | |
11 | * exception which allows to distribute the program in binary form when | |
12 | * it is linked against OpenSSL. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
22 | */ | |
23 | ||
24 | ||
25 | #ifdef HAVE_CONFIG_H | |
26 | #include "config.h" | |
27 | #endif | |
28 | #include <map> | |
29 | #include <string> | |
30 | #include <iostream> | |
31 | #include <sstream> | |
32 | #include <stdio.h> | |
33 | #include "arguments.hh" | |
34 | #include "bindparserclasses.hh" | |
35 | #include "statbag.hh" | |
36 | #include <boost/function.hpp> | |
37 | #include "dnsrecords.hh" | |
38 | #include "misc.hh" | |
39 | #include "dns.hh" | |
40 | #include "zoneparser-tng.hh" | |
41 | ||
42 | using std::map; | |
43 | using std::string; | |
44 | using std::vector; | |
45 | ||
46 | StatBag S; | |
47 | ArgvMap args; | |
48 | bool g_dnsttl; | |
49 | bool g_pdnsinfo; | |
50 | unsigned int g_domainid; | |
51 | string g_basedn; | |
52 | string g_metadatadn; | |
53 | DNSName g_zonename; | |
54 | map<DNSName,bool> g_objects; | |
55 | map<string, bool> g_entries; | |
56 | map<DNSName,bool> g_recorddata; | |
57 | map<DNSName, map<string, bool> > g_recordttl; | |
58 | ||
59 | static std::string encode_non_ascii( const std::string &input ) { | |
60 | std::ostringstream out; | |
61 | ||
62 | for ( auto i : input ) { | |
63 | if ( (unsigned char)i > 0x7F ) | |
64 | out << '\\' << int( (unsigned char)i ); | |
65 | else | |
66 | out << i; | |
67 | } | |
68 | ||
69 | return out.str(); | |
70 | } | |
71 | ||
72 | static void callback_simple( unsigned int domain_id, const DNSName &domain, const string &qtype, const string &content, int ttl ) | |
73 | { | |
74 | DNSName host; | |
75 | ||
76 | if( ! domain.isPartOf(g_zonename) ) | |
77 | { | |
78 | cerr << "Domain '" << domain << "'' not part of '" << g_zonename << "'"<< endl; | |
79 | return; | |
80 | } | |
81 | ||
82 | host = domain.makeRelative(g_zonename); | |
83 | ||
84 | if( g_pdnsinfo && qtype == "SOA" ) { | |
85 | cout << "dn: ou=" << domain << "," << g_metadatadn << endl; | |
86 | cout << "changetype: add" << endl; | |
87 | cout << "objectclass: organizationalUnit" << endl; | |
88 | cout << "ou: " << domain.toStringNoDot() << endl; | |
89 | cout << endl; | |
90 | } | |
91 | ||
92 | std::string stripped=stripDot(content); | |
93 | std::string rrvalue = stripped + ((stripped.empty() || stripped[stripped.size()-1]==' ') ? "." : ""); | |
94 | std::string dn = "dc="; | |
95 | if( host.countLabels() ) { dn += host.toStringNoDot() + ",dc="; } | |
96 | dn += g_zonename.toStringNoDot() + "," + g_basedn; | |
97 | cout << "dn: " << dn << endl; | |
98 | ||
99 | if( host.countLabels() == 0 ) { host = g_zonename; } | |
100 | ||
101 | if( !g_entries[dn] ) | |
102 | { | |
103 | g_entries[dn] = true; | |
104 | g_recorddata[domain] = true; | |
105 | ||
106 | cout << "changetype: add" << endl; | |
107 | cout << "objectclass: dnsdomain2" << endl; | |
108 | cout << "objectclass: domainrelatedobject" << endl; | |
109 | cout << "objectclass: PdnsRecordData" << endl; | |
110 | if( g_pdnsinfo && qtype == "SOA" ) { | |
111 | cout << "objectclass: PdnsDomain" << endl; | |
112 | cout << "PdnsDomainId: " << domain_id << endl; | |
113 | } | |
114 | cout << "dc: " << host.toStringNoDot() << endl; | |
115 | if( g_dnsttl ) { cout << "dnsttl: " << ttl << endl; } | |
116 | cout << "associateddomain: " << domain.toStringNoDot() << endl; | |
117 | } | |
118 | else | |
119 | { | |
120 | cout << "changetype: modify" << endl; | |
121 | if ( !g_recorddata[domain] ) { | |
122 | g_recorddata[domain] = true; | |
123 | cout << "add: objectClass" << endl; | |
124 | cout << "objectClass: PdnsRecordData" << endl; | |
125 | cout << "-" << endl; | |
126 | } | |
127 | if ( !g_recordttl.count( domain ) || !g_recordttl[domain].count( qtype ) ) { | |
128 | g_recordttl[domain][qtype] = true; | |
129 | cout << "add: PdnsRecordTTL" << endl; | |
130 | cout << "PdnsRecordTTL: " << qtype << "|" << ttl << endl; | |
131 | cout << "-" << endl; | |
132 | } | |
133 | cout << "add: " << qtype << "Record" << endl; | |
134 | } | |
135 | cout << qtype << "Record: " << rrvalue << endl << endl; | |
136 | } | |
137 | ||
138 | ||
139 | ||
140 | static void callback_tree( unsigned int domain_id, const DNSName &domain, const string &qtype, const string &content, int ttl ) | |
141 | { | |
142 | unsigned int i; | |
143 | string dn; | |
144 | DNSName net; | |
145 | vector<string> parts; | |
146 | ||
147 | stringtok( parts, domain.toStringNoDot(), "." ); | |
148 | if( parts.empty() ) { return; } | |
149 | ||
150 | for( i = parts.size() - 1; i > 0; i-- ) | |
151 | { | |
152 | net.prependRawLabel(parts[i]); | |
153 | dn = "dc=" + parts[i] + "," + dn; | |
154 | ||
155 | if( !g_objects[net] ) | |
156 | { | |
157 | g_objects[net] = true; | |
158 | ||
159 | cout << "dn: " << dn << g_basedn << endl; | |
160 | cout << "changetype: add" << endl; | |
161 | cout << "objectclass: dnsdomain2" << endl; | |
162 | cout << "objectclass: domainrelatedobject" << endl; | |
163 | cout << "dc: " << parts[i] << endl; | |
164 | cout << "associateddomain: " << net.toStringNoDot() << endl << endl; | |
165 | } | |
166 | ||
167 | } | |
168 | ||
169 | if( g_pdnsinfo && qtype == "SOA" ) { | |
170 | cout << "dn: ou=" << domain << "," << g_metadatadn << endl; | |
171 | cout << "changetype: add" << endl; | |
172 | cout << "objectclass: organizationalUnit" << endl; | |
173 | cout << "ou: " << domain.toStringNoDot() << endl; | |
174 | cout << endl; | |
175 | } | |
176 | ||
177 | std::string stripped=stripDot(content); | |
178 | std::string rrvalue = stripped + ((stripped.empty() || stripped[stripped.size()-1]==' ') ? "." : ""); | |
179 | cout << "dn: " << "dc=" << parts[0] << "," << dn << g_basedn << endl; | |
180 | ||
181 | if( !g_objects[domain] ) | |
182 | { | |
183 | g_objects[domain] = true; | |
184 | g_recorddata[domain] = true; | |
185 | ||
186 | cout << "changetype: add" << endl; | |
187 | cout << "objectclass: dnsdomain2" << endl; | |
188 | cout << "objectclass: domainrelatedobject" << endl; | |
189 | cout << "objectclass: PdnsRecordData" << endl; | |
190 | if( g_pdnsinfo && qtype == "SOA" ) { | |
191 | cout << "objectclass: PdnsDomain" << endl; | |
192 | cout << "PdnsDomainId: " << domain_id << endl; | |
193 | } | |
194 | cout << "dc: " << parts[0] << endl; | |
195 | if( g_dnsttl ) { cout << "dnsttl: " << ttl << endl; } | |
196 | cout << "associateddomain: " << domain.toStringNoDot() << endl; | |
197 | } | |
198 | else | |
199 | { | |
200 | cout << "changetype: modify" << endl; | |
201 | if( g_pdnsinfo && qtype == "SOA" ) { | |
202 | cout << "add: objectclass" << endl; | |
203 | cout << "objectclass: PdnsDomain" << endl; | |
204 | cout << "-" << endl; | |
205 | cout << "add: PdnsDomainId" << endl; | |
206 | cout << "PdnsDomainId: " << domain_id << endl; | |
207 | cout << "-" << endl; | |
208 | } | |
209 | if ( !g_recorddata[domain] ) { | |
210 | g_recorddata[domain] = true; | |
211 | cout << "add: objectClass" << endl; | |
212 | cout << "objectClass: PdnsRecordData" << endl; | |
213 | cout << "-" << endl; | |
214 | } | |
215 | if ( !g_recordttl.count( domain ) || !g_recordttl[domain].count( qtype ) ) { | |
216 | g_recordttl[domain][qtype] = true; | |
217 | cout << "add: PdnsRecordTTL" << endl; | |
218 | cout << "PdnsRecordTTL: " << qtype << "|" << ttl << endl; | |
219 | cout << "-" << endl; | |
220 | } | |
221 | cout << "add: " << qtype << "Record" << endl; | |
222 | } | |
223 | cout << qtype << "Record: " << rrvalue << endl << endl; | |
224 | } | |
225 | ||
226 | ||
227 | ||
228 | int main( int argc, char* argv[] ) | |
229 | { | |
230 | BindParser BP; | |
231 | vector<string> parts; | |
232 | ||
233 | ||
234 | try | |
235 | { | |
236 | std::ios_base::sync_with_stdio( false ); | |
237 | reportAllTypes(); | |
238 | args.setCmd( "help", "Provide a helpful message" ); | |
239 | args.setCmd( "version", "Print the version" ); | |
240 | args.setSwitch( "verbose", "Verbose comments on operation" ) = "no"; | |
241 | args.setSwitch( "resume", "Continue after errors" ) = "no"; | |
242 | args.setSwitch( "dnsttl", "Add dnsttl attribute to every entry" ) = "no"; | |
243 | args.setSwitch( "pdns-info", "Add the PDNS domain info attributes (this mandates setting --metadata-dn)" ) = "no"; | |
244 | args.set( "named-conf", "Bind 8 named.conf to parse" ) = ""; | |
245 | args.set( "zone-file", "Zone file to parse" ) = ""; | |
246 | args.set( "zone-name", "Specify a zone name if zone is set" ) = ""; | |
247 | args.set( "basedn", "Base DN to store objects below" ) = "ou=hosts,o=mycompany,c=de"; | |
248 | args.set( "layout", "How to arrange entries in the directory (simple or as tree)" ) = "simple"; | |
249 | args.set( "domainid", "Domain ID of the first domain found (incremented afterwards)" ) = "1"; | |
250 | args.set( "metadata-dn", "DN under which to store the domain metadata" ) = ""; | |
251 | args.set( "max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0"; | |
252 | ||
253 | args.parse( argc, argv ); | |
254 | ||
255 | if(args.mustDo("version")) { | |
256 | cerr<<"zone2ldap "<<VERSION<<endl; | |
257 | exit(0); | |
258 | } | |
259 | ||
260 | if( args.mustDo( "help" ) ) | |
261 | { | |
262 | cout << "Syntax:" << endl << endl; | |
263 | cout << args.helpstring() << endl; | |
264 | exit( 0 ); | |
265 | } | |
266 | ||
267 | if( argc < 2 ) | |
268 | { | |
269 | cerr << "Syntax:" << endl << endl; | |
270 | cerr << args.helpstring() << endl; | |
271 | exit( 1 ); | |
272 | } | |
273 | ||
274 | g_basedn = args["basedn"]; | |
275 | g_dnsttl = args.mustDo( "dnsttl" ); | |
276 | typedef boost::function<void(unsigned int, const DNSName &, const string &, const string &, int)> callback_t; | |
277 | callback_t callback = callback_simple; | |
278 | if( args["layout"] == "tree" ) | |
279 | { | |
280 | callback=callback_tree; | |
281 | } | |
282 | ||
283 | if ( args.mustDo( "pdns-info" ) ) { | |
284 | g_pdnsinfo = true; | |
285 | if( args["metadata-dn"].empty() ) { | |
286 | cerr << "You must set --metadata-dn when using --pdns-info" << endl; | |
287 | exit( 1 ); | |
288 | } | |
289 | g_metadatadn = args["metadata-dn"]; | |
290 | } | |
291 | else { | |
292 | g_pdnsinfo = false; | |
293 | } | |
294 | ||
295 | if ( !args["domainid"].empty() ) | |
296 | g_domainid = pdns_stou( args["domainid"] ); | |
297 | else | |
298 | g_domainid = 1; | |
299 | ||
300 | if( !args["named-conf"].empty() ) | |
301 | { | |
302 | BP.setVerbose( args.mustDo( "verbose" ) ); | |
303 | BP.parse( args["named-conf"] ); | |
304 | // ZP.setDirectory( BP.getDirectory() ); | |
305 | const vector<BindDomainInfo> &domains = BP.getDomains(); | |
306 | ||
307 | for(const auto& i: domains) | |
308 | { | |
309 | if(i.type!="master" && i.type!="slave") { | |
310 | cerr<<" Warning! Skipping '"<<i.type<<"' zone '"<<i.name<<"'"<<endl; | |
311 | continue; | |
312 | } | |
313 | try | |
314 | { | |
315 | if( i.name != g_rootdnsname && i.name != DNSName("localhost") && i.name != DNSName("0.0.127.in-addr.arpa") ) | |
316 | { | |
317 | cerr << "Parsing file: " << i.filename << ", domain: " << i.name << endl; | |
318 | g_zonename = i.name; | |
319 | ZoneParserTNG zpt(i.filename, i.name, BP.getDirectory()); | |
320 | zpt.setMaxGenerateSteps(args.asNum("max-generate-steps")); | |
321 | DNSResourceRecord rr; | |
322 | while(zpt.get(rr)) { | |
323 | callback(g_domainid, rr.qname, rr.qtype.getName(), encode_non_ascii(rr.content), rr.ttl); | |
324 | if( rr.qtype == QType::SOA ) | |
325 | ++g_domainid; | |
326 | } | |
327 | } | |
328 | } | |
329 | catch( PDNSException &ae ) | |
330 | { | |
331 | cerr << "Fatal error: " << ae.reason << endl; | |
332 | if( !args.mustDo( "resume" ) ) | |
333 | { | |
334 | return 1; | |
335 | } | |
336 | } | |
337 | } | |
338 | } | |
339 | else | |
340 | { | |
341 | if( args["zone-file"].empty() || args["zone-name"].empty() ) | |
342 | { | |
343 | cerr << "Error: At least zone-file and zone-name are required" << endl; | |
344 | return 1; | |
345 | } | |
346 | ||
347 | g_zonename = DNSName(args["zone-name"]); | |
348 | ZoneParserTNG zpt(args["zone-file"], g_zonename); | |
349 | zpt.setMaxGenerateSteps(args.asNum("max-generate-steps")); | |
350 | DNSResourceRecord rr; | |
351 | while(zpt.get(rr)) { | |
352 | callback(g_domainid, rr.qname, rr.qtype.getName(), encode_non_ascii(rr.content), rr.ttl); | |
353 | if ( rr.qtype == QType::SOA ) | |
354 | ++g_domainid; | |
355 | } | |
356 | } | |
357 | } | |
358 | catch( PDNSException &ae ) | |
359 | { | |
360 | cerr << "Fatal error: " << ae.reason << endl; | |
361 | return 1; | |
362 | } | |
363 | catch( std::exception &e ) | |
364 | { | |
365 | cerr << "Died because of STL error: " << e.what() << endl; | |
366 | return 1; | |
367 | } | |
368 | catch( ... ) | |
369 | { | |
370 | cerr << "Died because of unknown exception" << endl; | |
371 | return 1; | |
372 | } | |
373 | ||
374 | return 0; | |
375 | } |