packages feed

hans-2.1.0.0: src/Hans/Layer/Udp.hs

{-# LANGUAGE BangPatterns          #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances  #-}
{-# LANGUAGE FlexibleInstances     #-}

module Hans.Layer.Udp (
    UdpHandle
  , runUdpLayer

  , queueUdp
  , sendUdp
  , addUdpHandler
  , removeUdpHandler
  ) where

import Hans.Address.IP4
import Hans.Channel
import Hans.Layer
import Hans.Layer.IP4
import Hans.Layer.Icmp4
import Hans.Message.Ip4
import Hans.Message.Udp
import Hans.Ports
import Hans.Utils

import Control.Concurrent (forkIO)
import Data.Serialize.Get (runGet)
import MonadLib (get,set)


type Handler = IP4 -> UdpPort -> Packet -> IO ()

type UdpHandle = Channel (Udp ())

udpProtocol :: IP4Protocol
udpProtocol  = IP4Protocol 0x11

runUdpLayer :: UdpHandle -> IP4Handle -> Icmp4Handle -> IO ()
runUdpLayer h ip4 icmp4 = do
  addIP4Handler ip4 udpProtocol (queueUdp h)
  void (forkIO (loopLayer (emptyUdp4State ip4 icmp4) (receive h) id))

sendUdp :: UdpHandle -> IP4 -> Maybe UdpPort -> UdpPort -> Packet -> IO ()
sendUdp h !dst mb !dp !bs = send h (handleOutgoing dst mb dp bs)

queueUdp :: UdpHandle -> IP4 -> IP4 -> Packet -> IO ()
queueUdp h !src !dst !bs = send h (handleIncoming src dst bs)

addUdpHandler :: UdpHandle -> UdpPort -> Handler -> IO ()
addUdpHandler h !sp k = send h (handleAddHandler sp k)

removeUdpHandler :: UdpHandle -> UdpPort -> IO ()
removeUdpHandler h !sp = send h (handleRemoveHandler sp)


-- Udp State -------------------------------------------------------------------

type Udp = Layer UdpState

data UdpState = UdpState
  { udpPorts       :: PortManager UdpPort
  , udpHandlers    :: Handlers UdpPort Handler
  , udpIp4Handle   :: IP4Handle
  , udpIcmp4Handle :: Icmp4Handle
  }

emptyUdp4State :: IP4Handle -> Icmp4Handle -> UdpState
emptyUdp4State ip4 icmp4 = UdpState
  { udpPorts       = emptyPortManager [maxBound, maxBound - 1 .. 1 ]
  , udpHandlers    = emptyHandlers
  , udpIp4Handle   = ip4
  , udpIcmp4Handle = icmp4
  }

instance ProvidesHandlers UdpState UdpPort Handler where
  getHandlers      = udpHandlers
  setHandlers hs s = s { udpHandlers = hs }


-- Utilities -------------------------------------------------------------------

ip4Handle :: Udp IP4Handle
ip4Handle  = udpIp4Handle `fmap` get

--icmp4Handle :: Udp Icmp4Handle
--icmp4Handle  = udpIcmp4Handle `fmap` get

maybePort :: Maybe UdpPort -> Udp UdpPort
maybePort (Just p) = return p
maybePort Nothing  = do
  state   <- get
  (p,pm') <- nextPort (udpPorts state)
  pm' `seq` set state { udpPorts = pm' }
  return p

-- Message Handling ------------------------------------------------------------

handleAddHandler :: UdpPort -> Handler -> Udp ()
handleAddHandler sp k = do
  state <- get
  pm'   <- reserve sp (udpPorts state)
  pm' `seq` set state { udpPorts = pm' }
  addHandler sp k

handleRemoveHandler :: UdpPort -> Udp ()
handleRemoveHandler sp = do
  state <- get
  pm'   <- unreserve sp (udpPorts state)
  pm' `seq` set state { udpPorts = pm' }
  removeHandler sp


handleIncoming :: IP4 -> IP4 -> Packet -> Udp ()
handleIncoming src _dst bs = do
  UdpPacket hdr pkt <- liftRight (runGet parseUdpPacket bs)
  h                 <- getHandler (udpDestPort hdr)
  output (h src (udpSourcePort hdr) pkt)


handleOutgoing :: IP4 -> Maybe UdpPort -> UdpPort -> Packet -> Udp ()
handleOutgoing dst mb dp bs = do
  sp  <- maybePort mb
  ip4 <- ip4Handle
  let udp = UdpPacket (UdpHeader sp dp 0) bs
  output $ withIP4Source ip4 dst $ \ src -> do
    pkt <- renderUdpPacket udp (mkIP4PseudoHeader src dst udpProtocol)
    sendIP4Packet ip4 udpProtocol dst pkt