]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/zone2ldap.cc
rec: ensure correct service user on debian
[thirdparty/pdns.git] / pdns / zone2ldap.cc
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 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
252 args.parse( argc, argv );
253
254 if(args.mustDo("version")) {
255 cerr<<"zone2ldap "<<VERSION<<endl;
256 exit(0);
257 }
258
259 if( args.mustDo( "help" ) )
260 {
261 cout << "Syntax:" << endl << endl;
262 cout << args.helpstring() << endl;
263 exit( 0 );
264 }
265
266 if( argc < 2 )
267 {
268 cerr << "Syntax:" << endl << endl;
269 cerr << args.helpstring() << endl;
270 exit( 1 );
271 }
272
273 g_basedn = args["basedn"];
274 g_dnsttl = args.mustDo( "dnsttl" );
275 typedef boost::function<void(unsigned int, const DNSName &, const string &, const string &, int)> callback_t;
276 callback_t callback = callback_simple;
277 if( args["layout"] == "tree" )
278 {
279 callback=callback_tree;
280 }
281
282 if ( args.mustDo( "pdns-info" ) ) {
283 g_pdnsinfo = true;
284 if( args["metadata-dn"].empty() ) {
285 cerr << "You must set --metadata-dn when using --pdns-info" << endl;
286 exit( 1 );
287 }
288 g_metadatadn = args["metadata-dn"];
289 }
290 else {
291 g_pdnsinfo = false;
292 }
293
294 if ( !args["domainid"].empty() )
295 g_domainid = pdns_stou( args["domainid"] );
296 else
297 g_domainid = 1;
298
299 if( !args["named-conf"].empty() )
300 {
301 BP.setVerbose( args.mustDo( "verbose" ) );
302 BP.parse( args["named-conf"] );
303 // ZP.setDirectory( BP.getDirectory() );
304 const vector<BindDomainInfo> &domains = BP.getDomains();
305
306 for(const auto& i: domains)
307 {
308 if(i.type!="master" && i.type!="slave") {
309 cerr<<" Warning! Skipping '"<<i.type<<"' zone '"<<i.name<<"'"<<endl;
310 continue;
311 }
312 try
313 {
314 if( i.name != g_rootdnsname && i.name != DNSName("localhost") && i.name != DNSName("0.0.127.in-addr.arpa") )
315 {
316 cerr << "Parsing file: " << i.filename << ", domain: " << i.name << endl;
317 g_zonename = i.name;
318 ZoneParserTNG zpt(i.filename, i.name, BP.getDirectory());
319 DNSResourceRecord rr;
320 while(zpt.get(rr)) {
321 callback(g_domainid, rr.qname, rr.qtype.getName(), encode_non_ascii(rr.content), rr.ttl);
322 if( rr.qtype == QType::SOA )
323 ++g_domainid;
324 }
325 }
326 }
327 catch( PDNSException &ae )
328 {
329 cerr << "Fatal error: " << ae.reason << endl;
330 if( !args.mustDo( "resume" ) )
331 {
332 return 1;
333 }
334 }
335 }
336 }
337 else
338 {
339 if( args["zone-file"].empty() || args["zone-name"].empty() )
340 {
341 cerr << "Error: At least zone-file and zone-name are required" << endl;
342 return 1;
343 }
344
345 g_zonename = DNSName(args["zone-name"]);
346 ZoneParserTNG zpt(args["zone-file"], g_zonename);
347 DNSResourceRecord rr;
348 while(zpt.get(rr)) {
349 callback(g_domainid, rr.qname, rr.qtype.getName(), encode_non_ascii(rr.content), rr.ttl);
350 if ( rr.qtype == QType::SOA )
351 ++g_domainid;
352 }
353 }
354 }
355 catch( PDNSException &ae )
356 {
357 cerr << "Fatal error: " << ae.reason << endl;
358 return 1;
359 }
360 catch( std::exception &e )
361 {
362 cerr << "Died because of STL error: " << e.what() << endl;
363 return 1;
364 }
365 catch( ... )
366 {
367 cerr << "Died because of unknown exception" << endl;
368 return 1;
369 }
370
371 return 0;
372 }