Files
x/internal/net/net.go
T
ginuerzh 0e96f602fa fix(matcher,plugin): case-insensitive matching, port accumulation, nil guards, doc comments
matcher: fix IPv6 normalization via netip.ParseAddr, port-range overwrite
by switching to []*PortRange map, case-insensitive domain/host matching,
glob.MustCompile→Compile to avoid panic on invalid patterns.

plugin: guard nil *Options in NewGRPCConn and NewHTTPClient; add
HTTPClientTransport helper for idle-connection cleanup.

net/*: add doc comments for all exported symbols (no code changes).

Add 30 tests (matcher_test.go 22, plugin_test.go 8) covering all
matcher types, nil safety, case insensitivity, IPv6 normalization,
port ranges, and plugin option composition.
2026-05-24 18:18:42 +08:00

150 lines
3.6 KiB
Go

package net
import (
"context"
"fmt"
"io"
"net"
"runtime"
"strings"
"syscall"
xio "github.com/go-gost/x/internal/io"
"github.com/vishvananda/netns"
)
// SetBuffer is a connection that supports setting send and receive buffer sizes.
type SetBuffer interface {
SetReadBuffer(bytes int) error
SetWriteBuffer(bytes int) error
}
// SyscallConn is a connection that exposes its raw system file descriptor.
type SyscallConn interface {
SyscallConn() (syscall.RawConn, error)
}
// RemoteAddr is a connection that exposes its remote address.
type RemoteAddr interface {
RemoteAddr() net.Addr
}
// SetDSCP is a connection that supports setting the DSCP field.
type SetDSCP interface {
SetDSCP(int) error
}
// IsIPv4 reports whether address is an IPv4 address by checking whether
// it starts with ':' or '[' (IPv6) vs a digit (IPv4).
func IsIPv4(address string) bool {
return address != "" && address[0] != ':' && address[0] != '['
}
// ListenConfig extends net.ListenConfig with network namespace support.
type ListenConfig struct {
Netns string
net.ListenConfig
}
// Listen announces on the local network address, switching into the configured
// network namespace first if one is set.
func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (net.Listener, error) {
if lc.Netns != "" {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
originNs, err := netns.Get()
if err != nil {
return nil, fmt.Errorf("netns.Get(): %v", err)
}
defer netns.Set(originNs)
var ns netns.NsHandle
if strings.HasPrefix(lc.Netns, "/") {
ns, err = netns.GetFromPath(lc.Netns)
} else {
ns, err = netns.GetFromName(lc.Netns)
}
if err != nil {
return nil, fmt.Errorf("netns.Get(%s): %v", lc.Netns, err)
}
defer ns.Close()
if err := netns.Set(ns); err != nil {
return nil, fmt.Errorf("netns.Set(%s): %v", lc.Netns, err)
}
}
return lc.ListenConfig.Listen(ctx, network, address)
}
// ListenPacket announces on the local network address for packet connections,
// switching into the configured network namespace first if one is set.
func (lc *ListenConfig) ListenPacket(ctx context.Context, network, address string) (net.PacketConn, error) {
if lc.Netns != "" {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
originNs, err := netns.Get()
if err != nil {
return nil, fmt.Errorf("netns.Get(): %v", err)
}
defer netns.Set(originNs)
var ns netns.NsHandle
if strings.HasPrefix(lc.Netns, "/") {
ns, err = netns.GetFromPath(lc.Netns)
} else {
ns, err = netns.GetFromName(lc.Netns)
}
if err != nil {
return nil, fmt.Errorf("netns.Get(%s): %v", lc.Netns, err)
}
defer ns.Close()
if err := netns.Set(ns); err != nil {
return nil, fmt.Errorf("netns.Set(%s): %v", lc.Netns, err)
}
}
return lc.ListenConfig.ListenPacket(ctx, network, address)
}
type readWriteConn struct {
net.Conn
r io.Reader
w io.Writer
}
// NewReadWriteConn returns a net.Conn that reads from r and writes to w,
// delegating all other operations to c. If c supports CloseRead or CloseWrite,
// those are forwarded; otherwise they return ErrUnsupported.
func NewReadWriteConn(r io.Reader, w io.Writer, c net.Conn) net.Conn {
return &readWriteConn{
Conn: c,
r: r,
w: w,
}
}
func (c *readWriteConn) Read(p []byte) (int, error) {
return c.r.Read(p)
}
func (c *readWriteConn) Write(p []byte) (int, error) {
return c.w.Write(p)
}
func (c *readWriteConn) CloseRead() error {
if sc, ok := c.Conn.(xio.CloseRead); ok {
return sc.CloseRead()
}
return xio.ErrUnsupported
}
func (c *readWriteConn) CloseWrite() error {
if sc, ok := c.Conn.(xio.CloseWrite); ok {
return sc.CloseWrite()
}
return xio.ErrUnsupported
}