Files
lneto/x/xnet/stack-retrying.go
T
TuteMthCD d5ea2efdf4 Retransmit active-open SYN after RTO (#130)
* added: sync packet retransmission

* test: sync packet retransmission

* refactor: remove packet time from tcppool

* fix: return comment

* refactor: move retrying to stackRetrying

* added: retries & timeout to stackGoConfig

* test: test retries stack

* refactor: move StackRetrying to stackBlocking

* fix: line gofmt

---------

Co-authored-by: Pat Whittingslow <graded.sp@gmail.com>
2026-06-23 00:19:16 -03:00

129 lines
3.1 KiB
Go

package xnet
import (
"errors"
"net/netip"
"time"
"github.com/soypat/lneto"
"github.com/soypat/lneto/tcp"
)
func (s *StackAsync) StackRetrying(stackProtoBackoff lneto.BackoffStrategy) StackRetrying {
if stackProtoBackoff == nil {
panic("nil backoff to StackRetrying")
}
return s.StackBlocking(stackProtoBackoff).StackRetrying()
}
func (s StackBlocking) StackRetrying() StackRetrying {
return StackRetrying{block: s}
}
var (
errRetriesExceeded = errors.New("cywnet: retries exceeded")
)
type StackRetrying struct {
block StackBlocking
}
func (s StackRetrying) DoDHCPv4(reqAddr [4]byte, timeout time.Duration, retries int) (results *DHCPResults, err error) {
expectEnd := time.Now().Add(timeout * time.Duration(retries))
for i := range retries {
if i > 0 {
println("Retrying DHCP")
}
results, err = s.block.DoDHCPv4(reqAddr, timeout)
if err == nil {
return results, nil
}
}
if time.Now().Before(expectEnd) {
return nil, err
}
return nil, errRetriesExceeded
}
func (s StackRetrying) DoNTP(ntpHost netip.Addr, timeout time.Duration, retries int) (offset time.Duration, err error) {
expectEnd := time.Now().Add(timeout * time.Duration(retries))
for i := range retries {
if i > 0 {
println("Retrying NTP")
}
offset, err = s.block.DoNTP(ntpHost, timeout)
if err == nil {
return offset, nil
}
}
if time.Now().Before(expectEnd) {
return -1, err
}
return -1, errRetriesExceeded
}
func (s StackRetrying) DoLookupIP(host string, timeout time.Duration, retries int) (addrs []netip.Addr, err error) {
if !s.block.async.dnssv.IsValid() {
return nil, errNoDNSServer
}
expectEnd := time.Now().Add(timeout * time.Duration(retries))
for range retries {
addrs, err = s.block.DoLookupIP(host, timeout)
if err == nil {
return addrs, nil
}
}
if time.Now().Before(expectEnd) {
return addrs, err
}
return nil, errRetriesExceeded
}
func (s StackRetrying) DoResolveHardwareAddress6(addr netip.Addr, timeout time.Duration, retries int) (hw [6]byte, err error) {
expectEnd := time.Now().Add(timeout * time.Duration(retries))
for range retries {
hw, err = s.block.DoResolveHardwareAddress6(addr, timeout)
if err == nil {
return hw, nil
}
}
if time.Now().Before(expectEnd) {
return hw, err
}
return hw, errRetriesExceeded
}
func (s StackRetrying) DoDialTCP(conn *tcp.Conn, localPort uint16, addrp netip.AddrPort, timeout time.Duration, retries int) (err error) {
expectEnd := time.Now().Add(timeout * time.Duration(retries))
var firstErr error
for i := range retries {
if i == 0 || conn.State().IsClosed() {
err = s.block.async.DialTCP(conn, localPort, addrp)
if err != nil {
if firstErr == nil {
firstErr = err
}
continue
}
} else if conn.IsAwaitingControl() {
conn.RequeueControl()
}
err = s.block.waitDialTCP(conn, timeout)
if err == nil {
return nil
} else if firstErr == nil {
firstErr = err
}
if !conn.IsAwaitingControl() {
conn.Abort()
}
}
if time.Now().Before(expectEnd) {
if err != firstErr {
return errors.Join(firstErr, err)
}
return err
}
return errRetriesExceeded
}