packages feed

network-ip 0.1 → 0.2

raw patch · 3 files changed

+366/−12 lines, 3 filesdep +data-default-classdep +tastydep +tasty-quickcheckdep −QuickCheckdep −data-defaultdep −test-framework

Dependencies added: data-default-class, tasty, tasty-quickcheck

Dependencies removed: QuickCheck, data-default, test-framework, test-framework-quickcheck2

Files

network-ip.cabal view
@@ -1,5 +1,5 @@ Name: network-ip-Version: 0.1+Version: 0.2 Category: Network Stability: experimental Synopsis: Internet Protocol data structures@@ -26,7 +26,7 @@   Default-Language: Haskell2010   Build-Depends:     base         >= 4.3 && < 5,-    data-default >= 0.2,+    data-default-class,     data-endian  >= 0.0.1,     data-dword   >= 0.2,     tagged       >= 0.2,@@ -45,10 +45,9 @@   Default-Language: Haskell2010   Type: exitcode-stdio-1.0   Build-Depends:-    base                       >= 4 && < 5,-    test-framework             >= 0.6,-    test-framework-quickcheck2 >= 0.2,-    QuickCheck                 >= 2.4,+    base             >= 4 && < 5,+    tasty            >= 0.4,+    tasty-quickcheck >= 0.3,     data-dword,     text-printer,     data-textual,
src/Network/IP/Addr.hs view
@@ -24,6 +24,10 @@   , ip4FromOctets   , ip4FromOctetList   , anyIP4+  , loopbackIP4+  , broadcastIP4+  , Range4(..)+  , ip4Range     -- ** IPv6 address   , IP6(..)   , anIP6@@ -32,6 +36,9 @@   , ip6FromWords   , ip6FromWordList   , anyIP6+  , loopbackIP6+  , Range6(..)+  , ip6Range     -- ** IP address   , IP(..)   , anIP@@ -45,6 +52,8 @@   , aNet4Addr   , aNet6Addr   , aNetAddrIP+  , net4Addr+  , net6Addr   , printNetAddr   , net4Parser   , net6Parser@@ -71,7 +80,7 @@ import Data.DoubleWord (BinaryWord(..), DoubleWord(..), Word128) import Data.Ix (Ix) import Data.Endian-import Data.Default+import Data.Default.Class import Data.Hashable import Data.Binary (Binary) import qualified Data.Binary as B@@ -183,6 +192,63 @@   def = anyIP4   {-# INLINE def #-} +-- | IPv4 address @127.0.0.1@.+loopbackIP4 ∷ IP4+loopbackIP4 = IP4 0x7F000001+{-# INLINE loopbackIP4 #-}++-- | IPv4 address @255.255.255.255@.+broadcastIP4 ∷ IP4+broadcastIP4 = IP4 0xFFFFFFFF+{-# INLINE broadcastIP4 #-}++-- | IPv4 address range classification (per RFC6890).+data Range4 = GeneralIP4       -- ^ General IPv4 address.+            | ThisHostIP4      -- ^ This host on this network.+            | PrivateUseIP4    -- ^ Private-Use networks.+            | SharedSpaceIP4   -- ^ Shared address space.+            | LoopbackIP4      -- ^ Loopback address.+            | LinkLocalIP4     -- ^ Link local address.+            | ReservedIP4      -- ^ Reserved address.+            | DSLiteIP4        -- ^ Dual-Stack Lite.+            | DocumentationIP4 -- ^ Reserved for documentation.+            | IP6To4IP4        -- ^ 6to4.+            | BenchmarkingIP4  -- ^ Benchmark testing.+            | MulticastIP4     -- ^ Multicast address.+            | FutureUseIP4     -- ^ Future use.+            | BroadcastIP4     -- ^ Limited broadcast.+            deriving (Typeable, Show, Read, Eq, Ord, Enum)++-- | Determine the address range type.+ip4Range ∷ IP4 → Range4+ip4Range addr = case w1 of+    0   → ThisHostIP4+    10  → PrivateUseIP4+    100 → if w2 .&. 0xC0 == 0x40 then SharedSpaceIP4 else GeneralIP4+    127 → LoopbackIP4+    169 → if w2 == 254 then LinkLocalIP4 else GeneralIP4+    172 → if w2 .&. 0xF0 == 0x10 then PrivateUseIP4 else GeneralIP4+    192 → case w2 of+            0   → case w3 of+                    0 → if w4 <= 7 then DSLiteIP4 else ReservedIP4+                    2 → DocumentationIP4+                    _ → GeneralIP4+            88  → if w3 == 99 then IP6To4IP4 else GeneralIP4+            168 → PrivateUseIP4+            _   → GeneralIP4+    198 → case w2 of+            18  → BenchmarkingIP4+            19  → BenchmarkingIP4+            51  → if w3 == 100 then DocumentationIP4 else GeneralIP4+            _   → GeneralIP4+    203 → if w2 == 0 && w3 == 113 then DocumentationIP4 else GeneralIP4+    _   | addr == broadcastIP4 → BroadcastIP4+    _   → case w1 .&. 0xF0 of+            224 → MulticastIP4+            240 → FutureUseIP4+            _   → GeneralIP4+  where (w1, w2, w3, w4) = ip4ToOctets addr+ -- | IPv6 address. newtype IP6 = IP6 { unIP6 ∷ Word128 }   deriving (Typeable, Eq, Ord, Bounded, Enum, Ix, Num, Bits, Hashable)@@ -360,6 +426,60 @@   def = anyIP6   {-# INLINE def #-} +-- | IPv6 address @::1@.+loopbackIP6 ∷ IP6+loopbackIP6 = IP6 1++-- | IPv6 address range classification (per RFC6890).+data Range6 = GeneralIP6       -- ^ General IPv6 address.+            | AnyIP6           -- ^ Unspecified address.+            | LoopbackIP6      -- ^ Loopback address.+            | IP4MappedIP6     -- ^ Mapped IPv4 address.+            | IP4EmbeddedIP6   -- ^ Embedded IPv4 address.+            | DiscardIP6       -- ^ Discard address.+            | ReservedIP6      -- ^ Reserved address.+            | TeredoIP6        -- ^ Teredo address.+            | BenchmarkingIP6  -- ^ Benchmark testing.+            | DocumentationIP6 -- ^ Reserved for documentation.+            | OrchidIP6        -- ^ ORCHID address.+            | IP6To4IP6        -- ^ 6to4.+            | UniqueLocalIP6   -- ^ Unique local address.+            | LinkLocalIP6     -- ^ Link local address.+            | MulticastIP6     -- ^ Multicast address.+            deriving (Typeable, Show, Read, Eq, Ord, Enum)++-- | Determine the address range type.+ip6Range ∷ IP6 → Range6+ip6Range (IP6 w) = case hi of+    0 → case lo of+          0 → AnyIP6+          1 → LoopbackIP6+          _ → GeneralIP6+    0x000000000000FFFF → if q3 == 0 then IP4MappedIP6 else GeneralIP6+    0x0064FF9B00000000 → if q3 == 0 then IP4EmbeddedIP6 else GeneralIP6+    0x0100000000000000 → DiscardIP6+    _                  → case w1 of+      0x2001 → case w2 of+                 0     → TeredoIP6+                 2     → if w3 == 0 then BenchmarkingIP6 else GeneralIP6+                 0xDB8 → DocumentationIP6+                 _     | w2 .&. 0xFFF0 == 0x10 → OrchidIP6+                       | w2 .&. 0xFE00 == 0    → ReservedIP6 +                       | otherwise             → GeneralIP6+      0x2002 → IP6To4IP6+      _      | w1 .&. 0xFE00 == 0xFC00 → UniqueLocalIP6+             | w1 .&. 0xFFC0 == 0xFE80 → LinkLocalIP6+             | w1 >= 0xFF00            → MulticastIP6+             | otherwise               → GeneralIP6+  where hi = hiWord w+        lo = loWord w+        q1 = hiWord hi+        q2 = loWord hi+        q3 = hiWord lo+        w1 = hiWord q1+        w2 = loWord q1+        w3 = hiWord q2+ -- IP address. data IP = IPv4 {-# UNPACK #-} !IP4         | IPv6 {-# UNPACK #-} !IP6@@ -406,6 +526,10 @@   netAddr   ∷ NetHost n -- ^ Host address             → Word8     -- ^ Network prefix length             → n+  -- | Test if the address is in the network.+  inNetwork ∷ NetHost n -- ^ Host address+            → n         -- ^ Network address+            → Bool  -- | Network address: host address + network mask length. data NetAddr a = NetAddr a {-# UNPACK #-} !Word8@@ -519,6 +643,9 @@   {-# INLINE netLength #-}   netAddr a w = NetAddr a (w `min` 32)   {-# INLINE netAddr #-}+  inNetwork h (NetAddr a w) = h `shiftR` l == a `shiftR` l+    where l = 32 - fromIntegral w+  {-# INLINE inNetwork #-}  instance IsNetAddr Net6Addr where   type NetHost Net6Addr = IP6@@ -537,6 +664,9 @@   {-# INLINE netLength #-}   netAddr a w = NetAddr a (w `min` 128)   {-# INLINE netAddr #-}+  inNetwork h (NetAddr a w) = h `shiftR` l == a `shiftR` l+    where l = 128 - fromIntegral w+  {-# INLINE inNetwork #-}  instance IsNetAddr (NetAddr IP) where   type NetHost (NetAddr IP) = IP@@ -566,7 +696,20 @@                 IPv4 _ → 32                 IPv6 _ → 128   {-# INLINABLE netAddr #-}+  inNetwork (IPv4 h) (NetAddr (IPv4 a) w) = h `shiftR` l == a `shiftR` l+    where l = 32 - fromIntegral w+  inNetwork (IPv6 h) (NetAddr (IPv6 a) w) = h `shiftR` l == a `shiftR` l+    where l = 128 - fromIntegral w+  inNetwork _ _ = False +net4Addr ∷ IP4 → Word8 → Net4Addr+net4Addr = netAddr+{-# INLINE net4Addr #-}++net6Addr ∷ IP6 → Word8 → Net6Addr+net6Addr = netAddr+{-# INLINE net6Addr #-}+ -- | Print network address (CIDR notation). printNetAddr ∷ (IsNetAddr n, Printable (NetHost n), Printer p) ⇒ n → p printNetAddr n = print (netHost n) <> P.char7 '/' <> print (netLength n)@@ -644,7 +787,7 @@ type Inet6Addr = InetAddr IP6  -- | 'InetAddr' proxy value.-anInetAddr ∷ Proxy (InetAddr IP)+anInetAddr ∷ Proxy InetAddr anInetAddr = Proxy  -- | 'InetAddr' /a/ proxy value.
tests/Tests.hs view
@@ -1,12 +1,12 @@ {-# LANGUAGE UnicodeSyntax #-}+{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-missing-signatures #-} -import Test.Framework (defaultMain, testGroup)-import Test.Framework.Providers.QuickCheck2 (testProperty)-import Test.QuickCheck+import Test.Tasty (defaultMain, testGroup)+import Test.Tasty.QuickCheck  import Data.DoubleWord import Data.Textual@@ -52,7 +52,7 @@   arbitrary = InetAddr <$> arbitrary <*> arbitrary   shrink n = uncurry InetAddr <$> shrink (inetHost n, inetPort n) -main = defaultMain+main = defaultMain $ testGroup "Tests"          [ testGroup "IP4"              [ testProperty "show -> read" $ \a →                  read (show a) == (a ∷ IP4)@@ -80,6 +80,101 @@                  fromStringAs anIP4 "1.2.3.4.5" == Nothing              , testProperty "parsing \"1a.2.3.4\" fails" $                  fromStringAs anIP4 "1a.2.3.4" == Nothing+             , testProperty "0.1.2.3 is in the 'This host' range" $+                 ip4Range (ip4FromOctets 0 1 2 3) == ThisHostIP4+             , testProperty "10.1.2.3 is in the 'Private-Use' range" $+                 ip4Range (ip4FromOctets 10 1 2 3) == PrivateUseIP4+             , testProperty "100.64.1.2 is in the 'Shared address space' range" $+                 ip4Range (ip4FromOctets 100 64 1 2) == SharedSpaceIP4+             , testProperty "100.97.1.2 is in the 'Shared address space' range" $+                 ip4Range (ip4FromOctets 100 97 1 2) == SharedSpaceIP4+             , testProperty "100.128.1.2 is in the 'General' range" $+                 ip4Range (ip4FromOctets 100 128 1 2) == GeneralIP4+             , testProperty "127.0.0.1 is in the 'Loopback' range" $+                 ip4Range loopbackIP4 == LoopbackIP4+             , testProperty "169.254.0.1 is in the 'Link Local' range" $+                 ip4Range (ip4FromOctets 169 254 0 1) == LinkLocalIP4+             , testProperty "172.16.1.2 is in the 'Private-Use' range" $+                 ip4Range (ip4FromOctets 172 16 1 2) == PrivateUseIP4+             , testProperty "172.31.1.2 is in the 'Private-Use' range" $+                 ip4Range (ip4FromOctets 172 31 1 2) == PrivateUseIP4+             , testProperty "172.48.1.2 is in the 'General' range" $+                 ip4Range (ip4FromOctets 172 48 1 2) == GeneralIP4+             , testProperty "192.0.0.7 is in the 'DS-Lite' range" $+                 ip4Range (ip4FromOctets 192 0 0 7) == DSLiteIP4+             , testProperty "192.0.0.8 is in the 'Reserved' range" $+                 ip4Range (ip4FromOctets 192 0 0 8) == ReservedIP4+             , testProperty "192.0.2.1 is in the 'Documentation' range" $+                 ip4Range (ip4FromOctets 192 0 2 1) == DocumentationIP4+             , testProperty "192.88.99.1 is in the '6to4' range" $+                 ip4Range (ip4FromOctets 192 88 99 1) == IP6To4IP4+             , testProperty "192.168.1.2 is in the 'Private-Use' range" $+                 ip4Range (ip4FromOctets 192 168 1 2) == PrivateUseIP4+             , testProperty "198.18.1.2 is in the 'Benchmarking' range" $+                 ip4Range (ip4FromOctets 198 18 1 2) == BenchmarkingIP4+             , testProperty "198.19.1.2 is in the 'Benchmarking' range" $+                 ip4Range (ip4FromOctets 198 19 1 2) == BenchmarkingIP4+             , testProperty "198.20.1.2 is in the 'General' range" $+                 ip4Range (ip4FromOctets 198 20 1 2) == GeneralIP4+             , testProperty "198.51.100.1 is in the 'Documentation' range" $+                 ip4Range (ip4FromOctets 198 51 100 1) == DocumentationIP4+             , testProperty "198.51.101.1 is in the 'General' range" $+                 ip4Range (ip4FromOctets 198 51 101 1) == GeneralIP4+             , testProperty "198.52.100.1 is in the 'General' range" $+                 ip4Range (ip4FromOctets 198 52 100 1) == GeneralIP4+             , testProperty "203.0.113.1 is in the 'Documentation' range" $+                 ip4Range (ip4FromOctets 203 0 113 1) == DocumentationIP4+             , testProperty "203.0.114.1 is in the 'General' range" $+                 ip4Range (ip4FromOctets 203 0 114 1) == GeneralIP4+             , testProperty "203.1.113.1 is in the 'General' range" $+                 ip4Range (ip4FromOctets 203 1 113 1) == GeneralIP4+             , testProperty "224.1.2.3 is in the 'Multicast' range" $+                 ip4Range (ip4FromOctets 224 1 2 3) == MulticastIP4+             , testProperty "239.1.2.3 is in the 'Multicast' range" $+                 ip4Range (ip4FromOctets 239 1 2 3) == MulticastIP4+             , testProperty "223.1.2.3 is in the 'General' range" $+                 ip4Range (ip4FromOctets 223 1 2 3) == GeneralIP4+             , testProperty "240.1.2.3 is in the 'Future use' range" $+                 ip4Range (ip4FromOctets 240 1 2 3) == FutureUseIP4+             , testProperty "255.255.255.254 is in the 'Future use' range" $+                 ip4Range (ip4FromOctets 255 255 255 254) == FutureUseIP4+             , testProperty "255.255.255.255 is in the 'Broadcast' range" $+                 ip4Range broadcastIP4 == BroadcastIP4+             , testProperty "ip4Range" $ \a →+                 let r = ip4Range a in+                   if | inNetwork a (net4Addr anyIP4 8)+                      → r == ThisHostIP4+                      | inNetwork a (net4Addr (ip4FromOctets 10 0 0 0) 8) ||+                        inNetwork a (net4Addr (ip4FromOctets 172 16 0 0) 12) ||+                        inNetwork a (net4Addr (ip4FromOctets 192 168 0 0) 16)+                      → r == PrivateUseIP4+                      | inNetwork a (net4Addr (ip4FromOctets 100 64 0 0) 10)+                      → r == SharedSpaceIP4+                      | inNetwork a (net4Addr (ip4FromOctets 127 0 0 0) 8)+                      → r == LoopbackIP4+                      | inNetwork a (net4Addr (ip4FromOctets 169 254 0 0) 16)+                      → r == LinkLocalIP4+                      | inNetwork a (net4Addr (ip4FromOctets 192 0 0 0) 24)+                      → if inNetwork a (net4Addr (ip4FromOctets 192 0 0 0) 29)+                        then r == DSLiteIP4+                        else r == ReservedIP4+                      | inNetwork a (net4Addr (ip4FromOctets 192 0 2 0) 24) ||+                        inNetwork a+                          (net4Addr (ip4FromOctets 198 51 100 0) 24) ||+                        inNetwork a (net4Addr (ip4FromOctets 203 0 113 0) 24)+                      → r == DocumentationIP4+                      | inNetwork a (net4Addr (ip4FromOctets 192 88 99 0) 24)+                      → r == IP6To4IP4+                      | inNetwork a (net4Addr (ip4FromOctets 198 18 0 0) 15)+                      → r == BenchmarkingIP4+                      | inNetwork a (net4Addr (ip4FromOctets 224 0 0 0) 4)+                      → r == MulticastIP4+                      | inNetwork a (net4Addr (ip4FromOctets 240 0 0 0) 4)+                      → if a == broadcastIP4+                        then r == BroadcastIP4+                        else r == FutureUseIP4+                      | otherwise+                      → r == GeneralIP4              ]          , testGroup "IP6"              [ testProperty "show -> read" $ \a →@@ -133,6 +228,123 @@                  fromStringAs anIP6 "1:2::3:4:5g" == Nothing              , testProperty "parsing \"1:2:3:4:5:6:7:8g\" fails" $                  fromStringAs anIP6 "1:2:3:4:5:6:7:8g" == Nothing+             , testProperty ":: is in the 'Unspecified' range" $+                 ip6Range (IP6 0) == AnyIP6+             , testProperty "::1 is in the 'Loopback' range" $+                 ip6Range (IP6 1) == LoopbackIP6+             , testProperty "::ffff:0:0:102:304 is in the 'IPv4 mapped' range" $+                 ip6Range (ip6FromWords 0 0 0 0xFFFF 0 0 0x102 0x304) ==+                   IP4MappedIP6+             , testProperty "64:ff9b::102:304 is in the 'IPv4 embedded' range" $+                 ip6Range (ip6FromWords 0x64 0xFF9B 0 0 0 0 0x102 0x304) ==+                   IP4EmbeddedIP6+             , testProperty "100::1:2:3:4 is in the 'Discard' range" $+                 ip6Range (ip6FromWords 0x100 0 0 0 1 2 3 4) == DiscardIP6+             , testProperty "100::1:2:3:4:5 is in the 'General' range" $+                 ip6Range (ip6FromWords 0x100 0 0 1 2 3 4 5) == GeneralIP6+             , testProperty "2001::ffff:1:2:3:4:5 is in the 'Teredo' range" $+                 ip6Range (ip6FromWords 0x2001 0 0xFFFF 1 2 3 4 5) ==+                   TeredoIP6+             , testProperty "2001:2::0xFFFF:1:2:3:4 is in the 'Benchmarking' range" $+                 ip6Range (ip6FromWords 0x2001 2 0 0xFFFF 1 2 3 4) ==+                   BenchmarkingIP6+             , testProperty "2001:2:1:2:3:4:5:6 is in the 'General' range" $+                 ip6Range (ip6FromWords 0x2001 2 1 2 3 4 5 6) == GeneralIP6+             , testProperty "2001:db8:0xFFFF:1:2:3:4:5 is in the 'Documentation' range" $+                 ip6Range (ip6FromWords 0x2001 0xDB8 0xFFFF 1 2 3 4 5) ==+                   DocumentationIP6+             , testProperty "2001:db9:1:2:3:4:5:6 is in the 'General' range" $+                 ip6Range (ip6FromWords 0x2001 0xDB9 1 2 3 4 5 6) ==+                   GeneralIP6+             , testProperty "2001:10:0xFFFF:1:2:3:4:5 is in the 'Orchid' range" $+                 ip6Range (ip6FromWords 0x2001 0x10 0xFFFF 1 2 3 4 5) ==+                   OrchidIP6+             , testProperty "2001:18:1:2:3:4:5:6 is in the 'Orchid' range" $+                 ip6Range (ip6FromWords 0x2001 0x18 1 2 3 4 5 6) ==+                   OrchidIP6+             , testProperty "2001:100:1:2:3:4:5:6 is in the 'Reserved' range" $+                 ip6Range (ip6FromWords 0x2001 0x100 1 2 3 4 5 6) ==+                   ReservedIP6+             , testProperty "2001:200:1:2:3:4:5:6 is in the 'General' range" $+                 ip6Range (ip6FromWords 0x2001 0x200 1 2 3 4 5 6) ==+                   GeneralIP6+             , testProperty "2002::1:2:3:4:5:6 is in the '6to4' range" $+                 ip6Range (ip6FromWords 0x2002 0 1 2 3 4 5 6) ==+                   IP6To4IP6+             , testProperty "2002:ffff:1:2:3:4:5:6 is in the '6to4' range" $+                 ip6Range (ip6FromWords 0x2002 0xFFFF 1 2 3 4 5 6) ==+                   IP6To4IP6+             , testProperty "fc00::1:2:3:4:5:6 is in the 'Unique Local' range" $+                 ip6Range (ip6FromWords 0xFC00 0 1 2 3 4 5 6) ==+                   UniqueLocalIP6+             , testProperty "fdff::1:2:3:4:5:6 is in the 'Unique Local' range" $+                 ip6Range (ip6FromWords 0xFDFF 0 1 2 3 4 5 6) ==+                   UniqueLocalIP6+             , testProperty "fe00::1:2:3:4:5:6 is in the 'General' range" $+                 ip6Range (ip6FromWords 0xFE00 0 1 2 3 4 5 6) ==+                   GeneralIP6+             , testProperty "fe80::1:2:3:4:5:6 is in the 'Link Local' range" $+                 ip6Range (ip6FromWords 0xFE80 0 1 2 3 4 5 6) ==+                   LinkLocalIP6+             , testProperty "febf::1:2:3:4:5:6 is in the 'Link Local' range" $+                 ip6Range (ip6FromWords 0xFEBF 0 1 2 3 4 5 6) ==+                   LinkLocalIP6+             , testProperty "fec0::1:2:3:4:5:6 is in the 'General' range" $+                 ip6Range (ip6FromWords 0xFEC0 0 1 2 3 4 5 6) ==+                   GeneralIP6+             , testProperty "fe00::1:2:3:4:5:6 is in the 'General' range" $+                 ip6Range (ip6FromWords 0xFE00 0 1 2 3 4 5 6) ==+                   GeneralIP6+             , testProperty "ff00::1:2:3:4:5:6 is in the 'Multicast' range" $+                 ip6Range (ip6FromWords 0xFF00 0 1 2 3 4 5 6) ==+                   MulticastIP6+             , testProperty "ffff::1:2:3:4:5:6 is in the 'Multicast' range" $+                 ip6Range (ip6FromWords 0xFFFF 0 1 2 3 4 5 6) ==+                   MulticastIP6+             , testProperty "ip6Range" $ \a →+                 let r = ip6Range a in+                   if | a == anyIP6+                      → r == AnyIP6+                      | a == loopbackIP6+                      → r == LoopbackIP6 +                      | inNetwork a+                          (net6Addr (ip6FromWords 0 0 0 0xFFFF 0 0 0 0) 96)+                      → r == IP4MappedIP6+                      | inNetwork a+                          (net6Addr (ip6FromWords 0x100 0 0 0 0 0 0 0) 64)+                      → r == DiscardIP6+                      | inNetwork a+                          (net6Addr (ip6FromWords 0x64 0xFF9B 0 0 0 0 0 0) 96)+                      → r == IP4EmbeddedIP6+                      | inNetwork a+                          (net6Addr (ip6FromWords 0x2001 0 0 0 0 0 0 0) 23)+                      → if inNetwork a+                             (net6Addr (ip6FromWords 0x2001 0 0 0 0 0 0 0) 32)+                        then r == TeredoIP6+                        else+                          if inNetwork a+                               (net6Addr+                                  (ip6FromWords 0x2001 0x10 0 0 0 0 0 0) 28)+                          then r == OrchidIP6+                          else r == ReservedIP6+                      | inNetwork a+                          (net6Addr+                             (ip6FromWords 0x2001 0xDB8 0 0 0 0 0 0) 32)+                      → r == DocumentationIP6+                      | inNetwork a+                          (net6Addr (ip6FromWords 0x2002 0 0 0 0 0 0 0) 16)+                      → r == IP6To4IP6+                      | inNetwork a+                          (net6Addr (ip6FromWords 0xFC00 0 0 0 0 0 0 0) 7)+                      → r == UniqueLocalIP6+                      | inNetwork a+                          (net6Addr (ip6FromWords 0xFE80 0 0 0 0 0 0 0) 10)+                      → r == LinkLocalIP6+                      | inNetwork a+                          (net6Addr (ip6FromWords 0xFF00 0 0 0 0 0 0 0) 8)+                      → r == MulticastIP6+                      | otherwise+                      → r == GeneralIP6              ]          , testGroup "IP"              [ testProperty "show -> read" $ \a →