module Net.UDP_Client( initialize,UDP_API,Interface(..),Packet(..),template,Port(..) ) where import Net.Concurrent import Control.Monad.State import Control.Monad.Trans(lift) import Data.Map (Map) import qualified Data.Map as Map import Data.List((\\)) --import System.Random(randomRIO) import Net.UDP import qualified Net.IPv4 as IPv4 import qualified Net.Interface as Net import Net.Utils(doReq) import Net.Packet(InPacket,OutPacket) import Net.Wire import Monad.Util type UDP_API m = Net.TimedInterface m (IPv4.Addr,Packet InPacket) (IPv4.Addr,Packet OutPacket) data Interface m = Interface { listen :: Port -> m (UDP_API m), listenAny :: m (Port,UDP_API m), unlisten :: Port -> m () } data Req m = Listen Port (UDP_API m->m ()) | ListenAny ((Port,UDP_API m)->m ()) | Unlisten Port | FromNetwork (IPv4.Packet (Packet InPacket)) type Clients m = Map Port ((IPv4.Addr,Packet InPacket)->m ()) initialize putStrLn myIP iface = do reqChan <- newChan fork $ loop $ writeChan reqChan . FromNetwork =<< Net.rx iface fork $ server debug myIP iface reqChan let listen = doReq reqChan . Listen listenAny = doReq reqChan ListenAny unlisten = writeChan reqChan . Unlisten return $ Interface listen listenAny unlisten where debug = putStrLn . ("UDP: "++) -- I copied this type signature ghci. server :: (Eq (r ()), RefIO r m, ChannelIO c m, DelayIO m, ForkIO m) => ([Char] -> m ()) -> IPv4.Addr -> Net.Interface m i (IPv4.Packet (Packet OutPacket)) -> c (Req m) -> m a server debug myIP iface reqChan = flip evalStateT init $ loop (handle=< listen port reply ListenAny reply -> do port <- pickPort let reply' iface = reply (port,iface) listen port reply' Unlisten port -> modify (Map.delete port) FromNetwork ipPack -> do let udpPacket = IPv4.content ipPack src = IPv4.source ipPack dst = IPv4.dest ipPack sp = sourcePort udpPacket port = destPort udpPacket clients <- get case Map.lookup port clients of Just toClient -> lift $ toClient (src,udpPacket) _ -> lift $ debug $ "Dropped packet from "++show (src,sp) ++" to "++show (IPv4.dest ipPack,port) where listen port reply = do Net.TimedInterface{Net.rxT=rx,Net.txT=toClient} <- timedWire() lift $ reply (Net.TimedInterface rx tx) modify (\clients -> Map.insert port toClient clients) pickPort = do inuse <- gets Map.keys return $ head (map Port [32768..65535]\\inuse) tx (destIP,updPacket) = Net.tx iface (IPv4.template IPv4.UDP myIP destIP updPacket)