module Hans.Nat.Forward ( tryForwardUdp, tryForwardTcp ) where
import Hans.Addr.Types (Addr)
import Hans.Lens (view)
import Hans.Network (lookupRoute,RouteInfo(..))
import Hans.Tcp.Packet (TcpHeader(..),tcpSyn)
import Hans.Types
import Hans.Udp.Packet (UdpHeader(..))
tryForwardTcp :: NetworkStack
-> Addr
-> Addr
-> TcpHeader
-> IO (Maybe (RouteInfo Addr,Addr,TcpHeader))
tryForwardTcp ns local remote hdr =
do let key = Flow local (tcpDestPort hdr) remote (tcpSourcePort hdr)
mbEntry <- tcpForwardingActive ns key
case mbEntry of
Just entry -> return $! rewrite key entry
Nothing
| view tcpSyn hdr ->
do mbRule <- shouldForwardTcp ns key
case mbRule of
Nothing -> return Nothing
Just rule ->
do mbSess <- newSession ns key rule
case mbSess of
Just entry -> do addTcpSession ns entry
return $! rewrite key entry
Nothing -> return Nothing
| otherwise ->
return Nothing
where
rewrite key entry =
let other = otherSide key entry
hdr' = hdr { tcpSourcePort = flowLocalPort other
, tcpDestPort = flowRemotePort other
, tcpChecksum = 0 }
in hdr' `seq` Just (flowLocal other, flowRemote other, hdr')
tryForwardUdp :: NetworkStack
-> Addr
-> Addr
-> UdpHeader
-> IO (Maybe (RouteInfo Addr,Addr,UdpHeader))
tryForwardUdp ns local remote hdr =
do let key = Flow local (udpDestPort hdr) remote (udpSourcePort hdr)
mbEntry <- udpForwardingActive ns key
case mbEntry of
Just entry -> return $! rewrite key entry
Nothing ->
do mbRule <- shouldForwardUdp ns key
case mbRule of
Nothing -> return Nothing
Just rule ->
do mbSess <- newSession ns key rule
case mbSess of
Just entry -> do addUdpSession ns entry
return $! rewrite key entry
Nothing -> return Nothing
where
rewrite key entry =
let other = otherSide key entry
hdr' = hdr { udpSourcePort = flowLocalPort other
, udpDestPort = flowRemotePort other
, udpChecksum = 0 }
in hdr' `seq` Just (flowLocal other, flowRemote other, hdr')
newSession :: NetworkStack -> Flow Addr -> PortForward -> IO (Maybe Session)
newSession ns flow rule =
do l <- lookupRoute ns (flowRemote flow)
r <- lookupRoute ns (pfDestAddr rule)
p <- nextTcpPort ns (flowLocal flow) (pfDestAddr rule) (pfDestPort rule)
case (l,r,p) of
(Just riLeft, Just riRight, Just rightPort) ->
return $ Just
$ Session { sessLeft = flow { flowLocal = riLeft }
, sessRight = Flow { flowLocal = riRight
, flowLocalPort = rightPort
, flowRemote = pfDestAddr rule
, flowRemotePort = pfDestPort rule } }
_ -> return Nothing