1 <?php
4 /* All available record types
5 */
6 $RecordTypes['aRecord'] = "aRecord";
7 $RecordTypes['mDRecord'] = "mDRecord";
8 $RecordTypes['mXRecord'] = "mXRecord";
9 $RecordTypes['nSRecord'] = "nSRecord";
10 $RecordTypes['pTRRecord'] = "relativeDomainName";
11 $RecordTypes['hInfoRecord'] = "hInfoRecord";
12 $RecordTypes['mInfoRecord'] = "mInfoRecord";
13 $RecordTypes['cNAMERecord'] = "relativeDomainName";
14 $RecordTypes['tXTRecord'] = "tXTRecord";
15 $RecordTypes['aFSDBRecord'] = "aFSDBRecord";
16 $RecordTypes['SigRecord'] = "SigRecord";
17 $RecordTypes['KeyRecord'] = "KeyRecord";
18 $RecordTypes['aAAARecord'] = "aAAARecord";
19 $RecordTypes['LocRecord'] = "LocRecord";
20 $RecordTypes['nXTRecord'] = "nXTRecord";
21 $RecordTypes['sRVRecord'] = "sRVRecord";
22 $RecordTypes['nAPTRRecord'] = "nAPTRRecord";
23 $RecordTypes['kXRecord'] = "kXRecord";
24 $RecordTypes['certRecord'] = "certRecord";
25 $RecordTypes['a6Record'] = "a6Record";
26 $RecordTypes['dSRecord'] = "dSRecord";
27 $RecordTypes['sSHFPRecord'] = "sSHFPRecord";
28 $RecordTypes['rRSIGRecord'] = "rRSIGRecord";
29 $RecordTypes['nSECRecord'] = "nSECRecord";
32 /* Return all record types
33 */
34 function getDnsRecordTypes($ForZones = false)
35 {
36 global $RecordTypes;
37 if($ForZones){
38 $tmp = $RecordTypes;
39 unset($tmp['cNAMERecord']);
40 unset($tmp['pTRRecord']);
41 unset($tmp['tXTRecord']);
42 return($tmp);
43 }else{
44 return($RecordTypes);
45 }
46 }
49 /* This fucntion is used to flip the ip address, for example
50 12.3.45 -> 45.3.12
51 Because some entries (like zones) are store like that 45.3.12.in-addr.arpa
52 but we want to display 12.3.45.
53 */
54 function FlipIp($ip)
55 {
56 $tmp = array_reverse(split("\.",$ip));
57 $new = "";
58 foreach($tmp as $section){
59 $new .= $section.".";
60 }
61 return(preg_replace("/.$/","",$new));
62 }
65 /* This function returns the zones specified for given host
66 */
67 function getDNSZoneEntries($config,$HostDn,$silent = false)
68 {
69 global $RecordTypes;
71 $ldap = $config->get_ldap_link();
72 $ldap->cd($config->current['BASE']);
74 /* Not all records are allowed within a zone entry
75 */
76 $SkipRecords = array("tXTRecord","cNAMERecord","pTRRecord");
78 /* Special sOArecords
79 */
80 $sOAREcords = array("0"=>"sOAprimary","1"=>"sOAmail","2"=>"sOAserial","3"=>"sOArefresh","4"=>"sOAretry","5"=>"sOAexpire","6"=>"sOAttl");
82 /* Create tempalte for all fetched zone Data
83 */
84 $ZoneBase = array();
85 $ZoneBase['exists'] = false;
86 $ZoneBase['RECORDS'] = array();
87 $ZoneBase['zoneName'] = array();
88 $ZoneBase['dNSClass'] = array();
90 foreach($sOAREcords as $attr){
91 $ZoneBase[$attr] = "";
92 }
94 $Zones = array();
96 /* Get & Parse all zone entries
97 */
98 $ldap->ls("(&(objectClass=dNSZone)(zoneName=*)(relativeDomainName=@))",$HostDn,array("*"));
99 $tmp_res = array();
100 while($attrs = $ldap->fetch()) {
101 $tmp_res[] = $attrs;
102 }
104 /* Parse fetched zones
105 */
106 foreach($tmp_res as $attrs){
108 $zoneName = $attrs['zoneName'][0];
109 $Zones[$zoneName] = $ZoneBase;
110 $Zones[$zoneName]['exists'] = true;
112 /* Set basic attributes
113 */
114 foreach(array("zoneName","dNSClass") as $attr){
115 if(isset($attrs[$attr][0])){
116 $Zones[$zoneName][$attr] = $attrs[$attr][0];
117 }
118 }
120 /* Set initial zone name, to be able to detect if this entry was renamed
121 */
122 $Zones[$zoneName]['InitialzoneName'] = $zoneName;
124 /* Generate SOA entry
125 */
126 if(isset($attrs['sOARecord'][0])){
127 $tmp = split("\ ",$attrs['sOARecord'][0]) ;
128 $tmp2 = array();
130 /* Assign soa vars */
131 foreach($sOAREcords as $key => $name){
132 if(isset($tmp[$key])){
133 $Zones[$zoneName][$name] = $tmp[$key];
134 }else{
135 $Zones[$zoneName][$name] = "";
136 }
137 }
138 } // ENDE SOA Record
140 /* Get record attributes
141 */
142 foreach($RecordTypes as $name => $value){
144 /* Skip some attributes
145 */
146 if(in_array($name,$SkipRecords)) continue;
148 /* If there is a record attribute
149 */
150 if(isset($attrs[$name])){
152 /* get all entries
153 */
154 for($i = 0 ; $i < $attrs[$value]['count']; $i ++){
155 $Zones[$zoneName]['RECORDS'][] = array("type"=>$name,"value"=>$attrs[$value][$i]);
156 }
157 }
158 }
160 /* Get reverse record ..
161 */
162 $ldap->ls("(&(objectClass=dNSZone)(relativeDomainName=@)(zoneName=*))",$attrs['dn'],array("zoneName"));
164 if($ldap->count() == 0){
165 if(!$silent){
166 print_red(sprintf(_("Can't find reverse zone for dns zone '%s'. Aborting parsing this zone."),$zoneName));
167 }
168 unset($Zones[$zoneName]);
169 }elseif($ldap->count()>1){
170 if(!$silent){
171 print_red(sprintf(_("Found more than one reverse zone for dns zone '%s'. Aborting parsing this zone."),$zoneName));
172 }
173 unset($Zones[$zoneName]);
174 }else{
175 $tmp = $ldap->fetch();
176 $Zones[$zoneName]['ReverseZone'] = FlipIp(str_replace(".in-addr.arpa","",$tmp['zoneName'][0]));
177 $Zones[$zoneName]['InitialReverseZone'] = FlipIp(str_replace(".in-addr.arpa","",$tmp['zoneName'][0]));
178 }
179 }
180 return($Zones);
181 }
184 /* This function compares two dns zone objects and returns an
185 * array with following indexes
186 * - delete, for dns which must be deleted (only if dns zone is removed)
187 * - rename, if a dn must be renamed, for example, the zoneName has changed
188 * - add, if there is a new dns account created
189 */
190 function getDNSZoneEntriesDiff($config,$newZones,$HostDn)
191 {
192 $oldZones = getDNSZoneEntries($config,$HostDn,true);
194 $sOAattributes = array("sOAprimary","sOAmail","sOAserial","sOArefresh","sOAretry","sOAexpire","sOAttl");
196 $move = array();
197 $add = array();
198 $del = array();
200 /* Generate a template for zones with default values
201 */
202 $zoneBase = array();
203 $zoneBase['objectClass'] = array("top","dNSZone");
204 $zoneBase['zoneName'] = "";
205 $zoneBase['relativeDomainName'] = "@";
206 $zoneBase['dNSClass'] = "IN";
207 $zoneBase['sOARecord'] = "";
209 /* Contains all renamed zoneNames
210 * For zone entry udpdates
211 */
212 $PrePareZoneEntries = array();
214 /* Walk through all zones and detect renamed/added/deleted zones ...
215 */
216 foreach($newZones as $name => $zone){
218 /* This zone was renamed
219 */
220 if((!empty($zone['InitialzoneName'])) && ($zone['InitialzoneName'] != $zone['zoneName'])){
222 /* Move old zone to new position
223 */
224 $oldDn = "zoneName=".$zone['InitialzoneName'].",".$HostDn;
225 $newDn = "zoneName=".$zone['zoneName'].",".$HostDn;
226 $PrePareZoneEntries[$zone['InitialzoneName']] = $zone['zoneName'];
227 $move [$oldDn] = $newDn;
228 }
230 /* Get old zone if available
231 */
232 $oldZone=array();
233 if(!empty($oldZones[$zone['InitialzoneName']])){
234 $oldZone = $oldZones[$zone['InitialzoneName']];
235 }
237 /* Create forward zone entry and put it in our add queue
238 */
239 $newDn = "zoneName=".$zone['zoneName'].",".$HostDn;
240 $obj = $zoneBase;
241 $obj['zoneName'] = $zone['zoneName'];
243 /* Create sOARecord & add it to the obj
244 */
245 $soa = "";
246 foreach($sOAattributes as $attr){
247 $soa.=" ".$zone[$attr];
248 }
249 $obj['sOARecord'] = trim($soa);
251 /* If reverse zone was renamed, move entry
252 */
253 if(!empty($zone['InitialReverseZone'])){
254 if($zone['InitialReverseZone'] != $zone['ReverseZone']){
255 $base = "zoneName=".$zone['zoneName'].",".$HostDn;
256 $oldRDn = "zoneName=". FlipIp($zone['InitialReverseZone']).".in-addr.arpa,".$base;
257 $newRDn = "zoneName=". FlipIp($zone['ReverseZone']).".in-addr.arpa,".$base;
258 $PrePareZoneEntries[FlipIp($zone['InitialReverseZone']).".in-addr.arpa"] = FlipIp($zone['ReverseZone']).".in-addr.arpa";
259 $move [$oldRDn] = $newRDn;
260 }
261 }
263 /* Append record entries
264 * Set old value to array, to ensure that
265 * they will be deleted if necessary
266 */
267 if(isset($oldZone['RECORDS'])){
268 foreach($oldZone['RECORDS'] as $rec){
269 $obj[$rec['type']] = array();
270 }
271 }
273 /* Add new Records
274 */
275 foreach($zone['RECORDS'] as $rec){
276 $obj[$rec['type']][] = $rec['value'];
277 }
279 /* Append udpated Zone Forward Entry to our add queue
280 */
281 $add[$newDn] = $obj;
283 /* Create Reverse Entry
284 * And append it to our add queue
285 */
286 $zone['ReverseZone'] = FlipIp($zone['ReverseZone']).".in-addr.arpa";
287 $base = "zoneName=".$zone['zoneName'].",".$HostDn;
288 $newRDn = "zoneName=".$zone['ReverseZone'].",".$base;
289 $rObj = $obj;
290 $rObj['zoneName']= $zone['ReverseZone'];
291 $add[$newRDn] = $rObj;
293 /* Remove currently managed zone from oldZones.
294 * this gives us the ability to detect removed zones
295 */
296 if(isset($oldZones[$zone['InitialzoneName']])){
297 unset($oldZones[$zone['InitialzoneName']]);
298 }
299 }
301 /* The rest of our oldZones must be deleted
302 * because they are no longer available in newZones anymore.
303 */
304 foreach($oldZones as $zone) {
305 $oldDn = "zoneName=".$zone['InitialzoneName'].",".$HostDn;
306 $del[$oldDn] = "";
307 }
309 /* Check for entries which must be updated
310 */
311 $zoneUpdates = array();
312 $udpate = array();
313 if(count($PrePareZoneEntries)){
314 $ldap = $config->get_ldap_link();
315 foreach($PrePareZoneEntries as $FromZoneName => $ToZoneName){
316 $ldap->cd($HostDn);
317 $ldap->search("(&(objectClass=dNSZone)(zoneName=".$FromZoneName.")(!(relativeDomainName=@)))",array("zoneName"));
318 while($attrs = $ldap->fetch()){
319 $zoneUpdates[$attrs['dn']] = array("zoneName"=>$ToZoneName);
320 }
321 }
322 }
324 $ret = array("del" => $del , "move" => $move , "add" => $add,"zoneUpdates"=>$zoneUpdates);
325 return($ret);
326 }
329 /* This function returns the dns-host eintries for given
330 * name.
331 */
332 function getDNSHostEntries($config,$name,$silent = false)
333 {
334 global $RecordTypes;
336 $types = array();
337 $ret = array();
338 $ret['RECORDS'] = array();
339 $ret['dNSClass'] = "IN";
340 $ret['zoneName'] = "";
341 $ret['dNSTTL'] = "7440";
342 $ret['exists'] = false;
344 $ldap = $config->get_ldap_link();
345 $ldap->cd($config->current['BASE']);
347 /* First check all zones for an entry with the given name.
348 * If the name occurs in more than one entry alert the user ...
349 */
350 $foundIn = array();
351 $zones = getAvailableZones($config);
353 $zonesArr = array();
354 foreach($zones as $zoneMix){
355 $zoneIndex = split("/",$zoneMix);
356 if(!array_key_exists($zoneIndex[0],$zonesArr)) {
357 $zonesArr[$zoneIndex[0]] = array();
358 }
359 array_push($zonesArr[$zoneIndex[0]],$zoneIndex[1]);
360 }
362 foreach($zonesArr as $nameServer => $nameServerArr){
363 $foundInTmp = array();
364 foreach($nameServerArr as $zoneArr => $zone){
365 $zoneMix = $nameServer."/".$zone;
366 $zoneDn = getDNSZoneDN($config,$zoneMix);
367 $ldap->ls("(&(objectClass=dNSZone)(zoneName=*)(relativeDomainName=".$name.")(!(relativeDomainName=@)))", $zoneDn,$attrs = array("*"));
368 while($attrs = $ldap->fetch()){
369 $foundInTmp [$zoneMix] = $attrs['dn'];
370 $foundIn [$zoneMix] = $attrs['dn'];
371 }
372 }
373 }
375 /* No zone found which contains an entry for us
376 */
377 if(count($foundIn) == 0){
378 return($ret);
379 }
381 /* Get host informations from zone
382 */
383 $id_tmp = key($foundIn);
384 $ldap->cd($foundIn[$id_tmp]);
385 $ldap->search("(&(objectClass=dNSZone)(zoneName=*)(!(relativeDomainName=@)))",array("*"));
386 while($attrs = $ldap->fetch()){
388 /* If relative domainname == cn
389 * Try to read dnsclass / TTl / zone
390 */
391 if($attrs['relativeDomainName'][0] == $name){
392 $ret['exists'] = true;
393 $ret['zoneName'] = $id_tmp;
394 foreach(array("dNSClass","dNSTTL") as $atr){
395 if(isset($attrs[$atr][0])){
396 $ret[$atr] = $attrs[$atr][0];
397 }
398 }
399 }
401 /* Create list with all used records */
402 foreach($RecordTypes as $name => $value){
404 /* If there is a record attribute */
405 if(isset($attrs[$name])){
407 /* get all entries */
408 for($i = 0 ; $i < $attrs[$value]['count']; $i ++){
409 $types[] = array( "type" => $name,
410 "value" => $attrs[$value][$i]);
411 }
412 }
413 }
414 $ret['RECORDS'] = $types;
415 }
416 return($ret);
417 }
421 /* This function compares two dns settings and returns an
422 * array with following indexes
423 * - delete, for dns which must be deleted (only if dns account is removed)
424 * - rename, if a dn must be renamed, for example, the relativeDomainName has changed
425 * - add, if there is a new dns account created
426 */
427 function getDNSHostEntriesDiff($config,$oldName,$newEntry,$newName)
428 {
429 global $RecordTypes;
431 $oldEntry = getDNSHostEntries($config,$oldName);
433 $add = array();
434 $del = array();
435 $move = array();
437 $zones = getAvailableZones($config);
438 $specialAttributes = array("cNAMERecord","pTRRecord");
439 $newRecords = array(); // Used to remember which records are removed
440 $zoneNameMix = $newEntry['zoneName'];
441 $zoneDn = getDNSZoneDN($config,$zoneNameMix);
442 $tmp = array_flip($zones);
443 $zoneName = getNameFromMix($zoneNameMix);
445 /* If reverseZone can't be resolved ... this
446 * can't be a valid entry, so remove this account
447 */
448 if(isset($tmp[$zoneNameMix])){
449 $reverseNameMix = $tmp[$zoneNameMix];
450 $reverseDn = getDNSZoneDN($config,$reverseNameMix);
451 if(empty($reverseDn)){
452 $newEntry['exists'] = false;
453 }
454 }else{
455 $newEntry['exists'] = false;
456 }
458 /* Don't go further if there is nothing to do
459 * Is no account / was no account
460 */
461 if(($newEntry['exists'] == false )&& ($oldEntry['exists'] == false)){
462 return(array("move"=>$move,"add"=>$add,"del"=>$del));
463 }
465 /* If account was edited prepare some
466 * attributes & arrays ... if required add some
467 * dns to $move
468 */
469 if($oldEntry['exists']){
471 /* Check if the account was removed
472 */
473 if($newEntry['exists'] == false){
474 $dn = "relativeDomainName=".$oldName.",".getDNSZoneDN($config,$oldEntry['zoneName']);
475 $del[$dn] ="";
476 return(array("move"=>$move,"add"=>$add,"del"=>$del));
477 }
479 /* Check if zoneName has changed
480 */
481 if(count($newEntry['RECORDS'])){
482 if($oldEntry['zoneName'] != $newEntry['zoneName']){
483 $oldzoneDn = getDNSZoneDN($config,$oldEntry['zoneName']);
484 $dn = "relativeDomainName=".$oldName.",".$oldzoneDn;
485 $dn2= "relativeDomainName=".$oldName.",".$zoneDn;
486 $move[$dn]=$dn2;
487 }
489 /* Check if host name has changed
490 */
491 if($oldName != $newName){
492 $dn = "relativeDomainName=".$oldName.",".$zoneDn;
493 $dn2= "relativeDomainName=".$newName.",".$zoneDn;
494 $move[$dn]=$dn2;
495 $dn = "relativeDomainName=".$oldName.",".$dn2;
496 $dn2= "relativeDomainName=".$newName.",".$dn2;
497 $move[$dn]=$dn2;
498 }
499 }
501 /* Prepare record entries
502 * Fill old records with array();
503 * To ensure that they will be deleted if they stay unused
504 */
505 foreach($oldEntry['RECORDS'] as $id => $rec){
506 $newRecords[$rec['type']] = array();
507 }
508 }
510 /* There must be at least one record in our entry
511 */
512 if((!count($newEntry['RECORDS'])) || (!$newEntry['exists'])){
513 $dn = "relativeDomainName=".$newName.",".getDNSZoneDN($config,$oldEntry['zoneName']);
514 $del[$dn] ="";
515 $ret = array("move"=>$move,"add"=>$add,"del"=>$del);
516 return($ret);
517 }
519 /* Prepare temp obj
520 */
521 $baseObj = array();
522 $baseObj['objectClass'] = array("top","dNSZone");
523 $baseObj['dNSTTL'] = $newEntry['dNSTTL'];
524 $baseObj['dNSClass'] = $newEntry['dNSClass'];
525 $baseObj['zoneName'] = $zoneName;
526 $baseObj['relativeDomainName']= $newName;
528 /* Add Container Object to zone
529 * (this possibly already exists, check this before writing to ldap)
530 */
531 $baseDn = "relativeDomainName=".$newName.",".$zoneDn;
532 $add[$baseDn] = $baseObj;
534 /* Add base obejct which contains all std records
535 */
536 $stdDn = "relativeDomainName=".$newName.",".$baseDn;
537 $add[$stdDn] = $baseObj;
539 /* Set defaults. Normaly only contains old record names.
540 * The old names will be set to array, to ensure that they will be deleted.
541 * Or overwritten and filled with new values.
542 */
543 foreach($newRecords as $name => $def){
544 if(!in_array($name,$specialAttributes)){
545 $add[$stdDn][$name] = $def;
546 }
547 }
549 /* Delete all OLD special attributes.
550 */
551 foreach($oldEntry['RECORDS'] as $id => $rec){
552 if(in_array($rec['type'],$specialAttributes)){
553 $deldn= "relativeDomainName=".$rec['value'].",".$baseDn;
554 $del[$deldn] = "";
555 }
556 }
559 /* Create new record entries
560 */
561 foreach($newEntry['RECORDS'] as $id => $rec){
562 /* Create object which contains special records
563 * like pTRRecord or CNAMERecord
564 */
565 if($rec['type'] == "pTRRecord"){
566 $PTRdn= "relativeDomainName=".$rec['value'].",".$baseDn;
567 $ptrObj = $baseObj;
568 $reverseName = getNameFromMix($reverseNameMix);
569 $ptrObj['zoneName'] = $reverseName;
570 $ptrObj['pTRRecord'] = $newName.".".$zoneName.".";
571 $ptrObj['relativeDomainName'] = $rec['value'];
573 $add[$PTRdn] = $ptrObj;
574 }else
575 if($rec['type'] == "cNAMERecord"){
576 $PTRdn= "relativeDomainName=".$rec['value'].",".$baseDn;
577 $ptrObj = $baseObj;
578 $ptrObj['zoneName'] = $zoneName;
579 $ptrObj['cNAMERecord'] = $newName;
580 $ptrObj['relativeDomainName'] = $rec['value'];
582 $add[$PTRdn] = $ptrObj;
583 }else{
584 /* Append basic attributes
585 */
586 $add[$stdDn][$rec['type']][] = $rec['value'];
587 }
588 } // foreach record
590 $ret = array("move"=>$move,"add"=>$add,"del"=>$del);
591 return($ret);
592 }
594 function getNameFromMix($zoneMix){
595 $ret = "";
596 if(!strstr($zoneMix, '/')) return($ret);
597 $zoneIndex = split("/",$zoneMix);
598 return($zoneIndex[1]);
599 }
601 /* returns the dn for a specified zone
602 */
603 function getDNSZoneDN($config,$zoneNameMix)
604 {
605 $ret = "";
606 if(!strstr($zoneNameMix, '/')) {
607 print_red(sprintf(_("Undefined zone name '%s'. Zone name must look like this 'server/zone.com'."),$zoneNameMix));
608 return($ret);
609 }
611 $zoneNameIndex = split("/",$zoneNameMix);
612 $zoneName = $zoneNameIndex[1];
613 $nameServer = strtolower($zoneNameIndex[0]);
614 $ldap = $config->get_ldap_link();
616 /* search for the nameserver */
617 $ldap-> cd($config->current['BASE']);
618 $ldap->search("(&(objectClass=goServer)(cn=".$nameServer."))",array("cn"));
619 if($ldap->count()){
620 $attr = $ldap->fetch();
621 } else {
622 return($ret);
623 }
625 $ldap-> cd($attr['dn']);
626 $ldap->search("(&(objectClass=dNSZone)(sOARecord=*)(zoneName=".$zoneName."))",array("zoneName"));
627 if($ldap->count()){
628 $attr = $ldap->fetch();
629 return($attr['dn']);
630 }
632 return($ret);
633 }
636 /* returns all available zones
637 * array[reverseName] = zoneName;
638 */
639 function getAvailableZones($config)
640 {
641 $ret = array();
642 $ldap = $config->get_ldap_link();
643 $ldap->cd ($config->current['BASE']);
645 /* Search for zones ...
646 */
647 $ldap->search("(&(objectClass=dNSZone)(sOARecord=*))",array("zoneName"));
649 $ForwardZones = array();
650 $ReverseZones = array();
651 $zones = array();
653 while($at = $ldap->fetch()){
654 if(preg_match("/\.in\-addr\.arpa/",$at['zoneName'][0])){
655 $ReverseZones[$at['dn']] = $at;
656 }else{
657 $ForwardZones[$at['dn']] = $at;
658 }
659 }
661 foreach($ForwardZones as $dn => $obj){
663 /* try to find reverse
664 */
665 foreach($ReverseZones as $Rdn => $Robj ){
666 if(preg_match("/".$dn."/",$Rdn)){
667 $zones[strtoupper($ldap->getCn($dn))."/".$Robj['zoneName'][0]] =
668 strtoupper($ldap->getCn($dn))."/".$obj['zoneName'][0];
669 }
670 }
671 }
672 return($zones);
673 }
675 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
676 ?>