Code

Fixed some dns problems.
[gosa.git] / include / functions_dns.inc
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;
70  
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();
89    
90   foreach($sOAREcords as $attr){
91     $ZoneBase[$attr] = "";
92   }
93  
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 
139   
140     /* Get record attributes 
141     */
142     foreach($RecordTypes as $name => $value){
143   
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);
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)
192   $oldZones = getDNSZoneEntries($config,$HostDn,true);
193   
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();
213   
214   /* Walk through all zones and detect renamed/added/deleted zones ... 
215    */
216   foreach($newZones as $name => $zone){
217     
218     /* This zone was renamed 
219      */
220     if((!empty($zone['InitialzoneName'])) && ($zone['InitialzoneName'] != $zone['zoneName'])){
221       
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'];
242  
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;
292  
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   }
300  
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);
329 /* This function returns the dns-host eintries for given 
330  *  name.
331  */
332 function getDNSHostEntries($config,$name,$silent = false)
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;
343  
344   $ldap = $config->get_ldap_link();
345   $ldap->cd($config->current['BASE']);
346   
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);
352   
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   }
361   
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)
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);
444  
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   }
464   
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; 
527     
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'];
572     
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'];
581       
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]);
601 /* returns the dn for a specified zone
602  */
603 function getDNSZoneDN($config,$zoneNameMix)
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   }
624   
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   }
631   
632   return($ret);
636 /* returns all available zones 
637  *  array[reverseName] = zoneName;
638  */
639 function getAvailableZones($config)
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){
662     
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);
675 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
676 ?>