mirror of
https://github.com/soypat/lneto.git
synced 2026-08-15 12:23:44 +00:00
remove old examples and update readme
This commit is contained in:
@@ -1,459 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto/arp"
|
||||
"github.com/soypat/lneto/dhcpv4"
|
||||
"github.com/soypat/lneto/dns"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/internal"
|
||||
"github.com/soypat/lneto/internal/ltesto"
|
||||
"github.com/soypat/lneto/internet"
|
||||
"github.com/soypat/lneto/internet/pcap"
|
||||
"github.com/soypat/lneto/ntp"
|
||||
)
|
||||
|
||||
var softRand = time.Now().Unix()
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("success")
|
||||
}
|
||||
|
||||
func run() (err error) {
|
||||
var (
|
||||
flagInterface = "tap0"
|
||||
flagUseHTTP = false
|
||||
flagHostToResolve = ""
|
||||
flagRequestedIP = ""
|
||||
flagDoNTP = false
|
||||
)
|
||||
flag.StringVar(&flagInterface, "i", flagInterface, "Interface to use. Either tap* or the name of an existing interface to bridge to.")
|
||||
flag.BoolVar(&flagUseHTTP, "http", flagUseHTTP, "Use HTTP tap interface.")
|
||||
flag.StringVar(&flagHostToResolve, "host", flagHostToResolve, "Hostname to resolve via DNS.")
|
||||
flag.StringVar(&flagRequestedIP, "addr", flagRequestedIP, "IP address to request via DHCP.")
|
||||
flag.BoolVar(&flagDoNTP, "ntp", flagDoNTP, "Do NTP round and print result time")
|
||||
flag.Parse()
|
||||
fmt.Println("softrand", softRand)
|
||||
_, err = dns.NewName(flagHostToResolve)
|
||||
if err != nil {
|
||||
flag.Usage()
|
||||
return err
|
||||
}
|
||||
var iface ltesto.Interface
|
||||
if flagUseHTTP {
|
||||
iface = ltesto.NewHTTPTapClient("http://127.0.0.1:7070")
|
||||
} else {
|
||||
if strings.HasPrefix(flagInterface, "tap") {
|
||||
tap, err := internal.NewTap(flagInterface, netip.MustParsePrefix("192.168.1.1/24"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iface = tap
|
||||
} else {
|
||||
bridge, err := internal.NewBridge(flagInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iface = bridge
|
||||
}
|
||||
}
|
||||
defer iface.Close()
|
||||
|
||||
nicHW, err := iface.HardwareAddress6()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
brHW := nicHW
|
||||
mtu, err := iface.MTU()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nicAddr, err := iface.IPMask()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("NIC hardware address:", net.HardwareAddr(nicHW[:]).String(), "bridgeHW:", net.HardwareAddr(brHW[:]).String(), "mtu:", mtu, "addr:", nicAddr.String())
|
||||
var stack Stack
|
||||
|
||||
err = stack.Reset(brHW, netip.AddrFrom4([4]byte{}), uint16(mtu))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := make([]byte, mtu)
|
||||
lastAction := time.Now()
|
||||
const (
|
||||
stateDHCP = iota
|
||||
stateInitARP
|
||||
stateDNSNTP
|
||||
stateNTP
|
||||
stateDNS
|
||||
stateDone
|
||||
)
|
||||
|
||||
err = stack.BeginDHCPRequest([4]byte{192, 168, 1, 96})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state := stateDHCP
|
||||
prevState := state
|
||||
for {
|
||||
switch state {
|
||||
case stateDHCP:
|
||||
dhcpIsDone := stack.dhcp.State() == dhcpv4.StateBound
|
||||
if dhcpIsDone {
|
||||
state = stateInitARP
|
||||
assigned4, ok := stack.dhcp.AssignedAddr()
|
||||
if !ok {
|
||||
return errors.New("DHCP client address not assigned")
|
||||
}
|
||||
err = stack.ip.SetAddr(netip.AddrFrom4(assigned4))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
routeraddr, ok := stack.dhcp.RouterAddr()
|
||||
if !ok {
|
||||
return errors.New("DHCP router address not assigned")
|
||||
}
|
||||
err = stack.StartResolveHardwareAddress6(netip.AddrFrom4(routeraddr))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case stateInitARP:
|
||||
router, ok := stack.dhcp.RouterAddr()
|
||||
if !ok {
|
||||
return errors.New("DHCP router address not assigned")
|
||||
}
|
||||
hw, err := stack.ResultResolveHardwareAddress6(netip.AddrFrom4(router))
|
||||
if err == nil {
|
||||
stack.link.SetGateway6(hw)
|
||||
if flagDoNTP {
|
||||
state = stateDNSNTP
|
||||
err = stack.StartLookupIP("pool.ntp.org")
|
||||
} else {
|
||||
state = stateDNS
|
||||
err = stack.StartLookupIP(flagHostToResolve)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case stateDNSNTP:
|
||||
addrs, done, err := stack.ResultLookupIP()
|
||||
if err == nil {
|
||||
state = stateNTP
|
||||
fmt.Println("START NTP")
|
||||
err = stack.StartNTP(addrs[0])
|
||||
} else if !done {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case stateNTP:
|
||||
offset, done := stack.ResultNTP()
|
||||
if done {
|
||||
relative := "behind"
|
||||
if offset < 0 {
|
||||
relative = "ahead"
|
||||
}
|
||||
fmt.Println("NTP completed. You are", offset.Abs(), relative, "of the NTP server")
|
||||
state = stateDNS
|
||||
err = stack.StartLookupIP(flagHostToResolve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case stateDNS:
|
||||
addrs, done, err := stack.ResultLookupIP()
|
||||
if err == nil {
|
||||
fmt.Println(flagHostToResolve, "resolved to", addrs)
|
||||
return nil
|
||||
} else if done {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if prevState != state {
|
||||
fmt.Println("STATE CHANGE", prevState, state)
|
||||
}
|
||||
prevState = state
|
||||
|
||||
clear(buf)
|
||||
nwrite, err := stack.Encapsulate(buf[:], -1, 0)
|
||||
if err != nil {
|
||||
fmt.Println("ERR:ENCAPSULATE", err)
|
||||
} else if nwrite > 0 {
|
||||
n, err := iface.Write(buf[:nwrite])
|
||||
if err != nil {
|
||||
return err
|
||||
} else if n != nwrite {
|
||||
return fmt.Errorf("mismatch written bytes %d!=%d", nwrite, n)
|
||||
}
|
||||
}
|
||||
|
||||
clear(buf)
|
||||
nread, err := iface.Read(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if nread > 0 {
|
||||
err = stack.Demux(buf[:nread], 0)
|
||||
if err != nil {
|
||||
fmt.Println("ERR:DEMUX", err)
|
||||
}
|
||||
}
|
||||
|
||||
if nread == 0 && nwrite == 0 && time.Since(lastAction) > 4*time.Second {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
} else {
|
||||
lastAction = time.Now()
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Stack struct {
|
||||
link internet.StackEthernet
|
||||
ip internet.StackIP
|
||||
arp arp.Handler
|
||||
udps internet.StackPorts
|
||||
dhcp dhcpv4.Client
|
||||
dns dns.Client
|
||||
ednsopt dns.Resource
|
||||
lookup dns.Message
|
||||
ntp ntp.Client
|
||||
sysprec int8 // NTP system precision.
|
||||
|
||||
// Packet capture and top level filtering.
|
||||
shark pcap.PacketBreakdown
|
||||
aux []pcap.Frame
|
||||
}
|
||||
|
||||
func (s *Stack) Demux(b []byte, _ int) (err error) {
|
||||
s.aux, err = s.shark.CaptureEthernet(s.aux[:0], b, 0)
|
||||
topFrame := s.aux[len(s.aux)-1]
|
||||
isOK := topFrame.Protocol == "DHCPv4" || // Allow DHCP, DNS and NTP responses.
|
||||
topFrame.Protocol == "DNS" ||
|
||||
topFrame.Protocol == "NTP" ||
|
||||
topFrame.Protocol == ethernet.TypeARP // Allow ARP responses.
|
||||
if !isOK {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println("IN", s.aux, err.Error())
|
||||
} else {
|
||||
fmt.Println("IN", s.aux)
|
||||
}
|
||||
return s.link.Demux(b, 0)
|
||||
}
|
||||
|
||||
func (s *Stack) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (int, error) {
|
||||
n, err := s.link.Encapsulate(carrierData, offsetToIP, offsetToFrame)
|
||||
if n > 0 {
|
||||
iframes, errpcap := s.shark.CaptureEthernet(s.aux[:0], carrierData[:n], 0)
|
||||
if errpcap != nil {
|
||||
fmt.Println("OU", iframes, errpcap.Error())
|
||||
} else {
|
||||
fmt.Println("OU", iframes)
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s *Stack) Reset(mac [6]byte, addr netip.Addr, mtu uint16) error {
|
||||
const maxNodes = 8
|
||||
err := s.link.Reset6(mac, ethernet.BroadcastAddr(), int(mtu), maxNodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.ip.Reset(addr, maxNodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipaddr := addr.AsSlice()
|
||||
proto := ethernet.TypeIPv4
|
||||
if addr.Is6() {
|
||||
proto = ethernet.TypeIPv6
|
||||
}
|
||||
err = s.arp.Reset(arp.HandlerConfig{
|
||||
HardwareAddr: mac[:],
|
||||
ProtocolAddr: ipaddr,
|
||||
MaxQueries: 3,
|
||||
MaxPending: 3,
|
||||
HardwareType: 1,
|
||||
ProtocolType: proto,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.udps.ResetUDP(maxNodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now setup stacks.
|
||||
err = s.link.Register(&s.arp) // ARP.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.link.Register(&s.ip) // IPv4 | IPv6
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.ip.Register(&s.udps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.ip.SetLogger(slog.Default())
|
||||
var timebuf [32]time.Time
|
||||
s.sysprec = ntp.CalculateSystemPrecision(time.Now, timebuf[:])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stack) StartLookupIP(host string) error {
|
||||
dnsSrvs := s.dhcp.DNSServerFirst()
|
||||
if !dnsSrvs.IsValid() {
|
||||
return errors.New("no valid DNS server")
|
||||
}
|
||||
name, err := dns.NewName(host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.ednsopt.SetEDNS0(uint16(s.link.MTU())-100, 0, 0, nil)
|
||||
err = s.dns.StartResolve(uint16(softRand>>1)+1024, uint16(softRand), dns.ResolveConfig{
|
||||
Questions: []dns.Question{
|
||||
{
|
||||
Name: name,
|
||||
Type: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
},
|
||||
},
|
||||
Additional: []dns.Resource{
|
||||
s.ednsopt,
|
||||
},
|
||||
EnableRecursion: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var u internet.StackUDPPort
|
||||
dns4 := dnsSrvs.As4()
|
||||
u.SetStackNode(&s.dns, dns4[:], dns.ServerPort)
|
||||
err = s.udps.Register(&u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("START LOOKUP", host, dns4[:])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stack) ResultLookupIP() ([]netip.Addr, bool, error) {
|
||||
done, err := s.dns.MessageCopyTo(&s.lookup)
|
||||
if err != nil {
|
||||
return nil, done, err
|
||||
} else if !done {
|
||||
return nil, done, errors.New("DNS not done")
|
||||
}
|
||||
var addrs []netip.Addr
|
||||
ans := s.lookup.Answers
|
||||
for i := range ans {
|
||||
data := ans[i].RawData()
|
||||
if len(data) == 4 {
|
||||
addrs = append(addrs, netip.AddrFrom4([4]byte(data)))
|
||||
} else if len(data) == 16 {
|
||||
addrs = append(addrs, netip.AddrFrom16([16]byte(data)))
|
||||
}
|
||||
}
|
||||
return addrs, done, nil
|
||||
}
|
||||
|
||||
func (s *Stack) ResultNTP() (time.Duration, bool) {
|
||||
return s.ntp.Offset(), s.ntp.IsDone()
|
||||
}
|
||||
|
||||
func (s *Stack) BeginDHCPRequest(request [4]byte) error {
|
||||
var buf [4]byte
|
||||
rand.Read(buf[:])
|
||||
xid := binary.LittleEndian.Uint32(buf[:])
|
||||
err := s.dhcp.BeginRequest(xid, dhcpv4.RequestConfig{
|
||||
RequestedAddr: request,
|
||||
ClientHardwareAddr: s.link.HardwareAddr6(),
|
||||
Hostname: "lneto",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var u internet.StackUDPPort
|
||||
u.SetStackNode(&s.dhcp, nil, dhcpv4.DefaultServerPort)
|
||||
err = s.udps.Register(&u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Stack) StartNTP(addr netip.Addr) error {
|
||||
s.ntp.Reset(s.sysprec, time.Now)
|
||||
var u internet.StackUDPPort
|
||||
addr4 := addr.As4()
|
||||
u.SetStackNode(&s.ntp, addr4[:], ntp.ServerPort)
|
||||
err := s.udps.Register(&u)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Stack) StartResolveHardwareAddress6(ip netip.Addr) error {
|
||||
if !ip.Is4() {
|
||||
return errors.New("unsupported or invalid IP address")
|
||||
}
|
||||
addr := ip.As4()
|
||||
return s.arp.StartQuery(nil, addr[:])
|
||||
}
|
||||
|
||||
func (s *Stack) ResultResolveHardwareAddress6(ip netip.Addr) (hw [6]byte, err error) {
|
||||
if !ip.Is4() {
|
||||
return hw, errors.New("unsupported or invalid IP address")
|
||||
}
|
||||
addr := ip.As4()
|
||||
hwslice, err := s.arp.QueryResult(addr[:])
|
||||
if err != nil {
|
||||
return hw, err
|
||||
} else if len(hwslice) != 6 {
|
||||
panic("unreachable slice hw length")
|
||||
}
|
||||
return [6]byte(hwslice), nil
|
||||
}
|
||||
|
||||
func clear(buf []byte) {
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
func getField(frame pcap.Frame, pkt []byte, class pcap.FieldClass) uint64 {
|
||||
idx, err := frame.FieldByClass(class)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
v, _ := frame.FieldAsUint(idx, pkt)
|
||||
return v
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/arp"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/http/httpraw"
|
||||
"github.com/soypat/lneto/internal/ltesto"
|
||||
"github.com/soypat/lneto/internet"
|
||||
"github.com/soypat/lneto/internet/pcap"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
)
|
||||
|
||||
const (
|
||||
stackIP = "192.168.10.2"
|
||||
stackPort = 80
|
||||
iss = 100
|
||||
)
|
||||
|
||||
var stackHWAddr = [6]byte{0xc0, 0xff, 0xee, 0x00, 0xde, 0xad}
|
||||
|
||||
func main() {
|
||||
ip := netip.MustParseAddr(stackIP)
|
||||
tap := ltesto.NewHTTPTapClient("http://127.0.0.1:7070")
|
||||
|
||||
ippfx, _ := tap.IPMask()
|
||||
if !ippfx.Contains(ip) {
|
||||
log.Fatal("interface does not contain stack address")
|
||||
}
|
||||
addrPort := netip.AddrPortFrom(ip, stackPort)
|
||||
lg := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
}))
|
||||
|
||||
gatewayMAC, _ := tap.HardwareAddress6()
|
||||
mtu, _ := tap.MTU()
|
||||
|
||||
var stack Stack
|
||||
err := stack.Reset(stackHWAddr, gatewayMAC, addrPort.Addr(), mtu)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
listener, err := stack.OpenTCPListener(addrPort.Port())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer tap.Close()
|
||||
tap.ReadDiscard() // Discard all unread content.
|
||||
fmt.Println("hosting server at ", addrPort.String(), "over tap interface of mtu:", mtu, "prefix:", ippfx, "gateway:", net.HardwareAddr(gatewayMAC[:]).String())
|
||||
buf := make([]byte, mtu)
|
||||
var hdr httpraw.Header
|
||||
hdr.Reset(make([]byte, 0, 1024))
|
||||
const standbyDuration = 5 * time.Second
|
||||
lastHit := time.Now().Add(-standbyDuration)
|
||||
var cap pcap.PacketBreakdown
|
||||
var conn *tcp.Conn
|
||||
accepted := 0
|
||||
for {
|
||||
nread, err := tap.Read(buf[:])
|
||||
if err != nil {
|
||||
lg.Error("tap-err", slog.String("err", err.Error()))
|
||||
log.Fatal(err)
|
||||
} else if nread > 0 {
|
||||
frames, err := cap.CaptureEthernet(nil, buf[:nread], 0)
|
||||
if err == nil {
|
||||
flags := getTCPFlags(frames, buf[:nread])
|
||||
if flags == 0 {
|
||||
fmt.Println("IN", time.Now().Format("15:04:05.000"), frames)
|
||||
} else {
|
||||
fmt.Println("IN", time.Now().Format("15:04:05.000"), frames, flags.String())
|
||||
}
|
||||
}
|
||||
err = stack.ethernet.Demux(buf[:nread], 0)
|
||||
if err != nil {
|
||||
lg.Error("recv", slog.String("err", err.Error()), slog.Int("plen", nread))
|
||||
}
|
||||
}
|
||||
if conn == nil && listener.NumberOfReadyToAccept() > 0 {
|
||||
conn, err = listener.TryAccept()
|
||||
if err != nil {
|
||||
lg.Error("tryaccept", slog.String("err", err.Error()))
|
||||
}
|
||||
accepted++
|
||||
hdr.Reset(nil)
|
||||
lg.Info("ACCEPT!")
|
||||
}
|
||||
if conn != nil {
|
||||
done, err := doHTTP(conn, &hdr)
|
||||
if done {
|
||||
lg.Info("close forever")
|
||||
conn.Close()
|
||||
conn = nil
|
||||
}
|
||||
if err != nil {
|
||||
lg.Error("doHTTP", slog.String("err", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
nw, err := stack.ethernet.Encapsulate(buf[:], -1, 0)
|
||||
if err != nil {
|
||||
lg.Error("handle", slog.String("err", err.Error()))
|
||||
} else if nw > 0 {
|
||||
frames, err := cap.CaptureEthernet(nil, buf[:nread], 0)
|
||||
if err == nil {
|
||||
flags := getTCPFlags(frames, buf[:nread])
|
||||
if flags == 0 {
|
||||
fmt.Println("OU", time.Now().Format("15:04:05.000"), frames)
|
||||
} else {
|
||||
fmt.Println("OU", time.Now().Format("15:04:05.000"), frames, flags.String())
|
||||
}
|
||||
}
|
||||
_, err = tap.Write(buf[:nw])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
hit := nread > 0 || nw > 0
|
||||
if hit {
|
||||
// slogger.info("exchange", slog.Int("read", nread), slog.Int("nwrite", nw))
|
||||
lastHit = time.Now()
|
||||
} else {
|
||||
if time.Since(lastHit) > standbyDuration {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
} else {
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doHTTP(conn *tcp.Conn, hdr *httpraw.Header) (done bool, err error) {
|
||||
const asRequest = false
|
||||
if conn.State() != tcp.StateEstablished || conn.BufferedInput() == 0 {
|
||||
return false, nil // No data yet.
|
||||
}
|
||||
fmt.Println("state is established; check request and send response")
|
||||
_, err = hdr.ReadFromLimited(conn, hdr.BufferFree())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
needMore, err := hdr.TryParse(asRequest)
|
||||
if needMore {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return true, err
|
||||
}
|
||||
// HTTP parsed succesfully!
|
||||
fmt.Println("GOT HTTP:\n", hdr.String())
|
||||
fmt.Println("sending response...")
|
||||
hdr.Reset(nil)
|
||||
hdr.SetStatus("200", "OK")
|
||||
data := `{"ok":true}`
|
||||
response, err := hdr.AppendResponse(nil)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
response = append(response, data...)
|
||||
_, err = conn.Write(response)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type Stack struct {
|
||||
ethernet internet.StackEthernet
|
||||
ip internet.StackIP
|
||||
tcpports internet.StackPorts
|
||||
arp arp.Handler
|
||||
}
|
||||
|
||||
func (stack *Stack) Reset(ourMAC, gwMAC [6]byte, ip netip.Addr, mtu int) (err error) {
|
||||
const maxNodes = 8
|
||||
err = stack.ethernet.Reset6(ourMAC, gwMAC, mtu, maxNodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = stack.ip.Reset(ip, maxNodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stack.tcpports.ResetTCP(maxNodes)
|
||||
ipaddr := ip.As4()
|
||||
err = stack.arp.Reset(arp.HandlerConfig{
|
||||
HardwareAddr: ourMAC[:],
|
||||
ProtocolAddr: ipaddr[:],
|
||||
MaxQueries: 2,
|
||||
MaxPending: 2,
|
||||
HardwareType: 1,
|
||||
ProtocolType: ethernet.TypeIPv4,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Register stacks and nodes.
|
||||
err = stack.ethernet.Register(&stack.arp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = stack.ethernet.Register(&stack.ip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = stack.ip.Register(&stack.tcpports)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stack *Stack) Recv(b []byte) error {
|
||||
return stack.ethernet.Demux(b, 0)
|
||||
}
|
||||
|
||||
func (stack *Stack) Send(b []byte) (int, error) {
|
||||
return stack.ethernet.Encapsulate(b, -1, 0)
|
||||
}
|
||||
|
||||
func (stack *Stack) OpenTCPListener(port uint16) (*tcp.Listener, error) {
|
||||
var listener tcp.Listener
|
||||
err := listener.Reset(port, naiveTCPPool{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = stack.tcpports.Register(&listener) // Passive TCP requires no MAC setting.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &listener, nil
|
||||
}
|
||||
|
||||
func (stack *Stack) OpenPassiveTCP(port uint16, iss tcp.Value) (*tcp.Conn, error) {
|
||||
mtu := stack.ethernet.MTU()
|
||||
conn := new(tcp.Conn)
|
||||
err := conn.Configure(tcp.ConnConfig{
|
||||
RxBuf: make([]byte, mtu),
|
||||
TxBuf: make([]byte, mtu),
|
||||
TxPacketQueueSize: 3,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = conn.OpenListen(port, iss)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = stack.tcpports.Register(conn) // Passive MAC with no listening.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
const tblhex = "0123456789abcdef"
|
||||
|
||||
func getTCPFlags(frames []pcap.Frame, pkt []byte) (flags tcp.Flags) {
|
||||
for i := range frames {
|
||||
if frames[i].Protocol != lneto.IPProtoTCP {
|
||||
continue
|
||||
}
|
||||
iflags, err := frames[i].FieldByClass(pcap.FieldClassFlags)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
v, err := frames[i].FieldAsUint(iflags, pkt)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return tcp.Flags(v)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type naiveTCPPool struct {
|
||||
}
|
||||
|
||||
func (naiveTCPPool) GetTCP() (*tcp.Conn, tcp.Value) {
|
||||
var buf [4]byte
|
||||
rand.Read(buf[:])
|
||||
randVal := binary.LittleEndian.Uint32(buf[:])
|
||||
var conn tcp.Conn
|
||||
err := conn.Configure(tcp.ConnConfig{
|
||||
RxBuf: make([]byte, 1024),
|
||||
TxBuf: make([]byte, 1024),
|
||||
TxPacketQueueSize: 3,
|
||||
Logger: slog.Default(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &conn, tcp.Value(randVal)
|
||||
}
|
||||
|
||||
func (naiveTCPPool) PutTCP(*tcp.Conn) {}
|
||||
@@ -1,412 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/arp"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/http/httpraw"
|
||||
"github.com/soypat/lneto/internal"
|
||||
"github.com/soypat/lneto/internal/ltesto"
|
||||
"github.com/soypat/lneto/internet"
|
||||
"github.com/soypat/lneto/internet/pcap"
|
||||
"github.com/soypat/lneto/ipv4"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
)
|
||||
|
||||
const (
|
||||
stackIP = "192.168.10.2"
|
||||
stackPort = 80
|
||||
iss = 100
|
||||
)
|
||||
|
||||
var stackHWAddr = [6]byte{0xc0, 0xff, 0xee, 0x00, 0xde, 0xad}
|
||||
|
||||
func main() {
|
||||
ip := netip.MustParseAddr(stackIP)
|
||||
tap := ltesto.NewHTTPTapClient("http://127.0.0.1:7070")
|
||||
|
||||
ippfx, _ := tap.IPMask()
|
||||
if !ippfx.Contains(ip) {
|
||||
log.Fatal("interface does not contain stack address")
|
||||
}
|
||||
addrPort := netip.AddrPortFrom(ip, stackPort)
|
||||
lg := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
}))
|
||||
|
||||
slogger := logger{lg}
|
||||
gatewayMAC, _ := tap.HardwareAddress6()
|
||||
mtu, _ := tap.MTU()
|
||||
lStack, handler, err := NewEthernetTCPStack(stackHWAddr, gatewayMAC, addrPort, uint16(mtu), slogger)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = handler.OpenListen(addrPort.Port(), iss)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer tap.Close()
|
||||
tap.ReadDiscard() // Discard all unread content.
|
||||
fmt.Println("hosting server at ", addrPort.String(), "over tap interface of mtu:", mtu, "prefix:", ippfx, "gateway:", net.HardwareAddr(gatewayMAC[:]).String())
|
||||
buf := make([]byte, mtu)
|
||||
var hdr httpraw.Header
|
||||
hdr.Reset(make([]byte, 0, 1024))
|
||||
const standbyDuration = 5 * time.Second
|
||||
lastHit := time.Now().Add(-standbyDuration)
|
||||
var cap pcap.PacketBreakdown
|
||||
for {
|
||||
nread, err := tap.Read(buf[:])
|
||||
if err != nil {
|
||||
slogger.error("tap-err", slog.String("err", err.Error()))
|
||||
log.Fatal(err)
|
||||
} else if nread > 0 {
|
||||
frames, err := cap.CaptureEthernet(nil, buf[:nread], 0)
|
||||
if err == nil {
|
||||
flags := getTCPFlags(frames, buf[:nread])
|
||||
if flags == 0 {
|
||||
fmt.Println("IN", time.Now().Format("15:04:05.000"), frames)
|
||||
} else {
|
||||
fmt.Println("IN", time.Now().Format("15:04:05.000"), frames, flags.String())
|
||||
}
|
||||
}
|
||||
err = lStack.RecvEth(buf[:nread])
|
||||
if err != nil {
|
||||
slogger.error("recv", slog.String("err", err.Error()), slog.Int("plen", nread))
|
||||
}
|
||||
}
|
||||
doHTTP(handler, &hdr)
|
||||
nw, err := lStack.HandleEth(buf[:])
|
||||
if err != nil {
|
||||
slogger.error("handle", slog.String("err", err.Error()))
|
||||
} else if nw > 0 {
|
||||
frames, err := cap.CaptureEthernet(nil, buf[:nread], 0)
|
||||
if err == nil {
|
||||
flags := getTCPFlags(frames, buf[:nread])
|
||||
if flags == 0 {
|
||||
fmt.Println("OU", time.Now().Format("15:04:05.000"), frames)
|
||||
} else {
|
||||
fmt.Println("OU", time.Now().Format("15:04:05.000"), frames, flags.String())
|
||||
}
|
||||
}
|
||||
_, err = tap.Write(buf[:nw])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
hit := nread > 0 || nw > 0
|
||||
if hit {
|
||||
// slogger.info("exchange", slog.Int("read", nread), slog.Int("nwrite", nw))
|
||||
lastHit = time.Now()
|
||||
} else {
|
||||
if time.Since(lastHit) > standbyDuration {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
} else {
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doHTTP(conn *tcp.Conn, hdr *httpraw.Header) error {
|
||||
const asRequest = false
|
||||
if conn.State() != tcp.StateEstablished || conn.BufferedInput() == 0 {
|
||||
return nil // No data yet.
|
||||
}
|
||||
fmt.Println("state is established; check request and send response")
|
||||
_, err := hdr.ReadFromLimited(conn, hdr.BufferFree())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needMore, err := hdr.TryParse(asRequest)
|
||||
if err != nil {
|
||||
if !needMore {
|
||||
fmt.Println("IT's SO GOVER")
|
||||
conn.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
// HTTP parsed succesfully!
|
||||
fmt.Println("GOT HTTP:\n", hdr.String())
|
||||
fmt.Println("sending response...")
|
||||
hdr.Reset(nil)
|
||||
hdr.SetStatus("200", "OK")
|
||||
data := `{"ok":true}`
|
||||
response, err := hdr.AppendResponse(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response = append(response, data...)
|
||||
_, err = conn.Write(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewEthernetTCPStack(ourMAC, gwMAC [6]byte, ip netip.AddrPort, mtu uint16, slogger logger) (*LinkStack, *tcp.Conn, error) {
|
||||
var err error
|
||||
lStack := LinkStack{
|
||||
logger: slogger,
|
||||
mac: ourMAC,
|
||||
mtu: mtu,
|
||||
gwmac: gwMAC,
|
||||
}
|
||||
|
||||
var ipStack internet.StackIP
|
||||
addr := ip.Addr()
|
||||
addr4 := addr.As4()
|
||||
_ = addr4
|
||||
ipStack.SetAddr(addr)
|
||||
lStack.Register(handler{
|
||||
raddr: nil, //addr4[:],
|
||||
recv: ipStack.Demux,
|
||||
handle: ipStack.Encapsulate,
|
||||
proto: ethernet.TypeIPv4,
|
||||
lport: 0,
|
||||
})
|
||||
var conn tcp.Conn
|
||||
err = conn.Configure(tcp.ConnConfig{
|
||||
RxBuf: make([]byte, mtu),
|
||||
TxBuf: make([]byte, mtu),
|
||||
TxPacketQueueSize: 3,
|
||||
Logger: slog.Default(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = conn.OpenListen(ip.Port(), 100)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = ipStack.Register(&conn)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
proto := ethernet.TypeIPv4
|
||||
if ip.Addr().Is6() {
|
||||
proto = ethernet.TypeIPv6
|
||||
}
|
||||
var narp arp.Handler
|
||||
err = narp.Reset(arp.HandlerConfig{
|
||||
HardwareAddr: ourMAC[:],
|
||||
ProtocolAddr: ip.Addr().AsSlice(),
|
||||
MaxQueries: 4,
|
||||
MaxPending: 4,
|
||||
HardwareType: 1,
|
||||
ProtocolType: proto,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
arpStack := ARPStack{
|
||||
handler: narp,
|
||||
}
|
||||
err = lStack.Register(handler{
|
||||
recv: arpStack.Recv,
|
||||
handle: arpStack.Handle,
|
||||
proto: ethernet.TypeARP,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &lStack, &conn, nil
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
raddr []byte
|
||||
recv func([]byte, int) error
|
||||
handle func([]byte, int, int) (int, error)
|
||||
proto ethernet.Type
|
||||
lport uint16
|
||||
}
|
||||
|
||||
type LinkStack struct {
|
||||
handlers []handler
|
||||
logger
|
||||
mac [6]byte
|
||||
gwmac [6]byte
|
||||
mtu uint16
|
||||
}
|
||||
|
||||
func (ls *LinkStack) Register(h handler) error {
|
||||
proto := h.proto
|
||||
for i := range ls.handlers {
|
||||
if proto == ls.handlers[i].proto {
|
||||
return errors.New("protocol already registered")
|
||||
}
|
||||
}
|
||||
ls.handlers = append(ls.handlers, h)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ls *LinkStack) RecvEth(ethFrame []byte) (err error) {
|
||||
efrm, err := ethernet.NewFrame(ethFrame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
etype := efrm.EtherTypeOrSize()
|
||||
dstaddr := efrm.DestinationHardwareAddr()
|
||||
var vld lneto.Validator
|
||||
if !efrm.IsBroadcast() && ls.mac != *dstaddr {
|
||||
goto DROP
|
||||
}
|
||||
efrm.ValidateSize(&vld)
|
||||
if err := vld.ErrPop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range ls.handlers {
|
||||
h := &ls.handlers[i]
|
||||
if h.proto == etype {
|
||||
return h.recv(efrm.Payload(), 0)
|
||||
}
|
||||
}
|
||||
DROP:
|
||||
ls.info("LinkStack:drop-packet", slog.String("dsthw", net.HardwareAddr(dstaddr[:]).String()), slog.String("ethertype", efrm.EtherTypeOrSize().String()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ls *LinkStack) HandleEth(dst []byte) (n int, err error) {
|
||||
mtu := ls.mtu
|
||||
if len(dst) < int(mtu) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
efrm, err := ethernet.NewFrame(dst)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(efrm.DestinationHardwareAddr()[:], ls.gwmac[:]) // default set the gateway.
|
||||
for i := range ls.handlers {
|
||||
h := &ls.handlers[i]
|
||||
n, err = h.handle(dst[:mtu], 14, 14)
|
||||
if err != nil {
|
||||
ls.error("handling", slog.String("proto", ethernet.Type(h.proto).String()), slog.String("err", err.Error()))
|
||||
continue
|
||||
}
|
||||
if n > 0 {
|
||||
// Found packet
|
||||
*efrm.SourceHardwareAddr() = ls.mac
|
||||
efrm.SetEtherType(ethernet.Type(h.proto))
|
||||
return n + 14, nil
|
||||
}
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
type ARPStack struct {
|
||||
handler arp.Handler
|
||||
}
|
||||
|
||||
func (as *ARPStack) Protocol() uint32 { return uint32(ethernet.TypeARP) }
|
||||
|
||||
func (as *ARPStack) Recv(EtherFrame []byte, arpOff int) error {
|
||||
afrm, _ := arp.NewFrame(EtherFrame[arpOff:])
|
||||
slog.Info("recv", slog.String("in", afrm.String()))
|
||||
return as.handler.Demux(EtherFrame, arpOff)
|
||||
}
|
||||
|
||||
func (as *ARPStack) Handle(EtherFrame []byte, offsetToIP, arpOff int) (int, error) {
|
||||
n, err := as.handler.Encapsulate(EtherFrame, offsetToIP, arpOff)
|
||||
if err != nil || n == 0 {
|
||||
return 0, err
|
||||
}
|
||||
afrm, _ := arp.NewFrame(EtherFrame[arpOff:])
|
||||
hwaddr, _ := afrm.Target()
|
||||
efrm, _ := ethernet.NewFrame(EtherFrame)
|
||||
copy(efrm.DestinationHardwareAddr()[:], hwaddr)
|
||||
slog.Info("handle", slog.String("out", afrm.String()))
|
||||
return n, err
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func (l logger) error(msg string, attrs ...slog.Attr) {
|
||||
internal.LogAttrs(l.log, slog.LevelError, msg, attrs...)
|
||||
}
|
||||
func (l logger) info(msg string, attrs ...slog.Attr) {
|
||||
internal.LogAttrs(l.log, slog.LevelInfo, msg, attrs...)
|
||||
}
|
||||
func (l logger) warn(msg string, attrs ...slog.Attr) {
|
||||
internal.LogAttrs(l.log, slog.LevelWarn, msg, attrs...)
|
||||
}
|
||||
func (l logger) debug(msg string, attrs ...slog.Attr) {
|
||||
internal.LogAttrs(l.log, slog.LevelDebug, msg, attrs...)
|
||||
}
|
||||
func (l logger) trace(msg string, attrs ...slog.Attr) {
|
||||
internal.LogAttrs(l.log, internal.LevelTrace, msg, attrs...)
|
||||
}
|
||||
|
||||
func debugEthPacket(logger *slog.Logger, prefix string, b []byte) {
|
||||
frm, err := ethernet.NewFrame(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if frm.EtherTypeOrSize() != ethernet.TypeIPv4 {
|
||||
return
|
||||
}
|
||||
ihdr, err := ipv4.NewFrame(frm.Payload())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ihdr.Protocol() != lneto.IPProtoTCP {
|
||||
return
|
||||
}
|
||||
thdr, err := tcp.NewFrame(ihdr.Payload())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(prefix, ihdr.String()+" TCP:"+thdr.String())
|
||||
payload := thdr.Payload()
|
||||
if len(payload) > 0 {
|
||||
fmt.Println("PAYLOAD:", string(payload))
|
||||
}
|
||||
}
|
||||
|
||||
func debugHex(b []byte) string {
|
||||
var d []byte
|
||||
for i := 0; i < len(b); i++ {
|
||||
c1 := tblhex[b[i]&0xf]
|
||||
c2 := tblhex[b[i]>>4]
|
||||
d = append(d, c2, c1, ' ')
|
||||
}
|
||||
return string(d)
|
||||
}
|
||||
|
||||
const tblhex = "0123456789abcdef"
|
||||
|
||||
func getTCPFlags(frames []pcap.Frame, pkt []byte) (flags tcp.Flags) {
|
||||
for i := range frames {
|
||||
if frames[i].Protocol != lneto.IPProtoTCP {
|
||||
continue
|
||||
}
|
||||
iflags, err := frames[i].FieldByClass(pcap.FieldClassFlags)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
v, err := frames[i].FieldAsUint(iflags, pkt)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return tcp.Flags(v)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -48,11 +48,9 @@ func run() (err error) {
|
||||
flagHostToResolve = ""
|
||||
flagRequestedIP = ""
|
||||
flagDoNTP = false
|
||||
flagHTTPGet = false
|
||||
flagNoPcap = false
|
||||
flagPprof = false
|
||||
)
|
||||
flag.BoolVar(&flagHTTPGet, "httpget", flagHTTPGet, "Do an HTTP GET request ")
|
||||
flag.StringVar(&flagInterface, "i", flagInterface, "Interface to use. Either tap* or the name of an existing interface to bridge to.")
|
||||
flag.BoolVar(&flagUseHTTP, "ihttp", flagUseHTTP, "Use HTTP tap interface.")
|
||||
flag.StringVar(&flagHostToResolve, "host", flagHostToResolve, "Hostname to resolve via DNS.")
|
||||
@@ -60,6 +58,10 @@ func run() (err error) {
|
||||
flag.BoolVar(&flagDoNTP, "ntp", flagDoNTP, "Do NTP round and print result time")
|
||||
flag.BoolVar(&flagNoPcap, "nopcap", flagNoPcap, "Disable pcap logging.")
|
||||
flag.BoolVar(&flagPprof, "pprof", flagPprof, "Enable CPU profiling.")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "xcurl is a curl-like command line utility to test lneto's networking features.\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
if flagPprof {
|
||||
var b bytes.Buffer
|
||||
@@ -69,12 +71,12 @@ func run() (err error) {
|
||||
os.WriteFile("xnet.pprof", b.Bytes(), 0777)
|
||||
}()
|
||||
}
|
||||
fmt.Println("softrand", softRand)
|
||||
_, err = dns.NewName(flagHostToResolve)
|
||||
if err != nil {
|
||||
flag.Usage()
|
||||
return err
|
||||
}
|
||||
fmt.Println("softrand", softRand)
|
||||
var iface ltesto.Interface
|
||||
if flagUseHTTP {
|
||||
iface = ltesto.NewHTTPTapClient("http://127.0.0.1:7070")
|
||||
@@ -272,65 +274,65 @@ func run() (err error) {
|
||||
TxBuf: make([]byte, mtu),
|
||||
TxPacketQueueSize: 3,
|
||||
})
|
||||
if flagHTTPGet {
|
||||
timeHTTPCreate := timer("create HTTP GET request")
|
||||
var hdr httpraw.Header
|
||||
hdr.SetMethod("GET")
|
||||
hdr.SetRequestURI("/")
|
||||
hdr.SetProtocol("HTTP/1.1")
|
||||
hdr.Set("Host", flagHostToResolve)
|
||||
hdr.Set("User-Agent", "lneto")
|
||||
hdr.Set("Accept-Language", "en-US,en;q=0.5")
|
||||
hdr.Set("Connection", "close") // Encourage server to close connection after it finishes sending response.
|
||||
req, err := hdr.AppendRequest(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeHTTPCreate()
|
||||
|
||||
timeTCPDial := timer("TCP dial (handshake)")
|
||||
const tcpDialTimeout = 8 * time.Second // Was 60 * time.Minute causing 3.6s sleep per iteration!
|
||||
target := netip.AddrPortFrom(addrs[0], 80)
|
||||
err = rstack.DoDialTCP(&conn, uint16(softRand&0xefff)+1024, target, tcpDialTimeout, internetRetries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("TCP failed: %w", err)
|
||||
}
|
||||
timeTCPDial()
|
||||
timeHTTPSend := timer("send HTTP request")
|
||||
conn.SetDeadline(time.Now().Add(internetTimeout))
|
||||
_, err = conn.Write(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = conn.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeHTTPSend()
|
||||
timeHTTPRcv := timer("recv http request")
|
||||
|
||||
rxbuf := make([]byte, 2048)
|
||||
|
||||
var page []byte
|
||||
for {
|
||||
var n int
|
||||
n, err = conn.Read(rxbuf)
|
||||
page = append(page, rxbuf[:n]...)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
ptrimmed := bytes.TrimSpace(page)
|
||||
if bytes.EqualFold(ptrimmed[len(ptrimmed)-7:], []byte("</html>")) {
|
||||
// We've received the last part of the HTML. we're done.
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(page) == 0 {
|
||||
return err
|
||||
}
|
||||
timeHTTPRcv()
|
||||
os.Stdout.Write(page)
|
||||
timeHTTPCreate := timer("create HTTP GET request")
|
||||
var hdr httpraw.Header
|
||||
hdr.SetMethod("GET")
|
||||
hdr.SetRequestURI("/")
|
||||
hdr.SetProtocol("HTTP/1.1")
|
||||
hdr.Set("Host", flagHostToResolve)
|
||||
hdr.Set("User-Agent", "lneto")
|
||||
hdr.Set("Accept-Language", "en-US,en;q=0.5")
|
||||
hdr.Set("Connection", "close") // Encourage server to close connection after it finishes sending response.
|
||||
req, err := hdr.AppendRequest(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeHTTPCreate()
|
||||
|
||||
timeTCPDial := timer("TCP dial (handshake)")
|
||||
const tcpDialTimeout = 8 * time.Second // Was 60 * time.Minute causing 3.6s sleep per iteration!
|
||||
target := netip.AddrPortFrom(addrs[0], 80)
|
||||
err = rstack.DoDialTCP(&conn, uint16(softRand&0xefff)+1024, target, tcpDialTimeout, internetRetries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("TCP failed: %w", err)
|
||||
}
|
||||
timeTCPDial()
|
||||
timeHTTPSend := timer("send HTTP request")
|
||||
conn.SetDeadline(time.Now().Add(internetTimeout))
|
||||
_, err = conn.Write(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = conn.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeHTTPSend()
|
||||
timeHTTPRcv := timer("recv http request")
|
||||
|
||||
rxbuf := make([]byte, 2048)
|
||||
|
||||
var page []byte
|
||||
for {
|
||||
var n int
|
||||
n, err = conn.Read(rxbuf)
|
||||
page = append(page, rxbuf[:n]...)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
ptrimmed := bytes.TrimSpace(page)
|
||||
if bytes.EqualFold(ptrimmed[len(ptrimmed)-7:], []byte("</html>")) {
|
||||
// We've received the last part of the HTML. we're done.
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(page) == 0 {
|
||||
return err
|
||||
}
|
||||
timeHTTPRcv()
|
||||
os.Stdout.Write(page)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user