packages feed

danecheck-1.0.0.0: Dane/Scanner/SMTP/Addr.hs

{-# LANGUAGE RecordWildCards #-}

module Dane.Scanner.SMTP.Addr ( ipConn ) where

import           Control.Exception (IOException, bracket, try)
import           Data.IP (IP(IPv4, IPv6), toHostAddress, toHostAddress6)
import           Network.Socket (SockAddr(..), AddrInfo(..), Socket, SocketType(Stream))
import           Network.Socket (Family(AF_INET, AF_INET6), defaultProtocol, PortNumber)
import           Network.Socket (connect, close, socket)
import           System.Timeout (timeout)

import           Dane.Scanner.SMTP.Internal

connectIP :: Int
          -> (Either IOException Socket -> IO a)
          -> AddrInfo
          -> IO a
connectIP tmout action ~(AddrInfo{..}) =
    bracket (socket addrFamily addrSocketType addrProtocol) close \sock -> do
        res <- timeout tmout $ try $ connect sock addrAddress
        case res of
            Just (Right _) -> action . Right $ sock
            Just (Left e)  -> action . Left $ ioErr "connect" e
            Nothing        -> action . Left $ timeErr "connect"

-- | Connect to the specified peer on the given TCP port with the
-- given timeout (in microseconds).  The callback receives either
-- a connected 'Socket' or the 'IOException' describing the
-- failure to connect; the socket is always closed when the
-- callback returns, regardless of how it returns.
ipConn :: IP
       -> PortNumber
       -> Int
       -> (Either IOException Socket -> IO a)
       -> IO a
ipConn (IPv4 ip4) port tmout action = connectIP tmout action AddrInfo
    { addrFlags      = []
    , addrFamily     = AF_INET
    , addrSocketType = Stream
    , addrProtocol   = defaultProtocol
    , addrAddress    = SockAddrInet port (toHostAddress ip4)
    , addrCanonName  = Nothing
    }
ipConn (IPv6 ip6) port tmout action = connectIP tmout action AddrInfo
    { addrFlags      = []
    , addrFamily     = AF_INET6
    , addrSocketType = Stream
    , addrProtocol   = defaultProtocol
    , addrAddress    = SockAddrInet6 port 0 (toHostAddress6 ip6) 0
                       -- 0 for FlowInfo, ScopeID
    , addrCanonName  = Nothing
    }