DNSの正引きZONEファイルを元にIPアドレス逆引きするPHPクラスを作ってみました。

# やりたい事

DNSサーバ(Bind)にはzoneごとの正引き用zoneファイルが沢山あって、それらを全部読み込んで指定したIPアドレスに合致したFQDNを全て表示したい。
なお、逆引き設定ファイルは無い前提。

# 目的

* 正引き設定の間違い探しをしたい。
* 一つのIPに複数の名前が設定されているケースを見つけたい。

# 使用言語等

PHPを使用して、専用のクラスを作成する。

# プログラムの処理概要

コンストラクタでは、指定したフォルダに置いてあるファイルを全部読み込んで、$ORIGIN行のドメイン名と、その後に続くサブドメインを繋げたFQDNとそれに対応して設定されているIPアドレス全てのテーブルを構築する。
その際に、AレコードだけでなくCNAMEレコードも考慮して、CNAMEで設定されているAレコードを辿ってIPアドレスを引き出してテーブルに格納する。

getFqdnメソッドでは、引数のIPアドレスを元にコンストラクタが構築したテーブルの中から合致したFQDNを配列に入れて返す。

# ZONEファイルの記載例
```conf:ahoaho.jp.zone
$ORIGIN .
$TTL 600        ; 10 minutes
ahoaho.jp              IN SOA  ns1.hogehoge.jp. root.ns1.hogehoge.jp. (
                                1234567890 ; serial
                                10800      ; refresh (3 hours)
                                3600       ; retry (1 hour)
                                604800     ; expire (1 week)
                                86400      ; minimum (1 day)
                                )
                        NS      ns1.hogehoge.jp.
                        NS      ns2.hogehoge.jp.
                        NS      ns3.hogehoge.jp.
                        NS      ns4.hogehoge.jp.
$ORIGIN ahoaho.jp.
hogeaho                A       222.222.222.222
ahohoge                A       222.222.222.222
uhouho                 CNAME   ahohoge.ahoaho.jp.
```

# コード

```php:getzoneinfo
class getzoneinfo{

// Constructor
  function __construct($dirPath){
    $fileList = array();
    $fqdnAndIp = array();
    $domain = '';
    $fqdn = '';
    $cnameFqdn = '';
    $cnameIpaddr = '';
    $cnameOrigin = '';
    $cnameTrio = array();
    $cnameList = array();
    global $fqdnList;
    $iterator = new RecursiveDirectoryIterator($dirPath);
    $iterator = new RecursiveIteratorIterator($iterator);

    $fileList = array();
    foreach ($iterator as $fileinfo) {
        if ($fileinfo->isFile()) {
            $fileList[] = $fileinfo->getPathname();
        }
    }

    // Get file path from file list.
    foreach ($fileList as $filePath) {
      $contents = @file($filePath);
      $isFirstOrigin = 1;
      // Get word from line
      foreach($contents as $line){
        //echo $line."<br />";
        $line = trim($line);
        $line = preg_replace("/[　\s]+/"," ",$line);
        $line_array = explode(" ", $line);

        // Detect ORIGIN
        if ( $line_array[0] == '$ORIGIN' ) {
          // Ignore first ORIGIN
          if ( $isFirstOrigin == 1 ) {
            $isFirstOrigin = 0;
            continue;
          } else {
            $domain = $line_array[1];
            continue;
          }
        }

        // Detect Domain only
        if ( $line_array[0] == 'A' ) {
          $fqdn = $domain;
          $ipAddr = $line_array[1];
          $fqdnAndIp[] = $fqdn;
          $fqdnAndIp[] = $ipAddr;
          $fqdnList[] = $fqdnAndIp;
          $fqdnAndIp = array();
          $fqdn = '';
          $ipAddr = '';
          continue;
        }

        // Detect records
        if ( array_key_exists(1, $line_array) ) {
          switch ( $line_array[1] )
          {
            case "IN":
              $domain = $line_array[0];
              continue 2;
            case "A":
              $fqdn = $line_array[0].'.'.substr($domain, 0, -1);
              $ipAddr = $line_array[2];
              break;
            case "CNAME":
              // Stash CNAME Records
              $cnameFqdn = $line_array[0].'.'.substr($domain, 0, -1);
              $cnameIpaddr = $line_array[2];
              $cnameOrigin = $domain;
              $cnameTrio[] = $cnameFqdn;
              $cnameTrio[] = $cnameIpaddr;
              $cnameTrio[] = substr($cnameOrigin, 0, -1);;
              $cnameList[] = $cnameTrio;
              $cnameIpaddr = '';
              $cnameIpaddr = '';
              $cnameTrio = array();
              continue 2;
            default:
              continue 2;
           }
         }
        // Store fqdn and ipaddr
         if ( $fqdn != '' ) {
           $fqdnAndIp[] = $fqdn;
           $fqdnAndIp[] = $ipAddr;
           $fqdnList[] = $fqdnAndIp;
           $fqdnAndIp = array();
           $fqdn = '';
           $ipAddr = '';
         }
      }
    }
    // Store CNAME Records
    $cnamenum = count($cnameList);
    $fqdnnum = count($fqdnList);
    for ($i=0; $i < $cnamenum; $i++) {
      for ($j=0; $j < $fqdnnum; $j++) {
        if ($cnameList[$i][1].'.'.$cnameList[$i][2] == $fqdnList[$j][0]) {
          $fqdn = $cnameList[$i][0];
          $ipAddr = $fqdnList[$j][1];
          $fqdnAndIp[] = $fqdn;
          $fqdnAndIp[] = $ipAddr;
          $fqdnList[] = $fqdnAndIp;
          $fqdnAndIp = array();
          $fqdn = '';
          $ipAddr = '';
        }
      }
    }
//print "FQDN Table";
//print "<pre>";
//print_r($fqdnList);
//print "</pre>";
  }

  function getFqdn($ipAddr){
    global $fqdnList;
    $fqdns = array();
    $num = count($fqdnList);

    for ($i=0; $i < $num; $i++) {
      if ( $fqdnList[$i][1] == $ipAddr ) {
        $fqdns[] = $fqdnList[$i][0];
      }
    }
    return $fqdns;
  }
```

# 使用例
```php:getFqdn.php
$fqdn = new getzoneinfo('/var/www/html/zone');
$result = $fqdn->getFqdn('222.222.222.222');
print "-------------------------------<br>";
print "Query Result of 222.222.222.222";
print "<pre>";
print_r($result);
print "</pre>";
```

# 余談
PHPのクラスは今回初めて書きましたが、期限までに時間が無くて大急ぎでとりあえず動くものを作らなければならなかったので、文法を調べながら勢いで書いてリファクタリングも全くしていません。
なので、リファクタリング大歓迎です！ｗ