module Hans.Layer.Tcp.Messages where
import Hans.Layer ( time )
import Hans.Layer.Tcp.Monad
import Hans.Layer.Tcp.Types
import Hans.Layer.Tcp.Window
import Hans.Message.Tcp
import Data.Maybe ( fromMaybe )
import Data.Time.Clock.POSIX (POSIXTime)
import qualified Data.ByteString.Lazy as L
import qualified Data.Foldable as F
import qualified Data.Sequence as Seq
mkSegment :: TcpSocket -> TcpHeader
mkSegment tcp = case tcpTimestamp tcp of
Just ts -> setTcpOption (mkTimestamp ts) hdr
Nothing -> hdr
where
hdr = emptyTcpHeader
{ tcpDestPort = sidRemotePort (tcpSocketId tcp)
, tcpSourcePort = sidLocalPort (tcpSocketId tcp)
, tcpSeqNum = tcpSndNxt tcp
, tcpAckNum = tcpRcvNxt tcp
, tcpWindow = lwRcvWind (tcpIn tcp)
}
mkAck :: TcpSocket -> TcpHeader
mkAck tcp = addSackOption tcp
$ (mkSegment tcp)
{ tcpAck = True
}
addSackOption :: TcpSocket -> TcpHeader -> TcpHeader
addSackOption sock
| tcpSack sock && not (null bs) = setTcpOption (OptSack bs)
| otherwise = id
where
bs = F.toList (localWindowSackBlocks (tcpIn sock))
addSackPermitted :: TcpSocket -> TcpHeader -> TcpHeader
addSackPermitted sock
| tcpSack sock = setTcpOption OptSackPermitted
| otherwise = id
addWindowScale :: TcpSocket -> TcpHeader -> TcpHeader
addWindowScale sock
| tcpWindowScale sock = setTcpOption
$ OptWindowScaling
$ fromIntegral
$ lwRcvWindScale
$ tcpIn sock
| otherwise = id
mkRstAck :: TcpHeader -> Int -> TcpHeader
mkRstAck hdr len = emptyTcpHeader
{ tcpSeqNum = 0
, tcpAckNum = tcpSeqNum hdr + fromIntegral len + 1
, tcpRst = True
, tcpAck = True
, tcpDestPort = tcpSourcePort hdr
, tcpSourcePort = tcpDestPort hdr
}
mkRst :: TcpHeader -> TcpHeader
mkRst hdr = emptyTcpHeader
{ tcpSeqNum = tcpAckNum hdr
, tcpRst = True
, tcpDestPort = tcpSourcePort hdr
, tcpSourcePort = tcpDestPort hdr
}
mkSyn :: TcpSocket -> TcpHeader
mkSyn tcp = addSackPermitted tcp
$ addWindowScale tcp
$ setTcpOption (mkMSS tcp)
$ (mkSegment tcp)
{ tcpSyn = True
, tcpAckNum = 0
}
mkSynAck :: TcpSocket -> TcpHeader
mkSynAck tcp = addSackPermitted tcp
$ addWindowScale tcp
$ setTcpOption (mkMSS tcp)
$ (mkSegment tcp)
{ tcpSyn = True
, tcpAck = True
}
mkFinAck :: TcpSocket -> TcpHeader
mkFinAck tcp = (mkSegment tcp)
{ tcpFin = True
, tcpAck = True
}
mkData :: TcpSocket -> TcpHeader
mkData tcp = addSackOption tcp
$ (mkSegment tcp)
{ tcpAck = True
, tcpPsh = True
}
syn :: Sock ()
syn = do
tcp <- getTcpSocket
tcpOutput (mkSyn tcp) L.empty
advanceSndNxt 1
synAck :: Sock ()
synAck = do
advanceRcvNxt 1
tcp <- getTcpSocket
tcpOutput (mkSynAck tcp) L.empty
advanceSndNxt 1
ack :: Sock ()
ack = do
clearDelayedAck
tcp <- getTcpSocket
tcpOutput (mkAck tcp) L.empty
delayedAck :: Sock ()
delayedAck = modifyTcpTimers_ (\tt -> tt { ttDelayedAck = True })
clearDelayedAck :: Sock ()
clearDelayedAck = modifyTcpTimers_ (\tt -> tt { ttDelayedAck = False })
finAck :: Sock ()
finAck = do
now <- inTcp time
seg <- modifyTcpSocket $ \ tcp ->
let
hdr = mkFinAck tcp
finAckOutSeg = mkOutSegment now (ttRTO (tcpTimers tcp)) hdr L.empty
tcp' = tcp { tcpOut = addSegment finAckOutSeg (tcpOut tcp)
, tcpSndNxt = tcpSndNxt tcp + 1
}
in (finAckOutSeg, tcp')
outputSegment seg
clearDelayedAck
rstAck :: TcpHeader -> Int -> Sock ()
rstAck hdr len = tcpOutput (mkRstAck hdr len) L.empty
rst :: TcpHeader -> Sock ()
rst hdr = tcpOutput (mkRst hdr) L.empty
outputSegment :: OutSegment -> Sock ()
outputSegment seg = do
clearDelayedAck
tcpOutput (outHeader seg) (outBody seg)
type SetFlag = TcpHeader -> Bool
type UnsetFlag = TcpHeader -> Bool
testFlags :: [SetFlag] -> [UnsetFlag] -> TcpHeader -> Bool
testFlags sfs ufs hdr = all test sfs && all (not . test) ufs
where
test prj = prj hdr
isSyn :: TcpHeader -> Bool
isSyn = testFlags [ tcpSyn ]
[ tcpCwr, tcpEce, tcpUrg, tcpAck, tcpPsh, tcpRst, tcpFin ]
isSynAck :: TcpHeader -> Bool
isSynAck = testFlags [ tcpSyn, tcpAck ]
[ tcpCwr, tcpEce, tcpUrg, tcpPsh, tcpRst, tcpFin ]
isRstAck :: TcpHeader -> Bool
isRstAck = testFlags [ tcpRst, tcpAck ]
[ tcpCwr, tcpEce, tcpUrg, tcpPsh, tcpSyn, tcpFin ]
isAck :: TcpHeader -> Bool
isAck = testFlags [ tcpAck ]
[ tcpCwr, tcpEce, tcpUrg, tcpPsh, tcpRst, tcpSyn, tcpFin ]
isFin :: TcpHeader -> Bool
isFin = testFlags [ tcpFin ]
[ tcpCwr, tcpEce, tcpUrg, tcpAck, tcpPsh, tcpRst, tcpSyn ]
isFinAck :: TcpHeader -> Bool
isFinAck = testFlags [ tcpFin, tcpAck ]
[ tcpCwr, tcpEce, tcpUrg, tcpPsh, tcpRst, tcpSyn ]
genSegments :: POSIXTime -> TcpSocket -> (([Wakeup],OutSegments),TcpSocket)
genSegments now tcp0 = loop [] Seq.empty tcp0
where
loop ws segs tcp
| rwAvailable (tcpOut tcp) <= 0 = result
| otherwise = fromMaybe result $ do
let len = nextSegSize tcp
(mbWakeup,body,bufOut) <- takeBytes len (tcpOutBuffer tcp)
let seg = mkOutSegment now (ttRTO (tcpTimers tcp)) (mkData tcp) body
tcp' = tcp { tcpSndNxt = outAckNum seg
, tcpOut = addSegment seg (tcpOut tcp)
, tcpOutBuffer = bufOut
}
return (loop (addWakeup mbWakeup) (segs Seq.|> seg) tcp')
where
addWakeup Nothing = ws
addWakeup (Just w) = w:ws
result = ((ws,segs),tcp)