]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/zone2ldap.cc
Merge pull request #14021 from Habbie/auth-lua-join-whitespace
[thirdparty/pdns.git] / pdns / zone2ldap.cc
CommitLineData
ad26538e 1/*
eefd15f9
BH
2 * PowerDNS BIND Zone to LDAP converter
3 * Copyright (C) 2003 Norbert Sendetzky
874300a8 4 * Copyright (C) 2007 bert hubert
eefd15f9
BH
5 *
6 * This program is free software; you can redistribute it and/or modify
22dc646a
BH
7 * it under the terms of the GNU General Public License version 2
8 * the Free Software Foundation
fc3c07b4
PL
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.
eefd15f9
BH
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
06bd9ccf 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
ad26538e
BH
22*/
23
24
870a0fe4
AT
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
ad26538e 28#include <map>
eefd15f9 29#include <string>
ad26538e 30#include <iostream>
521a08fc 31#include <sstream>
ad26538e 32#include <stdio.h>
ad26538e 33#include "arguments.hh"
7c0cb150 34#include "bindparserclasses.hh"
ad26538e 35#include "statbag.hh"
ee0f881e 36#include "dnsrecords.hh"
ad26538e 37#include "misc.hh"
eefd15f9 38#include "dns.hh"
874300a8 39#include "zoneparser-tng.hh"
eefd15f9
BH
40
41using std::map;
42using std::string;
43using std::vector;
ad26538e 44
ad26538e
BH
45StatBag S;
46ArgvMap args;
eefd15f9 47bool g_dnsttl;
521a08fc
GO
48bool g_pdnsinfo;
49unsigned int g_domainid;
ad26538e 50string g_basedn;
521a08fc 51string g_metadatadn;
9a6abfe4
KM
52DNSName g_zonename;
53map<DNSName,bool> g_objects;
521a08fc
GO
54map<string, bool> g_entries;
55map<DNSName,bool> g_recorddata;
56map<DNSName, map<string, bool> > g_recordttl;
57
050e6877 58static std::string encode_non_ascii( const std::string &input ) {
521a08fc
GO
59 std::ostringstream out;
60
61 for ( auto i : input ) {
62 if ( (unsigned char)i > 0x7F )
63 out << '\\' << int( (unsigned char)i );
64 else
65 out << i;
66 }
67
68 return out.str();
69}
ad26538e 70
9a6abfe4 71static void callback_simple( unsigned int domain_id, const DNSName &domain, const string &qtype, const string &content, int ttl )
ad26538e 72{
9a6abfe4 73 DNSName host;
4957a608 74
9a6abfe4 75 if( ! domain.isPartOf(g_zonename) )
4957a608 76 {
6d9c6a5b 77 cerr << "Domain '" << domain << "'' not part of '" << g_zonename << "'"<< endl;
232f0877 78 return;
4957a608
BH
79 }
80
9a6abfe4 81 host = domain.makeRelative(g_zonename);
4957a608 82
521a08fc
GO
83 if( g_pdnsinfo && qtype == "SOA" ) {
84 cout << "dn: ou=" << domain << "," << g_metadatadn << endl;
85 cout << "changetype: add" << endl;
86 cout << "objectclass: organizationalUnit" << endl;
87 cout << "ou: " << domain.toStringNoDot() << endl;
88 cout << endl;
89 }
90
91 std::string stripped=stripDot(content);
92 std::string rrvalue = stripped + ((stripped.empty() || stripped[stripped.size()-1]==' ') ? "." : "");
93 std::string dn = "dc=";
94 if( host.countLabels() ) { dn += host.toStringNoDot() + ",dc="; }
95 dn += g_zonename.toStringNoDot() + "," + g_basedn;
96 cout << "dn: " << dn << endl;
4957a608 97
9a6abfe4 98 if( host.countLabels() == 0 ) { host = g_zonename; }
4957a608 99
521a08fc 100 if( !g_entries[dn] )
4957a608 101 {
521a08fc
GO
102 g_entries[dn] = true;
103 g_recorddata[domain] = true;
232f0877
CH
104
105 cout << "changetype: add" << endl;
106 cout << "objectclass: dnsdomain2" << endl;
107 cout << "objectclass: domainrelatedobject" << endl;
521a08fc
GO
108 cout << "objectclass: PdnsRecordData" << endl;
109 if( g_pdnsinfo && qtype == "SOA" ) {
110 cout << "objectclass: PdnsDomain" << endl;
111 cout << "PdnsDomainId: " << domain_id << endl;
112 }
9a6abfe4 113 cout << "dc: " << host.toStringNoDot() << endl;
232f0877 114 if( g_dnsttl ) { cout << "dnsttl: " << ttl << endl; }
9a6abfe4 115 cout << "associateddomain: " << domain.toStringNoDot() << endl;
4957a608
BH
116 }
117 else
118 {
232f0877 119 cout << "changetype: modify" << endl;
521a08fc
GO
120 if ( !g_recorddata[domain] ) {
121 g_recorddata[domain] = true;
122 cout << "add: objectClass" << endl;
123 cout << "objectClass: PdnsRecordData" << endl;
124 cout << "-" << endl;
125 }
126 if ( !g_recordttl.count( domain ) || !g_recordttl[domain].count( qtype ) ) {
127 g_recordttl[domain][qtype] = true;
128 cout << "add: PdnsRecordTTL" << endl;
129 cout << "PdnsRecordTTL: " << qtype << "|" << ttl << endl;
130 cout << "-" << endl;
131 }
232f0877 132 cout << "add: " << qtype << "Record" << endl;
4957a608 133 }
521a08fc 134 cout << qtype << "Record: " << rrvalue << endl << endl;
e1d03bb0
BH
135}
136
137
eefd15f9 138
9a6abfe4 139static void callback_tree( unsigned int domain_id, const DNSName &domain, const string &qtype, const string &content, int ttl )
e1d03bb0 140{
4957a608 141 unsigned int i;
9a6abfe4
KM
142 string dn;
143 DNSName net;
4957a608 144 vector<string> parts;
4957a608 145
9a6abfe4 146 stringtok( parts, domain.toStringNoDot(), "." );
4957a608
BH
147 if( parts.empty() ) { return; }
148
149 for( i = parts.size() - 1; i > 0; i-- )
150 {
9a6abfe4 151 net.prependRawLabel(parts[i]);
232f0877
CH
152 dn = "dc=" + parts[i] + "," + dn;
153
154 if( !g_objects[net] )
155 {
156 g_objects[net] = true;
157
158 cout << "dn: " << dn << g_basedn << endl;
159 cout << "changetype: add" << endl;
160 cout << "objectclass: dnsdomain2" << endl;
161 cout << "objectclass: domainrelatedobject" << endl;
162 cout << "dc: " << parts[i] << endl;
9a6abfe4 163 cout << "associateddomain: " << net.toStringNoDot() << endl << endl;
232f0877
CH
164 }
165
4957a608
BH
166 }
167
521a08fc
GO
168 if( g_pdnsinfo && qtype == "SOA" ) {
169 cout << "dn: ou=" << domain << "," << g_metadatadn << endl;
170 cout << "changetype: add" << endl;
171 cout << "objectclass: organizationalUnit" << endl;
172 cout << "ou: " << domain.toStringNoDot() << endl;
173 cout << endl;
174 }
175
176 std::string stripped=stripDot(content);
177 std::string rrvalue = stripped + ((stripped.empty() || stripped[stripped.size()-1]==' ') ? "." : "");
4957a608
BH
178 cout << "dn: " << "dc=" << parts[0] << "," << dn << g_basedn << endl;
179
9a6abfe4 180 if( !g_objects[domain] )
4957a608 181 {
9a6abfe4 182 g_objects[domain] = true;
521a08fc 183 g_recorddata[domain] = true;
232f0877
CH
184
185 cout << "changetype: add" << endl;
186 cout << "objectclass: dnsdomain2" << endl;
187 cout << "objectclass: domainrelatedobject" << endl;
521a08fc
GO
188 cout << "objectclass: PdnsRecordData" << endl;
189 if( g_pdnsinfo && qtype == "SOA" ) {
190 cout << "objectclass: PdnsDomain" << endl;
191 cout << "PdnsDomainId: " << domain_id << endl;
192 }
232f0877
CH
193 cout << "dc: " << parts[0] << endl;
194 if( g_dnsttl ) { cout << "dnsttl: " << ttl << endl; }
9a6abfe4 195 cout << "associateddomain: " << domain.toStringNoDot() << endl;
4957a608
BH
196 }
197 else
198 {
232f0877 199 cout << "changetype: modify" << endl;
521a08fc
GO
200 if( g_pdnsinfo && qtype == "SOA" ) {
201 cout << "add: objectclass" << endl;
202 cout << "objectclass: PdnsDomain" << endl;
203 cout << "-" << endl;
204 cout << "add: PdnsDomainId" << endl;
205 cout << "PdnsDomainId: " << domain_id << endl;
206 cout << "-" << endl;
207 }
208 if ( !g_recorddata[domain] ) {
209 g_recorddata[domain] = true;
210 cout << "add: objectClass" << endl;
211 cout << "objectClass: PdnsRecordData" << endl;
212 cout << "-" << endl;
213 }
214 if ( !g_recordttl.count( domain ) || !g_recordttl[domain].count( qtype ) ) {
215 g_recordttl[domain][qtype] = true;
216 cout << "add: PdnsRecordTTL" << endl;
217 cout << "PdnsRecordTTL: " << qtype << "|" << ttl << endl;
218 cout << "-" << endl;
219 }
232f0877 220 cout << "add: " << qtype << "Record" << endl;
4957a608 221 }
521a08fc 222 cout << qtype << "Record: " << rrvalue << endl << endl;
ad26538e
BH
223}
224
225
eefd15f9 226
ad26538e
BH
227int main( int argc, char* argv[] )
228{
4957a608
BH
229 BindParser BP;
230 vector<string> parts;
ad26538e
BH
231
232
4957a608
BH
233 try
234 {
232f0877 235 std::ios_base::sync_with_stdio( false );
232f0877
CH
236 reportAllTypes();
237 args.setCmd( "help", "Provide a helpful message" );
cf57329d 238 args.setCmd( "version", "Print the version" );
232f0877
CH
239 args.setSwitch( "verbose", "Verbose comments on operation" ) = "no";
240 args.setSwitch( "resume", "Continue after errors" ) = "no";
241 args.setSwitch( "dnsttl", "Add dnsttl attribute to every entry" ) = "no";
521a08fc 242 args.setSwitch( "pdns-info", "Add the PDNS domain info attributes (this mandates setting --metadata-dn)" ) = "no";
232f0877
CH
243 args.set( "named-conf", "Bind 8 named.conf to parse" ) = "";
244 args.set( "zone-file", "Zone file to parse" ) = "";
245 args.set( "zone-name", "Specify a zone name if zone is set" ) = "";
246 args.set( "basedn", "Base DN to store objects below" ) = "ou=hosts,o=mycompany,c=de";
247 args.set( "layout", "How to arrange entries in the directory (simple or as tree)" ) = "simple";
521a08fc
GO
248 args.set( "domainid", "Domain ID of the first domain found (incremented afterwards)" ) = "1";
249 args.set( "metadata-dn", "DN under which to store the domain metadata" ) = "";
ba3d53d1 250 args.set( "max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0";
e3fc3ebd 251 args.set( "max-include-depth", "Maximum nested $INCLUDE depth when loading a zone from a file")="20";
232f0877
CH
252
253 args.parse( argc, argv );
254
cf57329d
PL
255 if(args.mustDo("version")) {
256 cerr<<"zone2ldap "<<VERSION<<endl;
257 exit(0);
258 }
259
ff5ba4f9
WA
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 )
232f0877
CH
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" );
f4319769 276 typedef std::function<void(unsigned int, const DNSName &, const string &, const string &, int)> callback_t;
232f0877
CH
277 callback_t callback = callback_simple;
278 if( args["layout"] == "tree" )
279 {
280 callback=callback_tree;
281 }
282
521a08fc
GO
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() )
a0383aad 296 pdns::checked_stoi_into(g_domainid, args["domainid"]);
521a08fc
GO
297 else
298 g_domainid = 1;
299
232f0877
CH
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
cb167afd 307 for(const auto& i: domains)
232f0877 308 {
e63e16c1
KM
309 if (i.type != "primary" && i.type != "secondary" && !i.type.empty() && i.type != "master" && i.type != "slave") {
310 cerr << " Warning! Skipping '" << i.type << "' zone '" << i.name << "'" << endl;
311 continue;
312 }
232f0877
CH
313 try
314 {
cb167afd 315 if( i.name != g_rootdnsname && i.name != DNSName("localhost") && i.name != DNSName("0.0.127.in-addr.arpa") )
232f0877 316 {
cb167afd
CHB
317 cerr << "Parsing file: " << i.filename << ", domain: " << i.name << endl;
318 g_zonename = i.name;
319 ZoneParserTNG zpt(i.filename, i.name, BP.getDirectory());
ba3d53d1 320 zpt.setMaxGenerateSteps(args.asNum("max-generate-steps"));
e3fc3ebd 321 zpt.setMaxIncludes(args.asNum("max-include-depth"));
232f0877 322 DNSResourceRecord rr;
521a08fc 323 while(zpt.get(rr)) {
d5fcd583 324 callback(g_domainid, rr.qname, rr.qtype.toString(), encode_non_ascii(rr.content), rr.ttl);
521a08fc
GO
325 if( rr.qtype == QType::SOA )
326 ++g_domainid;
327 }
232f0877
CH
328 }
329 }
330 catch( PDNSException &ae )
331 {
332 cerr << "Fatal error: " << ae.reason << endl;
333 if( !args.mustDo( "resume" ) )
334 {
335 return 1;
336 }
337 }
338 }
339 }
340 else
341 {
342 if( args["zone-file"].empty() || args["zone-name"].empty() )
343 {
344 cerr << "Error: At least zone-file and zone-name are required" << endl;
345 return 1;
346 }
347
b873e23a 348 g_zonename = DNSName(args["zone-name"]);
349 ZoneParserTNG zpt(args["zone-file"], g_zonename);
ba3d53d1 350 zpt.setMaxGenerateSteps(args.asNum("max-generate-steps"));
232f0877 351 DNSResourceRecord rr;
521a08fc 352 while(zpt.get(rr)) {
d5fcd583 353 callback(g_domainid, rr.qname, rr.qtype.toString(), encode_non_ascii(rr.content), rr.ttl);
521a08fc
GO
354 if ( rr.qtype == QType::SOA )
355 ++g_domainid;
356 }
232f0877 357 }
4957a608 358 }
3f81d239 359 catch( PDNSException &ae )
4957a608 360 {
232f0877
CH
361 cerr << "Fatal error: " << ae.reason << endl;
362 return 1;
4957a608
BH
363 }
364 catch( std::exception &e )
365 {
232f0877
CH
366 cerr << "Died because of STL error: " << e.what() << endl;
367 return 1;
4957a608
BH
368 }
369 catch( ... )
370 {
232f0877
CH
371 cerr << "Died because of unknown exception" << endl;
372 return 1;
4957a608
BH
373 }
374
375 return 0;
ad26538e 376}