mirror of
https://github.com/soypat/lneto.git
synced 2026-08-12 02:43:44 +00:00
ec44889896
* begin adding icmp client * rely on anon structs * add tests * tests passing * icmp fleshed out * rework icmp to include ip addr * rename StackAsync.Demux/Encapsulate to RecvEthernet and SendEthernet * remove legacy unreachable TCP tests * remove uses of deprecated internet.StackNode in preference of lneto.StackNode * documentation * rename methods to signal no I/O happening
173 lines
3.6 KiB
Go
173 lines
3.6 KiB
Go
package xnet
|
|
|
|
import (
|
|
"net/netip"
|
|
"testing"
|
|
|
|
"github.com/soypat/lneto"
|
|
"github.com/soypat/lneto/ethernet"
|
|
"github.com/soypat/lneto/http/httpraw"
|
|
"github.com/soypat/lneto/ipv4"
|
|
"github.com/soypat/lneto/tcp"
|
|
)
|
|
|
|
func FuzzStackAsyncHTTP(f *testing.F) {
|
|
const MTU = 1500
|
|
const seed = 1
|
|
var buf [MTU + ethernet.MaxOverheadSize]byte
|
|
s1, s2, c1, c2 := newTCPStacks(f, seed, MTU)
|
|
var hdr httpraw.Header
|
|
err := s1.ListenTCP(c1, 80)
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
err = s2.DialTCP(c2, 1337, netip.AddrPortFrom(s1.Addr(), c1.LocalPort()))
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
hdr.SetMethod("GET")
|
|
hdr.SetProtocol("HTTP/1.1")
|
|
hdr.SetRequestURI("/")
|
|
data := hdr.AppendHeaders(nil)
|
|
|
|
pktnum := 0
|
|
written := false
|
|
closed := false
|
|
for {
|
|
n1, err := s1.EgressEthernet(buf[:])
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
if n1 > 0 {
|
|
err = s2.IngressEthernet(buf[:n1])
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
f.Add(pktnum, buf[:n1])
|
|
pktnum++
|
|
if !written && c2.State() >= tcp.StateEstablished {
|
|
_, err = c2.Write(data)
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
written = true
|
|
}
|
|
}
|
|
n2, err := s2.EgressEthernet(buf[:])
|
|
if n2 > 0 {
|
|
pktnum++
|
|
err = s1.IngressEthernet(buf[:n2])
|
|
if err != nil {
|
|
f.Fatal(err)
|
|
}
|
|
f.Add(pktnum, buf[:n2])
|
|
}
|
|
if n1 == 0 && n2 == 0 {
|
|
if !closed {
|
|
c2.Close()
|
|
closed = true
|
|
continue
|
|
}
|
|
break // No more data to send
|
|
}
|
|
}
|
|
|
|
f.Fuzz(func(t *testing.T, pktnum int, a []byte) {
|
|
var buf [MTU + ethernet.MaxOverheadSize]byte
|
|
s1, s2, c1, c2 := newTCPStacks(t, seed, MTU)
|
|
err := s1.ListenTCP(c1, 80)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = s2.DialTCP(c2, 1337, netip.AddrPortFrom(s1.Addr(), c1.LocalPort()))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pkt := 0
|
|
written := false
|
|
closed := false
|
|
const maxpkts = 100
|
|
for {
|
|
n1, err := s1.EgressEthernet(buf[:])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n1 > 0 {
|
|
if pkt == pktnum {
|
|
n1 = copy(buf[:], a)
|
|
fixIPTCPCRCs(buf[:n1])
|
|
}
|
|
s2.IngressEthernet(buf[:n1])
|
|
pkt++
|
|
if !written && c2.State() >= tcp.StateEstablished {
|
|
c2.Write(data)
|
|
written = true
|
|
}
|
|
}
|
|
n2, err := s2.EgressEthernet(buf[:])
|
|
if n2 > 0 {
|
|
if pkt == pktnum {
|
|
n2 = copy(buf[:], a)
|
|
fixIPTCPCRCs(buf[:n2])
|
|
}
|
|
pkt++
|
|
s1.IngressEthernet(buf[:n2])
|
|
}
|
|
if n1 == 0 && n2 == 0 {
|
|
if !closed {
|
|
if c1.BufferedInput() > 0 {
|
|
var hdr httpraw.Header
|
|
n, _ := c1.Read(buf[:])
|
|
hdr.ReadFromBytes(buf[:n])
|
|
hdr.TryParse(false)
|
|
}
|
|
c2.Close()
|
|
closed = true
|
|
continue
|
|
}
|
|
break // No more data to send
|
|
}
|
|
if pkt > maxpkts {
|
|
panic("infinite retransmission loop")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// fixIPTCPCRCs corrects CRCs of IP and TCP headers so that
|
|
// fuzzed packets are not discarded 99.9999% of the time.
|
|
func fixIPTCPCRCs(pkt []byte) (fixable bool) {
|
|
efrm, err := ethernet.NewFrame(pkt)
|
|
if err != nil || efrm.EtherTypeOrSize() != ethernet.TypeIPv4 {
|
|
return false
|
|
}
|
|
ifrm, err := ipv4.NewFrame(efrm.Payload())
|
|
if err != nil {
|
|
return false
|
|
}
|
|
v, ihl := ifrm.VersionAndIHL()
|
|
tl := ifrm.TotalLength()
|
|
if v != 4 || ihl < 5 || tl < uint16(ihl)*4 || int(tl) > len(pkt) {
|
|
return false // Invalid frame
|
|
}
|
|
var crc lneto.CRC791
|
|
ifrm.SetCRC(0)
|
|
ifrm.CRCWriteHeader(&crc)
|
|
ifrm.SetCRC(crc.Sum16())
|
|
if ifrm.Protocol() != lneto.IPProtoTCP {
|
|
return false
|
|
}
|
|
IPpayload := ifrm.Payload()
|
|
tfrm, err := tcp.NewFrame(IPpayload)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
crc.Reset()
|
|
ifrm.CRCWriteTCPPseudo(&crc)
|
|
// Zero the CRC field so its value does not add to the final result.
|
|
tfrm.SetCRC(0)
|
|
crcValue := crc.PayloadSum16(IPpayload)
|
|
tfrm.SetCRC(crcValue)
|
|
return true
|
|
}
|