fix(starnet): 重构请求执行链路并补齐代理/重试/trace边界
- 分离 Request 的配置态与执行态,修复二次 Do、raw 模式网络配置失效和 body 来源互斥问题 - 新增 starnet trace 抽象,补齐 DNS/连接/TLS/重试事件,并优化动态 transport 缓存与代理解析路径 - 收紧非法代理为 fail-fast,多目标目标回退仅限幂等请求,修复 Host/TLS/SNI 等语义边界 - 补充防御性拷贝、专项回归测试、本地代理/TLS 用例与 README 行为说明
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
package pingcore
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const icmpHeaderLen = 8
|
||||
|
||||
type ICMP struct {
|
||||
Type uint8
|
||||
Code uint8
|
||||
CheckSum uint16
|
||||
Identifier uint16
|
||||
SequenceNum uint16
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Count int
|
||||
Timeout time.Duration
|
||||
Interval time.Duration
|
||||
Deadline time.Time
|
||||
PreferIPv4 bool
|
||||
PreferIPv6 bool
|
||||
SourceIP net.IP
|
||||
PayloadSize int
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Duration time.Duration
|
||||
RecvCount int
|
||||
RemoteIP string
|
||||
}
|
||||
|
||||
var identifierSeed uint32
|
||||
|
||||
func NextIdentifier() uint16 {
|
||||
pid := uint32(os.Getpid() & 0xffff)
|
||||
n := atomic.AddUint32(&identifierSeed, 1)
|
||||
return uint16((pid + n) & 0xffff)
|
||||
}
|
||||
|
||||
func Payload(size int) []byte {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
payload := make([]byte, size)
|
||||
for index := 0; index < len(payload); index++ {
|
||||
payload[index] = byte(index)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
func BuildICMP(seq, identifier uint16, typ uint8, payload []byte) ICMP {
|
||||
icmp := ICMP{
|
||||
Type: typ,
|
||||
Code: 0,
|
||||
CheckSum: 0,
|
||||
Identifier: identifier,
|
||||
SequenceNum: seq,
|
||||
}
|
||||
buf := MarshalPacket(icmp, payload)
|
||||
icmp.CheckSum = Checksum(buf)
|
||||
return icmp
|
||||
}
|
||||
|
||||
func Checksum(data []byte) uint16 {
|
||||
var (
|
||||
sum uint32
|
||||
length = len(data)
|
||||
index int
|
||||
)
|
||||
for length > 1 {
|
||||
sum += uint32(data[index])<<8 + uint32(data[index+1])
|
||||
index += 2
|
||||
length -= 2
|
||||
}
|
||||
if length > 0 {
|
||||
sum += uint32(data[index]) << 8
|
||||
}
|
||||
for sum>>16 != 0 {
|
||||
sum = (sum & 0xffff) + (sum >> 16)
|
||||
}
|
||||
return uint16(^sum)
|
||||
}
|
||||
|
||||
func Marshal(icmp ICMP) []byte {
|
||||
return MarshalPacket(icmp, nil)
|
||||
}
|
||||
|
||||
func MarshalPacket(icmp ICMP, payload []byte) []byte {
|
||||
buf := make([]byte, icmpHeaderLen+len(payload))
|
||||
buf[0] = icmp.Type
|
||||
buf[1] = icmp.Code
|
||||
binary.BigEndian.PutUint16(buf[2:], icmp.CheckSum)
|
||||
binary.BigEndian.PutUint16(buf[4:], icmp.Identifier)
|
||||
binary.BigEndian.PutUint16(buf[6:], icmp.SequenceNum)
|
||||
copy(buf[icmpHeaderLen:], payload)
|
||||
return buf
|
||||
}
|
||||
|
||||
func IsExpectedEchoReply(packet []byte, family int, expectedType uint8, identifier, seq uint16) bool {
|
||||
for _, offset := range CandidateICMPOffsets(packet, family) {
|
||||
if offset < 0 || offset+icmpHeaderLen > len(packet) {
|
||||
continue
|
||||
}
|
||||
if packet[offset] != expectedType || packet[offset+1] != 0 {
|
||||
continue
|
||||
}
|
||||
if binary.BigEndian.Uint16(packet[offset+4:offset+6]) != identifier {
|
||||
continue
|
||||
}
|
||||
if binary.BigEndian.Uint16(packet[offset+6:offset+8]) != seq {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CandidateICMPOffsets(packet []byte, family int) []int {
|
||||
offsets := []int{0}
|
||||
if len(packet) == 0 {
|
||||
return offsets
|
||||
}
|
||||
|
||||
version := packet[0] >> 4
|
||||
if version == 4 && len(packet) >= 20 {
|
||||
ihl := int(packet[0]&0x0f) * 4
|
||||
if ihl >= 20 && ihl <= len(packet)-icmpHeaderLen {
|
||||
offsets = append(offsets, ihl)
|
||||
}
|
||||
} else if version == 6 && len(packet) >= 40+icmpHeaderLen {
|
||||
offsets = append(offsets, 40)
|
||||
}
|
||||
|
||||
if family == 4 && len(packet) >= 20+icmpHeaderLen {
|
||||
offsets = append(offsets, 20)
|
||||
}
|
||||
if family == 6 && len(packet) >= 40+icmpHeaderLen {
|
||||
offsets = append(offsets, 40)
|
||||
}
|
||||
|
||||
return DedupOffsets(offsets)
|
||||
}
|
||||
|
||||
func DedupOffsets(offsets []int) []int {
|
||||
if len(offsets) <= 1 {
|
||||
return offsets
|
||||
}
|
||||
seen := make(map[int]struct{}, len(offsets))
|
||||
out := make([]int, 0, len(offsets))
|
||||
for _, offset := range offsets {
|
||||
if _, ok := seen[offset]; ok {
|
||||
continue
|
||||
}
|
||||
seen[offset] = struct{}{}
|
||||
out = append(out, offset)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func ResolveTargets(host string, preferIPv4, preferIPv6 bool) ([]*net.IPAddr, error) {
|
||||
if parsed := net.ParseIP(host); parsed != nil {
|
||||
return []*net.IPAddr{{IP: parsed}}, nil
|
||||
}
|
||||
|
||||
var targets []*net.IPAddr
|
||||
var err4 error
|
||||
var err6 error
|
||||
|
||||
if ip4, err := net.ResolveIPAddr("ip4", host); err == nil && ip4 != nil && ip4.IP != nil {
|
||||
targets = append(targets, ip4)
|
||||
} else {
|
||||
err4 = err
|
||||
}
|
||||
|
||||
if ip6, err := net.ResolveIPAddr("ip6", host); err == nil && ip6 != nil && ip6.IP != nil {
|
||||
targets = append(targets, ip6)
|
||||
} else {
|
||||
err6 = err
|
||||
}
|
||||
|
||||
if len(targets) > 0 {
|
||||
return OrderTargets(targets, preferIPv4, preferIPv6), nil
|
||||
}
|
||||
if err4 != nil {
|
||||
return nil, err4
|
||||
}
|
||||
if err6 != nil {
|
||||
return nil, err6
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func OrderTargets(targets []*net.IPAddr, preferIPv4, preferIPv6 bool) []*net.IPAddr {
|
||||
if len(targets) <= 1 || preferIPv4 == preferIPv6 {
|
||||
return targets
|
||||
}
|
||||
|
||||
ordered := make([]*net.IPAddr, 0, len(targets))
|
||||
if preferIPv4 {
|
||||
for _, target := range targets {
|
||||
if target != nil && target.IP != nil && target.IP.To4() != nil {
|
||||
ordered = append(ordered, target)
|
||||
}
|
||||
}
|
||||
for _, target := range targets {
|
||||
if target != nil && target.IP != nil && target.IP.To4() == nil {
|
||||
ordered = append(ordered, target)
|
||||
}
|
||||
}
|
||||
return ordered
|
||||
}
|
||||
|
||||
for _, target := range targets {
|
||||
if target != nil && target.IP != nil && target.IP.To4() == nil {
|
||||
ordered = append(ordered, target)
|
||||
}
|
||||
}
|
||||
for _, target := range targets {
|
||||
if target != nil && target.IP != nil && target.IP.To4() != nil {
|
||||
ordered = append(ordered, target)
|
||||
}
|
||||
}
|
||||
return ordered
|
||||
}
|
||||
Reference in New Issue
Block a user