43bf219a3c
Would be nice to add CIDR notation to this, but this is the minimal thing needed for the security fix. This commit was sponsored by Ewen McNeill on Patreon.
93 lines
2.9 KiB
Haskell
93 lines
2.9 KiB
Haskell
{- IP addresses
|
|
-
|
|
- Copyright 2012 Joey Hess <id@joeyh.name>
|
|
-
|
|
- License: BSD-2-clause
|
|
-}
|
|
|
|
module Utility.IPAddress where
|
|
|
|
import Utility.Exception
|
|
|
|
import Network.Socket
|
|
import Data.Word
|
|
import Control.Applicative
|
|
import Prelude
|
|
|
|
{- Check if an IP address is a loopback address; connecting to it
|
|
- may connect back to the local host. -}
|
|
isLoopbackAddress :: SockAddr -> Bool
|
|
isLoopbackAddress (SockAddrInet _ ipv4) = case hostAddressToTuple ipv4 of
|
|
-- localhost
|
|
(127,_,_,_) -> True
|
|
-- current network; functions equivilant to loopback
|
|
(0,_,_, _) -> True
|
|
_ -> False
|
|
isLoopbackAddress (SockAddrInet6 _ _ ipv6 _) = case hostAddress6ToTuple ipv6 of
|
|
-- localhost
|
|
(0,0,0,0,0,0,0,1) -> True
|
|
-- unspecified address; functions equivilant to loopback
|
|
(0,0,0,0,0,0,0,0) -> True
|
|
v -> maybe False
|
|
(isLoopbackAddress . SockAddrInet 0)
|
|
(embeddedIpv4 v)
|
|
isLoopbackAddress _ = False
|
|
|
|
{- Check if an IP address is not globally routed, and is used
|
|
- for private communication, eg on a LAN. -}
|
|
isPrivateAddress :: SockAddr -> Bool
|
|
isPrivateAddress (SockAddrInet _ ipv4) = case hostAddressToTuple ipv4 of
|
|
-- lan
|
|
(10,_,_,_) -> True
|
|
(172,n,_,_) | n >= 16 && n <= 31 -> True -- 172.16.0.0/12
|
|
(192,168,_,_) -> True
|
|
-- carrier-grade NAT
|
|
(100,n,0,0) | n >= 64 && n <= 127 -> True -- 100.64.0.0/10
|
|
-- link-local
|
|
(169,254,_,_) -> True
|
|
_ -> False
|
|
isPrivateAddress (SockAddrInet6 _ _ ipv6 _) = case hostAddress6ToTuple ipv6 of
|
|
v@(n,_,_,_,_,_,_,_)
|
|
-- local to lan or private between orgs
|
|
| n >= 0xfc00 && n <= 0xfdff -> True -- fc00::/7
|
|
-- link-local
|
|
| n >= 0xfe80 && n <= 0xfebf -> True -- fe80::/10
|
|
| otherwise -> maybe False
|
|
(isPrivateAddress . SockAddrInet 0)
|
|
(embeddedIpv4 v)
|
|
isPrivateAddress _ = False
|
|
|
|
embeddedIpv4 :: (Word16, Word16, Word16, Word16, Word16, Word16, Word16, Word16) -> Maybe HostAddress
|
|
embeddedIpv4 v = case v of
|
|
-- IPv4 mapped address (::ffff:0:0/96)
|
|
(0,0,0,0,0,0xffff,a,b) -> Just (toipv4 a b)
|
|
-- IPV4 translated address (::ffff:0:ipv4)
|
|
(0,0,0,0,0xffff,0,a,b) -> Just (toipv4 a b)
|
|
-- IPV4/IPV6 translation (64:ff9b::ipv4)
|
|
(0x64,0xff9b,0,0,0,0,a,b) -> Just (toipv4 a b)
|
|
_ -> Nothing
|
|
where
|
|
toipv4 a b = htonl $ fromIntegral a * (2^halfipv4bits) + fromIntegral b
|
|
halfipv4bits = 16 :: Word32
|
|
|
|
{- Given a string containing an IP address, make a function that will
|
|
- match that address in a SockAddr. Nothing when the address cannot be
|
|
- parsed.
|
|
-
|
|
- This does not involve any DNS lookups.
|
|
-}
|
|
makeAddressMatcher :: String -> IO (Maybe (SockAddr -> Bool))
|
|
makeAddressMatcher s = go
|
|
<$> catchDefaultIO [] (getAddrInfo (Just hints) (Just s) Nothing)
|
|
where
|
|
hints = defaultHints
|
|
{ addrSocketType = Stream
|
|
, addrFlags = [AI_NUMERICHOST]
|
|
}
|
|
|
|
go [] = Nothing
|
|
go l = Just $ \sockaddr -> any (match sockaddr) (map addrAddress l)
|
|
|
|
match (SockAddrInet _ a) (SockAddrInet _ b) = a == b
|
|
match (SockAddrInet6 _ _ a _) (SockAddrInet6 _ _ b _) = a == b
|
|
match _ _ = False
|