mirror of
https://github.com/soypat/lneto.git
synced 2026-07-26 10:38:47 +00:00
faae7ab076
* fix for #58 * add fixes for both retransmit and fuzz failure found * fix infinite challenge ack due to RST+SYN+FIN corrupt packet in SYNRCV state * fuzz: previous recovery ack path led to infinite transmit * fuzz: calculate CRCs when fuzzing to reduce search space to non-CRC error cases * add local fuzz corpus to tests * add more cases to fuzz corpus
121 lines
3.3 KiB
Go
121 lines
3.3 KiB
Go
package tcp
|
|
|
|
func (tcb *ControlBlock) rcvListen(seg Segment) (pending Flags, err error) {
|
|
switch {
|
|
case !seg.Flags.HasAll(FlagSYN):
|
|
err = errExpectedSYN
|
|
}
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
// Initialize all connection state:
|
|
tcb.resetSnd(tcb.snd.ISS, seg.WND)
|
|
tcb.resetRcv(tcb.rcv.WND, seg.SEQ)
|
|
|
|
// We must respond with SYN|ACK frame after receiving SYN in listen state (three way handshake).
|
|
tcb.pending[0] = synack
|
|
tcb._state = StateSynRcvd
|
|
return synack, nil
|
|
}
|
|
|
|
func (tcb *ControlBlock) rcvSynSent(seg Segment) (pending Flags, err error) {
|
|
hasSyn := seg.Flags.HasAny(FlagSYN)
|
|
hasAck := seg.Flags.HasAny(FlagACK)
|
|
switch {
|
|
case !hasSyn:
|
|
err = errExpectedSYN
|
|
|
|
case hasAck && seg.ACK != tcb.snd.UNA+1:
|
|
err = errBadSegack
|
|
}
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if hasAck {
|
|
tcb._state = StateEstablished
|
|
pending = FlagACK
|
|
tcb.resetRcv(tcb.rcv.WND, seg.SEQ)
|
|
} else {
|
|
// Simultaneous connection sync edge case.
|
|
pending = synack
|
|
tcb._state = StateSynRcvd
|
|
tcb.resetSnd(tcb.snd.ISS, seg.WND)
|
|
tcb.resetRcv(tcb.rcv.WND, seg.SEQ)
|
|
}
|
|
return pending, nil
|
|
}
|
|
|
|
func (tcb *ControlBlock) rcvSynRcvd(seg Segment) (pending Flags, err error) {
|
|
switch {
|
|
case !seg.Flags.HasAll(FlagACK):
|
|
// RFC 9293 §3.10.7.4 step 5: "If the ACK bit is off, drop the segment and return."
|
|
err = errBadSegack
|
|
case seg.ACK != tcb.snd.UNA+1:
|
|
err = errBadSegack
|
|
}
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
tcb._state = StateEstablished
|
|
return 0, nil
|
|
}
|
|
|
|
func (tcb *ControlBlock) rcvEstablished(seg Segment) (pending Flags, err error) {
|
|
flags := seg.Flags
|
|
dataToAck := seg.DATALEN > 0
|
|
hasFin := flags.HasAny(FlagFIN)
|
|
if dataToAck || hasFin {
|
|
pending = FlagACK
|
|
if hasFin {
|
|
// See Figure 5: TCP Connection State Diagram of RFC 9293.
|
|
tcb._state = StateCloseWait
|
|
// RFC 9293 §3.5: CLOSE-WAIT allows local side to continue sending.
|
|
// Do NOT auto-queue FIN here; user must call Close() explicitly.
|
|
}
|
|
}
|
|
|
|
return pending, nil
|
|
}
|
|
|
|
func (tcb *ControlBlock) rcvFinWait1(seg Segment) (pending Flags, err error) {
|
|
flags := seg.Flags
|
|
hasFin := flags&FlagFIN != 0
|
|
hasAck := flags&FlagACK != 0
|
|
switch {
|
|
case hasFin && hasAck && seg.ACK == tcb.snd.NXT:
|
|
// Special case: Server sent a FINACK response to our FIN so we enter TimeWait directly.
|
|
// We have to check ACK against send NXT to avoid simultaneous close sequence edge case.
|
|
tcb._state = StateTimeWait
|
|
case hasFin:
|
|
tcb._state = StateClosing
|
|
case hasAck && seg.ACK == tcb.snd.NXT:
|
|
// RFC 9293 §3.10.7.4 step 5: enter FIN-WAIT-2 only if the FIN is now acknowledged.
|
|
tcb._state = StateFinWait2
|
|
case hasAck:
|
|
// Partial ACK: acknowledges data but not the FIN. Stay in FIN-WAIT-1.
|
|
default:
|
|
return 0, errFinwaitExpectedACK
|
|
}
|
|
// Only queue ACK when there is data or FIN to acknowledge.
|
|
// Bare ACKs must not elicit an ACK response; doing so creates an
|
|
// infinite ACK ping-pong when the peer sends challenge ACKs.
|
|
if seg.DATALEN > 0 || hasFin {
|
|
pending = FlagACK
|
|
}
|
|
return pending, nil
|
|
}
|
|
|
|
func (tcb *ControlBlock) rcvFinWait2(seg Segment) (pending Flags, err error) {
|
|
if seg.Flags.HasAny(FlagFIN) {
|
|
tcb._state = StateTimeWait
|
|
return FlagACK, nil
|
|
}
|
|
// Bare ACKs and data are valid in FIN-WAIT-2 per RFC 9293 §3.10.7.4.
|
|
// The remote side hasn't closed yet; it may still send data.
|
|
if seg.DATALEN > 0 {
|
|
return FlagACK, nil
|
|
}
|
|
return 0, nil
|
|
}
|