Repository: mlocati/ip-lib (php)

Issue:
arpa to normal ip?
Hello,

there is the possibility to convert an ip into an arpa.
But is there also a way to convert an arpa into an ip?

Thanks

Pull request description:
Ability to parse addresses in ARPA (Reverse DNS Lookup) format
Close #77

Patch:
diff --git a/README.md b/README.md
index 867b315..762f0c3 100644
--- a/README.md
+++ b/README.md
@@ -413,6 +413,19 @@ echo $ipv4->getReverseDNSLookupName();
 echo $ipv6->getReverseDNSLookupName();
 ```
 
+To parse addresses in reverse DNS lookup format you can use the `IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS` flag when parsing a string:
+
+```php
+$ipv4 = \IPLib\Factory::parseAddressString('255.3.2.1.in-addr.arpa', \IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS);
+$ipv6 = \IPLib\Factory::parseAddressString('e.b.a.b.e.f.a.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.ip6.arpa', \IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS);
+
+// This will print 1.2.3.255
+echo $ipv4->toString();
+
+// This will print 1234:abcd::cafe:babe
+echo (string) $ipv6->toString();
+```
+
 You can also use `getReverseDNSLookupName()` for IP ranges.
 In this case, the result is an array of strings:
 
diff --git a/src/Address/IPv4.php b/src/Address/IPv4.php
index f2fe3fb..cd2c416 100644
--- a/src/Address/IPv4.php
+++ b/src/Address/IPv4.php
@@ -120,6 +120,13 @@ class IPv4 implements AddressInterface
             return null;
         }
         $flags = (int) $flags;
+        $matches = null;
+        if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) {
+            if (preg_match('/^([12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2})\.in-addr\.arpa\.?$/i', $address, $matches)) {
+                $address = implode('.', array_reverse(explode('.', $matches[1])));
+                $flags = $flags & ~(ParseStringFlag::IPV4_MAYBE_NON_DECIMAL | ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED);
+            }
+        }
         if ($flags & ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED) {
             if (strpos($address, '.') === 0) {
                 return null;
@@ -151,7 +158,6 @@ class IPv4 implements AddressInterface
         if ($flags & ParseStringFlag::MAY_INCLUDE_PORT) {
             $rx .= '(?::\d+)?';
         }
-        $matches = null;
         if (!preg_match('/^' . $rx . '$/', $address, $matches)) {
             return null;
         }
diff --git a/src/Address/IPv6.php b/src/Address/IPv6.php
index 6f92197..e094881 100644
--- a/src/Address/IPv6.php
+++ b/src/Address/IPv6.php
@@ -126,10 +126,23 @@ class IPv6 implements AddressInterface
      */
     public static function parseString($address, $flags = 0)
     {
+        if (!is_string($address)) {
+            return null;
+        }
+        $matches = null;
         $flags = (int) $flags;
+        if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) {
+            if (preg_match('/^([0-9a-f](?:\.[0-9a-f]){31})\.ip6\.arpa\.?/i', $address, $matches)) {
+                $nibbles = array_reverse(explode('.', $matches[1]));
+                $quibbles = array();
+                foreach (array_chunk($nibbles, 4) as $n) {
+                    $quibbles[] = implode('', $n);
+                }
+                $address = implode(':', $quibbles);
+            }
+        }
         $result = null;
         if (is_string($address) && strpos($address, ':') !== false && strpos($address, ':::') === false) {
-            $matches = null;
             if ($flags & ParseStringFlag::MAY_INCLUDE_PORT && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) {
                 $address = $matches[1];
             }
diff --git a/src/ParseStringFlag.php b/src/ParseStringFlag.php
index 4be38d5..860256e 100644
--- a/src/ParseStringFlag.php
+++ b/src/ParseStringFlag.php
@@ -64,4 +64,16 @@ class ParseStringFlag
      * @see http://git.musl-libc.org/cgit/musl/tree/src/network/inet_aton.c?h=v1.2.2
      */
     const IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED = 16;
+
+    /**
+     * Use this flag if you want to accept parsing IPv4/IPv6 addresses in Reverse DNS Lookup Address format.
+     *
+     * @var int
+     *
+     * @since 1.18.0
+     *
+     * @example 140.13.12.10.in-addr.arpa => 10.12.13.140
+     * @example b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa => 4321:0:1:2:3:4:567:89ab
+     */
+    const ADDRESS_MAYBE_RDNS = 32;
 }


Test patch:
diff --git a/test/tests/Addresses/ReverseDNSLookupNameTest.php b/test/tests/Addresses/ReverseDNSLookupNameTest.php
index d5ef39a..2b0500b 100644
--- a/test/tests/Addresses/ReverseDNSLookupNameTest.php
+++ b/test/tests/Addresses/ReverseDNSLookupNameTest.php
@@ -3,6 +3,7 @@
 namespace IPLib\Test\Addresses;
 
 use IPLib\Factory;
+use IPLib\ParseStringFlag;
 use IPLib\Test\TestCase;
 
 class ReverseDNSLookupNameTest extends TestCase
@@ -33,5 +34,8 @@ class ReverseDNSLookupNameTest extends TestCase
         $address = Factory::addressFromString($addressString);
         $actualReverseDNSAddress = $address->getReverseDNSLookupName();
         $this->assertSame($expectedReverseDNSAddress, $actualReverseDNSAddress);
+        $address2 = Factory::parseAddressString($actualReverseDNSAddress, ParseStringFlag::ADDRESS_MAYBE_RDNS);
+        $this->assertNotNull($address2);
+        $this->assertSame((string) $address, (string) $address2);
     }
 }