quic-0.0.0: Network/QUIC/Socket.hs
module Network.QUIC.Socket where
import Control.Concurrent
import qualified UnliftIO.Exception as E
import Data.IP hiding (addr)
import qualified GHC.IO.Exception as E
import Network.Socket
import qualified System.IO.Error as E
sockAddrFamily :: SockAddr -> Family
sockAddrFamily SockAddrInet{} = AF_INET
sockAddrFamily SockAddrInet6{} = AF_INET6
sockAddrFamily _ = error "sockAddrFamily"
anySockAddr :: SockAddr -> SockAddr
anySockAddr (SockAddrInet p _) = SockAddrInet p 0
anySockAddr (SockAddrInet6 p f _ s) = SockAddrInet6 p f (0,0,0,0) s
anySockAddr _ = error "anySockAddr"
udpServerListenSocket :: (IP, PortNumber) -> IO (Socket, SockAddr)
udpServerListenSocket ip = E.bracketOnError open close $ \s -> do
setSocketOption s ReuseAddr 1
withFdSocket s setCloseOnExecIfNeeded
-- setSocketOption s IPv6Only 1 -- fixme
bind s sa
return (s,sa)
where
sa = toSockAddr ip
family = sockAddrFamily sa
open = socket family Datagram defaultProtocol
udpServerConnectedSocket :: SockAddr -> SockAddr -> IO Socket
udpServerConnectedSocket mysa peersa = E.bracketOnError open close $ \s -> do
setSocketOption s ReuseAddr 1
withFdSocket s setCloseOnExecIfNeeded
-- bind and connect is not atomic
-- So, bind may results in EADDRINUSE
bind s anysa -- (UDP, *:13443, *:*)
`E.catch` postphone (bind s anysa)
connect s peersa -- (UDP, 127.0.0.1:13443, pa:pp)
return s
where
postphone action e
| E.ioeGetErrorType e == E.ResourceBusy = threadDelay 10000 >> action
| otherwise = E.throwIO e
anysa = anySockAddr mysa
family = sockAddrFamily mysa
open = socket family Datagram defaultProtocol
udpClientSocket :: HostName -> ServiceName -> IO (Socket,SockAddr)
udpClientSocket host port = do
addr <- head <$> getAddrInfo (Just hints) (Just host) (Just port)
E.bracketOnError (openSocket addr) close $ \s -> do
let sa = addrAddress addr
return (s,sa)
where
hints = defaultHints { addrSocketType = Datagram }
udpClientConnectedSocket :: HostName -> ServiceName -> IO (Socket,SockAddr)
udpClientConnectedSocket host port = do
addr <- head <$> getAddrInfo (Just hints) (Just host) (Just port)
E.bracketOnError (openSocket addr) close $ \s -> do
let sa = addrAddress addr
connect s sa
return (s,sa)
where
hints = defaultHints { addrSocketType = Datagram }
udpNATRebindingSocket :: SockAddr -> IO Socket
udpNATRebindingSocket peersa = E.bracketOnError open close $ \s ->
return s
where
family = sockAddrFamily peersa
open = socket family Datagram defaultProtocol
udpNATRebindingConnectedSocket :: SockAddr -> IO Socket
udpNATRebindingConnectedSocket peersa = E.bracketOnError open close $ \s -> do
connect s peersa
return s
where
family = sockAddrFamily peersa
open = socket family Datagram defaultProtocol